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.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