1860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root/*
2860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * Copyright (C) 2011 The Android Open Source Project
3860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root *
4860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * Licensed under the Apache License, Version 2.0 (the "License");
5860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * you may not use this file except in compliance with the License.
6860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * You may obtain a copy of the License at
7860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root *
8860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root *      http://www.apache.org/licenses/LICENSE-2.0
9860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root *
10860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * Unless required by applicable law or agreed to in writing, software
11860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * distributed under the License is distributed on an "AS IS" BASIS,
12860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * See the License for the specific language governing permissions and
14860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root * limitations under the License.
15860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root */
16860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
17860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootpackage org.conscrypt;
18860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
19860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.io.File;
20860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.io.FileWriter;
21d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Rootimport java.security.cert.Certificate;
22860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.security.cert.CertificateException;
23860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.security.cert.X509Certificate;
24860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.security.KeyStore;
25860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.security.MessageDigest;
26d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Rootimport java.security.Principal;
27860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.util.ArrayList;
28860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.util.Arrays;
29860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport java.util.List;
30d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Rootimport javax.net.ssl.SSLPeerUnverifiedException;
31d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Rootimport javax.net.ssl.SSLSession;
32d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Rootimport javax.net.ssl.SSLSessionContext;
33860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport javax.net.ssl.TrustManagerFactory;
34860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport javax.net.ssl.X509TrustManager;
35860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport junit.framework.TestCase;
36860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootimport libcore.java.security.TestKeyStore;
37860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
38860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Rootpublic class TrustManagerImplTest extends TestCase {
39860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
40860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private List<File> tmpFiles = new ArrayList<File>();
41860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
42860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private String getFingerprint(X509Certificate cert) throws Exception {
43860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        MessageDigest dgst = MessageDigest.getInstance("SHA512");
44860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        byte[] encoded = cert.getPublicKey().getEncoded();
45860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        byte[] fingerprint = dgst.digest(encoded);
46860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        return IntegralToString.bytesToHexString(fingerprint, false);
47860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
48860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
49860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private String writeTmpPinFile(String text) throws Exception {
50860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        File tmp = File.createTempFile("pins", null);
51860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        FileWriter fstream = new FileWriter(tmp);
52860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        fstream.write(text);
53860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        fstream.close();
54860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        tmpFiles.add(tmp);
55860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        return tmp.getPath();
56860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
57860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
58860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    @Override
59860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    public void tearDown() throws Exception {
60860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        try {
61860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            for (File f : tmpFiles) {
62860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root                f.delete();
63860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            }
64860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            tmpFiles.clear();
65860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        } finally {
66860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            super.tearDown();
67860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        }
68860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
69860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
70860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    /**
71860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root     * Ensure that our non-standard behavior of learning to trust new
72860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root     * intermediate CAs does not regress. http://b/3404902
73860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root     */
74860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    public void testLearnIntermediate() throws Exception {
75860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // chain3 should be server/intermediate/root
76860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
77860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain();
78860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate root = chain3[2];
79860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate intermediate = chain3[1];
80860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate server = chain3[0];
81860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain2 =  new X509Certificate[] { server, intermediate };
82860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain1 =  new X509Certificate[] { server };
83860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
84860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // Normal behavior
85860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain3,   trustManager(root));
86860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain2,   trustManager(root));
87860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertInvalid(chain1, trustManager(root));
88860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain3,   trustManager(intermediate));
89860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain2,   trustManager(intermediate));
90860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain1,   trustManager(intermediate));
91860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain3,   trustManager(server));
92860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain2,   trustManager(server));
93860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain1,   trustManager(server));
94860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
95860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // non-standard behavior
96860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509TrustManager tm = trustManager(root);
97860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // fail on short chain with only root trusted
98860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertInvalid(chain1, tm);
99860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // succeed on longer chain, learn intermediate
100860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain2, tm);
101860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // now we can validate the short chain
102860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain1, tm);
103860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
104860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
105860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    // We should ignore duplicate cruft in the certificate chain
106860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    // See https://code.google.com/p/android/issues/detail?id=52295 http://b/8313312
107860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    public void testDuplicateInChain() throws Exception {
108860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // chain3 should be server/intermediate/root
109860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
110860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain();
111860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate root = chain3[2];
112860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate intermediate = chain3[1];
113860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate server = chain3[0];
114860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
115860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain4 = new X509Certificate[] { server, intermediate,
116860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root                                                           server, intermediate
117860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        };
118860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValid(chain4, trustManager(root));
119860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
120860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
121860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    public void testGetFullChain() throws Exception {
122860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // build the trust manager
123860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
124860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain();
125860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate root = chain3[2];
126860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509TrustManager tm = trustManager(root);
127860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
128860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // build the chains we'll use for testing
129860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate intermediate = chain3[1];
130860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate server = chain3[0];
131860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain2 =  new X509Certificate[] { server, intermediate };
132860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain1 =  new X509Certificate[] { server };
133860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
134860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertTrue(tm instanceof TrustManagerImpl);
135860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        TrustManagerImpl tmi = (TrustManagerImpl) tm;
136d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        List<X509Certificate> certs = tmi.checkServerTrusted(chain2, "RSA", new MySSLSession(
137d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root                "purple.com"));
138860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertEquals(Arrays.asList(chain3), certs);
139d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        certs = tmi.checkServerTrusted(chain1, "RSA", new MySSLSession("purple.com"));
140860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertEquals(Arrays.asList(chain3), certs);
141860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
142860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
143860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    public void testCertPinning() throws Exception {
144860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // chain3 should be server/intermediate/root
145860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
146860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain3 = (X509Certificate[]) pke.getCertificateChain();
147860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate root = chain3[2];
148860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate intermediate = chain3[1];
149860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate server = chain3[0];
150860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain2 =  new X509Certificate[] { server, intermediate };
151860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        X509Certificate[] chain1 =  new X509Certificate[] { server };
152860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
153860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // test without a hostname, expecting failure
154860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertInvalidPinned(chain1, trustManager(root, "gugle.com", root), null);
155860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // test without a hostname, expecting success
156860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValidPinned(chain3, trustManager(root, "gugle.com", root), null, chain3);
157860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // test an unpinned hostname that should fail
158860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertInvalidPinned(chain1, trustManager(root, "gugle.com", root), "purple.com");
159860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // test an unpinned hostname that should succeed
160860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValidPinned(chain3, trustManager(root, "gugle.com", root), "purple.com", chain3);
161860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // test a pinned hostname that should fail
162860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertInvalidPinned(chain1, trustManager(intermediate, "gugle.com", root), "gugle.com");
163860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // test a pinned hostname that should succeed
164860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertValidPinned(chain2, trustManager(intermediate, "gugle.com", server), "gugle.com",
1659be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                          chain2);
1669be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        // test a pinned hostname that chains to user installed that should succeed
1679be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        assertValidPinned(chain2, trustManagerUserInstalled(
1689be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh            (X509Certificate)TestKeyStore.getIntermediateCa2().getPrivateKey("RSA", "RSA")
1699be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                .getCertificateChain()[1], intermediate, "gugle.com", server), "gugle.com",
1709be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                chain2, true);
171860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
172860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
173860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private X509TrustManager trustManager(X509Certificate ca) throws Exception {
174860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        KeyStore keyStore = TestKeyStore.createKeyStore();
175860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        keyStore.setCertificateEntry("alias", ca);
176860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
177860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
178860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
179860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        tmf.init(keyStore);
180860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        return (X509TrustManager) tmf.getTrustManagers()[0];
181860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
182860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
183860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private TrustManagerImpl trustManager(X509Certificate ca, String hostname, X509Certificate pin)
184860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root                                          throws Exception {
185860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // build the cert pin manager
186860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        CertPinManager cm = certManager(hostname, pin);
187860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // insert it into the trust manager
188860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        KeyStore keyStore = TestKeyStore.createKeyStore();
189860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        keyStore.setCertificateEntry("alias", ca);
190860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        return new TrustManagerImpl(keyStore, cm);
191860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
192860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
1939be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh    private TrustManagerImpl trustManagerUserInstalled(
1949be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        X509Certificate caKeyStore, X509Certificate caUserStore, String hostname,
1959be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        X509Certificate pin) throws Exception {
1969be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        // build the cert pin manager
1979be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        CertPinManager cm = certManager(hostname, pin);
1989be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh
1999be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        // install at least one cert in the store (requirement)
2009be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        KeyStore keyStore = TestKeyStore.createKeyStore();
2019be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        keyStore.setCertificateEntry("alias", caKeyStore);
2029be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh
2039be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        // install a cert into the user installed store
2049be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
2059be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        final File DIR_TEST = new File(DIR_TEMP, "test");
2069be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        final File system = new File(DIR_TEST, "system-test");
2079be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        final File added = new File(DIR_TEST, "added-test");
2089be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        final File deleted = new File(DIR_TEST, "deleted-test");
2099be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh
2109be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        TrustedCertificateStore tcs = new TrustedCertificateStore(system, added, deleted);
2119be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        added.mkdirs();
2129be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        tcs.installCertificate(caUserStore);
2139be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        return new TrustManagerImpl(keyStore, cm, tcs);
2149be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh    }
2159be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh
216860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private CertPinManager certManager(String hostname, X509Certificate pin) throws Exception {
217860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        String pinString = "";
218860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        if (pin != null) {
219860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            pinString = hostname + "=true|" + getFingerprint(pin);
220860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        }
221860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // write it to a pinfile
222860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        String path = writeTmpPinFile(pinString);
223860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        // build the certpinmanager
224860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        return new CertPinManager(path, new TrustedCertificateStore());
225860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
226860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
227860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private void assertValid(X509Certificate[] chain, X509TrustManager tm) throws Exception {
228860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        if (tm instanceof TrustManagerImpl) {
229860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            TrustManagerImpl tmi = (TrustManagerImpl) tm;
230860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            tmi.checkServerTrusted(chain, "RSA");
231860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        }
232860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        tm.checkServerTrusted(chain, "RSA");
233860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
234860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
235860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private void assertValidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname,
236860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root                                   X509Certificate[] fullChain) throws Exception {
2379be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh        assertValidPinned(chain, tm, hostname, fullChain, false);
2389be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh    }
2399be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh
2409be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh    private void assertValidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname,
2419be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                                   X509Certificate[] fullChain, boolean expectUserInstalled)
2429be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                                   throws Exception {
243860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        if (tm instanceof TrustManagerImpl) {
244860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            TrustManagerImpl tmi = (TrustManagerImpl) tm;
245d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            List<X509Certificate> checkedChain = tmi.checkServerTrusted(chain, "RSA",
246d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root                    new MySSLSession(hostname));
247860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            assertEquals(checkedChain, Arrays.asList(fullChain));
2489be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh            boolean chainContainsUserInstalled = false;
2499be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh            for (X509Certificate cert : checkedChain) {
2509be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                if (tmi.isUserAddedCertificate(cert)) {
2519be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                    chainContainsUserInstalled = true;
2529be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh                }
2539be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh            }
2549be0a37b7d5075e879159f25dc1d5c007e9cbc18William Luh            assertEquals(expectUserInstalled, chainContainsUserInstalled);
255860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        }
256860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        tm.checkServerTrusted(chain, "RSA");
257860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
258860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
259860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private void assertInvalid(X509Certificate[] chain, X509TrustManager tm) {
260860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        try {
261860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            tm.checkClientTrusted(chain, "RSA");
262860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            fail();
263860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        } catch (CertificateException expected) {
264860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        }
265860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        try {
266860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            tm.checkServerTrusted(chain, "RSA");
267860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            fail();
268860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        } catch (CertificateException expected) {
269860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        }
270860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
271860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root
272860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    private void assertInvalidPinned(X509Certificate[] chain, X509TrustManager tm, String hostname)
273860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root                                     throws Exception {
274860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        assertTrue(tm.getClass().getName(), tm instanceof TrustManagerImpl);
275860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        try {
276860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            TrustManagerImpl tmi = (TrustManagerImpl) tm;
277d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            tmi.checkServerTrusted(chain, "RSA", new MySSLSession(hostname));
278860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root            fail();
279860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        } catch (CertificateException expected) {
280860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root        }
281860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root    }
282d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
283d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root    private class MySSLSession implements SSLSession {
284d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        private final String hostname;
285d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
286d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public MySSLSession(String hostname) {
287d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            this.hostname = hostname;
288d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
289d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
290d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
291d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public int getApplicationBufferSize() {
292d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
293d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
294d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
295d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
296d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public String getCipherSuite() {
297d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
298d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
299d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
300d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
301d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public long getCreationTime() {
302d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
303d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
304d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
305d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
306d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public byte[] getId() {
307d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
308d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
309d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
310d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
311d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public long getLastAccessedTime() {
312d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
313d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
314d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
315d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
316d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public Certificate[] getLocalCertificates() {
317d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
318d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
319d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
320d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
321d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public Principal getLocalPrincipal() {
322d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
323d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
324d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
325d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
326d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public int getPacketBufferSize() {
327d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
328d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
329d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
330d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
331d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public javax.security.cert.X509Certificate[] getPeerCertificateChain()
332d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root                throws SSLPeerUnverifiedException {
333d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
334d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
335d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
336d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
337d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
338d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
339d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
340d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
341d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
342d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public String getPeerHost() {
343d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            return hostname;
344d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
345d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
346d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
347d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public int getPeerPort() {
348d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
349d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
350d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
351d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
352d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
353d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
354d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
355d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
356d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
357d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public String getProtocol() {
358d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
359d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
360d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
361d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
362d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public SSLSessionContext getSessionContext() {
363d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
364d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
365d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
366d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
367d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public Object getValue(String name) {
368d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
369d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
370d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
371d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
372d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public String[] getValueNames() {
373d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
374d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
375d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
376d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
377d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public void invalidate() {
378d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
379d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
380d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
381d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
382d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public boolean isValid() {
383d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
384d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
385d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
386d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
387d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public void putValue(String name, Object value) {
388d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
389d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
390d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root
391d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        @Override
392d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        public void removeValue(String name) {
393d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root            throw new UnsupportedOperationException();
394d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root        }
395d2cced8b10f5e4f600a5eb9464eba0da7c8f09deKenny Root    }
396860d2707ce126ef8f66e3eac7ceeab6d24218cd8Kenny Root}
397