001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.security.keystore;
018    
019    import java.io.BufferedInputStream;
020    import java.io.BufferedOutputStream;
021    import java.io.ByteArrayInputStream;
022    import java.io.ByteArrayOutputStream;
023    import java.io.File;
024    import java.io.FileInputStream;
025    import java.io.FileNotFoundException;
026    import java.io.FileOutputStream;
027    import java.io.IOException;
028    import java.io.InputStream;
029    import java.math.BigInteger;
030    import java.net.URI;
031    import java.security.InvalidKeyException;
032    import java.security.KeyPair;
033    import java.security.KeyPairGenerator;
034    import java.security.KeyStore;
035    import java.security.KeyStoreException;
036    import java.security.NoSuchAlgorithmException;
037    import java.security.NoSuchProviderException;
038    import java.security.PrivateKey;
039    import java.security.PublicKey;
040    import java.security.SignatureException;
041    import java.security.UnrecoverableKeyException;
042    import java.security.cert.Certificate;
043    import java.security.cert.CertificateEncodingException;
044    import java.security.cert.CertificateException;
045    import java.security.cert.CertificateFactory;
046    import java.security.cert.X509Certificate;
047    import java.util.ArrayList;
048    import java.util.Collection;
049    import java.util.Date;
050    import java.util.Enumeration;
051    import java.util.HashMap;
052    import java.util.Hashtable;
053    import java.util.Iterator;
054    import java.util.List;
055    import java.util.Map;
056    import java.util.Vector;
057    import javax.net.ssl.KeyManager;
058    import javax.net.ssl.KeyManagerFactory;
059    import javax.net.ssl.TrustManager;
060    import javax.net.ssl.TrustManagerFactory;
061    import org.apache.commons.logging.Log;
062    import org.apache.commons.logging.LogFactory;
063    import org.apache.geronimo.gbean.GBeanInfo;
064    import org.apache.geronimo.gbean.GBeanInfoBuilder;
065    import org.apache.geronimo.gbean.GBeanLifecycle;
066    import org.apache.geronimo.gbean.AbstractName;
067    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
068    import org.apache.geronimo.kernel.Kernel;
069    import org.apache.geronimo.management.geronimo.KeyNotFoundException;
070    import org.apache.geronimo.management.geronimo.KeystoreException;
071    import org.apache.geronimo.management.geronimo.KeystoreInstance;
072    import org.apache.geronimo.management.geronimo.KeystoreIsLocked;
073    import org.apache.geronimo.system.serverinfo.ServerInfo;
074    import org.apache.geronimo.util.asn1.ASN1InputStream;
075    import org.apache.geronimo.util.asn1.ASN1Sequence;
076    import org.apache.geronimo.util.asn1.ASN1Set;
077    import org.apache.geronimo.util.asn1.DEROutputStream;
078    import org.apache.geronimo.util.asn1.x509.X509CertificateStructure;
079    import org.apache.geronimo.util.asn1.x509.X509Name;
080    import org.apache.geronimo.util.encoders.Base64;
081    import org.apache.geronimo.util.jce.PKCS10CertificationRequest;
082    import org.apache.geronimo.util.jce.X509Principal;
083    import org.apache.geronimo.util.jce.X509V1CertificateGenerator;
084    
085    /**
086     * Implementation of KeystoreInstance that accesses a keystore file on the
087     * local filesystem, identified by the file's name (the last component of
088     * the name only, not the full path).
089     *
090     * @version $Rev: 550523 $ $Date: 2007-06-25 11:02:09 -0400 (Mon, 25 Jun 2007) $
091     */
092    public class FileKeystoreInstance implements KeystoreInstance, GBeanLifecycle {
093        private static final Log log = LogFactory.getLog(FileKeystoreInstance.class);
094        final static String JKS = "JKS";
095        private URI keystorePath; // relative path
096        private ServerInfo serverInfo; // used to decode relative path
097        private File keystoreFile; // Only valid after startup
098        private String keystoreName;
099        private char[] keystorePassword; // Used to "unlock" the keystore for other services
100        private Map keyPasswords = new HashMap();
101        private Kernel kernel;
102        private AbstractName abstractName;
103        private char[] openPassword; // The password last used to open the keystore for editing
104        // The following variables are the state of the keystore, which should be chucked if the file on disk changes
105        private List privateKeys = new ArrayList();
106        private List trustCerts = new ArrayList();
107        private KeyStore keystore;
108        private long keystoreReadDate = Long.MIN_VALUE;
109    
110        public FileKeystoreInstance(ServerInfo serverInfo, URI keystorePath, String keystoreName, String keystorePassword, String keyPasswords, Kernel kernel, AbstractName abstractName) {
111            this.serverInfo = serverInfo;
112            this.keystorePath = keystorePath;
113            this.keystoreName = keystoreName;
114            this.kernel = kernel;
115            this.abstractName = abstractName;
116            this.keystorePassword = keystorePassword == null ? null : keystorePassword.toCharArray();
117            if(keyPasswords != null) {
118                String[] keys = keyPasswords.split("\\]\\!\\[");
119                for (int i = 0; i < keys.length; i++) {
120                    String key = keys[i];
121                    int pos = key.indexOf('=');
122                    this.keyPasswords.put(key.substring(0, pos), key.substring(pos+1).toCharArray());
123                }
124            }
125        }
126    
127        public void doStart() throws Exception {
128            keystoreFile = new File(serverInfo.resolveServer(keystorePath));
129            if(!keystoreFile.exists() || !keystoreFile.canRead()) {
130                throw new IllegalArgumentException("Invalid keystore file ("+keystorePath+" = "+keystoreFile.getAbsolutePath()+")");
131            }
132        }
133    
134        public void doStop() throws Exception {
135        }
136    
137        public void doFail() {
138        }
139    
140        public static final GBeanInfo GBEAN_INFO;
141    
142        static {
143            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(FileKeystoreInstance.class, NameFactory.KEYSTORE_INSTANCE);
144            infoFactory.addAttribute("keystorePath", URI.class, true, false);
145            infoFactory.addAttribute("keystoreName", String.class, true, false);
146            infoFactory.addAttribute("keystorePassword", String.class, true, true);
147            infoFactory.addAttribute("keyPasswords", String.class, true, true);
148            infoFactory.addAttribute("kernel", Kernel.class, false);
149            infoFactory.addAttribute("abstractName", AbstractName.class, false);
150            infoFactory.addReference("ServerInfo", ServerInfo.class, NameFactory.GERONIMO_SERVICE);
151            infoFactory.addInterface(KeystoreInstance.class);
152            infoFactory.setConstructor(new String[]{"ServerInfo","keystorePath", "keystoreName", "keystorePassword", "keyPasswords", "kernel", "abstractName"});
153    
154            GBEAN_INFO = infoFactory.getBeanInfo();
155        }
156    
157        public static GBeanInfo getGBeanInfo() {
158            return GBEAN_INFO;
159        }
160    
161    
162        // KeystoreInstnace interface
163        
164        public String getKeystoreName() {
165            return keystoreName;
166        }
167    
168        public void unlockKeystore(char[] password) throws KeystoreException {
169            if (password == null) {
170                throw new NullPointerException("password is null");
171            }
172            ensureLoaded(password);
173            try {
174                kernel.setAttribute(abstractName, "keystorePassword", new String(password));
175            } catch (Exception e) {
176                throw new KeystoreException("Unable to set attribute keystorePassword on myself!", e);
177            }
178        }
179    
180        public void setKeystorePassword(String password) {
181            keystorePassword = password == null ? null : password.toCharArray();
182        }
183    
184        public void lockKeystore(char[] password) throws KeystoreException {
185            try {
186                kernel.setAttribute(abstractName, "keystorePassword", null);
187                keyPasswords.clear();
188                storePasswords();
189            } catch (Exception e) {
190                throw new KeystoreException("Unable to set attribute keystorePassword on myself!", e);
191            }
192        }
193    
194        public boolean isKeystoreLocked() {
195            return keystorePassword == null;
196        }
197    
198        public String[] listPrivateKeys(char[] storePassword) throws KeystoreException {
199            ensureLoaded(storePassword);
200            return (String[]) privateKeys.toArray(new String[privateKeys.size()]);
201        }
202    
203        public void unlockPrivateKey(String alias, char[] storePassword, char[] password) throws KeystoreException {
204            if (storePassword == null && keystorePassword == null) {
205                throw new KeystoreException("storePassword is null and keystore is locked for availability.");
206            }
207            if(storePassword != null)
208                getPrivateKey(alias, storePassword, password);
209            else
210                getPrivateKey(alias, keystorePassword, password);
211            keyPasswords.put(alias, password);
212            storePasswords();
213        }
214    
215        public String[] getUnlockedKeys(char[] storePassword) throws KeystoreException {
216            ensureLoaded(storePassword);
217            return (String[]) keyPasswords.keySet().toArray(new String[keyPasswords.size()]);
218        }
219    
220        public boolean isTrustStore(char[] storePassword) throws KeystoreException {
221            ensureLoaded(storePassword);
222            return trustCerts.size() > 0;
223        }
224    
225        public void lockPrivateKey(String alias, char[] storePassword) throws KeystoreException {
226            if (storePassword == null) {
227                throw new NullPointerException("storePassword is null");
228            }
229            ensureLoaded(storePassword);
230            keyPasswords.remove(alias);
231            storePasswords();
232        }
233    
234        private void storePasswords() throws KeystoreException {
235            StringBuffer buf = new StringBuffer();
236            for (Iterator it = keyPasswords.entrySet().iterator(); it.hasNext();) {
237                if(buf.length() > 0) {
238                    buf.append("]![");
239                }
240                Map.Entry entry = (Map.Entry) it.next();
241                buf.append(entry.getKey()).append("=").append((char[])entry.getValue());
242            }
243            try {
244                kernel.setAttribute(abstractName, "keyPasswords", buf.length() == 0 ? null : buf.toString());
245            } catch (Exception e) {
246                throw new KeystoreException("Unable to save key passwords in keystore '"+keystoreName+"'", e);
247            }
248        }
249    
250        public void setKeyPasswords(String passwords) {} // Just so the kernel sees the new value
251    
252        /**
253         * Checks whether the specified private key is locked, which is to say,
254         * available for other components to use to generate socket factories.
255         * Does not check whether the unlock password is actually correct.
256         */
257        public boolean isKeyLocked(String alias) {
258            return keyPasswords.get(alias) == null;
259        }
260    
261        public String[] listTrustCertificates(char[] storePassword) throws KeystoreException {
262            ensureLoaded(storePassword);
263            return (String[]) trustCerts.toArray(new String[trustCerts.size()]);
264        }
265    
266        public void importTrustCertificate(Certificate cert, String alias, char[] storePassword) throws KeystoreException {
267            if (storePassword == null) {
268                throw new NullPointerException("storePassword is null");
269            }
270            ensureLoaded(storePassword);
271            try {
272                keystore.setCertificateEntry(alias, cert);
273            } catch (KeyStoreException e) {
274                throw new KeystoreException("Unable to set certificate entry in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
275            }
276            trustCerts.add(alias);
277            saveKeystore(storePassword);
278        }
279    
280        public void generateKeyPair(String alias, char[] storePassword, char[] keyPassword, String keyAlgorithm, int keySize, String signatureAlgorithm, int validity, String commonName, String orgUnit, String organization, String locality, String state, String country) throws KeystoreException {
281            if (storePassword == null) {
282                throw new NullPointerException("storePassword is null");
283            }
284            ensureLoaded(storePassword);
285            try {
286                KeyPairGenerator kpgen = KeyPairGenerator.getInstance(keyAlgorithm);
287                kpgen.initialize(keySize);
288                KeyPair keyPair = kpgen.generateKeyPair();
289                X509Certificate cert = generateCertificate(keyPair.getPublic(), keyPair.getPrivate(), signatureAlgorithm,
290                        validity, commonName, orgUnit, organization, locality, state, country);
291        
292                keystore.setKeyEntry(alias, keyPair.getPrivate(), keyPassword, new Certificate[] { cert });
293                privateKeys.add(alias);
294            } catch (KeyStoreException e) {
295                throw new KeystoreException("Unable to generate key pair in keystore '" + keystoreName + "'", e);
296            } catch (InvalidKeyException e) {
297                throw new KeystoreException("Unable to generate key pair in keystore '" + keystoreName + "'", e);
298            } catch (SignatureException e) {
299                throw new KeystoreException("Unable to generate key pair in keystore '" + keystoreName + "'", e);
300            } catch (NoSuchAlgorithmException e) {
301                throw new KeystoreException("Unable to generate key pair in keystore '" + keystoreName + "'", e);
302            }
303            saveKeystore(storePassword);
304        }
305    
306    
307        public String generateCSR(String alias, char[] storePassword) throws KeystoreException {
308            ensureLoaded(storePassword);
309            try {
310                // find certificate by alias
311                X509Certificate cert = (X509Certificate) keystore.getCertificate(alias);
312                // find private key by alias
313                PrivateKey key = (PrivateKey) keystore.getKey(alias, (char[])keyPasswords.get(alias));
314                // generate csr
315                String csr = generateCSR(cert, key);
316                return csr;
317            } catch (KeyStoreException e) {
318                throw new KeystoreException("Unable to generate CSR in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
319            } catch (NoSuchAlgorithmException e) {
320                throw new KeystoreException("Unable to generate CSR in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
321            } catch (UnrecoverableKeyException e) {
322                throw new KeystoreException("Unable to generate CSR in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
323            } catch (InvalidKeyException e) {
324                throw new KeystoreException("Unable to generate CSR in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
325            } catch (NoSuchProviderException e) {
326                throw new KeystoreException("Unable to generate CSR in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
327            } catch (SignatureException e) {
328                throw new KeystoreException("Unable to generate CSR in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
329            } catch (IOException e) {
330                throw new KeystoreException("Unable to generate CSR in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
331            }
332        }
333    
334        private String generateCSR(X509Certificate cert, PrivateKey signingKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException, KeyStoreException, IOException {
335            String sigalg = cert.getSigAlgName();
336            X509Name subject;
337            try{
338                ASN1InputStream ais = new ASN1InputStream(cert.getEncoded());
339                X509CertificateStructure x509Struct = new X509CertificateStructure((ASN1Sequence)ais.readObject());
340                ais.close();
341                subject = x509Struct.getSubject();
342            } catch(CertificateEncodingException e) {
343                log.warn(e.toString()+" while retrieving subject from certificate to create CSR.  Using subjectDN instead.");
344                subject = new X509Name(cert.getSubjectDN().toString());
345            }
346            PublicKey publicKey = cert.getPublicKey();
347            ASN1Set attributes = null;
348    
349            PKCS10CertificationRequest csr = new PKCS10CertificationRequest(sigalg,
350                    subject, publicKey, attributes, signingKey);
351    
352            if (!csr.verify()) {
353                throw new KeyStoreException("CSR verification failed");
354            }
355    
356            ByteArrayOutputStream os = new ByteArrayOutputStream();
357            DEROutputStream deros = new DEROutputStream(os);
358            deros.writeObject(csr.getDERObject());
359            String b64 = new String(Base64.encode(os.toByteArray()));
360    
361            final String BEGIN_CERT_REQ = "-----BEGIN CERTIFICATE REQUEST-----";
362            final String END_CERT_REQ = "-----END CERTIFICATE REQUEST-----";
363            final int CERT_REQ_LINE_LENGTH = 70;
364    
365            StringBuffer sbuf = new StringBuffer(BEGIN_CERT_REQ).append('\n');
366    
367            int idx = 0;
368            while (idx < b64.length()) {
369    
370                int len = (idx + CERT_REQ_LINE_LENGTH > b64.length()) ? b64
371                        .length()
372                        - idx : CERT_REQ_LINE_LENGTH;
373    
374                String chunk = b64.substring(idx, idx + len);
375    
376                sbuf.append(chunk).append('\n');
377                idx += len;
378            }
379    
380            sbuf.append(END_CERT_REQ);
381            return sbuf.toString();
382        }
383    
384        public void importPKCS7Certificate(String alias, String certbuf, char[] storePassword) throws KeystoreException {
385            if (storePassword == null) {
386                throw new NullPointerException("storePassword is null");
387            }
388            ensureLoaded(storePassword);
389            InputStream is = null;
390            try {
391                is = new ByteArrayInputStream(certbuf.getBytes());
392                CertificateFactory cf = CertificateFactory.getInstance("X.509");
393                Collection certcoll = cf.generateCertificates(is);
394                Certificate[] chain = new Certificate[certcoll.size()];
395                Iterator iter = certcoll.iterator();
396                for (int i = 0; iter.hasNext(); i++) {
397                    chain[i] = (Certificate) iter.next();
398                }
399                if(keystore.getCertificate(alias).getPublicKey().equals(chain[0].getPublicKey())) {
400                    char[] keyPassword = (char[])keyPasswords.get(alias);
401                    keystore.setKeyEntry(alias, keystore.getKey(alias, keyPassword), keyPassword, chain);
402                    saveKeystore(keystorePassword);
403                } else {
404                    log.error("Error in importPKCS7Certificate.  PublicKey in the certificate received is not related to the PrivateKey in the keystore. keystore = "+keystoreName+", alias = "+alias);
405                }
406            } catch (CertificateException e) {
407                throw new KeystoreException("Unable to import PKCS7 certificat in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
408            } catch (KeyStoreException e) {
409                throw new KeystoreException("Unable to import PKCS7 certificat in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
410            } catch (NoSuchAlgorithmException e) {
411                throw new KeystoreException("Unable to import PKCS7 certificat in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
412            } catch (UnrecoverableKeyException e) {
413                throw new KeystoreException("Unable to import PKCS7 certificat in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
414            } finally {
415                if (is != null) {
416                    try {
417                        is.close();
418                    } catch (Exception e) {
419                    }
420                }
421            }
422        }
423    
424        public void deleteEntry(String alias, char[] storePassword) throws KeystoreException {
425            if (storePassword == null) {
426                throw new NullPointerException("storePassword is null");
427            }
428            ensureLoaded(storePassword);
429            try {
430                keystore.deleteEntry(alias);
431            } catch (KeyStoreException e) {
432                throw new KeystoreException("Unable to delete key in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
433            }
434            privateKeys.remove(alias);
435            trustCerts.remove(alias);
436            if (keyPasswords.containsKey(alias)) {
437                keyPasswords.remove(alias);
438                storePasswords();
439            }
440            saveKeystore(storePassword);
441        }
442    
443        public KeyManager[] getKeyManager(String algorithm, String alias, char[] storePassword) throws KeystoreException {
444            ensureLoaded(storePassword);
445            try {
446                KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(algorithm);
447                if(privateKeys.size() == 1) {
448                    keyFactory.init(keystore, (char[]) keyPasswords.get(alias));
449                } else {
450                    // When there is more than one private key in the keystore, we create a temporary "sub keystore"
451                    // with only one entry of our interest and use it
452                    KeyStore subKeystore = KeyStore.getInstance(keystore.getType(), keystore.getProvider());
453                    try {
454                        subKeystore.load(null, null);
455                    } catch (NoSuchAlgorithmException e) {
456                        // should not occur
457                    } catch (CertificateException e) {
458                        // should not occur
459                    } catch (IOException e) {
460                        // should not occur
461                    }
462                    subKeystore.setKeyEntry(alias, keystore.getKey(alias, (char[]) keyPasswords.get(alias)),
463                                            (char[]) keyPasswords.get(alias), keystore.getCertificateChain(alias));
464                    keyFactory.init(subKeystore, (char[]) keyPasswords.get(alias));
465                }
466                return keyFactory.getKeyManagers();
467            } catch (KeyStoreException e) {
468                throw new KeystoreException("Unable to retrieve key manager in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
469            } catch (NoSuchAlgorithmException e) {
470                throw new KeystoreException("Unable to retrieve key manager in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
471            } catch (UnrecoverableKeyException e) {
472                throw new KeystoreException("Unable to retrieve key manager in keystore '" + keystoreName + "' for alias '" + alias + "'", e);
473            }
474        }
475    
476        public TrustManager[] getTrustManager(String algorithm, char[] storePassword) throws KeystoreException {
477            ensureLoaded(storePassword);
478            try {
479                TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(algorithm);
480                trustFactory.init(keystore);
481                return trustFactory.getTrustManagers();
482            } catch (KeyStoreException e) {
483                throw new KeystoreException("Unable to retrieve trust manager in keystore '" + keystoreName + "'", e);
484            } catch (NoSuchAlgorithmException e) {
485                throw new KeystoreException("Unable to retrieve trust manager in keystore '" + keystoreName + "'", e);
486            }
487        }
488    
489        /**
490         * Gets the private key with the specified alias.
491         * @param alias The alias of the private key to be retrieved
492         * @param storePassword The password used to access the keystore
493         * @param keyPassword The password to use to protect the new key
494         * @return PrivateKey with the alias specified
495         */
496        public PrivateKey getPrivateKey(String alias, char[] storePassword, char[] keyPassword)  throws KeyNotFoundException, KeystoreException, KeystoreIsLocked {
497            ensureLoaded(storePassword);
498            try {
499                PrivateKey key = (PrivateKey) keystore.getKey(alias, keyPassword);
500                if (key == null) {
501                    throw new KeyNotFoundException("Keystore '"+keystoreName+"' does not contain a private key with alias'"+alias+"'.");
502                }
503                return key;
504            } catch (KeyStoreException e) {
505                throw new KeystoreException("Unable to retrieve private key from keystore", e);
506            } catch (NoSuchAlgorithmException e) {
507                throw new KeystoreException("Unable to retrieve private key from keystore", e);
508            } catch (UnrecoverableKeyException e) {
509                throw new KeystoreException("Unable to retrieve private key from keystore", e);
510            }
511        }
512    
513        /**
514         * Gets a particular certificate from the keystore.  This may be a trust
515         * certificate or the certificate corresponding to a particular private
516         * key.
517         * This only works if the keystore is unlocked.
518         * @param alias The certificate to look at
519         * @throws KeyNotFoundException 
520         * @throws KeyStoreException 
521         */
522        public Certificate getCertificate(String alias, char[] storePassword) throws KeystoreIsLocked, KeyNotFoundException, KeystoreException {
523            ensureLoaded(storePassword);
524            try {
525                Certificate cert = keystore.getCertificate(alias);
526                if (cert == null) {
527                    throw new KeyNotFoundException("Keystore '"+keystoreName+"' does not contain a certificate with alias'"+alias+"'.");
528                }
529                return cert;
530            } catch (KeyStoreException e) {
531                throw new KeystoreException("Unable to retrieve certificate from keystore", e);
532            }
533        }
534    
535        public String getCertificateAlias(Certificate cert, char[] storePassword) throws KeystoreException {
536            ensureLoaded(storePassword);
537            try {
538                String alias = keystore.getCertificateAlias(cert);
539                if (alias == null) {
540                    throw new KeyNotFoundException("Keystore '"+keystoreName+"' does not contain an alias corresponding to the given certificate.");
541                }
542                return alias;
543            } catch (KeyStoreException e) {
544                throw new KeystoreException("Unable to read certificate alias from keystore", e);
545            }
546        }
547    
548        public Certificate[] getCertificateChain(String alias, char[] storePassword) throws KeystoreException {
549            ensureLoaded(storePassword);
550            try {
551                Certificate[] certs = keystore.getCertificateChain(alias);
552                if (certs == null) {
553                    throw new KeyNotFoundException("Keystore '"+keystoreName+"' does not contain a certificate chain with alias'"+alias+"'.");
554                }
555                return certs;
556            } catch (KeyStoreException e) {
557                throw new KeystoreException("Unable to read certificate chain from keystore", e);
558            }
559        }
560        
561        /**
562         * Gets a particular certificate from the keystore.  This may be a trust
563         * certificate or the certificate corresponding to a particular private
564         * key.
565         * This only works if the keystore is unlocked.
566         * @param alias The certificate to look at
567         */
568        public Certificate getCertificate(String alias) {
569            if(isKeystoreLocked()) {
570                return null;
571            }
572            try {
573                return keystore.getCertificate(alias);
574            } catch (KeyStoreException e) {
575                log.error("Unable to read certificate from keystore", e);
576            }
577            return null;
578        }
579    
580        // ==================== Internals =====================
581    
582        private void loadKeystoreData(char[] password) throws KeystoreException {
583            InputStream in = null;
584            try {
585                // Make sure the keystore is loadable using the provided password before resetting the instance variables.
586                KeyStore tempKeystore = KeyStore.getInstance(JKS);
587                in = new BufferedInputStream(new FileInputStream(keystoreFile));
588                long readDate = System.currentTimeMillis();
589                tempKeystore.load(in, password);
590                // Keystore could be loaded successfully.  Initialize the instance variables to reflect the new keystore.
591                keystore = tempKeystore;
592                keystoreReadDate = readDate;
593                privateKeys.clear();
594                trustCerts.clear();
595                openPassword = password;
596                Enumeration aliases = keystore.aliases();
597                while (aliases.hasMoreElements()) {
598                    String alias = (String) aliases.nextElement();
599                    if(keystore.isKeyEntry(alias)) {
600                        privateKeys.add(alias);
601                    } else if(keystore.isCertificateEntry(alias)) {
602                        trustCerts.add(alias);
603                    }
604                }
605            } catch (KeyStoreException e) {
606                throw new KeystoreException("Unable to open keystore with provided password", e);
607            } catch (IOException e) {
608                throw new KeystoreException("Unable to open keystore with provided password", e);
609            } catch (NoSuchAlgorithmException e) {
610                throw new KeystoreException("Unable to open keystore with provided password", e);
611            } catch (CertificateException e) {
612                throw new KeystoreException("Unable to open keystore with provided password", e);
613            } finally {
614                if(in != null) {
615                    try {
616                        in.close();
617                    } catch (IOException e) {
618                        log.error("Error while closing keystore file "+keystoreFile.getAbsolutePath(), e);
619                    }
620                }
621            }
622        }
623    
624        private boolean isLoaded(char[] password) {
625            if(openPassword == null || openPassword.length != password.length) {
626                return false;
627            }
628            if(keystoreReadDate < keystoreFile.lastModified()) {
629                return false;
630            }
631            for (int i = 0; i < password.length; i++) {
632                if(password[i] != openPassword[i]) {
633                    return false;
634                }
635            }
636            return true;
637        }
638    
639        private void ensureLoaded(char[] storePassword) throws KeystoreException {
640            char[] password;
641            if (storePassword == null) {
642                if (isKeystoreLocked()) {
643                    throw new KeystoreIsLocked("Keystore '"+keystoreName+"' is locked; please unlock it in the console.");
644                }
645                password = keystorePassword;
646            } else {
647                password = storePassword;
648            }
649            if (!isLoaded(password)) {
650                loadKeystoreData(password);
651            }
652        }
653        
654        private X509Certificate generateCertificate(PublicKey publicKey, PrivateKey privateKey, String algorithm, int validity, String commonName, String orgUnit, String organization, String locality, String state, String country) throws SignatureException, InvalidKeyException {
655            X509V1CertificateGenerator certgen = new X509V1CertificateGenerator();
656            Vector order = new Vector();
657            Hashtable attrmap = new Hashtable();
658    
659            if (commonName != null) {
660                attrmap.put(X509Principal.CN, commonName);
661                order.add(X509Principal.CN);
662            }
663    
664            if (orgUnit != null) {
665                attrmap.put(X509Principal.OU, orgUnit);
666                order.add(X509Principal.OU);
667            }
668    
669            if (organization != null) {
670                attrmap.put(X509Principal.O, organization);
671                order.add(X509Principal.O);
672            }
673    
674            if (locality != null) {
675                attrmap.put(X509Principal.L, locality);
676                order.add(X509Principal.L);
677            }
678    
679            if (state != null) {
680                attrmap.put(X509Principal.ST, state);
681                order.add(X509Principal.ST);
682            }
683    
684            if (country != null) {
685                attrmap.put(X509Principal.C, country);
686                order.add(X509Principal.C);
687            }
688    
689            X509Principal issuerDN = new X509Principal(order, attrmap);
690    
691            // validity
692            long curr = System.currentTimeMillis();
693            long untill = curr + (long) validity * 24 * 60 * 60 * 1000;
694    
695            certgen.setNotBefore(new Date(curr));
696            certgen.setNotAfter(new Date(untill));
697            certgen.setIssuerDN(issuerDN);
698            certgen.setSubjectDN(issuerDN);
699            certgen.setPublicKey(publicKey);
700            certgen.setSignatureAlgorithm(algorithm);
701            certgen.setSerialNumber(new BigInteger(String.valueOf(curr)));
702    
703            // make certificate
704            return certgen.generateX509Certificate(privateKey);
705        }
706    
707        private void saveKeystore(char[] password) throws KeystoreException {
708            try {
709                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(keystoreFile));
710                keystore.store(out, password);
711                out.flush();
712                out.close();
713                keystoreReadDate = System.currentTimeMillis();
714            } catch (KeyStoreException e) {
715                throw new KeystoreException("Unable to save keystore '" + keystoreName + "'", e);
716            } catch (FileNotFoundException e) {
717                throw new KeystoreException("Unable to save keystore '" + keystoreName + "'", e);
718            } catch (IOException e) {
719                throw new KeystoreException("Unable to save keystore '" + keystoreName + "'", e);
720            } catch (NoSuchAlgorithmException e) {
721                throw new KeystoreException("Unable to save keystore '" + keystoreName + "'", e);
722            } catch (CertificateException e) {
723                throw new KeystoreException("Unable to save keystore '" + keystoreName + "'", e);
724            }
725        }
726    
727    }