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