1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.java.security; 18 19 import com.android.org.bouncycastle.asn1.DEROctetString; 20 import com.android.org.bouncycastle.asn1.x509.BasicConstraints; 21 import com.android.org.bouncycastle.asn1.x509.ExtendedKeyUsage; 22 import com.android.org.bouncycastle.asn1.x509.GeneralName; 23 import com.android.org.bouncycastle.asn1.x509.GeneralNames; 24 import com.android.org.bouncycastle.asn1.x509.GeneralSubtree; 25 import com.android.org.bouncycastle.asn1.x509.KeyPurposeId; 26 import com.android.org.bouncycastle.asn1.x509.KeyUsage; 27 import com.android.org.bouncycastle.asn1.x509.NameConstraints; 28 import com.android.org.bouncycastle.asn1.x509.X509Extensions; 29 import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider; 30 import com.android.org.bouncycastle.x509.X509V3CertificateGenerator; 31 import java.io.ByteArrayInputStream; 32 import java.io.PrintStream; 33 import java.math.BigInteger; 34 import java.net.InetAddress; 35 import java.net.UnknownHostException; 36 import java.security.KeyFactory; 37 import java.security.KeyPair; 38 import java.security.KeyPairGenerator; 39 import java.security.KeyStore; 40 import java.security.KeyStore.PasswordProtection; 41 import java.security.KeyStore.PrivateKeyEntry; 42 import java.security.KeyStore.TrustedCertificateEntry; 43 import java.security.KeyStoreException; 44 import java.security.NoSuchAlgorithmException; 45 import java.security.Principal; 46 import java.security.PrivateKey; 47 import java.security.PublicKey; 48 import java.security.SecureRandom; 49 import java.security.Security; 50 import java.security.UnrecoverableKeyException; 51 import java.security.cert.Certificate; 52 import java.security.cert.CertificateFactory; 53 import java.security.cert.X509Certificate; 54 import java.security.interfaces.ECPrivateKey; 55 import java.security.spec.PKCS8EncodedKeySpec; 56 import java.util.ArrayList; 57 import java.util.Collections; 58 import java.util.Date; 59 import java.util.List; 60 import java.util.Vector; 61 import javax.net.ssl.KeyManager; 62 import javax.net.ssl.KeyManagerFactory; 63 import javax.net.ssl.TrustManager; 64 import javax.net.ssl.TrustManagerFactory; 65 import javax.security.auth.x500.X500Principal; 66 import junit.framework.Assert; 67 import libcore.javax.net.ssl.TestKeyManager; 68 import libcore.javax.net.ssl.TestTrustManager; 69 70 /** 71 * TestKeyStore is a convenience class for other tests that 72 * want a canned KeyStore with a variety of key pairs. 73 * 74 * Creating a key store is relatively slow, so a singleton instance is 75 * accessible via TestKeyStore.get(). 76 */ 77 public final class TestKeyStore extends Assert { 78 79 private static TestKeyStore ROOT_CA; 80 private static TestKeyStore INTERMEDIATE_CA; 81 82 private static TestKeyStore SERVER; 83 private static TestKeyStore CLIENT; 84 private static TestKeyStore CLIENT_CERTIFICATE; 85 86 private static TestKeyStore CLIENT_2; 87 88 static { 89 if (StandardNames.IS_RI) { 90 // Needed to create BKS keystore but add at end so most 91 // algorithm come from the default providers 92 Security.insertProviderAt(new BouncyCastleProvider(), 93 Security.getProviders().length+1); 94 } 95 } 96 97 private static final boolean TEST_MANAGERS = true; 98 99 public final KeyStore keyStore; 100 public final char[] storePassword; 101 public final char[] keyPassword; 102 public final KeyManager[] keyManagers; 103 public final TrustManager[] trustManagers; 104 public final TestKeyManager keyManager; 105 public final TestTrustManager trustManager; 106 107 private TestKeyStore(KeyStore keyStore, char[] storePassword, char[] keyPassword) { 108 this.keyStore = keyStore; 109 this.storePassword = storePassword; 110 this.keyPassword = keyPassword; 111 this.keyManagers = createKeyManagers(keyStore, storePassword); 112 this.trustManagers = createTrustManagers(keyStore); 113 this.keyManager = (TestKeyManager)keyManagers[0]; 114 this.trustManager = (TestTrustManager)trustManagers[0]; 115 } 116 117 public static KeyManager[] createKeyManagers(KeyStore keyStore, char[] storePassword) { 118 try { 119 String kmfa = KeyManagerFactory.getDefaultAlgorithm(); 120 KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa); 121 kmf.init(keyStore, storePassword); 122 return TestKeyManager.wrap(kmf.getKeyManagers()); 123 } catch (Exception e) { 124 throw new RuntimeException(e); 125 } 126 } 127 128 public static TrustManager[] createTrustManagers(final KeyStore keyStore) { 129 try { 130 String tmfa = TrustManagerFactory.getDefaultAlgorithm(); 131 TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa); 132 tmf.init(keyStore); 133 return TestTrustManager.wrap(tmf.getTrustManagers()); 134 } catch (Exception e) { 135 throw new RuntimeException(e); 136 } 137 } 138 139 /** 140 * Lazily create shared test certificates. 141 */ 142 private static synchronized void initCerts() { 143 if (ROOT_CA != null) { 144 return; 145 } 146 ROOT_CA = new Builder() 147 .aliasPrefix("RootCA") 148 .subject("CN=Test Root Certificate Authority") 149 .ca(true) 150 .build(); 151 INTERMEDIATE_CA = new Builder() 152 .aliasPrefix("IntermediateCA") 153 .subject("CN=Test Intermediate Certificate Authority") 154 .ca(true) 155 .signer(ROOT_CA.getPrivateKey("RSA", "RSA")) 156 .rootCa(ROOT_CA.getRootCertificate("RSA")) 157 .build(); 158 SERVER = new Builder() 159 .aliasPrefix("server") 160 .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA")) 161 .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA")) 162 .build(); 163 CLIENT = new TestKeyStore(createClient(INTERMEDIATE_CA.keyStore), null, null); 164 CLIENT_CERTIFICATE = new Builder() 165 .aliasPrefix("client") 166 .subject("emailAddress=test@user") 167 .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA")) 168 .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA")) 169 .build(); 170 TestKeyStore rootCa2 = new Builder() 171 .aliasPrefix("RootCA2") 172 .subject("CN=Test Root Certificate Authority 2") 173 .ca(true) 174 .build(); 175 CLIENT_2 = new TestKeyStore(createClient(rootCa2.keyStore), null, null); 176 } 177 178 /** 179 * Return an root CA that can be used to issue new certificates. 180 */ 181 public static TestKeyStore getRootCa() { 182 initCerts(); 183 return ROOT_CA; 184 } 185 186 /** 187 * Return an intermediate CA that can be used to issue new certificates. 188 */ 189 public static TestKeyStore getIntermediateCa() { 190 initCerts(); 191 return INTERMEDIATE_CA; 192 } 193 194 /** 195 * Return a server keystore with a matched RSA certificate and 196 * private key as well as a CA certificate. 197 */ 198 public static TestKeyStore getServer() { 199 initCerts(); 200 return SERVER; 201 } 202 203 /** 204 * Return a keystore with a CA certificate 205 */ 206 public static TestKeyStore getClient() { 207 initCerts(); 208 return CLIENT; 209 } 210 211 /** 212 * Return a client keystore with a matched RSA certificate and 213 * private key as well as a CA certificate. 214 */ 215 public static TestKeyStore getClientCertificate() { 216 initCerts(); 217 return CLIENT_CERTIFICATE; 218 } 219 220 /** 221 * Return a keystore with a second CA certificate that does not 222 * trust the server certificate returned by getServer for negative 223 * testing. 224 */ 225 public static TestKeyStore getClientCA2() { 226 initCerts(); 227 return CLIENT_2; 228 } 229 230 /** 231 * Creates KeyStores containing the requested key types. Since key 232 * generation can be expensive, most tests should reuse the RSA-only 233 * singleton instance returned by TestKeyStore.get. 234 */ 235 public static class Builder { 236 private String[] keyAlgorithms = { "RSA" }; 237 private char[] storePassword; 238 private char[] keyPassword; 239 private String aliasPrefix; 240 private X500Principal subject; 241 private int keyUsage; 242 private boolean ca; 243 private PrivateKeyEntry signer; 244 private Certificate rootCa; 245 private final List<KeyPurposeId> extendedKeyUsages = new ArrayList<KeyPurposeId>(); 246 private final List<Boolean> criticalExtendedKeyUsages = new ArrayList<Boolean>(); 247 private final List<GeneralName> subjectAltNames = new ArrayList<GeneralName>(); 248 private final List<GeneralSubtree> permittedNameConstraints 249 = new ArrayList<GeneralSubtree>(); 250 private final List<GeneralSubtree> excludedNameConstraints 251 = new ArrayList<GeneralSubtree>(); 252 253 public Builder() { 254 subject = localhost(); 255 } 256 257 /** 258 * Sets the requested key types to generate and include. The default is 259 * RSA only. 260 */ 261 public Builder keyAlgorithms(String... keyAlgorithms) { 262 this.keyAlgorithms = keyAlgorithms; 263 return this; 264 } 265 266 /** A unique prefix to identify the key aliases */ 267 public Builder aliasPrefix(String aliasPrefix) { 268 this.aliasPrefix = aliasPrefix; 269 return this; 270 } 271 272 /** 273 * Sets the subject common name. The default is the local host's 274 * canonical name. 275 */ 276 public Builder subject(X500Principal subject) { 277 this.subject = subject; 278 return this; 279 } 280 281 public Builder subject(String commonName) { 282 return subject(new X500Principal(commonName)); 283 } 284 285 /** {@link KeyUsage} bit mask for 2.5.29.15 extension */ 286 public Builder keyUsage(int keyUsage) { 287 this.keyUsage = keyUsage; 288 return this; 289 } 290 291 /** true If the keys being created are for a CA */ 292 public Builder ca(boolean ca) { 293 this.ca = ca; 294 return this; 295 } 296 297 /** a private key entry to be used for signing, otherwise self-sign */ 298 public Builder signer(PrivateKeyEntry signer) { 299 this.signer = signer; 300 return this; 301 } 302 303 /** a root CA to include in the final store */ 304 public Builder rootCa(Certificate rootCa) { 305 this.rootCa = rootCa; 306 return this; 307 } 308 309 public Builder addExtendedKeyUsage(KeyPurposeId keyPurposeId, boolean critical) { 310 extendedKeyUsages.add(keyPurposeId); 311 criticalExtendedKeyUsages.add(critical); 312 return this; 313 } 314 315 public Builder addSubjectAltName(GeneralName generalName) { 316 subjectAltNames.add(generalName); 317 return this; 318 } 319 320 public Builder addSubjectAltNameIpAddress(byte[] ipAddress) { 321 return addSubjectAltName( 322 new GeneralName(GeneralName.iPAddress, new DEROctetString(ipAddress))); 323 } 324 325 private Builder addNameConstraint(boolean permitted, GeneralName generalName) { 326 if (permitted) { 327 permittedNameConstraints.add(new GeneralSubtree(generalName)); 328 } else { 329 excludedNameConstraints.add(new GeneralSubtree(generalName)); 330 } 331 return this; 332 } 333 334 public Builder addNameConstraint(boolean permitted, byte[] ipAddress) { 335 return addNameConstraint(permitted, 336 new GeneralName(GeneralName.iPAddress, new DEROctetString(ipAddress))); 337 } 338 339 public TestKeyStore build() { 340 try { 341 if (StandardNames.IS_RI) { 342 // JKS does not allow null password 343 if (storePassword == null) { 344 storePassword = "password".toCharArray(); 345 } 346 if (keyPassword == null) { 347 keyPassword = "password".toCharArray(); 348 } 349 } 350 351 KeyStore keyStore = createKeyStore(); 352 for (String keyAlgorithm : keyAlgorithms) { 353 String publicAlias = aliasPrefix + "-public-" + keyAlgorithm; 354 String privateAlias = aliasPrefix + "-private-" + keyAlgorithm; 355 if (keyAlgorithm.equals("EC_RSA") && signer == null && rootCa == null) { 356 createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, 357 privateKey(keyStore, keyPassword, "RSA", "RSA")); 358 continue; 359 } 360 createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, signer); 361 } 362 if (rootCa != null) { 363 keyStore.setCertificateEntry(aliasPrefix 364 + "-root-ca-" 365 + rootCa.getPublicKey().getAlgorithm(), 366 rootCa); 367 } 368 return new TestKeyStore(keyStore, storePassword, keyPassword); 369 } catch (Exception e) { 370 throw new RuntimeException(e); 371 } 372 } 373 374 /** 375 * Add newly generated keys of a given key type to an existing 376 * KeyStore. The PrivateKey will be stored under the specified 377 * private alias name. The X509Certificate will be stored on the 378 * public alias name and have the given subject distinguished 379 * name. 380 * 381 * If a CA is provided, it will be used to sign the generated 382 * certificate. Otherwise, the certificate will be self 383 * signed. The certificate will be valid for one day before and 384 * one day after the time of creation. 385 * 386 * Based on: 387 * org.bouncycastle.jce.provider.test.SigTest 388 * org.bouncycastle.jce.provider.test.CertTest 389 */ 390 private KeyStore createKeys(KeyStore keyStore, 391 String keyAlgorithm, 392 String publicAlias, 393 String privateAlias, 394 PrivateKeyEntry signer) throws Exception { 395 PrivateKey caKey; 396 X509Certificate caCert; 397 X509Certificate[] caCertChain; 398 if (signer == null) { 399 caKey = null; 400 caCert = null; 401 caCertChain = null; 402 } else { 403 caKey = signer.getPrivateKey(); 404 caCert = (X509Certificate)signer.getCertificate(); 405 caCertChain = (X509Certificate[])signer.getCertificateChain(); 406 } 407 408 PrivateKey privateKey; 409 X509Certificate x509c; 410 if (publicAlias == null && privateAlias == null) { 411 // don't want anything apparently 412 privateKey = null; 413 x509c = null; 414 } else { 415 // 1.) we make the keys 416 int keySize; 417 if (keyAlgorithm.equals("RSA")) { 418 // 512 breaks SSL_RSA_EXPORT_* on RI and TLS_ECDHE_RSA_WITH_RC4_128_SHA for us 419 keySize = 1024; 420 } else if (keyAlgorithm.equals("DSA")) { 421 keySize = 512; 422 } else if (keyAlgorithm.equals("EC")) { 423 keySize = 256; 424 } else if (keyAlgorithm.equals("EC_RSA")) { 425 keySize = 256; 426 keyAlgorithm = "EC"; 427 } else { 428 throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm); 429 } 430 431 KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm); 432 kpg.initialize(keySize, new SecureRandom()); 433 434 KeyPair kp = kpg.generateKeyPair(); 435 privateKey = kp.getPrivate(); 436 PublicKey publicKey = kp.getPublic(); 437 438 // 2.) use keys to make certificate 439 X500Principal issuer = ((caCert != null) 440 ? caCert.getSubjectX500Principal() 441 : subject); 442 PrivateKey signingKey = (caKey == null) ? privateKey : caKey; 443 x509c = createCertificate(publicKey, signingKey, subject, issuer, keyUsage, ca, 444 extendedKeyUsages, criticalExtendedKeyUsages, 445 subjectAltNames, 446 permittedNameConstraints, excludedNameConstraints); 447 } 448 449 X509Certificate[] x509cc; 450 if (privateAlias == null) { 451 // don't need certificate chain 452 x509cc = null; 453 } else if (caCertChain == null) { 454 x509cc = new X509Certificate[] { x509c }; 455 } else { 456 x509cc = new X509Certificate[caCertChain.length+1]; 457 x509cc[0] = x509c; 458 System.arraycopy(caCertChain, 0, x509cc, 1, caCertChain.length); 459 } 460 461 // 3.) put certificate and private key into the key store 462 if (privateAlias != null) { 463 keyStore.setKeyEntry(privateAlias, privateKey, keyPassword, x509cc); 464 } 465 if (publicAlias != null) { 466 keyStore.setCertificateEntry(publicAlias, x509c); 467 } 468 return keyStore; 469 } 470 471 private X500Principal localhost() { 472 try { 473 return new X500Principal("CN=" + InetAddress.getLocalHost().getHostName()); 474 } catch (UnknownHostException e) { 475 throw new RuntimeException(e); 476 } 477 } 478 } 479 480 public static X509Certificate createCa(PublicKey publicKey, 481 PrivateKey privateKey, 482 String subject) { 483 try { 484 X500Principal principal = new X500Principal(subject); 485 return createCertificate(publicKey, privateKey, 486 principal, principal, 487 0, true, 488 new ArrayList<KeyPurposeId>(), 489 new ArrayList<Boolean>(), 490 new ArrayList<GeneralName>(), 491 new ArrayList<GeneralSubtree>(), 492 new ArrayList<GeneralSubtree>()); 493 } catch (Exception e) { 494 throw new RuntimeException(e); 495 } 496 } 497 498 private static X509Certificate createCertificate( 499 PublicKey publicKey, 500 PrivateKey privateKey, 501 X500Principal subject, 502 X500Principal issuer, 503 int keyUsage, 504 boolean ca, 505 List<KeyPurposeId> extendedKeyUsages, 506 List<Boolean> criticalExtendedKeyUsages, 507 List<GeneralName> subjectAltNames, 508 List<GeneralSubtree> permittedNameConstraints, 509 List<GeneralSubtree> excludedNameConstraints) throws Exception { 510 // Note that there is no way to programmatically make a 511 // Certificate using java.* or javax.* APIs. The 512 // CertificateFactory interface assumes you want to read 513 // in a stream of bytes, typically the X.509 factory would 514 // allow ASN.1 DER encoded bytes and optionally some PEM 515 // formats. Here we use Bouncy Castle's 516 // X509V3CertificateGenerator and related classes. 517 518 long millisPerDay = 24 * 60 * 60 * 1000; 519 long now = System.currentTimeMillis(); 520 Date start = new Date(now - millisPerDay); 521 Date end = new Date(now + millisPerDay); 522 BigInteger serial = BigInteger.valueOf(1); 523 524 String keyAlgorithm = privateKey.getAlgorithm(); 525 String signatureAlgorithm; 526 if (keyAlgorithm.equals("RSA")) { 527 signatureAlgorithm = "sha1WithRSA"; 528 } else if (keyAlgorithm.equals("DSA")) { 529 signatureAlgorithm = "sha1WithDSA"; 530 } else if (keyAlgorithm.equals("EC")) { 531 signatureAlgorithm = "sha1WithECDSA"; 532 } else if (keyAlgorithm.equals("EC_RSA")) { 533 signatureAlgorithm = "sha1WithRSA"; 534 } else { 535 throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm); 536 } 537 538 X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator(); 539 x509cg.setSubjectDN(subject); 540 x509cg.setIssuerDN(issuer); 541 x509cg.setNotBefore(start); 542 x509cg.setNotAfter(end); 543 x509cg.setPublicKey(publicKey); 544 x509cg.setSignatureAlgorithm(signatureAlgorithm); 545 x509cg.setSerialNumber(serial); 546 if (keyUsage != 0) { 547 x509cg.addExtension(X509Extensions.KeyUsage, 548 true, 549 new KeyUsage(keyUsage)); 550 } 551 if (ca) { 552 x509cg.addExtension(X509Extensions.BasicConstraints, 553 true, 554 new BasicConstraints(true)); 555 } 556 for (int i = 0; i < extendedKeyUsages.size(); i++) { 557 KeyPurposeId keyPurposeId = extendedKeyUsages.get(i); 558 boolean critical = criticalExtendedKeyUsages.get(i); 559 x509cg.addExtension(X509Extensions.ExtendedKeyUsage, 560 critical, 561 new ExtendedKeyUsage(keyPurposeId)); 562 } 563 for (GeneralName subjectAltName : subjectAltNames) { 564 x509cg.addExtension(X509Extensions.SubjectAlternativeName, 565 false, 566 new GeneralNames(subjectAltName).getEncoded()); 567 } 568 if (!permittedNameConstraints.isEmpty() || !excludedNameConstraints.isEmpty()) { 569 x509cg.addExtension(X509Extensions.NameConstraints, 570 true, 571 new NameConstraints(permittedNameConstraints.toArray( 572 new GeneralSubtree[ 573 permittedNameConstraints.size()]), 574 excludedNameConstraints.toArray( 575 new GeneralSubtree[ 576 excludedNameConstraints.size()]))); 577 } 578 579 if (privateKey instanceof ECPrivateKey) { 580 /* 581 * bouncycastle needs its own ECPrivateKey implementation 582 */ 583 KeyFactory kf = KeyFactory.getInstance(keyAlgorithm, "BC"); 584 PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(privateKey.getEncoded()); 585 privateKey = kf.generatePrivate(ks); 586 } 587 X509Certificate x509c = x509cg.generateX509Certificate(privateKey); 588 if (StandardNames.IS_RI) { 589 /* 590 * The RI can't handle the BC EC signature algorithm 591 * string of "ECDSA", since it expects "...WITHEC...", 592 * so convert from BC to RI X509Certificate 593 * implementation via bytes. 594 */ 595 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 596 ByteArrayInputStream bais = new ByteArrayInputStream(x509c.getEncoded()); 597 Certificate c = cf.generateCertificate(bais); 598 x509c = (X509Certificate) c; 599 } 600 return x509c; 601 } 602 603 /** 604 * Return the key algorithm for a possible compound algorithm 605 * identifier containing an underscore. If not underscore is 606 * present, the argument is returned unmodified. However for an 607 * algorithm such as EC_RSA, return EC. 608 */ 609 public static String keyAlgorithm(String algorithm) { 610 int index = algorithm.indexOf('_'); 611 if (index == -1) { 612 return algorithm; 613 } 614 return algorithm.substring(0, index); 615 } 616 617 618 /** 619 * Return the signature algorithm for a possible compound 620 * algorithm identifier containing an underscore. If not 621 * underscore is present, the argument is returned 622 * unmodified. However for an algorithm such as EC_RSA, return 623 * RSA. 624 */ 625 public static String signatureAlgorithm(String algorithm) { 626 int index = algorithm.indexOf('_'); 627 if (index == -1) { 628 return algorithm; 629 } 630 return algorithm.substring(index+1, algorithm.length()); 631 } 632 633 /** 634 * Create an empty KeyStore 635 */ 636 public static KeyStore createKeyStore() { 637 try { 638 KeyStore keyStore = KeyStore.getInstance(StandardNames.KEY_STORE_ALGORITHM); 639 keyStore.load(null, null); 640 return keyStore; 641 } catch (Exception e) { 642 throw new RuntimeException(e); 643 } 644 } 645 646 /** 647 * Return the only private key in a TestKeyStore for the given 648 * algorithms. Throws IllegalStateException if there are are more 649 * or less than one. 650 */ 651 public PrivateKeyEntry getPrivateKey(String keyAlgorithm, String signatureAlgorithm) { 652 return privateKey(keyStore, keyPassword, keyAlgorithm, signatureAlgorithm); 653 } 654 655 /** 656 * Return the only private key in a keystore for the given 657 * algorithms. Throws IllegalStateException if there are are more 658 * or less than one. 659 */ 660 public static PrivateKeyEntry privateKey(KeyStore keyStore, char[] keyPassword, 661 String keyAlgorithm, String signatureAlgorithm) { 662 try { 663 PrivateKeyEntry found = null; 664 PasswordProtection password = new PasswordProtection(keyPassword); 665 for (String alias : Collections.list(keyStore.aliases())) { 666 if (!keyStore.entryInstanceOf(alias, PrivateKeyEntry.class)) { 667 continue; 668 } 669 PrivateKeyEntry privateKey = (PrivateKeyEntry) keyStore.getEntry(alias, password); 670 if (!privateKey.getPrivateKey().getAlgorithm().equals(keyAlgorithm)) { 671 continue; 672 } 673 X509Certificate certificate = (X509Certificate) privateKey.getCertificate(); 674 if (!certificate.getSigAlgName().contains(signatureAlgorithm)) { 675 continue; 676 } 677 if (found != null) { 678 throw new IllegalStateException("KeyStore has more than one private key for " 679 + " keyAlgorithm: " + keyAlgorithm 680 + " signatureAlgorithm: " + signatureAlgorithm 681 + "\nfirst: " + found.getPrivateKey() 682 + "\nsecond: " + privateKey.getPrivateKey() ); 683 } 684 found = privateKey; 685 } 686 if (found == null) { 687 throw new IllegalStateException("KeyStore contained no private key for " 688 + " keyAlgorithm: " + keyAlgorithm 689 + " signatureAlgorithm: " + signatureAlgorithm); 690 } 691 return found; 692 } catch (Exception e) { 693 throw new RuntimeException(e); 694 } 695 } 696 697 /** 698 * Return the issuing CA certificate of the given 699 * certificate. Throws IllegalStateException if there are are more 700 * or less than one. 701 */ 702 public Certificate getIssuer(Certificate cert) throws Exception { 703 return issuer(keyStore, cert); 704 } 705 706 /** 707 * Return the issuing CA certificate of the given 708 * certificate. Throws IllegalStateException if there are are more 709 * or less than one. 710 */ 711 public static Certificate issuer(KeyStore keyStore, Certificate c) 712 throws Exception { 713 if (!(c instanceof X509Certificate)) { 714 throw new IllegalStateException("issuer requires an X509Certificate, found " + c); 715 } 716 X509Certificate cert = (X509Certificate) c; 717 718 Certificate found = null; 719 for (String alias : Collections.list(keyStore.aliases())) { 720 if (!keyStore.entryInstanceOf(alias, TrustedCertificateEntry.class)) { 721 continue; 722 } 723 TrustedCertificateEntry certificateEntry = 724 (TrustedCertificateEntry) keyStore.getEntry(alias, null); 725 Certificate certificate = certificateEntry.getTrustedCertificate(); 726 if (!(certificate instanceof X509Certificate)) { 727 continue; 728 } 729 X509Certificate x = (X509Certificate) certificate; 730 if (!cert.getIssuerDN().equals(x.getSubjectDN())) { 731 continue; 732 } 733 if (found != null) { 734 throw new IllegalStateException("KeyStore has more than one issuing CA for " 735 + cert 736 + "\nfirst: " + found 737 + "\nsecond: " + certificate ); 738 } 739 found = certificate; 740 } 741 if (found == null) { 742 throw new IllegalStateException("KeyStore contained no issuing CA for " + cert); 743 } 744 return found; 745 } 746 747 /** 748 * Return the only self-signed root certificate in a TestKeyStore 749 * for the given algorithm. Throws IllegalStateException if there 750 * are are more or less than one. 751 */ 752 public X509Certificate getRootCertificate(String algorithm) { 753 return rootCertificate(keyStore, algorithm); 754 } 755 756 /** 757 * Return the only self-signed root certificate in a keystore for 758 * the given algorithm. Throws IllegalStateException if there are 759 * are more or less than one. 760 */ 761 public static X509Certificate rootCertificate(KeyStore keyStore, String algorithm) { 762 try { 763 X509Certificate found = null; 764 for (String alias : Collections.list(keyStore.aliases())) { 765 if (!keyStore.entryInstanceOf(alias, TrustedCertificateEntry.class)) { 766 continue; 767 } 768 TrustedCertificateEntry certificateEntry = 769 (TrustedCertificateEntry) keyStore.getEntry(alias, null); 770 Certificate certificate = certificateEntry.getTrustedCertificate(); 771 if (!certificate.getPublicKey().getAlgorithm().equals(algorithm)) { 772 continue; 773 } 774 if (!(certificate instanceof X509Certificate)) { 775 continue; 776 } 777 X509Certificate x = (X509Certificate) certificate; 778 if (!x.getIssuerDN().equals(x.getSubjectDN())) { 779 continue; 780 } 781 if (found != null) { 782 throw new IllegalStateException("KeyStore has more than one root CA for " 783 + algorithm 784 + "\nfirst: " + found 785 + "\nsecond: " + certificate ); 786 } 787 found = x; 788 } 789 if (found == null) { 790 throw new IllegalStateException("KeyStore contained no root CA for " + algorithm); 791 } 792 return found; 793 } catch (Exception e) { 794 throw new RuntimeException(e); 795 } 796 } 797 798 /** 799 * Create a client key store that only contains self-signed certificates but no private keys 800 */ 801 public static KeyStore createClient(KeyStore caKeyStore) { 802 KeyStore clientKeyStore = createKeyStore(); 803 copySelfSignedCertificates(clientKeyStore, caKeyStore); 804 return clientKeyStore; 805 } 806 807 /** 808 * Copy self-signed certificates from one key store to another. 809 * Returns true if successful, false if no match found. 810 */ 811 public static boolean copySelfSignedCertificates(KeyStore dst, KeyStore src) { 812 try { 813 boolean copied = false; 814 for (String alias : Collections.list(src.aliases())) { 815 if (!src.isCertificateEntry(alias)) { 816 continue; 817 } 818 X509Certificate cert = (X509Certificate)src.getCertificate(alias); 819 if (!cert.getSubjectDN().equals(cert.getIssuerDN())) { 820 continue; 821 } 822 dst.setCertificateEntry(alias, cert); 823 copied = true; 824 } 825 return copied; 826 } catch (Exception e) { 827 throw new RuntimeException(e); 828 } 829 } 830 831 /** 832 * Copy named certificates from one key store to another. 833 * Returns true if successful, false if no match found. 834 */ 835 public static boolean copyCertificate(Principal subject, KeyStore dst, KeyStore src) 836 throws Exception { 837 for (String alias : Collections.list(src.aliases())) { 838 if (!src.isCertificateEntry(alias)) { 839 continue; 840 } 841 X509Certificate cert = (X509Certificate)src.getCertificate(alias); 842 if (!cert.getSubjectDN().equals(subject)) { 843 continue; 844 } 845 dst.setCertificateEntry(alias, cert); 846 return true; 847 } 848 return false; 849 } 850 851 /** 852 * Dump a key store for debugging. 853 */ 854 public void dump(String context) throws KeyStoreException, NoSuchAlgorithmException { 855 dump(context, keyStore, keyPassword); 856 } 857 858 /** 859 * Dump a key store for debugging. 860 */ 861 public static void dump(String context, KeyStore keyStore, char[] keyPassword) 862 throws KeyStoreException, NoSuchAlgorithmException { 863 PrintStream out = System.out; 864 out.println("context=" + context); 865 out.println("\tkeyStore=" + keyStore); 866 out.println("\tkeyStore.type=" + keyStore.getType()); 867 out.println("\tkeyStore.provider=" + keyStore.getProvider()); 868 out.println("\tkeyPassword=" 869 + ((keyPassword == null) ? null : new String(keyPassword))); 870 out.println("\tsize=" + keyStore.size()); 871 for (String alias : Collections.list(keyStore.aliases())) { 872 out.println("alias=" + alias); 873 out.println("\tcreationDate=" + keyStore.getCreationDate(alias)); 874 if (keyStore.isCertificateEntry(alias)) { 875 out.println("\tcertificate:"); 876 out.println("=========================================="); 877 out.println(keyStore.getCertificate(alias)); 878 out.println("=========================================="); 879 continue; 880 } 881 if (keyStore.isKeyEntry(alias)) { 882 out.println("\tkey:"); 883 out.println("=========================================="); 884 String key; 885 try { 886 key = ("Key retrieved using password\n" 887 + keyStore.getKey(alias, keyPassword)); 888 } catch (UnrecoverableKeyException e1) { 889 try { 890 key = ("Key retrieved without password\n" 891 + keyStore.getKey(alias, null)); 892 } catch (UnrecoverableKeyException e2) { 893 key = "Key could not be retrieved"; 894 } 895 } 896 out.println(key); 897 out.println("=========================================="); 898 Certificate[] chain = keyStore.getCertificateChain(alias); 899 if (chain == null) { 900 out.println("No certificate chain associated with key"); 901 out.println("=========================================="); 902 } else { 903 for (int i = 0; i < chain.length; i++) { 904 out.println("Certificate chain element #" + i); 905 out.println(chain[i]); 906 out.println("=========================================="); 907 } 908 } 909 continue; 910 } 911 out.println("\tunknown entry type"); 912 } 913 } 914 915 public static void assertChainLength(Object[] chain) { 916 /* 917 * Note chain is Object[] to support both 918 * java.security.cert.X509Certificate and 919 * javax.security.cert.X509Certificate 920 */ 921 assertEquals(3, chain.length); 922 } 923 } 924