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 }