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 }