View Javadoc

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