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