1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.javax.net.ssl;
18
19import com.android.org.bouncycastle.asn1.x509.KeyPurposeId;
20import java.security.InvalidAlgorithmParameterException;
21import java.security.KeyStore.PrivateKeyEntry;
22import java.security.KeyStore;
23import java.security.Provider;
24import java.security.Security;
25import java.security.cert.CertificateException;
26import java.security.cert.PKIXBuilderParameters;
27import java.security.cert.PKIXParameters;
28import java.security.cert.X509CertSelector;
29import java.security.cert.X509Certificate;
30import java.util.Set;
31import javax.net.ssl.CertPathTrustManagerParameters;
32import javax.net.ssl.ManagerFactoryParameters;
33import javax.net.ssl.TrustManager;
34import javax.net.ssl.TrustManagerFactory;
35import javax.net.ssl.X509TrustManager;
36import junit.framework.TestCase;
37import libcore.java.security.StandardNames;
38import libcore.java.security.TestKeyStore;
39
40public class TrustManagerFactoryTest extends TestCase {
41
42    private static final String [] KEY_TYPES = new String[] { "RSA", "DSA", "EC", "EC_RSA" };
43
44    private static TestKeyStore TEST_KEY_STORE;
45
46    // note the rare usage of DSA keys here in addition to RSA
47    private static TestKeyStore getTestKeyStore() throws Exception {
48        if (TEST_KEY_STORE == null) {
49            TEST_KEY_STORE = new TestKeyStore.Builder()
50                    .keyAlgorithms(KEY_TYPES)
51                    .aliasPrefix("rsa-dsa-ec")
52                    .build();
53        }
54        return TEST_KEY_STORE;
55    }
56
57    private static boolean supportsManagerFactoryParameters(String algorithm) {
58        return (StandardNames.IS_RI && algorithm.equals("PKIX"));
59    }
60
61    public void test_TrustManagerFactory_getDefaultAlgorithm() throws Exception {
62        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
63        assertEquals(StandardNames.TRUST_MANAGER_FACTORY_DEFAULT, algorithm);
64        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
65        test_TrustManagerFactory(tmf);
66    }
67
68    private static class UselessManagerFactoryParameters implements ManagerFactoryParameters {}
69
70    private void test_TrustManagerFactory(TrustManagerFactory tmf)
71            throws Exception {
72        assertNotNull(tmf);
73        assertNotNull(tmf.getAlgorithm());
74        assertNotNull(tmf.getProvider());
75
76        // before init
77        try {
78            tmf.getTrustManagers();
79            fail();
80        } catch (IllegalStateException expected) {
81        }
82
83        // init with null ManagerFactoryParameters
84        try {
85            tmf.init((ManagerFactoryParameters) null);
86            fail();
87        } catch (InvalidAlgorithmParameterException expected) {
88        }
89
90        // init with useless ManagerFactoryParameters
91        try {
92            tmf.init(new UselessManagerFactoryParameters());
93            fail();
94        } catch (InvalidAlgorithmParameterException expected) {
95        }
96
97        // init with PKIXParameters ManagerFactoryParameters
98        try {
99            PKIXParameters pp = new PKIXParameters(getTestKeyStore().keyStore);
100            CertPathTrustManagerParameters cptmp = new CertPathTrustManagerParameters(pp);
101            tmf.init(cptmp);
102            fail();
103        } catch (InvalidAlgorithmParameterException expected) {
104        }
105
106        // init with PKIXBuilderParameters ManagerFactoryParameters
107        X509CertSelector xcs = new X509CertSelector();
108        PKIXBuilderParameters pbp = new PKIXBuilderParameters(getTestKeyStore().keyStore, xcs);
109        CertPathTrustManagerParameters cptmp = new CertPathTrustManagerParameters(pbp);
110        if (supportsManagerFactoryParameters(tmf.getAlgorithm())) {
111            tmf.init(cptmp);
112            test_TrustManagerFactory_getTrustManagers(tmf);
113        } else {
114            try {
115                tmf.init(cptmp);
116                fail();
117            } catch (InvalidAlgorithmParameterException expected) {
118            }
119        }
120
121        // init with null for default KeyStore
122        tmf.init((KeyStore) null);
123        test_TrustManagerFactory_getTrustManagers(tmf);
124
125        // init with specific key store
126        tmf.init(getTestKeyStore().keyStore);
127        test_TrustManagerFactory_getTrustManagers(tmf);
128    }
129
130    private void test_TrustManagerFactory_getTrustManagers(TrustManagerFactory tmf)
131            throws Exception {
132        TrustManager[] trustManagers = tmf.getTrustManagers();
133        assertNotNull(trustManagers);
134        assertTrue(trustManagers.length > 0);
135        for (TrustManager trustManager : trustManagers) {
136            assertNotNull(trustManager);
137            if (trustManager instanceof X509TrustManager) {
138                test_X509TrustManager((X509TrustManager) trustManager);
139            }
140        }
141    }
142
143    private void test_X509TrustManager(X509TrustManager tm) throws Exception {
144        for (String keyType : KEY_TYPES) {
145            X509Certificate[] issuers = tm.getAcceptedIssuers();
146            assertNotNull(issuers);
147            assertTrue(issuers.length > 1);
148            assertNotSame(issuers, tm.getAcceptedIssuers());
149            boolean defaultTrustManager
150                    // RI de-duplicates certs from TrustedCertificateEntry and PrivateKeyEntry
151                    = issuers.length > (StandardNames.IS_RI ? 1 : 2) * KEY_TYPES.length;
152
153            String keyAlgName = TestKeyStore.keyAlgorithm(keyType);
154            String sigAlgName = TestKeyStore.signatureAlgorithm(keyType);
155            PrivateKeyEntry pke = getTestKeyStore().getPrivateKey(keyAlgName, sigAlgName);
156            X509Certificate[] chain = (X509Certificate[]) pke.getCertificateChain();
157            if (defaultTrustManager) {
158                try {
159                    tm.checkClientTrusted(chain, keyType);
160                    fail();
161                } catch (CertificateException expected) {
162                }
163                try {
164                    tm.checkServerTrusted(chain, keyType);
165                    fail();
166                } catch (CertificateException expected) {
167                }
168            } else {
169                tm.checkClientTrusted(chain, keyType);
170                tm.checkServerTrusted(chain, keyType);
171            }
172
173        }
174    }
175
176    public void test_TrustManagerFactory_getInstance() throws Exception {
177        Provider[] providers = Security.getProviders();
178        for (Provider provider : providers) {
179            Set<Provider.Service> services = provider.getServices();
180            for (Provider.Service service : services) {
181                String type = service.getType();
182                if (!type.equals("TrustManagerFactory")) {
183                    continue;
184                }
185                String algorithm = service.getAlgorithm();
186                {
187                    TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
188                    assertEquals(algorithm, tmf.getAlgorithm());
189                    test_TrustManagerFactory(tmf);
190                }
191
192                {
193                    TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm,
194                                                                          provider);
195                    assertEquals(algorithm, tmf.getAlgorithm());
196                    assertEquals(provider, tmf.getProvider());
197                    test_TrustManagerFactory(tmf);
198                }
199
200                {
201                    TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm,
202                                                                          provider.getName());
203                    assertEquals(algorithm, tmf.getAlgorithm());
204                    assertEquals(provider, tmf.getProvider());
205                    test_TrustManagerFactory(tmf);
206                }
207            }
208        }
209    }
210
211    public void test_TrustManagerFactory_intermediate() throws Exception {
212        // chain should be server/intermediate/root
213        PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
214        X509Certificate[] chain = (X509Certificate[])pke.getCertificateChain();
215        assertEquals(3, chain.length);
216
217        // keyStore should contain only the intermediate CA so we can
218        // test proper validation even if there are extra certs after
219        // the trusted one (in this case the original root is "extra")
220        KeyStore keyStore = TestKeyStore.createKeyStore();
221        keyStore.setCertificateEntry("alias", chain[1]);
222
223        Provider[] providers = Security.getProviders();
224        for (Provider provider : providers) {
225            Set<Provider.Service> services = provider.getServices();
226            for (Provider.Service service : services) {
227                String type = service.getType();
228                if (!type.equals("TrustManagerFactory")) {
229                    continue;
230                }
231                String algorithm = service.getAlgorithm();
232                TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
233                tmf.init(keyStore);
234                TrustManager[] trustManagers = tmf.getTrustManagers();
235                for (TrustManager trustManager : trustManagers) {
236                    if (!(trustManager instanceof X509TrustManager)) {
237                        continue;
238                    }
239                    X509TrustManager tm = (X509TrustManager) trustManager;
240                    tm.checkClientTrusted(chain, "RSA");
241                    tm.checkServerTrusted(chain, "RSA");
242                }
243            }
244        }
245    }
246
247    public void test_TrustManagerFactory_keyOnly() throws Exception {
248        // create a KeyStore containing only a private key with chain.
249        // unlike PKIXParameters(KeyStore), the cert chain of the key should be trusted.
250        KeyStore ks = TestKeyStore.createKeyStore();
251        KeyStore.PrivateKeyEntry pke = getTestKeyStore().getPrivateKey("RSA", "RSA");
252        ks.setKeyEntry("key", pke.getPrivateKey(), "pw".toCharArray(), pke.getCertificateChain());
253
254        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
255        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
256        tmf.init(ks);
257        X509TrustManager trustManager = (X509TrustManager) tmf.getTrustManagers()[0];
258        trustManager.checkServerTrusted((X509Certificate[]) pke.getCertificateChain(), "RSA");
259    }
260
261    public void test_TrustManagerFactory_extendedKeyUsage() throws Exception {
262        // anyExtendedKeyUsage should work for client or server
263        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.anyExtendedKeyUsage, false, true, true);
264        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.anyExtendedKeyUsage, true,  true, true);
265
266        // critical clientAuth should work for client
267        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_clientAuth,    false, true, false);
268        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_clientAuth,    true,  true, false);
269
270        // critical serverAuth should work for server
271        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_serverAuth,    false, false, true);
272        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_serverAuth,    true,  false, true);
273
274        // codeSigning should not work
275        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_codeSigning,   false, false, false);
276        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_codeSigning,   true,  false, false);
277    }
278
279    private void test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId keyPurposeId,
280                                                           boolean critical,
281                                                           boolean client,
282                                                           boolean server)
283            throws Exception {
284        String algorithm = "RSA";
285        TestKeyStore intermediateCa = TestKeyStore.getIntermediateCa();
286        TestKeyStore leaf = new TestKeyStore.Builder()
287                .keyAlgorithms(new String[] { algorithm })
288                .aliasPrefix("criticalCodeSigning")
289                .signer(intermediateCa.getPrivateKey("RSA", "RSA"))
290                .rootCa(intermediateCa.getRootCertificate("RSA"))
291                .addExtendedKeyUsage(keyPurposeId, critical)
292                .build();
293        // leaf.dump("test_TrustManagerFactory_criticalCodeSigning");
294        PrivateKeyEntry privateKeyEntry = leaf.getPrivateKey(algorithm, algorithm);
295        X509Certificate[] chain = (X509Certificate[]) privateKeyEntry.getCertificateChain();
296
297        TestKeyStore rootCa = TestKeyStore.getRootCa();
298        X509TrustManager trustManager = (X509TrustManager) rootCa.trustManagers[0];
299        try {
300            trustManager.checkClientTrusted(chain, algorithm);
301            assertTrue(client);
302        } catch (Exception e) {
303            assertFalse(client);
304        }
305        try {
306            trustManager.checkServerTrusted(chain, algorithm);
307            assertTrue(server);
308        } catch (Exception e) {
309            assertFalse(server);
310        }
311    }
312}
313