001/*
002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.15/src/java/org/apache/commons/ssl/KeyMaterial.java $
003 * $Revision: 138 $
004 * $Date: 2008-03-03 23:50:07 -0800 (Mon, 03 Mar 2008) $
005 *
006 * ====================================================================
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements.  See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership.  The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License.  You may obtain a copy of the License at
014 *
015 *   http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied.  See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 * ====================================================================
024 *
025 * This software consists of voluntary contributions made by many
026 * individuals on behalf of the Apache Software Foundation.  For more
027 * information on the Apache Software Foundation, please see
028 * <http://www.apache.org/>.
029 *
030 */
031
032package org.apache.commons.ssl;
033
034import java.io.File;
035import java.io.FileInputStream;
036import java.io.IOException;
037import java.io.InputStream;
038import java.net.URL;
039import java.security.GeneralSecurityException;
040import java.security.KeyStore;
041import java.security.KeyStoreException;
042import java.security.cert.Certificate;
043import java.security.cert.CertificateEncodingException;
044import java.security.cert.X509Certificate;
045import java.util.Collections;
046import java.util.Enumeration;
047import java.util.Iterator;
048import java.util.LinkedList;
049import java.util.List;
050
051/**
052 * @author Credit Union Central of British Columbia
053 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
054 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
055 * @since 27-Feb-2006
056 */
057public class KeyMaterial extends TrustMaterial {
058    private final Object keyManagerFactory;
059    private final List aliases;
060    private final List associatedChains;
061
062    public KeyMaterial(InputStream jks, char[] password)
063        throws GeneralSecurityException, IOException {
064        this(Util.streamToBytes(jks), password);
065    }
066
067    public KeyMaterial(InputStream jks, char[] jksPass, char[] keyPass)
068        throws GeneralSecurityException, IOException {
069        this(Util.streamToBytes(jks), jksPass, keyPass);
070    }
071
072    public KeyMaterial(InputStream jks, InputStream key, char[] password)
073        throws GeneralSecurityException, IOException {
074        this(jks != null ? Util.streamToBytes(jks) : null,
075            key != null ? Util.streamToBytes(key) : null,
076            password);
077    }
078
079    public KeyMaterial(InputStream jks, InputStream key, char[] jksPass,
080                       char[] keyPass)
081        throws GeneralSecurityException, IOException {
082        this(jks != null ? Util.streamToBytes(jks) : null,
083            key != null ? Util.streamToBytes(key) : null,
084            jksPass, keyPass);
085    }
086
087    public KeyMaterial(String pathToJksFile, char[] password)
088        throws GeneralSecurityException, IOException {
089        this(new File(pathToJksFile), password);
090    }
091
092    public KeyMaterial(String pathToJksFile, char[] jksPass, char[] keyPass)
093        throws GeneralSecurityException, IOException {
094        this(new File(pathToJksFile), jksPass, keyPass);
095    }
096
097    public KeyMaterial(String pathToCerts, String pathToKey, char[] password)
098        throws GeneralSecurityException, IOException {
099        this(pathToCerts != null ? new File(pathToCerts) : null,
100            pathToKey != null ? new File(pathToKey) : null,
101            password);
102    }
103
104    public KeyMaterial(String pathToCerts, String pathToKey, char[] jksPass,
105                       char[] keyPass)
106        throws GeneralSecurityException, IOException {
107        this(pathToCerts != null ? new File(pathToCerts) : null,
108            pathToKey != null ? new File(pathToKey) : null,
109            jksPass, keyPass);
110    }
111
112    public KeyMaterial(File jksFile, char[] password)
113        throws GeneralSecurityException, IOException {
114        this(new FileInputStream(jksFile), password);
115    }
116
117    public KeyMaterial(File jksFile, char[] jksPass, char[] keyPass)
118        throws GeneralSecurityException, IOException {
119        this(new FileInputStream(jksFile), jksPass, keyPass);
120    }
121
122    public KeyMaterial(File certsFile, File keyFile, char[] password)
123        throws GeneralSecurityException, IOException {
124        this(certsFile != null ? new FileInputStream(certsFile) : null,
125            keyFile != null ? new FileInputStream(keyFile) : null,
126            password);
127    }
128
129    public KeyMaterial(File certsFile, File keyFile, char[] jksPass,
130                       char[] keyPass)
131        throws GeneralSecurityException, IOException {
132        this(certsFile != null ? new FileInputStream(certsFile) : null,
133            keyFile != null ? new FileInputStream(keyFile) : null,
134            jksPass, keyPass);
135    }
136
137    public KeyMaterial(URL urlToJKS, char[] password)
138        throws GeneralSecurityException, IOException {
139        this(urlToJKS.openStream(), password);
140    }
141
142    public KeyMaterial(URL urlToJKS, char[] jksPass, char[] keyPass)
143        throws GeneralSecurityException, IOException {
144        this(urlToJKS.openStream(), jksPass, keyPass);
145    }
146
147    public KeyMaterial(URL urlToCerts, URL urlToKey, char[] password)
148        throws GeneralSecurityException, IOException {
149        this(urlToCerts.openStream(), urlToKey.openStream(), password);
150    }
151
152    public KeyMaterial(URL urlToCerts, URL urlToKey, char[] jksPass,
153                       char[] keyPass)
154        throws GeneralSecurityException, IOException {
155        this(urlToCerts.openStream(), urlToKey.openStream(), jksPass, keyPass);
156    }
157
158    public KeyMaterial(byte[] jks, char[] password)
159        throws GeneralSecurityException, IOException {
160        this(jks, (byte[]) null, password);
161    }
162
163    public KeyMaterial(byte[] jks, char[] jksPass, char[] keyPass)
164        throws GeneralSecurityException, IOException {
165        this(jks, null, jksPass, keyPass);
166    }
167
168    public KeyMaterial(byte[] jksOrCerts, byte[] key, char[] password)
169        throws GeneralSecurityException, IOException {
170        this(jksOrCerts, key, password, password);
171    }
172
173
174    public KeyMaterial(byte[] jksOrCerts, byte[] key, char[] jksPass,
175                       char[] keyPass)
176        throws GeneralSecurityException, IOException {
177        // We're not a simple trust type, so set "simpleTrustType" value to 0.
178        // Only TRUST_ALL and TRUST_THIS_JVM are simple trust types.
179        super(KeyStoreBuilder.build(jksOrCerts, key, jksPass, keyPass), 0);
180        KeyStore ks = getKeyStore();
181        Enumeration en = ks.aliases();
182        List myAliases = new LinkedList();
183        List myChains = new LinkedList();
184        while (en.hasMoreElements()) {
185            X509Certificate[] c; // chain
186            String alias = (String) en.nextElement();
187            if (ks.isKeyEntry(alias)) {
188                try {
189                    ks.getKey(alias, keyPass);
190                    // No Exception thrown, so we're good!
191                    myAliases.add(alias);
192                    Certificate[] chain = ks.getCertificateChain(alias);
193                    if (chain != null) {
194                        c = Certificates.x509ifyChain(chain);
195                        // Cleanup chain to remove any spurious entries.
196                        if (c != null) {
197                            X509Certificate l = c[0]; // The leaf node.
198                            c = X509CertificateChainBuilder.buildPath(l, c);
199                        }
200                        myChains.add(c);
201                    } else {
202                        throw new KeyStoreException("Could not find KeyMaterial's associated certificate chain with alis=[" + alias + "]");
203                    }
204
205                } catch (GeneralSecurityException gse) {
206                    // oh well, we can't use that KeyStore alias.
207                }
208            }
209        }
210        if (myAliases.isEmpty()) {
211            throw new KeyStoreException("KeyMaterial provided does not contain any keys!");
212        }
213        this.aliases = Collections.unmodifiableList(myAliases);
214        this.associatedChains = Collections.unmodifiableList(myChains);
215        this.keyManagerFactory = JavaImpl.newKeyManagerFactory(ks, keyPass);
216    }
217
218    public Object[] getKeyManagers() {
219        return JavaImpl.getKeyManagers(keyManagerFactory);
220    }
221
222    public List getAssociatedCertificateChains() {
223        return associatedChains;
224    }
225
226    public KeyStore getKeyStore() {
227        return super.getKeyStore();
228    }
229
230    public List getAliases() {
231        return aliases;
232    }
233
234    public static void main(String[] args) throws Exception {
235        if (args.length < 2) {
236            System.out.println("Usage1:  java org.apache.commons.ssl.KeyMaterial [password] [pkcs12 or jks]");
237            System.out.println("Usage2:  java org.apache.commons.ssl.KeyMaterial [password] [private-key] [cert-chain]");
238            System.exit(1);
239        }
240        char[] jksPass = args[0].toCharArray();
241        char[] keyPass = jksPass;
242        String path1 = args[1];
243        String path2 = null;
244        if (args.length >= 3) {
245            path2 = args[2];
246        }
247        if (args.length >= 4) {
248            keyPass = args[3].toCharArray();
249        } else if (path2 != null) {
250            File f = new File(path2);
251            if (!f.exists()) {
252                // Hmmm... maybe it's a password.
253                keyPass = path2.toCharArray();
254                path2 = null;
255            }
256        }
257
258        KeyMaterial km = new KeyMaterial(path1, path2, jksPass, keyPass);
259        System.out.println(km);
260    }
261
262    public String toString() {
263        List chains = getAssociatedCertificateChains();
264        List aliases = getAliases();
265        Iterator it = chains.iterator();
266        Iterator aliasesIt = aliases.iterator();
267        StringBuffer buf = new StringBuffer(8192);
268        while (it.hasNext()) {
269            X509Certificate[] certs = (X509Certificate[]) it.next();
270            String alias = (String) aliasesIt.next();
271            buf.append("Alias: ");
272            buf.append(alias);
273            buf.append('\n');
274            if (certs != null) {
275                for (int i = 0; i < certs.length; i++) {
276                    buf.append(Certificates.toString(certs[i]));
277                    try {
278                        buf.append(Certificates.toPEMString(certs[i]));
279                    }
280                    catch (CertificateEncodingException cee) {
281                        buf.append(cee.toString());
282                        buf.append('\n');
283                    }
284                }
285            }
286        }
287        return buf.toString();
288    }
289}