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
17package libcore.java.security;
18
19import com.android.org.bouncycastle.asn1.DEROctetString;
20import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
21import com.android.org.bouncycastle.asn1.x509.GeneralName;
22import com.android.org.bouncycastle.asn1.x509.GeneralNames;
23import com.android.org.bouncycastle.asn1.x509.GeneralSubtree;
24import com.android.org.bouncycastle.asn1.x509.KeyUsage;
25import com.android.org.bouncycastle.asn1.x509.NameConstraints;
26import com.android.org.bouncycastle.asn1.x509.X509Extensions;
27import com.android.org.bouncycastle.jce.provider.BouncyCastleProvider;
28import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
29import java.io.ByteArrayInputStream;
30import java.io.PrintStream;
31import java.math.BigInteger;
32import java.net.InetAddress;
33import java.net.UnknownHostException;
34import java.security.KeyFactory;
35import java.security.KeyPair;
36import java.security.KeyPairGenerator;
37import java.security.KeyStore;
38import java.security.KeyStore.PasswordProtection;
39import java.security.KeyStore.PrivateKeyEntry;
40import java.security.KeyStore.TrustedCertificateEntry;
41import java.security.KeyStoreException;
42import java.security.NoSuchAlgorithmException;
43import java.security.Principal;
44import java.security.PrivateKey;
45import java.security.PublicKey;
46import java.security.SecureRandom;
47import java.security.Security;
48import java.security.UnrecoverableKeyException;
49import java.security.cert.Certificate;
50import java.security.cert.CertificateFactory;
51import java.security.cert.X509Certificate;
52import java.security.interfaces.ECPrivateKey;
53import java.security.spec.PKCS8EncodedKeySpec;
54import java.util.ArrayList;
55import java.util.Collections;
56import java.util.Date;
57import java.util.List;
58import java.util.Vector;
59import javax.net.ssl.KeyManager;
60import javax.net.ssl.KeyManagerFactory;
61import javax.net.ssl.TrustManager;
62import javax.net.ssl.TrustManagerFactory;
63import javax.security.auth.x500.X500Principal;
64import junit.framework.Assert;
65import libcore.javax.net.ssl.TestKeyManager;
66import 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 */
75public 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