11864f965419c5b14ac9317c576c8a0f7169c067enmittler/*
21864f965419c5b14ac9317c576c8a0f7169c067enmittler * Copyright (C) 2010 The Android Open Source Project
31864f965419c5b14ac9317c576c8a0f7169c067enmittler *
41864f965419c5b14ac9317c576c8a0f7169c067enmittler * Licensed under the Apache License, Version 2.0 (the "License");
51864f965419c5b14ac9317c576c8a0f7169c067enmittler * you may not use this file except in compliance with the License.
61864f965419c5b14ac9317c576c8a0f7169c067enmittler * You may obtain a copy of the License at
71864f965419c5b14ac9317c576c8a0f7169c067enmittler *
81864f965419c5b14ac9317c576c8a0f7169c067enmittler *      http://www.apache.org/licenses/LICENSE-2.0
91864f965419c5b14ac9317c576c8a0f7169c067enmittler *
101864f965419c5b14ac9317c576c8a0f7169c067enmittler * Unless required by applicable law or agreed to in writing, software
111864f965419c5b14ac9317c576c8a0f7169c067enmittler * distributed under the License is distributed on an "AS IS" BASIS,
121864f965419c5b14ac9317c576c8a0f7169c067enmittler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131864f965419c5b14ac9317c576c8a0f7169c067enmittler * See the License for the specific language governing permissions and
141864f965419c5b14ac9317c576c8a0f7169c067enmittler * limitations under the License.
151864f965419c5b14ac9317c576c8a0f7169c067enmittler */
161864f965419c5b14ac9317c576c8a0f7169c067enmittler
171864f965419c5b14ac9317c576c8a0f7169c067enmittlerpackage libcore.java.security;
181864f965419c5b14ac9317c576c8a0f7169c067enmittler
19f65a3de1e35d7f734ae4bb2ca33ecfd776a92c4dKenny Rootimport static org.junit.Assert.assertEquals;
20f65a3de1e35d7f734ae4bb2ca33ecfd776a92c4dKenny Root
211864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.io.ByteArrayInputStream;
221864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.io.IOException;
231864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.io.PrintStream;
241864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.math.BigInteger;
251864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.KeyPair;
261864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.KeyPairGenerator;
271864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.KeyStore;
281864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.KeyStore.PasswordProtection;
291864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.KeyStore.PrivateKeyEntry;
301864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.KeyStore.TrustedCertificateEntry;
311864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.KeyStoreException;
321864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.NoSuchAlgorithmException;
331864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.Principal;
341864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.PrivateKey;
351864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.PublicKey;
361864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.SecureRandom;
371864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.Security;
381864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.UnrecoverableEntryException;
391864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.UnrecoverableKeyException;
401864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.cert.Certificate;
411864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.cert.CertificateException;
421864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.cert.CertificateFactory;
431864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.cert.X509Certificate;
441864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.security.spec.AlgorithmParameterSpec;
451864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.util.ArrayList;
461864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.util.Collections;
471864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.util.Date;
481864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport java.util.List;
491864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport javax.crypto.spec.DHParameterSpec;
501864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport javax.net.ssl.KeyManager;
511864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport javax.net.ssl.KeyManagerFactory;
521864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport javax.net.ssl.TrustManager;
531864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport javax.net.ssl.TrustManagerFactory;
541864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport javax.security.auth.x500.X500Principal;
551864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport libcore.javax.net.ssl.TestKeyManager;
561864f965419c5b14ac9317c576c8a0f7169c067enmittlerimport libcore.javax.net.ssl.TestTrustManager;
57027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.DEROctetString;
58027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x500.X500Name;
59027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.BasicConstraints;
60027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.CRLReason;
61027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.ExtendedKeyUsage;
62027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.Extension;
63027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.GeneralName;
64027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.GeneralNames;
65027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.GeneralSubtree;
66027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.KeyPurposeId;
67027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.KeyUsage;
68027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.NameConstraints;
69027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
70027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.X509CertificateHolder;
71027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.X509v3CertificateBuilder;
72027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
73027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
74027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.ocsp.BasicOCSPResp;
75027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
76027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.ocsp.CertificateID;
77027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.ocsp.CertificateStatus;
78027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.ocsp.OCSPResp;
79027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.ocsp.OCSPRespBuilder;
80027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.cert.ocsp.RevokedStatus;
81027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.jce.provider.BouncyCastleProvider;
82027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.operator.DigestCalculatorProvider;
83027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
84027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Rootimport org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
851864f965419c5b14ac9317c576c8a0f7169c067enmittler
861864f965419c5b14ac9317c576c8a0f7169c067enmittler/**
871864f965419c5b14ac9317c576c8a0f7169c067enmittler * TestKeyStore is a convenience class for other tests that
881864f965419c5b14ac9317c576c8a0f7169c067enmittler * want a canned KeyStore with a variety of key pairs.
891864f965419c5b14ac9317c576c8a0f7169c067enmittler *
901864f965419c5b14ac9317c576c8a0f7169c067enmittler * Creating a key store is relatively slow, so a singleton instance is
911864f965419c5b14ac9317c576c8a0f7169c067enmittler * accessible via TestKeyStore.get().
921864f965419c5b14ac9317c576c8a0f7169c067enmittler */
93f65a3de1e35d7f734ae4bb2ca33ecfd776a92c4dKenny Rootpublic final class TestKeyStore {
941864f965419c5b14ac9317c576c8a0f7169c067enmittler    /** Size of DSA keys to generate for testing. */
951864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static final int DSA_KEY_SIZE_BITS = 1024;
961864f965419c5b14ac9317c576c8a0f7169c067enmittler
971864f965419c5b14ac9317c576c8a0f7169c067enmittler    /** Size of EC keys to generate for testing. */
981864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static final int EC_KEY_SIZE_BITS = 256;
991864f965419c5b14ac9317c576c8a0f7169c067enmittler
1001864f965419c5b14ac9317c576c8a0f7169c067enmittler    /** Size of RSA keys to generate for testing. */
1011864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static final int RSA_KEY_SIZE_BITS = 1024;
1021864f965419c5b14ac9317c576c8a0f7169c067enmittler
1031864f965419c5b14ac9317c576c8a0f7169c067enmittler    // Generated with: openssl dhparam -C 1024
104b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root    private static final BigInteger DH_PARAMS_P = new BigInteger(1,
105b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root            new byte[] {
106b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0xA2, (byte) 0x31, (byte) 0xB4, (byte) 0xB3, (byte) 0x6D, (byte) 0x9B,
107b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x7E, (byte) 0xF4, (byte) 0xE7, (byte) 0x21, (byte) 0x51, (byte) 0x40,
108b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0xEB, (byte) 0xC6, (byte) 0xB6, (byte) 0xD6, (byte) 0x54, (byte) 0x56,
109b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x72, (byte) 0xBE, (byte) 0x43, (byte) 0x18, (byte) 0x30, (byte) 0x5C,
110b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x15, (byte) 0x5A, (byte) 0xF9, (byte) 0x19, (byte) 0x62, (byte) 0xAD,
111b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0xF4, (byte) 0x29, (byte) 0xCB, (byte) 0xC6, (byte) 0xF6, (byte) 0x64,
112b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x0B, (byte) 0x9D, (byte) 0x23, (byte) 0x80, (byte) 0xF9, (byte) 0x5B,
113b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x1C, (byte) 0x1C, (byte) 0x6A, (byte) 0xB4, (byte) 0xEA, (byte) 0xB9,
114b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x80, (byte) 0x98, (byte) 0x8B, (byte) 0xAF, (byte) 0x15, (byte) 0xA8,
115b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x5C, (byte) 0xC4, (byte) 0xB0, (byte) 0x41, (byte) 0x29, (byte) 0x66,
116b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x9F, (byte) 0x9F, (byte) 0x1F, (byte) 0x88, (byte) 0x50, (byte) 0x97,
117b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x38, (byte) 0x0B, (byte) 0x01, (byte) 0x16, (byte) 0xD6, (byte) 0x84,
118b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x1D, (byte) 0x48, (byte) 0x6F, (byte) 0x7C, (byte) 0x06, (byte) 0x8C,
119b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x6E, (byte) 0x68, (byte) 0xCD, (byte) 0x38, (byte) 0xE6, (byte) 0x22,
120b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x30, (byte) 0x61, (byte) 0x37, (byte) 0x02, (byte) 0x3D, (byte) 0x47,
121b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x62, (byte) 0xCE, (byte) 0xB9, (byte) 0x1A, (byte) 0x69, (byte) 0x9D,
122b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0xA1, (byte) 0x9F, (byte) 0x10, (byte) 0xA1, (byte) 0xAA, (byte) 0x70,
123b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0xF7, (byte) 0x27, (byte) 0x9C, (byte) 0xD4, (byte) 0xA5, (byte) 0x15,
124b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0xE2, (byte) 0x15, (byte) 0x0C, (byte) 0x20, (byte) 0x90, (byte) 0x08,
125b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0xB6, (byte) 0xF5, (byte) 0xDF, (byte) 0x1C, (byte) 0xCB, (byte) 0x82,
126b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x6D, (byte) 0xC0, (byte) 0xE1, (byte) 0xBD, (byte) 0xCC, (byte) 0x4A,
127b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                    (byte) 0x76, (byte) 0xE3,
128b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root            });
1291864f965419c5b14ac9317c576c8a0f7169c067enmittler
1301864f965419c5b14ac9317c576c8a0f7169c067enmittler    // generator of 2
1311864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static final BigInteger DH_PARAMS_G = BigInteger.valueOf(2);
1321864f965419c5b14ac9317c576c8a0f7169c067enmittler
1331864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore ROOT_CA;
1341864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore INTERMEDIATE_CA;
1351864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore INTERMEDIATE_CA_2;
1361864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore INTERMEDIATE_CA_EC;
1371864f965419c5b14ac9317c576c8a0f7169c067enmittler
1381864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore SERVER;
1391864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore CLIENT;
1401864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore CLIENT_CERTIFICATE;
1411864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore CLIENT_EC_RSA_CERTIFICATE;
1421864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore CLIENT_EC_EC_CERTIFICATE;
1431864f965419c5b14ac9317c576c8a0f7169c067enmittler
1441864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static TestKeyStore CLIENT_2;
1451864f965419c5b14ac9317c576c8a0f7169c067enmittler
1461864f965419c5b14ac9317c576c8a0f7169c067enmittler    static {
1471864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (StandardNames.IS_RI) {
1481864f965419c5b14ac9317c576c8a0f7169c067enmittler            // Needed to create BKS keystore but add at end so most
1491864f965419c5b14ac9317c576c8a0f7169c067enmittler            // algorithm come from the default providers
1501864f965419c5b14ac9317c576c8a0f7169c067enmittler            Security.insertProviderAt(
1511864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new BouncyCastleProvider(), Security.getProviders().length + 1);
1521864f965419c5b14ac9317c576c8a0f7169c067enmittler        } else if (!BouncyCastleProvider.class.getName().startsWith("com.android")) {
1531864f965419c5b14ac9317c576c8a0f7169c067enmittler            // If we run outside of the Android system, we need to make sure
1541864f965419c5b14ac9317c576c8a0f7169c067enmittler            // that the BouncyCastleProvider's static field keyInfoConverters
1551864f965419c5b14ac9317c576c8a0f7169c067enmittler            // is initialized. This happens in the default constructor only.
1561864f965419c5b14ac9317c576c8a0f7169c067enmittler            new BouncyCastleProvider();
1571864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
1581864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
1591864f965419c5b14ac9317c576c8a0f7169c067enmittler
1601864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static final byte[] LOCAL_HOST_ADDRESS = {127, 0, 0, 1};
1611864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static final String LOCAL_HOST_NAME = "localhost";
1621864f965419c5b14ac9317c576c8a0f7169c067enmittler
1631864f965419c5b14ac9317c576c8a0f7169c067enmittler    public final KeyStore keyStore;
1641864f965419c5b14ac9317c576c8a0f7169c067enmittler    public final char[] storePassword;
1651864f965419c5b14ac9317c576c8a0f7169c067enmittler    public final char[] keyPassword;
1661864f965419c5b14ac9317c576c8a0f7169c067enmittler    public final KeyManager[] keyManagers;
1671864f965419c5b14ac9317c576c8a0f7169c067enmittler    public final TrustManager[] trustManagers;
1681864f965419c5b14ac9317c576c8a0f7169c067enmittler    public final TestTrustManager trustManager;
1691864f965419c5b14ac9317c576c8a0f7169c067enmittler
1701864f965419c5b14ac9317c576c8a0f7169c067enmittler    private TestKeyStore(KeyStore keyStore, char[] storePassword, char[] keyPassword) {
1711864f965419c5b14ac9317c576c8a0f7169c067enmittler        this.keyStore = keyStore;
1721864f965419c5b14ac9317c576c8a0f7169c067enmittler        this.storePassword = storePassword;
1731864f965419c5b14ac9317c576c8a0f7169c067enmittler        this.keyPassword = keyPassword;
1741864f965419c5b14ac9317c576c8a0f7169c067enmittler        this.keyManagers = createKeyManagers(keyStore, storePassword);
1751864f965419c5b14ac9317c576c8a0f7169c067enmittler        this.trustManagers = createTrustManagers(keyStore);
1761864f965419c5b14ac9317c576c8a0f7169c067enmittler        this.trustManager = (TestTrustManager) trustManagers[0];
1771864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
1781864f965419c5b14ac9317c576c8a0f7169c067enmittler
1791864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static KeyManager[] createKeyManagers(KeyStore keyStore, char[] storePassword) {
1801864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
1811864f965419c5b14ac9317c576c8a0f7169c067enmittler            String kmfa = KeyManagerFactory.getDefaultAlgorithm();
1821864f965419c5b14ac9317c576c8a0f7169c067enmittler            KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa);
1831864f965419c5b14ac9317c576c8a0f7169c067enmittler            kmf.init(keyStore, storePassword);
1841864f965419c5b14ac9317c576c8a0f7169c067enmittler            return TestKeyManager.wrap(kmf.getKeyManagers());
1851864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
1861864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException(e);
1871864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
1881864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
1891864f965419c5b14ac9317c576c8a0f7169c067enmittler
1901864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TrustManager[] createTrustManagers(final KeyStore keyStore) {
1911864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
1921864f965419c5b14ac9317c576c8a0f7169c067enmittler            String tmfa = TrustManagerFactory.getDefaultAlgorithm();
1931864f965419c5b14ac9317c576c8a0f7169c067enmittler            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa);
1941864f965419c5b14ac9317c576c8a0f7169c067enmittler            tmf.init(keyStore);
1951864f965419c5b14ac9317c576c8a0f7169c067enmittler            return TestTrustManager.wrap(tmf.getTrustManagers());
1961864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
1971864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException(e);
1981864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
1991864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
2001864f965419c5b14ac9317c576c8a0f7169c067enmittler
2011864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
2021864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Lazily create shared test certificates.
2031864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
2041864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static synchronized void initCerts() {
2051864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (ROOT_CA != null) {
2061864f965419c5b14ac9317c576c8a0f7169c067enmittler            return;
2071864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
2081864f965419c5b14ac9317c576c8a0f7169c067enmittler        ROOT_CA = new Builder()
2091864f965419c5b14ac9317c576c8a0f7169c067enmittler                          .aliasPrefix("RootCA")
2101864f965419c5b14ac9317c576c8a0f7169c067enmittler                          .subject("CN=Test Root Certificate Authority")
2111864f965419c5b14ac9317c576c8a0f7169c067enmittler                          .ca(true)
2121864f965419c5b14ac9317c576c8a0f7169c067enmittler                          .certificateSerialNumber(BigInteger.valueOf(1))
2131864f965419c5b14ac9317c576c8a0f7169c067enmittler                          .build();
2141864f965419c5b14ac9317c576c8a0f7169c067enmittler        INTERMEDIATE_CA_EC = new Builder()
2151864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .aliasPrefix("IntermediateCA-EC")
2161864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .keyAlgorithms("EC")
2171864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .subject("CN=Test Intermediate Certificate Authority ECDSA")
2181864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .ca(true)
2191864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .signer(ROOT_CA.getPrivateKey("RSA", "RSA"))
2201864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .rootCa(ROOT_CA.getRootCertificate("RSA"))
2211864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .certificateSerialNumber(BigInteger.valueOf(2))
2221864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .build();
2231864f965419c5b14ac9317c576c8a0f7169c067enmittler        INTERMEDIATE_CA = new Builder()
2241864f965419c5b14ac9317c576c8a0f7169c067enmittler                                  .aliasPrefix("IntermediateCA")
2251864f965419c5b14ac9317c576c8a0f7169c067enmittler                                  .subject("CN=Test Intermediate Certificate Authority")
2261864f965419c5b14ac9317c576c8a0f7169c067enmittler                                  .ca(true)
2271864f965419c5b14ac9317c576c8a0f7169c067enmittler                                  .signer(ROOT_CA.getPrivateKey("RSA", "RSA"))
2281864f965419c5b14ac9317c576c8a0f7169c067enmittler                                  .rootCa(ROOT_CA.getRootCertificate("RSA"))
2291864f965419c5b14ac9317c576c8a0f7169c067enmittler                                  .certificateSerialNumber(BigInteger.valueOf(2))
2301864f965419c5b14ac9317c576c8a0f7169c067enmittler                                  .build();
2311864f965419c5b14ac9317c576c8a0f7169c067enmittler        SERVER = new Builder()
2321864f965419c5b14ac9317c576c8a0f7169c067enmittler                         .aliasPrefix("server")
2331864f965419c5b14ac9317c576c8a0f7169c067enmittler                         .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
2341864f965419c5b14ac9317c576c8a0f7169c067enmittler                         .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
2351864f965419c5b14ac9317c576c8a0f7169c067enmittler                         .addSubjectAltNameIpAddress(LOCAL_HOST_ADDRESS)
2361864f965419c5b14ac9317c576c8a0f7169c067enmittler                         .certificateSerialNumber(BigInteger.valueOf(3))
2371864f965419c5b14ac9317c576c8a0f7169c067enmittler                         .build();
2381864f965419c5b14ac9317c576c8a0f7169c067enmittler        CLIENT = new TestKeyStore(createClient(INTERMEDIATE_CA.keyStore), null, null);
2391864f965419c5b14ac9317c576c8a0f7169c067enmittler        CLIENT_EC_RSA_CERTIFICATE = new Builder()
2401864f965419c5b14ac9317c576c8a0f7169c067enmittler                                            .aliasPrefix("client-ec")
2411864f965419c5b14ac9317c576c8a0f7169c067enmittler                                            .keyAlgorithms("EC")
2421864f965419c5b14ac9317c576c8a0f7169c067enmittler                                            .subject("emailAddress=test-ec@user")
2431864f965419c5b14ac9317c576c8a0f7169c067enmittler                                            .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
2441864f965419c5b14ac9317c576c8a0f7169c067enmittler                                            .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
2451864f965419c5b14ac9317c576c8a0f7169c067enmittler                                            .build();
2461864f965419c5b14ac9317c576c8a0f7169c067enmittler        CLIENT_EC_EC_CERTIFICATE = new Builder()
2471864f965419c5b14ac9317c576c8a0f7169c067enmittler                                           .aliasPrefix("client-ec")
2481864f965419c5b14ac9317c576c8a0f7169c067enmittler                                           .keyAlgorithms("EC")
2491864f965419c5b14ac9317c576c8a0f7169c067enmittler                                           .subject("emailAddress=test-ec@user")
2501864f965419c5b14ac9317c576c8a0f7169c067enmittler                                           .signer(INTERMEDIATE_CA_EC.getPrivateKey("EC", "RSA"))
2511864f965419c5b14ac9317c576c8a0f7169c067enmittler                                           .rootCa(INTERMEDIATE_CA_EC.getRootCertificate("RSA"))
2521864f965419c5b14ac9317c576c8a0f7169c067enmittler                                           .build();
2531864f965419c5b14ac9317c576c8a0f7169c067enmittler        CLIENT_CERTIFICATE = new Builder()
2541864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .aliasPrefix("client")
2551864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .subject("emailAddress=test@user")
2561864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
2571864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
2581864f965419c5b14ac9317c576c8a0f7169c067enmittler                                     .build();
2591864f965419c5b14ac9317c576c8a0f7169c067enmittler        TestKeyStore rootCa2 = new Builder()
2601864f965419c5b14ac9317c576c8a0f7169c067enmittler                                       .aliasPrefix("RootCA2")
2611864f965419c5b14ac9317c576c8a0f7169c067enmittler                                       .subject("CN=Test Root Certificate Authority 2")
2621864f965419c5b14ac9317c576c8a0f7169c067enmittler                                       .ca(true)
2631864f965419c5b14ac9317c576c8a0f7169c067enmittler                                       .build();
2641864f965419c5b14ac9317c576c8a0f7169c067enmittler        INTERMEDIATE_CA_2 = new Builder()
2651864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    .aliasPrefix("IntermediateCA")
2661864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    .subject("CN=Test Intermediate Certificate Authority")
2671864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    .ca(true)
2681864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    .signer(rootCa2.getPrivateKey("RSA", "RSA"))
2691864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    .rootCa(rootCa2.getRootCertificate("RSA"))
2701864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    .build();
2711864f965419c5b14ac9317c576c8a0f7169c067enmittler        CLIENT_2 = new TestKeyStore(createClient(rootCa2.keyStore), null, null);
2721864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
2731864f965419c5b14ac9317c576c8a0f7169c067enmittler
2741864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
2751864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return an root CA that can be used to issue new certificates.
2761864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
2771864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getRootCa() {
2781864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
2791864f965419c5b14ac9317c576c8a0f7169c067enmittler        return ROOT_CA;
2801864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
2811864f965419c5b14ac9317c576c8a0f7169c067enmittler
2821864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
2831864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return an intermediate CA that can be used to issue new certificates.
2841864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
2851864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getIntermediateCa() {
2861864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
2871864f965419c5b14ac9317c576c8a0f7169c067enmittler        return INTERMEDIATE_CA;
2881864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
2891864f965419c5b14ac9317c576c8a0f7169c067enmittler
2901864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
2911864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return an intermediate CA that can be used to issue new certificates.
2921864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
2931864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getIntermediateCa2() {
2941864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
2951864f965419c5b14ac9317c576c8a0f7169c067enmittler        return INTERMEDIATE_CA_2;
2961864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
2971864f965419c5b14ac9317c576c8a0f7169c067enmittler
2981864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
2991864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return a server keystore with a matched RSA certificate and
3001864f965419c5b14ac9317c576c8a0f7169c067enmittler     * private key as well as a CA certificate.
3011864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
3021864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getServer() {
3031864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
3041864f965419c5b14ac9317c576c8a0f7169c067enmittler        return SERVER;
3051864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
3061864f965419c5b14ac9317c576c8a0f7169c067enmittler
3071864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
3081864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return a keystore with a CA certificate
3091864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
3101864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getClient() {
3111864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
3121864f965419c5b14ac9317c576c8a0f7169c067enmittler        return CLIENT;
3131864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
3141864f965419c5b14ac9317c576c8a0f7169c067enmittler
3151864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
3161864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return a client keystore with a matched RSA certificate and
3171864f965419c5b14ac9317c576c8a0f7169c067enmittler     * private key as well as a CA certificate.
3181864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
3191864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getClientCertificate() {
3201864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
3211864f965419c5b14ac9317c576c8a0f7169c067enmittler        return CLIENT_CERTIFICATE;
3221864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
3231864f965419c5b14ac9317c576c8a0f7169c067enmittler
3241864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
3251864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return a client keystore with a matched RSA certificate and
3261864f965419c5b14ac9317c576c8a0f7169c067enmittler     * private key as well as a CA certificate.
3271864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
3281864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getClientEcRsaCertificate() {
3291864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
3301864f965419c5b14ac9317c576c8a0f7169c067enmittler        return CLIENT_EC_RSA_CERTIFICATE;
3311864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
3321864f965419c5b14ac9317c576c8a0f7169c067enmittler
3331864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
3341864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return a client keystore with a matched RSA certificate and
3351864f965419c5b14ac9317c576c8a0f7169c067enmittler     * private key as well as a CA certificate.
3361864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
3371864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getClientEcEcCertificate() {
3381864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
3391864f965419c5b14ac9317c576c8a0f7169c067enmittler        return CLIENT_EC_EC_CERTIFICATE;
3401864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
3411864f965419c5b14ac9317c576c8a0f7169c067enmittler
3421864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
3431864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return a keystore with a second CA certificate that does not
3441864f965419c5b14ac9317c576c8a0f7169c067enmittler     * trust the server certificate returned by getServer for negative
3451864f965419c5b14ac9317c576c8a0f7169c067enmittler     * testing.
3461864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
3471864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static TestKeyStore getClientCA2() {
3481864f965419c5b14ac9317c576c8a0f7169c067enmittler        initCerts();
3491864f965419c5b14ac9317c576c8a0f7169c067enmittler        return CLIENT_2;
3501864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
3511864f965419c5b14ac9317c576c8a0f7169c067enmittler
3521864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
3531864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Creates KeyStores containing the requested key types. Since key
3541864f965419c5b14ac9317c576c8a0f7169c067enmittler     * generation can be expensive, most tests should reuse the RSA-only
3551864f965419c5b14ac9317c576c8a0f7169c067enmittler     * singleton instance returned by TestKeyStore.get.
3561864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
3571864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static class Builder {
3581864f965419c5b14ac9317c576c8a0f7169c067enmittler        private String[] keyAlgorithms = {"RSA"};
3591864f965419c5b14ac9317c576c8a0f7169c067enmittler        private char[] storePassword;
3601864f965419c5b14ac9317c576c8a0f7169c067enmittler        private char[] keyPassword;
3611864f965419c5b14ac9317c576c8a0f7169c067enmittler        private String aliasPrefix;
3621864f965419c5b14ac9317c576c8a0f7169c067enmittler        private X500Principal subject;
3631864f965419c5b14ac9317c576c8a0f7169c067enmittler        private int keyUsage;
3641864f965419c5b14ac9317c576c8a0f7169c067enmittler        private boolean ca;
3651864f965419c5b14ac9317c576c8a0f7169c067enmittler        private PrivateKeyEntry privateEntry;
3661864f965419c5b14ac9317c576c8a0f7169c067enmittler        private PrivateKeyEntry signer;
3671864f965419c5b14ac9317c576c8a0f7169c067enmittler        private Certificate rootCa;
3681864f965419c5b14ac9317c576c8a0f7169c067enmittler        private final List<KeyPurposeId> extendedKeyUsages = new ArrayList<KeyPurposeId>();
3691864f965419c5b14ac9317c576c8a0f7169c067enmittler        private final List<Boolean> criticalExtendedKeyUsages = new ArrayList<Boolean>();
3701864f965419c5b14ac9317c576c8a0f7169c067enmittler        private final List<GeneralName> subjectAltNames = new ArrayList<GeneralName>();
3711864f965419c5b14ac9317c576c8a0f7169c067enmittler        private final List<GeneralSubtree> permittedNameConstraints =
3721864f965419c5b14ac9317c576c8a0f7169c067enmittler                new ArrayList<GeneralSubtree>();
3731864f965419c5b14ac9317c576c8a0f7169c067enmittler        private final List<GeneralSubtree> excludedNameConstraints =
3741864f965419c5b14ac9317c576c8a0f7169c067enmittler                new ArrayList<GeneralSubtree>();
3751864f965419c5b14ac9317c576c8a0f7169c067enmittler        // Generated randomly if not set
3761864f965419c5b14ac9317c576c8a0f7169c067enmittler        private BigInteger certificateSerialNumber = null;
3771864f965419c5b14ac9317c576c8a0f7169c067enmittler
3781864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder() {
3791864f965419c5b14ac9317c576c8a0f7169c067enmittler            subject = localhost();
3801864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
3811864f965419c5b14ac9317c576c8a0f7169c067enmittler
3821864f965419c5b14ac9317c576c8a0f7169c067enmittler        /**
3831864f965419c5b14ac9317c576c8a0f7169c067enmittler         * Sets the requested key types to generate and include. The default is
3841864f965419c5b14ac9317c576c8a0f7169c067enmittler         * RSA only.
3851864f965419c5b14ac9317c576c8a0f7169c067enmittler         */
3861864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder keyAlgorithms(String... keyAlgorithms) {
3871864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.keyAlgorithms = keyAlgorithms;
3881864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
3891864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
3901864f965419c5b14ac9317c576c8a0f7169c067enmittler
3911864f965419c5b14ac9317c576c8a0f7169c067enmittler        /** A unique prefix to identify the key aliases */
3921864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder aliasPrefix(String aliasPrefix) {
3931864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.aliasPrefix = aliasPrefix;
3941864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
3951864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
3961864f965419c5b14ac9317c576c8a0f7169c067enmittler
3971864f965419c5b14ac9317c576c8a0f7169c067enmittler        /**
3981864f965419c5b14ac9317c576c8a0f7169c067enmittler         * Sets the subject common name. The default is the local host's
3991864f965419c5b14ac9317c576c8a0f7169c067enmittler         * canonical name.
4001864f965419c5b14ac9317c576c8a0f7169c067enmittler         */
4011864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder subject(X500Principal subject) {
4021864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.subject = subject;
4031864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4041864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4051864f965419c5b14ac9317c576c8a0f7169c067enmittler
4061864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder subject(String commonName) {
4071864f965419c5b14ac9317c576c8a0f7169c067enmittler            return subject(new X500Principal(commonName));
4081864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4091864f965419c5b14ac9317c576c8a0f7169c067enmittler
4101864f965419c5b14ac9317c576c8a0f7169c067enmittler        /** {@link KeyUsage} bit mask for 2.5.29.15 extension */
4111864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder keyUsage(int keyUsage) {
4121864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.keyUsage = keyUsage;
4131864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4141864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4151864f965419c5b14ac9317c576c8a0f7169c067enmittler
4161864f965419c5b14ac9317c576c8a0f7169c067enmittler        /** true If the keys being created are for a CA */
4171864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder ca(boolean ca) {
4181864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.ca = ca;
4191864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4201864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4211864f965419c5b14ac9317c576c8a0f7169c067enmittler
4221864f965419c5b14ac9317c576c8a0f7169c067enmittler        /** a private key entry to use for the generation of the certificate */
4231864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder privateEntry(PrivateKeyEntry privateEntry) {
4241864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.privateEntry = privateEntry;
4251864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4261864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4271864f965419c5b14ac9317c576c8a0f7169c067enmittler
4281864f965419c5b14ac9317c576c8a0f7169c067enmittler        /** a private key entry to be used for signing, otherwise self-sign */
4291864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder signer(PrivateKeyEntry signer) {
4301864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.signer = signer;
4311864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4321864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4331864f965419c5b14ac9317c576c8a0f7169c067enmittler
4341864f965419c5b14ac9317c576c8a0f7169c067enmittler        /** a root CA to include in the final store */
4351864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder rootCa(Certificate rootCa) {
4361864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.rootCa = rootCa;
4371864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4381864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4391864f965419c5b14ac9317c576c8a0f7169c067enmittler
4401864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder addExtendedKeyUsage(KeyPurposeId keyPurposeId, boolean critical) {
4411864f965419c5b14ac9317c576c8a0f7169c067enmittler            extendedKeyUsages.add(keyPurposeId);
4421864f965419c5b14ac9317c576c8a0f7169c067enmittler            criticalExtendedKeyUsages.add(critical);
4431864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4441864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4451864f965419c5b14ac9317c576c8a0f7169c067enmittler
4461864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder addSubjectAltName(GeneralName generalName) {
4471864f965419c5b14ac9317c576c8a0f7169c067enmittler            subjectAltNames.add(generalName);
4481864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4491864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4501864f965419c5b14ac9317c576c8a0f7169c067enmittler
4511864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder addSubjectAltNameIpAddress(byte[] ipAddress) {
4521864f965419c5b14ac9317c576c8a0f7169c067enmittler            return addSubjectAltName(
4531864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new GeneralName(GeneralName.iPAddress, new DEROctetString(ipAddress)));
4541864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4551864f965419c5b14ac9317c576c8a0f7169c067enmittler
4561864f965419c5b14ac9317c576c8a0f7169c067enmittler        private Builder addNameConstraint(boolean permitted, GeneralName generalName) {
4571864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (permitted) {
4581864f965419c5b14ac9317c576c8a0f7169c067enmittler                permittedNameConstraints.add(new GeneralSubtree(generalName));
4591864f965419c5b14ac9317c576c8a0f7169c067enmittler            } else {
4601864f965419c5b14ac9317c576c8a0f7169c067enmittler                excludedNameConstraints.add(new GeneralSubtree(generalName));
4611864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
4621864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4631864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4641864f965419c5b14ac9317c576c8a0f7169c067enmittler
4651864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder addNameConstraint(boolean permitted, byte[] ipAddress) {
4661864f965419c5b14ac9317c576c8a0f7169c067enmittler            return addNameConstraint(permitted,
4671864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new GeneralName(GeneralName.iPAddress, new DEROctetString(ipAddress)));
4681864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4691864f965419c5b14ac9317c576c8a0f7169c067enmittler
4701864f965419c5b14ac9317c576c8a0f7169c067enmittler        public Builder certificateSerialNumber(BigInteger certificateSerialNumber) {
4711864f965419c5b14ac9317c576c8a0f7169c067enmittler            this.certificateSerialNumber = certificateSerialNumber;
4721864f965419c5b14ac9317c576c8a0f7169c067enmittler            return this;
4731864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
4741864f965419c5b14ac9317c576c8a0f7169c067enmittler
4751864f965419c5b14ac9317c576c8a0f7169c067enmittler        public TestKeyStore build() {
4761864f965419c5b14ac9317c576c8a0f7169c067enmittler            try {
4771864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (StandardNames.IS_RI) {
4781864f965419c5b14ac9317c576c8a0f7169c067enmittler                    // JKS does not allow null password
4791864f965419c5b14ac9317c576c8a0f7169c067enmittler                    if (storePassword == null) {
4801864f965419c5b14ac9317c576c8a0f7169c067enmittler                        storePassword = "password".toCharArray();
4811864f965419c5b14ac9317c576c8a0f7169c067enmittler                    }
4821864f965419c5b14ac9317c576c8a0f7169c067enmittler                    if (keyPassword == null) {
4831864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keyPassword = "password".toCharArray();
4841864f965419c5b14ac9317c576c8a0f7169c067enmittler                    }
4851864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
4861864f965419c5b14ac9317c576c8a0f7169c067enmittler
4871864f965419c5b14ac9317c576c8a0f7169c067enmittler                /*
4881864f965419c5b14ac9317c576c8a0f7169c067enmittler                 * This is not implemented for other key types because the logic
4891864f965419c5b14ac9317c576c8a0f7169c067enmittler                 * would be long to write and it's not needed currently.
4901864f965419c5b14ac9317c576c8a0f7169c067enmittler                 */
4911864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (privateEntry != null
4921864f965419c5b14ac9317c576c8a0f7169c067enmittler                        && (keyAlgorithms.length != 1 || !"RSA".equals(keyAlgorithms[0]))) {
4931864f965419c5b14ac9317c576c8a0f7169c067enmittler                    throw new IllegalStateException(
4941864f965419c5b14ac9317c576c8a0f7169c067enmittler                            "Only reusing an existing key is implemented for RSA");
4951864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
4961864f965419c5b14ac9317c576c8a0f7169c067enmittler
4971864f965419c5b14ac9317c576c8a0f7169c067enmittler                KeyStore keyStore = createKeyStore();
4981864f965419c5b14ac9317c576c8a0f7169c067enmittler                for (String keyAlgorithm : keyAlgorithms) {
4991864f965419c5b14ac9317c576c8a0f7169c067enmittler                    String publicAlias = aliasPrefix + "-public-" + keyAlgorithm;
5001864f965419c5b14ac9317c576c8a0f7169c067enmittler                    String privateAlias = aliasPrefix + "-private-" + keyAlgorithm;
5011864f965419c5b14ac9317c576c8a0f7169c067enmittler                    if ((keyAlgorithm.equals("EC_RSA") || keyAlgorithm.equals("DH_RSA"))
5021864f965419c5b14ac9317c576c8a0f7169c067enmittler                            && signer == null && rootCa == null) {
5031864f965419c5b14ac9317c576c8a0f7169c067enmittler                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
5041864f965419c5b14ac9317c576c8a0f7169c067enmittler                                privateKey(keyStore, keyPassword, "RSA", "RSA"));
5051864f965419c5b14ac9317c576c8a0f7169c067enmittler                        continue;
5061864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else if (keyAlgorithm.equals("DH_DSA") && signer == null && rootCa == null) {
5071864f965419c5b14ac9317c576c8a0f7169c067enmittler                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
5081864f965419c5b14ac9317c576c8a0f7169c067enmittler                                privateKey(keyStore, keyPassword, "DSA", "DSA"));
5091864f965419c5b14ac9317c576c8a0f7169c067enmittler                        continue;
5101864f965419c5b14ac9317c576c8a0f7169c067enmittler                    }
5111864f965419c5b14ac9317c576c8a0f7169c067enmittler                    createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, privateEntry,
5121864f965419c5b14ac9317c576c8a0f7169c067enmittler                            signer);
5131864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
5141864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (rootCa != null) {
5151864f965419c5b14ac9317c576c8a0f7169c067enmittler                    keyStore.setCertificateEntry(
5161864f965419c5b14ac9317c576c8a0f7169c067enmittler                            aliasPrefix + "-root-ca-" + rootCa.getPublicKey().getAlgorithm(),
5171864f965419c5b14ac9317c576c8a0f7169c067enmittler                            rootCa);
5181864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
5191864f965419c5b14ac9317c576c8a0f7169c067enmittler                return new TestKeyStore(keyStore, storePassword, keyPassword);
5201864f965419c5b14ac9317c576c8a0f7169c067enmittler            } catch (Exception e) {
5211864f965419c5b14ac9317c576c8a0f7169c067enmittler                throw new RuntimeException(e);
5221864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
5231864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
5241864f965419c5b14ac9317c576c8a0f7169c067enmittler
5251864f965419c5b14ac9317c576c8a0f7169c067enmittler        /**
5261864f965419c5b14ac9317c576c8a0f7169c067enmittler         * Add newly generated keys of a given key type to an existing
5271864f965419c5b14ac9317c576c8a0f7169c067enmittler         * KeyStore. The PrivateKey will be stored under the specified
5281864f965419c5b14ac9317c576c8a0f7169c067enmittler         * private alias name. The X509Certificate will be stored on the
5291864f965419c5b14ac9317c576c8a0f7169c067enmittler         * public alias name and have the given subject distinguished
5301864f965419c5b14ac9317c576c8a0f7169c067enmittler         * name.
5311864f965419c5b14ac9317c576c8a0f7169c067enmittler         *
5321864f965419c5b14ac9317c576c8a0f7169c067enmittler         * If a CA is provided, it will be used to sign the generated
5331864f965419c5b14ac9317c576c8a0f7169c067enmittler         * certificate and OCSP responses. Otherwise, the certificate
5341864f965419c5b14ac9317c576c8a0f7169c067enmittler         * will be self signed. The certificate will be valid for one
5351864f965419c5b14ac9317c576c8a0f7169c067enmittler         * day before and one day after the time of creation.
5361864f965419c5b14ac9317c576c8a0f7169c067enmittler         *
5371864f965419c5b14ac9317c576c8a0f7169c067enmittler         * Based on:
5381864f965419c5b14ac9317c576c8a0f7169c067enmittler         * org.bouncycastle.jce.provider.test.SigTest
5391864f965419c5b14ac9317c576c8a0f7169c067enmittler         * org.bouncycastle.jce.provider.test.CertTest
5401864f965419c5b14ac9317c576c8a0f7169c067enmittler         */
5411864f965419c5b14ac9317c576c8a0f7169c067enmittler        private KeyStore createKeys(KeyStore keyStore, String keyAlgorithm, String publicAlias,
5421864f965419c5b14ac9317c576c8a0f7169c067enmittler                String privateAlias, PrivateKeyEntry privateEntry, PrivateKeyEntry signer)
5431864f965419c5b14ac9317c576c8a0f7169c067enmittler                throws Exception {
5441864f965419c5b14ac9317c576c8a0f7169c067enmittler            PrivateKey caKey;
5451864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate caCert;
5461864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate[] caCertChain;
5471864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (signer == null) {
5481864f965419c5b14ac9317c576c8a0f7169c067enmittler                caKey = null;
5491864f965419c5b14ac9317c576c8a0f7169c067enmittler                caCert = null;
5501864f965419c5b14ac9317c576c8a0f7169c067enmittler                caCertChain = null;
5511864f965419c5b14ac9317c576c8a0f7169c067enmittler            } else {
5521864f965419c5b14ac9317c576c8a0f7169c067enmittler                caKey = signer.getPrivateKey();
5531864f965419c5b14ac9317c576c8a0f7169c067enmittler                caCert = (X509Certificate) signer.getCertificate();
5541864f965419c5b14ac9317c576c8a0f7169c067enmittler                caCertChain = (X509Certificate[]) signer.getCertificateChain();
5551864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
5561864f965419c5b14ac9317c576c8a0f7169c067enmittler
5571864f965419c5b14ac9317c576c8a0f7169c067enmittler            final PrivateKey privateKey;
5581864f965419c5b14ac9317c576c8a0f7169c067enmittler            final PublicKey publicKey;
5591864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate x509c;
5601864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (publicAlias == null && privateAlias == null) {
5611864f965419c5b14ac9317c576c8a0f7169c067enmittler                // don't want anything apparently
5621864f965419c5b14ac9317c576c8a0f7169c067enmittler                privateKey = null;
5631864f965419c5b14ac9317c576c8a0f7169c067enmittler                publicKey = null;
5641864f965419c5b14ac9317c576c8a0f7169c067enmittler                x509c = null;
5651864f965419c5b14ac9317c576c8a0f7169c067enmittler            } else {
5661864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (privateEntry == null) {
5671864f965419c5b14ac9317c576c8a0f7169c067enmittler                    // 1a.) we make the keys
5681864f965419c5b14ac9317c576c8a0f7169c067enmittler                    int keySize = -1;
5691864f965419c5b14ac9317c576c8a0f7169c067enmittler                    AlgorithmParameterSpec spec = null;
5701864f965419c5b14ac9317c576c8a0f7169c067enmittler                    if (keyAlgorithm.equals("RSA")) {
5711864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keySize = RSA_KEY_SIZE_BITS;
5721864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else if (keyAlgorithm.equals("DH_RSA")) {
5731864f965419c5b14ac9317c576c8a0f7169c067enmittler                        spec = new DHParameterSpec(DH_PARAMS_P, DH_PARAMS_G);
5741864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keyAlgorithm = "DH";
5751864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else if (keyAlgorithm.equals("DSA")) {
5761864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keySize = DSA_KEY_SIZE_BITS;
5771864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else if (keyAlgorithm.equals("DH_DSA")) {
5781864f965419c5b14ac9317c576c8a0f7169c067enmittler                        spec = new DHParameterSpec(DH_PARAMS_P, DH_PARAMS_G);
5791864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keyAlgorithm = "DH";
5801864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else if (keyAlgorithm.equals("EC")) {
5811864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keySize = EC_KEY_SIZE_BITS;
5821864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else if (keyAlgorithm.equals("EC_RSA")) {
5831864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keySize = EC_KEY_SIZE_BITS;
5841864f965419c5b14ac9317c576c8a0f7169c067enmittler                        keyAlgorithm = "EC";
5851864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else {
5861864f965419c5b14ac9317c576c8a0f7169c067enmittler                        throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
5871864f965419c5b14ac9317c576c8a0f7169c067enmittler                    }
5881864f965419c5b14ac9317c576c8a0f7169c067enmittler
5891864f965419c5b14ac9317c576c8a0f7169c067enmittler                    KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm);
5901864f965419c5b14ac9317c576c8a0f7169c067enmittler                    if (spec != null) {
5911864f965419c5b14ac9317c576c8a0f7169c067enmittler                        kpg.initialize(spec);
5921864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else if (keySize != -1) {
5931864f965419c5b14ac9317c576c8a0f7169c067enmittler                        kpg.initialize(keySize);
5941864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } else {
5951864f965419c5b14ac9317c576c8a0f7169c067enmittler                        throw new AssertionError(
5961864f965419c5b14ac9317c576c8a0f7169c067enmittler                                "Must either have set algorithm parameters or key size!");
5971864f965419c5b14ac9317c576c8a0f7169c067enmittler                    }
5981864f965419c5b14ac9317c576c8a0f7169c067enmittler
5991864f965419c5b14ac9317c576c8a0f7169c067enmittler                    KeyPair kp = kpg.generateKeyPair();
6001864f965419c5b14ac9317c576c8a0f7169c067enmittler                    privateKey = kp.getPrivate();
6011864f965419c5b14ac9317c576c8a0f7169c067enmittler                    publicKey = kp.getPublic();
6021864f965419c5b14ac9317c576c8a0f7169c067enmittler                } else {
6031864f965419c5b14ac9317c576c8a0f7169c067enmittler                    // 1b.) we use the previous keys
6041864f965419c5b14ac9317c576c8a0f7169c067enmittler                    privateKey = privateEntry.getPrivateKey();
6051864f965419c5b14ac9317c576c8a0f7169c067enmittler                    publicKey = privateEntry.getCertificate().getPublicKey();
6061864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
6071864f965419c5b14ac9317c576c8a0f7169c067enmittler
6081864f965419c5b14ac9317c576c8a0f7169c067enmittler                // 2.) use keys to make certificate
6091864f965419c5b14ac9317c576c8a0f7169c067enmittler                X500Principal issuer =
6101864f965419c5b14ac9317c576c8a0f7169c067enmittler                        ((caCert != null) ? caCert.getSubjectX500Principal() : subject);
6111864f965419c5b14ac9317c576c8a0f7169c067enmittler                PrivateKey signingKey = (caKey == null) ? privateKey : caKey;
6121864f965419c5b14ac9317c576c8a0f7169c067enmittler                x509c = createCertificate(publicKey, signingKey, subject, issuer, keyUsage, ca,
6131864f965419c5b14ac9317c576c8a0f7169c067enmittler                        extendedKeyUsages, criticalExtendedKeyUsages, subjectAltNames,
6141864f965419c5b14ac9317c576c8a0f7169c067enmittler                        permittedNameConstraints, excludedNameConstraints, certificateSerialNumber);
6151864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
6161864f965419c5b14ac9317c576c8a0f7169c067enmittler
6171864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate[] x509cc;
6181864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (privateAlias == null) {
6191864f965419c5b14ac9317c576c8a0f7169c067enmittler                // don't need certificate chain
6201864f965419c5b14ac9317c576c8a0f7169c067enmittler                x509cc = null;
6211864f965419c5b14ac9317c576c8a0f7169c067enmittler            } else if (caCertChain == null) {
6221864f965419c5b14ac9317c576c8a0f7169c067enmittler                x509cc = new X509Certificate[] {x509c};
6231864f965419c5b14ac9317c576c8a0f7169c067enmittler            } else {
6241864f965419c5b14ac9317c576c8a0f7169c067enmittler                x509cc = new X509Certificate[caCertChain.length + 1];
6251864f965419c5b14ac9317c576c8a0f7169c067enmittler                x509cc[0] = x509c;
6261864f965419c5b14ac9317c576c8a0f7169c067enmittler                System.arraycopy(caCertChain, 0, x509cc, 1, caCertChain.length);
6271864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
6281864f965419c5b14ac9317c576c8a0f7169c067enmittler
6291864f965419c5b14ac9317c576c8a0f7169c067enmittler            // 3.) put certificate and private key into the key store
6301864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (privateAlias != null) {
6311864f965419c5b14ac9317c576c8a0f7169c067enmittler                keyStore.setKeyEntry(privateAlias, privateKey, keyPassword, x509cc);
6321864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
6331864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (publicAlias != null) {
6341864f965419c5b14ac9317c576c8a0f7169c067enmittler                keyStore.setCertificateEntry(publicAlias, x509c);
6351864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
6361864f965419c5b14ac9317c576c8a0f7169c067enmittler            return keyStore;
6371864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
6381864f965419c5b14ac9317c576c8a0f7169c067enmittler
6391864f965419c5b14ac9317c576c8a0f7169c067enmittler        private X500Principal localhost() {
6401864f965419c5b14ac9317c576c8a0f7169c067enmittler            return new X500Principal("CN=" + LOCAL_HOST_NAME);
6411864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
6421864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
6431864f965419c5b14ac9317c576c8a0f7169c067enmittler
6441864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static X509Certificate createCa(
6451864f965419c5b14ac9317c576c8a0f7169c067enmittler            PublicKey publicKey, PrivateKey privateKey, String subject) {
6461864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
6471864f965419c5b14ac9317c576c8a0f7169c067enmittler            X500Principal principal = new X500Principal(subject);
6481864f965419c5b14ac9317c576c8a0f7169c067enmittler            return createCertificate(publicKey, privateKey, principal, principal, 0, true,
6491864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new ArrayList<KeyPurposeId>(), new ArrayList<Boolean>(),
6501864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new ArrayList<GeneralName>(), new ArrayList<GeneralSubtree>(),
6511864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new ArrayList<GeneralSubtree>(), null /* serialNumber, generated randomly */);
6521864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
6531864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException(e);
6541864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
6551864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
6561864f965419c5b14ac9317c576c8a0f7169c067enmittler
6571864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static X509Certificate createCertificate(PublicKey publicKey, PrivateKey privateKey,
6581864f965419c5b14ac9317c576c8a0f7169c067enmittler            X500Principal subject, X500Principal issuer, int keyUsage, boolean ca,
6591864f965419c5b14ac9317c576c8a0f7169c067enmittler            List<KeyPurposeId> extendedKeyUsages, List<Boolean> criticalExtendedKeyUsages,
6601864f965419c5b14ac9317c576c8a0f7169c067enmittler            List<GeneralName> subjectAltNames, List<GeneralSubtree> permittedNameConstraints,
6611864f965419c5b14ac9317c576c8a0f7169c067enmittler            List<GeneralSubtree> excludedNameConstraints, BigInteger serialNumber)
6621864f965419c5b14ac9317c576c8a0f7169c067enmittler            throws Exception {
6631864f965419c5b14ac9317c576c8a0f7169c067enmittler        // Note that there is no way to programmatically make a
6641864f965419c5b14ac9317c576c8a0f7169c067enmittler        // Certificate using java.* or javax.* APIs. The
6651864f965419c5b14ac9317c576c8a0f7169c067enmittler        // CertificateFactory interface assumes you want to read
6661864f965419c5b14ac9317c576c8a0f7169c067enmittler        // in a stream of bytes, typically the X.509 factory would
6671864f965419c5b14ac9317c576c8a0f7169c067enmittler        // allow ASN.1 DER encoded bytes and optionally some PEM
6681864f965419c5b14ac9317c576c8a0f7169c067enmittler        // formats. Here we use Bouncy Castle's
6691864f965419c5b14ac9317c576c8a0f7169c067enmittler        // X509V3CertificateGenerator and related classes.
6701864f965419c5b14ac9317c576c8a0f7169c067enmittler
6711864f965419c5b14ac9317c576c8a0f7169c067enmittler        long millisPerDay = 24 * 60 * 60 * 1000;
6721864f965419c5b14ac9317c576c8a0f7169c067enmittler        long now = System.currentTimeMillis();
6731864f965419c5b14ac9317c576c8a0f7169c067enmittler        Date start = new Date(now - millisPerDay);
6741864f965419c5b14ac9317c576c8a0f7169c067enmittler        Date end = new Date(now + millisPerDay);
6751864f965419c5b14ac9317c576c8a0f7169c067enmittler
6761864f965419c5b14ac9317c576c8a0f7169c067enmittler        String keyAlgorithm = privateKey.getAlgorithm();
6771864f965419c5b14ac9317c576c8a0f7169c067enmittler        String signatureAlgorithm;
6781864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (keyAlgorithm.equals("RSA")) {
6791864f965419c5b14ac9317c576c8a0f7169c067enmittler            signatureAlgorithm = "sha1WithRSA";
6801864f965419c5b14ac9317c576c8a0f7169c067enmittler        } else if (keyAlgorithm.equals("DSA")) {
6811864f965419c5b14ac9317c576c8a0f7169c067enmittler            signatureAlgorithm = "sha1WithDSA";
6821864f965419c5b14ac9317c576c8a0f7169c067enmittler        } else if (keyAlgorithm.equals("EC")) {
6831864f965419c5b14ac9317c576c8a0f7169c067enmittler            signatureAlgorithm = "sha1WithECDSA";
6841864f965419c5b14ac9317c576c8a0f7169c067enmittler        } else if (keyAlgorithm.equals("EC_RSA")) {
6851864f965419c5b14ac9317c576c8a0f7169c067enmittler            signatureAlgorithm = "sha1WithRSA";
6861864f965419c5b14ac9317c576c8a0f7169c067enmittler        } else {
6871864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
6881864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
6891864f965419c5b14ac9317c576c8a0f7169c067enmittler
6901864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (serialNumber == null) {
6911864f965419c5b14ac9317c576c8a0f7169c067enmittler            byte[] serialBytes = new byte[16];
6921864f965419c5b14ac9317c576c8a0f7169c067enmittler            new SecureRandom().nextBytes(serialBytes);
6931864f965419c5b14ac9317c576c8a0f7169c067enmittler            serialNumber = new BigInteger(1, serialBytes);
6941864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
695027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root
696027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root        X509v3CertificateBuilder x509cg =
697027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root                new X509v3CertificateBuilder(X500Name.getInstance(issuer.getEncoded()),
698027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root                        serialNumber, start, end, X500Name.getInstance(subject.getEncoded()),
699027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root                        SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
7001864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (keyUsage != 0) {
701027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root            x509cg.addExtension(Extension.keyUsage, true, new KeyUsage(keyUsage));
7021864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7031864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (ca) {
704027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root            x509cg.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
7051864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7061864f965419c5b14ac9317c576c8a0f7169c067enmittler        for (int i = 0; i < extendedKeyUsages.size(); i++) {
7071864f965419c5b14ac9317c576c8a0f7169c067enmittler            KeyPurposeId keyPurposeId = extendedKeyUsages.get(i);
7081864f965419c5b14ac9317c576c8a0f7169c067enmittler            boolean critical = criticalExtendedKeyUsages.get(i);
7091864f965419c5b14ac9317c576c8a0f7169c067enmittler            x509cg.addExtension(
710027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root                    Extension.extendedKeyUsage, critical, new ExtendedKeyUsage(keyPurposeId));
7111864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7121864f965419c5b14ac9317c576c8a0f7169c067enmittler        for (GeneralName subjectAltName : subjectAltNames) {
713027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root            x509cg.addExtension(Extension.subjectAlternativeName, false,
7141864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new GeneralNames(subjectAltName).getEncoded());
7151864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7161864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (!permittedNameConstraints.isEmpty() || !excludedNameConstraints.isEmpty()) {
717027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root            x509cg.addExtension(Extension.nameConstraints, true,
7181864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new NameConstraints(
7191864f965419c5b14ac9317c576c8a0f7169c067enmittler                            permittedNameConstraints.toArray(
7201864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    new GeneralSubtree[permittedNameConstraints.size()]),
7211864f965419c5b14ac9317c576c8a0f7169c067enmittler                            excludedNameConstraints.toArray(
7221864f965419c5b14ac9317c576c8a0f7169c067enmittler                                    new GeneralSubtree[excludedNameConstraints.size()])));
7231864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7241864f965419c5b14ac9317c576c8a0f7169c067enmittler
725027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root        X509CertificateHolder x509holder =
726027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root                x509cg.build(new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey));
727027eb2c41cadcf47472d5c9a2e46eafa4a3440b9Kenny Root        X509Certificate x509c = new JcaX509CertificateConverter().getCertificate(x509holder);
7281864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (StandardNames.IS_RI) {
7291864f965419c5b14ac9317c576c8a0f7169c067enmittler            /*
7301864f965419c5b14ac9317c576c8a0f7169c067enmittler             * The RI can't handle the BC EC signature algorithm
7311864f965419c5b14ac9317c576c8a0f7169c067enmittler             * string of "ECDSA", since it expects "...WITHEC...",
7321864f965419c5b14ac9317c576c8a0f7169c067enmittler             * so convert from BC to RI X509Certificate
7331864f965419c5b14ac9317c576c8a0f7169c067enmittler             * implementation via bytes.
7341864f965419c5b14ac9317c576c8a0f7169c067enmittler             */
7351864f965419c5b14ac9317c576c8a0f7169c067enmittler            CertificateFactory cf = CertificateFactory.getInstance("X.509");
7361864f965419c5b14ac9317c576c8a0f7169c067enmittler            ByteArrayInputStream bais = new ByteArrayInputStream(x509c.getEncoded());
7371864f965419c5b14ac9317c576c8a0f7169c067enmittler            Certificate c = cf.generateCertificate(bais);
7381864f965419c5b14ac9317c576c8a0f7169c067enmittler            x509c = (X509Certificate) c;
7391864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7401864f965419c5b14ac9317c576c8a0f7169c067enmittler        return x509c;
7411864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
7421864f965419c5b14ac9317c576c8a0f7169c067enmittler
7431864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
7441864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the key algorithm for a possible compound algorithm
7451864f965419c5b14ac9317c576c8a0f7169c067enmittler     * identifier containing an underscore. If not underscore is
7461864f965419c5b14ac9317c576c8a0f7169c067enmittler     * present, the argument is returned unmodified. However for an
7471864f965419c5b14ac9317c576c8a0f7169c067enmittler     * algorithm such as EC_RSA, return EC.
7481864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
7491864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static String keyAlgorithm(String algorithm) {
7501864f965419c5b14ac9317c576c8a0f7169c067enmittler        int index = algorithm.indexOf('_');
7511864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (index == -1) {
7521864f965419c5b14ac9317c576c8a0f7169c067enmittler            return algorithm;
7531864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7541864f965419c5b14ac9317c576c8a0f7169c067enmittler        return algorithm.substring(0, index);
7551864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
7561864f965419c5b14ac9317c576c8a0f7169c067enmittler
7571864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
7581864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the signature algorithm for a possible compound
7591864f965419c5b14ac9317c576c8a0f7169c067enmittler     * algorithm identifier containing an underscore. If not
7601864f965419c5b14ac9317c576c8a0f7169c067enmittler     * underscore is present, the argument is returned
7611864f965419c5b14ac9317c576c8a0f7169c067enmittler     * unmodified. However for an algorithm such as EC_RSA, return
7621864f965419c5b14ac9317c576c8a0f7169c067enmittler     * RSA.
7631864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
7641864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static String signatureAlgorithm(String algorithm) {
7651864f965419c5b14ac9317c576c8a0f7169c067enmittler        int index = algorithm.indexOf('_');
7661864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (index == -1) {
7671864f965419c5b14ac9317c576c8a0f7169c067enmittler            return algorithm;
7681864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7691864f965419c5b14ac9317c576c8a0f7169c067enmittler        return algorithm.substring(index + 1, algorithm.length());
7701864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
7711864f965419c5b14ac9317c576c8a0f7169c067enmittler
7721864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
7731864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Create an empty KeyStore
7741864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
7751864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static KeyStore createKeyStore() {
7761864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
7771864f965419c5b14ac9317c576c8a0f7169c067enmittler            KeyStore keyStore = KeyStore.getInstance(StandardNames.KEY_STORE_ALGORITHM);
7781864f965419c5b14ac9317c576c8a0f7169c067enmittler            keyStore.load(null, null);
7791864f965419c5b14ac9317c576c8a0f7169c067enmittler            return keyStore;
7801864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
7811864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException(e);
7821864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
7831864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
7841864f965419c5b14ac9317c576c8a0f7169c067enmittler
7851864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
7861864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the only private key in a TestKeyStore for the given
7871864f965419c5b14ac9317c576c8a0f7169c067enmittler     * algorithms. Throws IllegalStateException if there are are more
7881864f965419c5b14ac9317c576c8a0f7169c067enmittler     * or less than one.
7891864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
7901864f965419c5b14ac9317c576c8a0f7169c067enmittler    public PrivateKeyEntry getPrivateKey(String keyAlgorithm, String signatureAlgorithm) {
7911864f965419c5b14ac9317c576c8a0f7169c067enmittler        return privateKey(keyStore, keyPassword, keyAlgorithm, signatureAlgorithm);
7921864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
7931864f965419c5b14ac9317c576c8a0f7169c067enmittler
7941864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
7951864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the only private key in a keystore for the given
7961864f965419c5b14ac9317c576c8a0f7169c067enmittler     * algorithms. Throws IllegalStateException if there are are more
7971864f965419c5b14ac9317c576c8a0f7169c067enmittler     * or less than one.
7981864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
7991864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static PrivateKeyEntry privateKey(
8001864f965419c5b14ac9317c576c8a0f7169c067enmittler            KeyStore keyStore, char[] keyPassword, String keyAlgorithm, String signatureAlgorithm) {
8011864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
8021864f965419c5b14ac9317c576c8a0f7169c067enmittler            PrivateKeyEntry found = null;
8031864f965419c5b14ac9317c576c8a0f7169c067enmittler            PasswordProtection password = new PasswordProtection(keyPassword);
8041864f965419c5b14ac9317c576c8a0f7169c067enmittler            for (String alias : Collections.list(keyStore.aliases())) {
8051864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!keyStore.entryInstanceOf(alias, PrivateKeyEntry.class)) {
8061864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
8071864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
8081864f965419c5b14ac9317c576c8a0f7169c067enmittler                PrivateKeyEntry privateKey = (PrivateKeyEntry) keyStore.getEntry(alias, password);
8091864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!privateKey.getPrivateKey().getAlgorithm().equals(keyAlgorithm)) {
8101864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
8111864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
8121864f965419c5b14ac9317c576c8a0f7169c067enmittler                X509Certificate certificate = (X509Certificate) privateKey.getCertificate();
8131864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!certificate.getSigAlgName().contains(signatureAlgorithm)) {
8141864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
8151864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
8161864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (found != null) {
8171864f965419c5b14ac9317c576c8a0f7169c067enmittler                    throw new IllegalStateException("KeyStore has more than one private key for"
8181864f965419c5b14ac9317c576c8a0f7169c067enmittler                            + " keyAlgorithm: " + keyAlgorithm + " signatureAlgorithm: "
8191864f965419c5b14ac9317c576c8a0f7169c067enmittler                            + signatureAlgorithm + "\nfirst: " + found.getPrivateKey()
8201864f965419c5b14ac9317c576c8a0f7169c067enmittler                            + "\nsecond: " + privateKey.getPrivateKey());
8211864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
8221864f965419c5b14ac9317c576c8a0f7169c067enmittler                found = privateKey;
8231864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
8241864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (found == null) {
8251864f965419c5b14ac9317c576c8a0f7169c067enmittler                throw new IllegalStateException("KeyStore contained no private key for"
826b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                        + " keyAlgorithm: " + keyAlgorithm
827b6aab0c31d784c009ff2e576cf1db38fbcf2b691Kenny Root                        + " signatureAlgorithm: " + signatureAlgorithm);
8281864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
8291864f965419c5b14ac9317c576c8a0f7169c067enmittler            return found;
8301864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
8311864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException("Problem getting key for " + keyAlgorithm + " and signature "
8321864f965419c5b14ac9317c576c8a0f7169c067enmittler                            + signatureAlgorithm,
8331864f965419c5b14ac9317c576c8a0f7169c067enmittler                    e);
8341864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
8351864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
8361864f965419c5b14ac9317c576c8a0f7169c067enmittler
8371864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
8381864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the issuing CA certificate of the given
8391864f965419c5b14ac9317c576c8a0f7169c067enmittler     * certificate. Throws IllegalStateException if there are are more
8401864f965419c5b14ac9317c576c8a0f7169c067enmittler     * or less than one.
8411864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
8421864f965419c5b14ac9317c576c8a0f7169c067enmittler    public Certificate getIssuer(Certificate cert) throws Exception {
8431864f965419c5b14ac9317c576c8a0f7169c067enmittler        return issuer(keyStore, cert);
8441864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
8451864f965419c5b14ac9317c576c8a0f7169c067enmittler
8461864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
8471864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the issuing CA certificate of the given
8481864f965419c5b14ac9317c576c8a0f7169c067enmittler     * certificate. Throws IllegalStateException if there are are more
8491864f965419c5b14ac9317c576c8a0f7169c067enmittler     * or less than one.
8501864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
8511864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static Certificate issuer(KeyStore keyStore, Certificate c) throws Exception {
8521864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (!(c instanceof X509Certificate)) {
8531864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new IllegalStateException("issuer requires an X509Certificate, found " + c);
8541864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
8551864f965419c5b14ac9317c576c8a0f7169c067enmittler        X509Certificate cert = (X509Certificate) c;
8561864f965419c5b14ac9317c576c8a0f7169c067enmittler
8571864f965419c5b14ac9317c576c8a0f7169c067enmittler        Certificate found = null;
8581864f965419c5b14ac9317c576c8a0f7169c067enmittler        for (String alias : Collections.list(keyStore.aliases())) {
8591864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (!keyStore.entryInstanceOf(alias, TrustedCertificateEntry.class)) {
8601864f965419c5b14ac9317c576c8a0f7169c067enmittler                continue;
8611864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
8621864f965419c5b14ac9317c576c8a0f7169c067enmittler            TrustedCertificateEntry certificateEntry =
8631864f965419c5b14ac9317c576c8a0f7169c067enmittler                    (TrustedCertificateEntry) keyStore.getEntry(alias, null);
8641864f965419c5b14ac9317c576c8a0f7169c067enmittler            Certificate certificate = certificateEntry.getTrustedCertificate();
8651864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (!(certificate instanceof X509Certificate)) {
8661864f965419c5b14ac9317c576c8a0f7169c067enmittler                continue;
8671864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
8681864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate x = (X509Certificate) certificate;
8691864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (!cert.getIssuerDN().equals(x.getSubjectDN())) {
8701864f965419c5b14ac9317c576c8a0f7169c067enmittler                continue;
8711864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
8721864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (found != null) {
8731864f965419c5b14ac9317c576c8a0f7169c067enmittler                throw new IllegalStateException("KeyStore has more than one issuing CA for " + cert
8741864f965419c5b14ac9317c576c8a0f7169c067enmittler                        + "\nfirst: " + found + "\nsecond: " + certificate);
8751864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
8761864f965419c5b14ac9317c576c8a0f7169c067enmittler            found = certificate;
8771864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
8781864f965419c5b14ac9317c576c8a0f7169c067enmittler        if (found == null) {
8791864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new IllegalStateException("KeyStore contained no issuing CA for " + cert);
8801864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
8811864f965419c5b14ac9317c576c8a0f7169c067enmittler        return found;
8821864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
8831864f965419c5b14ac9317c576c8a0f7169c067enmittler
8841864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
8851864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the only self-signed root certificate in a TestKeyStore
8861864f965419c5b14ac9317c576c8a0f7169c067enmittler     * for the given algorithm. Throws IllegalStateException if there
8871864f965419c5b14ac9317c576c8a0f7169c067enmittler     * are are more or less than one.
8881864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
8891864f965419c5b14ac9317c576c8a0f7169c067enmittler    public X509Certificate getRootCertificate(String algorithm) {
8901864f965419c5b14ac9317c576c8a0f7169c067enmittler        return rootCertificate(keyStore, algorithm);
8911864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
8921864f965419c5b14ac9317c576c8a0f7169c067enmittler
8931864f965419c5b14ac9317c576c8a0f7169c067enmittler    private static OCSPResp generateOCSPResponse(PrivateKeyEntry server, PrivateKeyEntry issuer,
8941864f965419c5b14ac9317c576c8a0f7169c067enmittler            CertificateStatus status) throws CertificateException {
8951864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
8961864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate serverCertJca = (X509Certificate) server.getCertificate();
8971864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate caCertJca = (X509Certificate) issuer.getCertificate();
8981864f965419c5b14ac9317c576c8a0f7169c067enmittler
8991864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509CertificateHolder caCert = new JcaX509CertificateHolder(caCertJca);
9001864f965419c5b14ac9317c576c8a0f7169c067enmittler
9011864f965419c5b14ac9317c576c8a0f7169c067enmittler            DigestCalculatorProvider digCalcProv = new BcDigestCalculatorProvider();
9021864f965419c5b14ac9317c576c8a0f7169c067enmittler            BasicOCSPRespBuilder basicBuilder = new BasicOCSPRespBuilder(
9031864f965419c5b14ac9317c576c8a0f7169c067enmittler                    SubjectPublicKeyInfo.getInstance(caCertJca.getPublicKey().getEncoded()),
9041864f965419c5b14ac9317c576c8a0f7169c067enmittler                    digCalcProv.get(CertificateID.HASH_SHA1));
9051864f965419c5b14ac9317c576c8a0f7169c067enmittler
9061864f965419c5b14ac9317c576c8a0f7169c067enmittler            CertificateID certId = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1),
9071864f965419c5b14ac9317c576c8a0f7169c067enmittler                    caCert, serverCertJca.getSerialNumber());
9081864f965419c5b14ac9317c576c8a0f7169c067enmittler
9091864f965419c5b14ac9317c576c8a0f7169c067enmittler            basicBuilder.addResponse(certId, status);
9101864f965419c5b14ac9317c576c8a0f7169c067enmittler
9111864f965419c5b14ac9317c576c8a0f7169c067enmittler            BasicOCSPResp resp = basicBuilder.build(
9121864f965419c5b14ac9317c576c8a0f7169c067enmittler                    new JcaContentSignerBuilder("SHA256withRSA").build(issuer.getPrivateKey()),
9131864f965419c5b14ac9317c576c8a0f7169c067enmittler                    null, new Date());
9141864f965419c5b14ac9317c576c8a0f7169c067enmittler
9151864f965419c5b14ac9317c576c8a0f7169c067enmittler            OCSPRespBuilder builder = new OCSPRespBuilder();
9161864f965419c5b14ac9317c576c8a0f7169c067enmittler            return builder.build(OCSPRespBuilder.SUCCESSFUL, resp);
9171864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
9181864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new CertificateException("cannot generate OCSP response", e);
9191864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
9201864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
9211864f965419c5b14ac9317c576c8a0f7169c067enmittler
9221864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static byte[] getOCSPResponseForGood(PrivateKeyEntry server, PrivateKeyEntry issuer)
9231864f965419c5b14ac9317c576c8a0f7169c067enmittler            throws CertificateException {
9241864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
9251864f965419c5b14ac9317c576c8a0f7169c067enmittler            return generateOCSPResponse(server, issuer, CertificateStatus.GOOD).getEncoded();
9261864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (IOException e) {
9271864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new CertificateException(e);
9281864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
9291864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
9301864f965419c5b14ac9317c576c8a0f7169c067enmittler
9311864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static byte[] getOCSPResponseForRevoked(PrivateKeyEntry server, PrivateKeyEntry issuer)
9321864f965419c5b14ac9317c576c8a0f7169c067enmittler            throws CertificateException {
9331864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
9341864f965419c5b14ac9317c576c8a0f7169c067enmittler            return generateOCSPResponse(
9351864f965419c5b14ac9317c576c8a0f7169c067enmittler                    server, issuer, new RevokedStatus(new Date(), CRLReason.keyCompromise))
9361864f965419c5b14ac9317c576c8a0f7169c067enmittler                    .getEncoded();
9371864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (IOException e) {
9381864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new CertificateException(e);
9391864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
9401864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
9411864f965419c5b14ac9317c576c8a0f7169c067enmittler
9421864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
9431864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Return the only self-signed root certificate in a keystore for
9441864f965419c5b14ac9317c576c8a0f7169c067enmittler     * the given algorithm. Throws IllegalStateException if there are
9451864f965419c5b14ac9317c576c8a0f7169c067enmittler     * are more or less than one.
9461864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
9471864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static X509Certificate rootCertificate(KeyStore keyStore, String algorithm) {
9481864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
9491864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate found = null;
9501864f965419c5b14ac9317c576c8a0f7169c067enmittler            for (String alias : Collections.list(keyStore.aliases())) {
9511864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!keyStore.entryInstanceOf(alias, TrustedCertificateEntry.class)) {
9521864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
9531864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
9541864f965419c5b14ac9317c576c8a0f7169c067enmittler                TrustedCertificateEntry certificateEntry =
9551864f965419c5b14ac9317c576c8a0f7169c067enmittler                        (TrustedCertificateEntry) keyStore.getEntry(alias, null);
9561864f965419c5b14ac9317c576c8a0f7169c067enmittler                Certificate certificate = certificateEntry.getTrustedCertificate();
9571864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!certificate.getPublicKey().getAlgorithm().equals(algorithm)) {
9581864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
9591864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
9601864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!(certificate instanceof X509Certificate)) {
9611864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
9621864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
9631864f965419c5b14ac9317c576c8a0f7169c067enmittler                X509Certificate x = (X509Certificate) certificate;
9641864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!x.getIssuerDN().equals(x.getSubjectDN())) {
9651864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
9661864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
9671864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (found != null) {
9681864f965419c5b14ac9317c576c8a0f7169c067enmittler                    throw new IllegalStateException("KeyStore has more than one root CA for "
9691864f965419c5b14ac9317c576c8a0f7169c067enmittler                            + algorithm + "\nfirst: " + found + "\nsecond: " + certificate);
9701864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
9711864f965419c5b14ac9317c576c8a0f7169c067enmittler                found = x;
9721864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
9731864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (found == null) {
9741864f965419c5b14ac9317c576c8a0f7169c067enmittler                throw new IllegalStateException("KeyStore contained no root CA for " + algorithm);
9751864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
9761864f965419c5b14ac9317c576c8a0f7169c067enmittler            return found;
9771864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
9781864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException(e);
9791864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
9801864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
9811864f965419c5b14ac9317c576c8a0f7169c067enmittler
9821864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
983ae6e905323df3356e110c15808c7d4102da554c8Nathan Mittler     * Return an {@code X509Certificate} that matches the given {@code alias}.
9841864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
9851864f965419c5b14ac9317c576c8a0f7169c067enmittler    public KeyStore.Entry getEntryByAlias(String alias) {
9861864f965419c5b14ac9317c576c8a0f7169c067enmittler        return entryByAlias(keyStore, alias);
9871864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
9881864f965419c5b14ac9317c576c8a0f7169c067enmittler
9891864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
9901864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Finds an entry in the keystore by the given alias.
9911864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
9921864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static KeyStore.Entry entryByAlias(KeyStore keyStore, String alias) {
9931864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
9941864f965419c5b14ac9317c576c8a0f7169c067enmittler            return keyStore.getEntry(alias, null);
9951864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
9961864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException(e);
9971864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
9981864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
9991864f965419c5b14ac9317c576c8a0f7169c067enmittler
10001864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
10011864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Create a client key store that only contains self-signed certificates but no private keys
10021864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
10031864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static KeyStore createClient(KeyStore caKeyStore) {
10041864f965419c5b14ac9317c576c8a0f7169c067enmittler        KeyStore clientKeyStore = createKeyStore();
10051864f965419c5b14ac9317c576c8a0f7169c067enmittler        copySelfSignedCertificates(clientKeyStore, caKeyStore);
10061864f965419c5b14ac9317c576c8a0f7169c067enmittler        return clientKeyStore;
10071864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
10081864f965419c5b14ac9317c576c8a0f7169c067enmittler
10091864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
10101864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Copy self-signed certificates from one key store to another.
10111864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Returns true if successful, false if no match found.
10121864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
10131864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static boolean copySelfSignedCertificates(KeyStore dst, KeyStore src) {
10141864f965419c5b14ac9317c576c8a0f7169c067enmittler        try {
10151864f965419c5b14ac9317c576c8a0f7169c067enmittler            boolean copied = false;
10161864f965419c5b14ac9317c576c8a0f7169c067enmittler            for (String alias : Collections.list(src.aliases())) {
10171864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!src.isCertificateEntry(alias)) {
10181864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
10191864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
10201864f965419c5b14ac9317c576c8a0f7169c067enmittler                X509Certificate cert = (X509Certificate) src.getCertificate(alias);
10211864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (!cert.getSubjectDN().equals(cert.getIssuerDN())) {
10221864f965419c5b14ac9317c576c8a0f7169c067enmittler                    continue;
10231864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
10241864f965419c5b14ac9317c576c8a0f7169c067enmittler                dst.setCertificateEntry(alias, cert);
10251864f965419c5b14ac9317c576c8a0f7169c067enmittler                copied = true;
10261864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
10271864f965419c5b14ac9317c576c8a0f7169c067enmittler            return copied;
10281864f965419c5b14ac9317c576c8a0f7169c067enmittler        } catch (Exception e) {
10291864f965419c5b14ac9317c576c8a0f7169c067enmittler            throw new RuntimeException(e);
10301864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
10311864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
10321864f965419c5b14ac9317c576c8a0f7169c067enmittler
10331864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
10341864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Copy named certificates from one key store to another.
10351864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Returns true if successful, false if no match found.
10361864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
10371864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static boolean copyCertificate(Principal subject, KeyStore dst, KeyStore src)
10381864f965419c5b14ac9317c576c8a0f7169c067enmittler            throws Exception {
10391864f965419c5b14ac9317c576c8a0f7169c067enmittler        for (String alias : Collections.list(src.aliases())) {
10401864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (!src.isCertificateEntry(alias)) {
10411864f965419c5b14ac9317c576c8a0f7169c067enmittler                continue;
10421864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
10431864f965419c5b14ac9317c576c8a0f7169c067enmittler            X509Certificate cert = (X509Certificate) src.getCertificate(alias);
10441864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (!cert.getSubjectDN().equals(subject)) {
10451864f965419c5b14ac9317c576c8a0f7169c067enmittler                continue;
10461864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
10471864f965419c5b14ac9317c576c8a0f7169c067enmittler            dst.setCertificateEntry(alias, cert);
10481864f965419c5b14ac9317c576c8a0f7169c067enmittler            return true;
10491864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
10501864f965419c5b14ac9317c576c8a0f7169c067enmittler        return false;
10511864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
10521864f965419c5b14ac9317c576c8a0f7169c067enmittler
10531864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
10541864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Dump a key store for debugging.
10551864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
10561864f965419c5b14ac9317c576c8a0f7169c067enmittler    public void dump(String context) throws KeyStoreException, NoSuchAlgorithmException {
10571864f965419c5b14ac9317c576c8a0f7169c067enmittler        dump(context, keyStore, keyPassword);
10581864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
10591864f965419c5b14ac9317c576c8a0f7169c067enmittler
10601864f965419c5b14ac9317c576c8a0f7169c067enmittler    /**
10611864f965419c5b14ac9317c576c8a0f7169c067enmittler     * Dump a key store for debugging.
10621864f965419c5b14ac9317c576c8a0f7169c067enmittler     */
10631864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static void dump(String context, KeyStore keyStore, char[] keyPassword)
10641864f965419c5b14ac9317c576c8a0f7169c067enmittler            throws KeyStoreException, NoSuchAlgorithmException {
10651864f965419c5b14ac9317c576c8a0f7169c067enmittler        PrintStream out = System.out;
10661864f965419c5b14ac9317c576c8a0f7169c067enmittler        out.println("context=" + context);
10671864f965419c5b14ac9317c576c8a0f7169c067enmittler        out.println("\tkeyStore=" + keyStore);
10681864f965419c5b14ac9317c576c8a0f7169c067enmittler        out.println("\tkeyStore.type=" + keyStore.getType());
10691864f965419c5b14ac9317c576c8a0f7169c067enmittler        out.println("\tkeyStore.provider=" + keyStore.getProvider());
10701864f965419c5b14ac9317c576c8a0f7169c067enmittler        out.println("\tkeyPassword=" + ((keyPassword == null) ? null : new String(keyPassword)));
10711864f965419c5b14ac9317c576c8a0f7169c067enmittler        out.println("\tsize=" + keyStore.size());
10721864f965419c5b14ac9317c576c8a0f7169c067enmittler        for (String alias : Collections.list(keyStore.aliases())) {
10731864f965419c5b14ac9317c576c8a0f7169c067enmittler            out.println("alias=" + alias);
10741864f965419c5b14ac9317c576c8a0f7169c067enmittler            out.println("\tcreationDate=" + keyStore.getCreationDate(alias));
10751864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (keyStore.isCertificateEntry(alias)) {
10761864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println("\tcertificate:");
10771864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println("==========================================");
10781864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println(keyStore.getCertificate(alias));
10791864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println("==========================================");
10801864f965419c5b14ac9317c576c8a0f7169c067enmittler                continue;
10811864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
10821864f965419c5b14ac9317c576c8a0f7169c067enmittler            if (keyStore.isKeyEntry(alias)) {
10831864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println("\tkey:");
10841864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println("==========================================");
10851864f965419c5b14ac9317c576c8a0f7169c067enmittler                String key;
10861864f965419c5b14ac9317c576c8a0f7169c067enmittler                try {
10871864f965419c5b14ac9317c576c8a0f7169c067enmittler                    key = ("Key retrieved using password\n" + keyStore.getKey(alias, keyPassword));
10881864f965419c5b14ac9317c576c8a0f7169c067enmittler                } catch (UnrecoverableKeyException e1) {
10891864f965419c5b14ac9317c576c8a0f7169c067enmittler                    try {
10901864f965419c5b14ac9317c576c8a0f7169c067enmittler                        key = ("Key retrieved without password\n" + keyStore.getKey(alias, null));
10911864f965419c5b14ac9317c576c8a0f7169c067enmittler                    } catch (UnrecoverableKeyException e2) {
10921864f965419c5b14ac9317c576c8a0f7169c067enmittler                        key = "Key could not be retrieved";
10931864f965419c5b14ac9317c576c8a0f7169c067enmittler                    }
10941864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
10951864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println(key);
10961864f965419c5b14ac9317c576c8a0f7169c067enmittler                out.println("==========================================");
10971864f965419c5b14ac9317c576c8a0f7169c067enmittler                Certificate[] chain = keyStore.getCertificateChain(alias);
10981864f965419c5b14ac9317c576c8a0f7169c067enmittler                if (chain == null) {
10991864f965419c5b14ac9317c576c8a0f7169c067enmittler                    out.println("No certificate chain associated with key");
11001864f965419c5b14ac9317c576c8a0f7169c067enmittler                    out.println("==========================================");
11011864f965419c5b14ac9317c576c8a0f7169c067enmittler                } else {
11021864f965419c5b14ac9317c576c8a0f7169c067enmittler                    for (int i = 0; i < chain.length; i++) {
11031864f965419c5b14ac9317c576c8a0f7169c067enmittler                        out.println("Certificate chain element #" + i);
11041864f965419c5b14ac9317c576c8a0f7169c067enmittler                        out.println(chain[i]);
11051864f965419c5b14ac9317c576c8a0f7169c067enmittler                        out.println("==========================================");
11061864f965419c5b14ac9317c576c8a0f7169c067enmittler                    }
11071864f965419c5b14ac9317c576c8a0f7169c067enmittler                }
11081864f965419c5b14ac9317c576c8a0f7169c067enmittler                continue;
11091864f965419c5b14ac9317c576c8a0f7169c067enmittler            }
11101864f965419c5b14ac9317c576c8a0f7169c067enmittler            out.println("\tunknown entry type");
11111864f965419c5b14ac9317c576c8a0f7169c067enmittler        }
11121864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
11131864f965419c5b14ac9317c576c8a0f7169c067enmittler
11141864f965419c5b14ac9317c576c8a0f7169c067enmittler    public static void assertChainLength(Object[] chain) {
11151864f965419c5b14ac9317c576c8a0f7169c067enmittler        /*
11161864f965419c5b14ac9317c576c8a0f7169c067enmittler         * Note chain is Object[] to support both
11171864f965419c5b14ac9317c576c8a0f7169c067enmittler         * java.security.cert.X509Certificate and
11181864f965419c5b14ac9317c576c8a0f7169c067enmittler         * javax.security.cert.X509Certificate
11191864f965419c5b14ac9317c576c8a0f7169c067enmittler         */
11201864f965419c5b14ac9317c576c8a0f7169c067enmittler        assertEquals(3, chain.length);
11211864f965419c5b14ac9317c576c8a0f7169c067enmittler    }
11221864f965419c5b14ac9317c576c8a0f7169c067enmittler}
1123