• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.conscrypt;
18 
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.security.KeyStore;
28 import java.security.KeyStore.PrivateKeyEntry;
29 import java.security.KeyStore.TrustedCertificateEntry;
30 import java.security.PrivateKey;
31 import java.security.PublicKey;
32 import java.security.cert.Certificate;
33 import java.security.cert.CertificateFactory;
34 import java.security.cert.X509Certificate;
35 import java.util.Arrays;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Random;
39 import java.util.Set;
40 import java.util.concurrent.Callable;
41 import java.util.concurrent.ExecutorService;
42 import java.util.concurrent.Executors;
43 import java.util.concurrent.Future;
44 import java.util.concurrent.TimeUnit;
45 import java.util.concurrent.TimeoutException;
46 
47 import javax.security.auth.x500.X500Principal;
48 
49 import junit.framework.TestCase;
50 import libcore.java.security.TestKeyStore;
51 
52 public class TrustedCertificateStoreTest extends TestCase {
53     private static final Random tempFileRandom = new Random();
54 
55     private final File dirTest = new File(System.getProperty("java.io.tmpdir", "."),
56             "cert-store-test" + tempFileRandom.nextInt());
57     private final File dirSystem = new File(dirTest, "system");
58     private final File dirAdded = new File(dirTest, "added");
59     private final File dirDeleted = new File(dirTest, "removed");
60 
61     private static X509Certificate CA1;
62     private static X509Certificate CA2;
63 
64     private static KeyStore.PrivateKeyEntry PRIVATE;
65     private static X509Certificate[] CHAIN;
66 
67     private static X509Certificate CA3_WITH_CA1_SUBJECT;
68     private static String ALIAS_SYSTEM_CA1;
69     private static String ALIAS_SYSTEM_CA2;
70     private static String ALIAS_USER_CA1;
71     private static String ALIAS_USER_CA2;
72 
73     private static String ALIAS_SYSTEM_CHAIN0;
74     private static String ALIAS_SYSTEM_CHAIN1;
75     private static String ALIAS_SYSTEM_CHAIN2;
76     private static String ALIAS_USER_CHAIN0;
77     private static String ALIAS_USER_CHAIN1;
78     private static String ALIAS_USER_CHAIN2;
79 
80     private static String ALIAS_SYSTEM_CA3;
81     private static String ALIAS_SYSTEM_CA3_COLLISION;
82     private static String ALIAS_USER_CA3;
83     private static String ALIAS_USER_CA3_COLLISION;
84 
85     private static X509Certificate CERTLOOP_EE;
86     private static X509Certificate CERTLOOP_CA1;
87     private static X509Certificate CERTLOOP_CA2;
88     private static String ALIAS_USER_CERTLOOP_EE;
89     private static String ALIAS_USER_CERTLOOP_CA1;
90     private static String ALIAS_USER_CERTLOOP_CA2;
91 
92     private static X509Certificate MULTIPLE_ISSUERS_CA1;
93     private static X509Certificate MULTIPLE_ISSUERS_CA1_CROSS;
94     private static X509Certificate MULTIPLE_ISSUERS_CA2;
95     private static X509Certificate MULTIPLE_ISSUERS_EE;
96     private static String ALIAS_MULTIPLE_ISSUERS_CA1;
97     private static String ALIAS_MULTIPLE_ISSUERS_CA1_CROSS;
98     private static String ALIAS_MULTIPLE_ISSUERS_CA2;
99     private static String ALIAS_MULTIPLE_ISSUERS_EE;
100 
getCa1()101     private static X509Certificate getCa1() {
102         initCerts();
103         return CA1;
104     }
getCa2()105     private static X509Certificate getCa2() {
106         initCerts();
107         return CA2;
108     }
109 
getPrivate()110     private static KeyStore.PrivateKeyEntry getPrivate() {
111         initCerts();
112         return PRIVATE;
113     }
getChain()114     private static X509Certificate[] getChain() {
115         initCerts();
116         return CHAIN;
117     }
118 
getCa3WithCa1Subject()119     private static X509Certificate getCa3WithCa1Subject() {
120         initCerts();
121         return CA3_WITH_CA1_SUBJECT;
122     }
123 
getAliasSystemCa1()124     private static String getAliasSystemCa1() {
125         initCerts();
126         return ALIAS_SYSTEM_CA1;
127     }
getAliasSystemCa2()128     private static String getAliasSystemCa2() {
129         initCerts();
130         return ALIAS_SYSTEM_CA2;
131     }
getAliasUserCa1()132     private static String getAliasUserCa1() {
133         initCerts();
134         return ALIAS_USER_CA1;
135     }
getAliasUserCa2()136     private static String getAliasUserCa2() {
137         initCerts();
138         return ALIAS_USER_CA2;
139     }
140 
getAliasSystemChain0()141     private static String getAliasSystemChain0() {
142         initCerts();
143         return ALIAS_SYSTEM_CHAIN0;
144     }
getAliasSystemChain1()145     private static String getAliasSystemChain1() {
146         initCerts();
147         return ALIAS_SYSTEM_CHAIN1;
148     }
getAliasSystemChain2()149     private static String getAliasSystemChain2() {
150         initCerts();
151         return ALIAS_SYSTEM_CHAIN2;
152     }
getAliasUserChain0()153     private static String getAliasUserChain0() {
154         initCerts();
155         return ALIAS_USER_CHAIN0;
156     }
getAliasUserChain1()157     private static String getAliasUserChain1() {
158         initCerts();
159         return ALIAS_USER_CHAIN1;
160     }
getAliasUserChain2()161     private static String getAliasUserChain2() {
162         initCerts();
163         return ALIAS_USER_CHAIN2;
164     }
165 
getAliasSystemCa3()166     private static String getAliasSystemCa3() {
167         initCerts();
168         return ALIAS_SYSTEM_CA3;
169     }
getAliasSystemCa3Collision()170     private static String getAliasSystemCa3Collision() {
171         initCerts();
172         return ALIAS_SYSTEM_CA3_COLLISION;
173     }
getAliasUserCa3()174     private static String getAliasUserCa3() {
175         initCerts();
176         return ALIAS_USER_CA3;
177     }
getAliasUserCa3Collision()178     private static String getAliasUserCa3Collision() {
179         initCerts();
180         return ALIAS_USER_CA3_COLLISION;
181     }
getCertLoopEe()182     private static X509Certificate getCertLoopEe() {
183         initCerts();
184         return CERTLOOP_EE;
185     }
getCertLoopCa1()186     private static X509Certificate getCertLoopCa1() {
187         initCerts();
188         return CERTLOOP_CA1;
189     }
getCertLoopCa2()190     private static X509Certificate getCertLoopCa2() {
191         initCerts();
192         return CERTLOOP_CA2;
193     }
getAliasCertLoopEe()194     private static String getAliasCertLoopEe() {
195         initCerts();
196         return ALIAS_USER_CERTLOOP_EE;
197     }
getAliasCertLoopCa1()198     private static String getAliasCertLoopCa1() {
199         initCerts();
200         return ALIAS_USER_CERTLOOP_CA1;
201     }
getAliasCertLoopCa2()202     private static String getAliasCertLoopCa2() {
203         initCerts();
204         return ALIAS_USER_CERTLOOP_CA2;
205     }
getAliasMultipleIssuersCa1()206     private static String getAliasMultipleIssuersCa1() {
207         initCerts();
208         return ALIAS_MULTIPLE_ISSUERS_CA1;
209     }
getAliasMultipleIssuersCa2()210     private static String getAliasMultipleIssuersCa2() {
211         initCerts();
212         return ALIAS_MULTIPLE_ISSUERS_CA2;
213     }
getAliasMultipleIssuersCa1Cross()214     private static String getAliasMultipleIssuersCa1Cross() {
215         initCerts();
216         return ALIAS_MULTIPLE_ISSUERS_CA1_CROSS;
217     }
getAliasMultipleIssuersEe()218     private static String getAliasMultipleIssuersEe() {
219         initCerts();
220         return ALIAS_MULTIPLE_ISSUERS_EE;
221     }
getMultipleIssuersCa1()222     private static X509Certificate getMultipleIssuersCa1() {
223         initCerts();
224         return MULTIPLE_ISSUERS_CA1;
225     }
getMultipleIssuersCa2()226     private static X509Certificate getMultipleIssuersCa2() {
227         initCerts();
228         return MULTIPLE_ISSUERS_CA2;
229     }
getMultipleIssuersCa1Cross()230     private static X509Certificate getMultipleIssuersCa1Cross() {
231         initCerts();
232         return MULTIPLE_ISSUERS_CA1_CROSS;
233     }
getMultipleIssuersEe()234     private static X509Certificate getMultipleIssuersEe() {
235         initCerts();
236         return MULTIPLE_ISSUERS_EE;
237     }
238 
239     /**
240      * Lazily create shared test certificates.
241      */
initCerts()242     private static synchronized void initCerts() {
243         if (CA1 != null) {
244             return;
245         }
246         try {
247             CA1 = TestKeyStore.getClient().getRootCertificate("RSA");
248             CA2 = TestKeyStore.getClientCA2().getRootCertificate("RSA");
249             PRIVATE = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
250             CHAIN = (X509Certificate[]) PRIVATE.getCertificateChain();
251             CA3_WITH_CA1_SUBJECT = new TestKeyStore.Builder()
252                     .aliasPrefix("unused")
253                     .subject(CA1.getSubjectX500Principal())
254                     .ca(true)
255                     .build().getRootCertificate("RSA");
256 
257 
258             ALIAS_SYSTEM_CA1 = alias(false, CA1, 0);
259             ALIAS_SYSTEM_CA2 = alias(false, CA2, 0);
260             ALIAS_USER_CA1 = alias(true, CA1, 0);
261             ALIAS_USER_CA2 = alias(true, CA2, 0);
262 
263             ALIAS_SYSTEM_CHAIN0 = alias(false, getChain()[0], 0);
264             ALIAS_SYSTEM_CHAIN1 = alias(false, getChain()[1], 0);
265             ALIAS_SYSTEM_CHAIN2 = alias(false, getChain()[2], 0);
266             ALIAS_USER_CHAIN0 = alias(true, getChain()[0], 0);
267             ALIAS_USER_CHAIN1 = alias(true, getChain()[1], 0);
268             ALIAS_USER_CHAIN2 = alias(true, getChain()[2], 0);
269 
270             ALIAS_SYSTEM_CA3 = alias(false, CA3_WITH_CA1_SUBJECT, 0);
271             ALIAS_SYSTEM_CA3_COLLISION = alias(false, CA3_WITH_CA1_SUBJECT, 1);
272             ALIAS_USER_CA3 = alias(true, CA3_WITH_CA1_SUBJECT, 0);
273             ALIAS_USER_CA3_COLLISION = alias(true, CA3_WITH_CA1_SUBJECT, 1);
274 
275             /*
276              * The construction below is to build a certificate chain that has a loop
277              * in it:
278              *
279              *   EE ---> CA1 ---> CA2 ---+
280              *            ^              |
281              *            |              |
282              *            +--------------+
283              */
284             TestKeyStore certLoopTempCa1 = new TestKeyStore.Builder()
285                     .keyAlgorithms("RSA")
286                     .aliasPrefix("certloop-ca1")
287                     .subject("CN=certloop-ca1")
288                     .ca(true)
289                     .build();
290             Certificate certLoopTempCaCert1 = ((TrustedCertificateEntry) certLoopTempCa1
291                     .getEntryByAlias("certloop-ca1-public-RSA")).getTrustedCertificate();
292             PrivateKeyEntry certLoopCaKey1 = (PrivateKeyEntry) certLoopTempCa1
293                     .getEntryByAlias("certloop-ca1-private-RSA");
294 
295             TestKeyStore certLoopCa2 = new TestKeyStore.Builder()
296                     .keyAlgorithms("RSA")
297                     .aliasPrefix("certloop-ca2")
298                     .subject("CN=certloop-ca2")
299                     .rootCa(certLoopTempCaCert1)
300                     .signer(certLoopCaKey1)
301                     .ca(true)
302                     .build();
303             CERTLOOP_CA2 = (X509Certificate) ((TrustedCertificateEntry) certLoopCa2
304                     .getEntryByAlias("certloop-ca2-public-RSA")).getTrustedCertificate();
305             ALIAS_USER_CERTLOOP_CA2 = alias(true, CERTLOOP_CA2, 0);
306             PrivateKeyEntry certLoopCaKey2 = (PrivateKeyEntry) certLoopCa2
307                     .getEntryByAlias("certloop-ca2-private-RSA");
308 
309             TestKeyStore certLoopCa1 = new TestKeyStore.Builder()
310                     .keyAlgorithms("RSA")
311                     .aliasPrefix("certloop-ca1")
312                     .subject("CN=certloop-ca1")
313                     .privateEntry(certLoopCaKey1)
314                     .rootCa(CERTLOOP_CA2)
315                     .signer(certLoopCaKey2)
316                     .ca(true)
317                     .build();
318             CERTLOOP_CA1 = (X509Certificate) ((TrustedCertificateEntry) certLoopCa1
319                     .getEntryByAlias("certloop-ca1-public-RSA")).getTrustedCertificate();
320             ALIAS_USER_CERTLOOP_CA1 = alias(true, CERTLOOP_CA1, 0);
321 
322             TestKeyStore certLoopEe = new TestKeyStore.Builder()
323                     .keyAlgorithms("RSA")
324                     .aliasPrefix("certloop-ee")
325                     .subject("CN=certloop-ee")
326                     .rootCa(CERTLOOP_CA1)
327                     .signer(certLoopCaKey1)
328                     .build();
329             CERTLOOP_EE = (X509Certificate) ((TrustedCertificateEntry) certLoopEe
330                     .getEntryByAlias("certloop-ee-public-RSA")).getTrustedCertificate();
331             ALIAS_USER_CERTLOOP_EE = alias(true, CERTLOOP_EE, 0);
332 
333             /*
334              * The construction below creates a certificate with multiple possible issuer certs.
335              *
336              *    EE ----> CA1 ---> CA2
337              *
338              *    Where CA1 also exists in a self-issued form.
339              */
340             TestKeyStore multipleIssuersCa1 = new TestKeyStore.Builder()
341                     .keyAlgorithms("RSA")
342                     .aliasPrefix("multiple-issuers-ca1")
343                     .subject("CN=multiple-issuers-ca1")
344                     .ca(true)
345                     .build();
346             MULTIPLE_ISSUERS_CA1 = (X509Certificate) ((TrustedCertificateEntry) multipleIssuersCa1
347                     .getEntryByAlias("multiple-issuers-ca1-public-RSA")).getTrustedCertificate();
348             ALIAS_MULTIPLE_ISSUERS_CA1 = alias(false, MULTIPLE_ISSUERS_CA1, 0);
349             PrivateKeyEntry multipleIssuersCa1Key = (PrivateKeyEntry) multipleIssuersCa1
350                     .getEntryByAlias("multiple-issuers-ca1-private-RSA");
351 
352             TestKeyStore multipleIssuersCa2 = new TestKeyStore.Builder()
353                     .keyAlgorithms("RSA")
354                     .aliasPrefix("multiple-issuers-ca2")
355                     .subject("CN=multiple-issuers-ca2")
356                     .ca(true)
357                     .build();
358             MULTIPLE_ISSUERS_CA2 = (X509Certificate) ((TrustedCertificateEntry) multipleIssuersCa2
359                     .getEntryByAlias("multiple-issuers-ca2-public-RSA")).getTrustedCertificate();
360             ALIAS_MULTIPLE_ISSUERS_CA2 = alias(false, MULTIPLE_ISSUERS_CA2, 0);
361             PrivateKeyEntry multipleIssuersCa2Key = (PrivateKeyEntry) multipleIssuersCa2
362                     .getEntryByAlias("multiple-issuers-ca2-private-RSA");
363 
364             TestKeyStore multipleIssuersCa1SignedByCa2 = new TestKeyStore.Builder()
365                     .keyAlgorithms("RSA")
366                     .aliasPrefix("multiple-issuers-ca1")
367                     .subject("CN=multiple-issuers-ca1")
368                     .privateEntry(multipleIssuersCa1Key)
369                     .rootCa(MULTIPLE_ISSUERS_CA2)
370                     .signer(multipleIssuersCa2Key)
371                     .ca(true)
372                     .build();
373             MULTIPLE_ISSUERS_CA1_CROSS =
374                     (X509Certificate) ((TrustedCertificateEntry) multipleIssuersCa1SignedByCa2
375                             .getEntryByAlias("multiple-issuers-ca1-public-RSA"))
376                     .getTrustedCertificate();
377             ALIAS_MULTIPLE_ISSUERS_CA1_CROSS = alias(false, MULTIPLE_ISSUERS_CA1_CROSS, 1);
378 
379             TestKeyStore multipleIssuersEe = new TestKeyStore.Builder()
380                     .keyAlgorithms("RSA")
381                     .aliasPrefix("multiple-issuers-ee")
382                     .subject("CN=multiple-issuers-ee")
383                     .rootCa(MULTIPLE_ISSUERS_CA1)
384                     .signer(multipleIssuersCa1Key)
385                     .build();
386             MULTIPLE_ISSUERS_EE = (X509Certificate) ((TrustedCertificateEntry) multipleIssuersEe
387                     .getEntryByAlias("multiple-issuers-ee-public-RSA")).getTrustedCertificate();
388             ALIAS_MULTIPLE_ISSUERS_EE = alias(false, MULTIPLE_ISSUERS_EE, 0);
389         } catch (Exception e) {
390             throw new RuntimeException(e);
391         }
392     }
393 
394     private TrustedCertificateStore store;
395 
setUp()396     @Override protected void setUp() {
397         setupStore();
398     }
399 
setupStore()400     private void setupStore() {
401         dirSystem.mkdirs();
402         cleanStore();
403         createStore();
404     }
405 
createStore()406     private void createStore() {
407         store = new TrustedCertificateStore(dirSystem, dirAdded, dirDeleted);
408     }
409 
tearDown()410     @Override protected void tearDown() {
411         cleanStore();
412     }
413 
cleanStore()414     private void cleanStore() {
415         for (File dir : new File[] { dirSystem, dirAdded, dirDeleted, dirTest }) {
416             File[] files = dir.listFiles();
417             if (files == null) {
418                 continue;
419             }
420             for (File file : files) {
421                 assertTrue("Should delete " + file.getPath(), file.delete());
422             }
423         }
424         store = null;
425     }
426 
resetStore()427     private void resetStore() {
428         cleanStore();
429         setupStore();
430     }
431 
testEmptyDirectories()432     public void testEmptyDirectories() throws Exception {
433         assertEmpty();
434     }
435 
testOneSystemOneDeleted()436     public void testOneSystemOneDeleted() throws Exception {
437         install(getCa1(), getAliasSystemCa1());
438         store.deleteCertificateEntry(getAliasSystemCa1());
439         assertEmpty();
440         assertDeleted(getCa1(), getAliasSystemCa1());
441     }
442 
testTwoSystemTwoDeleted()443     public void testTwoSystemTwoDeleted() throws Exception {
444         install(getCa1(), getAliasSystemCa1());
445         store.deleteCertificateEntry(getAliasSystemCa1());
446         install(getCa2(), getAliasSystemCa2());
447         store.deleteCertificateEntry(getAliasSystemCa2());
448         assertEmpty();
449         assertDeleted(getCa1(), getAliasSystemCa1());
450         assertDeleted(getCa2(), getAliasSystemCa2());
451     }
452 
testPartialFileIsIgnored()453     public void testPartialFileIsIgnored() throws Exception {
454         File file = file(getAliasSystemCa1());
455         file.getParentFile().mkdirs();
456         OutputStream os = new FileOutputStream(file);
457         os.write(0);
458         os.close();
459         assertTrue(file.exists());
460         assertEmpty();
461         assertTrue(file.exists());
462     }
463 
assertEmpty()464     private void assertEmpty() throws Exception {
465         try {
466             store.getCertificate(null);
467             fail();
468         } catch (NullPointerException expected) {
469         }
470         assertNull(store.getCertificate(""));
471 
472         try {
473             store.getCreationDate(null);
474             fail();
475         } catch (NullPointerException expected) {
476         }
477         assertNull(store.getCreationDate(""));
478 
479         Set<String> s = store.aliases();
480         assertNotNull(s);
481         assertTrue(s.isEmpty());
482         assertAliases();
483 
484         Set<String> u = store.userAliases();
485         assertNotNull(u);
486         assertTrue(u.isEmpty());
487 
488         try {
489             store.containsAlias(null);
490             fail();
491         } catch (NullPointerException expected) {
492         }
493         assertFalse(store.containsAlias(""));
494 
495         assertNull(store.getCertificateAlias(null));
496         assertNull(store.getCertificateAlias(getCa1()));
497 
498         try {
499             store.getTrustAnchor(null);
500             fail();
501         } catch (NullPointerException expected) {
502         }
503         assertNull(store.getTrustAnchor(getCa1()));
504 
505         try {
506             store.findIssuer(null);
507             fail();
508         } catch (NullPointerException expected) {
509         }
510         assertNull(store.findIssuer(getCa1()));
511 
512         try {
513             store.installCertificate(null);
514             fail();
515         } catch (NullPointerException expected) {
516         }
517 
518         store.deleteCertificateEntry(null);
519         store.deleteCertificateEntry("");
520 
521         String[] userFiles = dirAdded.list();
522         assertTrue(userFiles == null || userFiles.length == 0);
523     }
524 
testTwoSystem()525     public void testTwoSystem() throws Exception {
526         testTwo(getCa1(), getAliasSystemCa1(),
527                 getCa2(), getAliasSystemCa2());
528     }
529 
testTwoUser()530     public void testTwoUser() throws Exception {
531         testTwo(getCa1(), getAliasUserCa1(),
532                 getCa2(), getAliasUserCa2());
533     }
534 
testOneSystemOneUser()535     public void testOneSystemOneUser() throws Exception {
536         testTwo(getCa1(), getAliasSystemCa1(),
537                 getCa2(), getAliasUserCa2());
538     }
539 
testTwoSystemSameSubject()540     public void testTwoSystemSameSubject() throws Exception {
541         testTwo(getCa1(), getAliasSystemCa1(),
542                 getCa3WithCa1Subject(), getAliasSystemCa3Collision());
543     }
544 
testTwoUserSameSubject()545     public void testTwoUserSameSubject() throws Exception {
546         testTwo(getCa1(), getAliasUserCa1(),
547                 getCa3WithCa1Subject(), getAliasUserCa3Collision());
548 
549         store.deleteCertificateEntry(getAliasUserCa1());
550         assertDeleted(getCa1(), getAliasUserCa1());
551         assertTombstone(getAliasUserCa1());
552         assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3Collision());
553         assertAliases(getAliasUserCa3Collision());
554 
555         store.deleteCertificateEntry(getAliasUserCa3Collision());
556         assertDeleted(getCa3WithCa1Subject(), getAliasUserCa3Collision());
557         assertNoTombstone(getAliasUserCa3Collision());
558         assertNoTombstone(getAliasUserCa1());
559         assertEmpty();
560     }
561 
testOneSystemOneUserSameSubject()562     public void testOneSystemOneUserSameSubject() throws Exception {
563         testTwo(getCa1(), getAliasSystemCa1(),
564                 getCa3WithCa1Subject(), getAliasUserCa3());
565         testTwo(getCa1(), getAliasUserCa1(),
566                 getCa3WithCa1Subject(), getAliasSystemCa3());
567     }
568 
testTwo(X509Certificate x1, String alias1, X509Certificate x2, String alias2)569     private void testTwo(X509Certificate x1, String alias1,
570                          X509Certificate x2, String alias2) {
571         install(x1, alias1);
572         install(x2, alias2);
573         assertRootCa(x1, alias1);
574         assertRootCa(x2, alias2);
575         assertAliases(alias1, alias2);
576     }
577 
578 
testOneSystemOneUserOneDeleted()579     public void testOneSystemOneUserOneDeleted() throws Exception {
580         install(getCa1(), getAliasSystemCa1());
581         store.installCertificate(getCa2());
582         store.deleteCertificateEntry(getAliasSystemCa1());
583         assertDeleted(getCa1(), getAliasSystemCa1());
584         assertRootCa(getCa2(), getAliasUserCa2());
585         assertAliases(getAliasUserCa2());
586     }
587 
testOneSystemOneUserOneDeletedSameSubject()588     public void testOneSystemOneUserOneDeletedSameSubject() throws Exception {
589         install(getCa1(), getAliasSystemCa1());
590         store.installCertificate(getCa3WithCa1Subject());
591         store.deleteCertificateEntry(getAliasSystemCa1());
592         assertDeleted(getCa1(), getAliasSystemCa1());
593         assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3());
594         assertAliases(getAliasUserCa3());
595     }
596 
testUserMaskingSystem()597     public void testUserMaskingSystem() throws Exception {
598         install(getCa1(), getAliasSystemCa1());
599         install(getCa1(), getAliasUserCa1());
600         assertMasked(getCa1(), getAliasSystemCa1());
601         assertRootCa(getCa1(), getAliasUserCa1());
602         assertAliases(getAliasSystemCa1(), getAliasUserCa1());
603     }
604 
testChain()605     public void testChain() throws Exception {
606         testChain(getAliasSystemChain1(), getAliasSystemChain2());
607         testChain(getAliasSystemChain1(), getAliasUserChain2());
608         testChain(getAliasUserChain1(), getAliasSystemCa1());
609         testChain(getAliasUserChain1(), getAliasUserChain2());
610     }
611 
testChain(String alias1, String alias2)612     private void testChain(String alias1, String alias2) throws Exception {
613         install(getChain()[1], alias1);
614         install(getChain()[2], alias2);
615         assertIntermediateCa(getChain()[1], alias1);
616         assertRootCa(getChain()[2], alias2);
617         assertAliases(alias1, alias2);
618         assertEquals(getChain()[2], store.findIssuer(getChain()[1]));
619         assertEquals(getChain()[1], store.findIssuer(getChain()[0]));
620 
621         X509Certificate[] expected = getChain();
622         List<X509Certificate> actualList = store.getCertificateChain(expected[0]);
623 
624         assertEquals("Generated CA list should be same length", expected.length, actualList.size());
625         for (int i = 0; i < expected.length; i++) {
626             assertEquals("Chain value should be the same for position " + i, expected[i],
627                     actualList.get(i));
628         }
629         resetStore();
630     }
631 
testMissingSystemDirectory()632     public void testMissingSystemDirectory() throws Exception {
633         cleanStore();
634         createStore();
635         assertEmpty();
636     }
637 
testWithExistingUserDirectories()638     public void testWithExistingUserDirectories() throws Exception {
639         dirAdded.mkdirs();
640         dirDeleted.mkdirs();
641         install(getCa1(), getAliasSystemCa1());
642         assertRootCa(getCa1(), getAliasSystemCa1());
643         assertAliases(getAliasSystemCa1());
644     }
645 
testIsTrustAnchorWithReissuedgetCa()646     public void testIsTrustAnchorWithReissuedgetCa() throws Exception {
647         PublicKey publicKey = getPrivate().getCertificate().getPublicKey();
648         PrivateKey privateKey = getPrivate().getPrivateKey();
649         String name = "CN=CA4";
650         X509Certificate ca1 = TestKeyStore.createCa(publicKey, privateKey, name);
651         Thread.sleep(1 * 1000); // wait to ensure CAs vary by expiration
652         X509Certificate ca2 = TestKeyStore.createCa(publicKey, privateKey, name);
653         assertFalse(ca1.equals(ca2));
654 
655         String systemAlias = alias(false, ca1, 0);
656         install(ca1, systemAlias);
657         assertRootCa(ca1, systemAlias);
658         assertEquals(ca1, store.getTrustAnchor(ca2));
659         assertEquals(ca1, store.findIssuer(ca2));
660         resetStore();
661 
662         String userAlias = alias(true, ca1, 0);
663         store.installCertificate(ca1);
664         assertRootCa(ca1, userAlias);
665         assertNotNull(store.getTrustAnchor(ca2));
666         assertEquals(ca1, store.findIssuer(ca2));
667         resetStore();
668     }
669 
testInstallEmpty()670     public void testInstallEmpty() throws Exception {
671         store.installCertificate(getCa1());
672         assertRootCa(getCa1(), getAliasUserCa1());
673         assertAliases(getAliasUserCa1());
674 
675         // reinstalling should not change anything
676         store.installCertificate(getCa1());
677         assertRootCa(getCa1(), getAliasUserCa1());
678         assertAliases(getAliasUserCa1());
679     }
680 
testInstallEmptySystemExists()681     public void testInstallEmptySystemExists() throws Exception {
682         install(getCa1(), getAliasSystemCa1());
683         assertRootCa(getCa1(), getAliasSystemCa1());
684         assertAliases(getAliasSystemCa1());
685 
686         // reinstalling should not affect system CA
687         store.installCertificate(getCa1());
688         assertRootCa(getCa1(), getAliasSystemCa1());
689         assertAliases(getAliasSystemCa1());
690 
691     }
692 
testInstallEmptyDeletedSystemExists()693     public void testInstallEmptyDeletedSystemExists() throws Exception {
694         install(getCa1(), getAliasSystemCa1());
695         store.deleteCertificateEntry(getAliasSystemCa1());
696         assertEmpty();
697         assertDeleted(getCa1(), getAliasSystemCa1());
698 
699         // installing should restore deleted system CA
700         store.installCertificate(getCa1());
701         assertRootCa(getCa1(), getAliasSystemCa1());
702         assertAliases(getAliasSystemCa1());
703     }
704 
testDeleteEmpty()705     public void testDeleteEmpty() throws Exception {
706         store.deleteCertificateEntry(getAliasSystemCa1());
707         assertEmpty();
708         assertDeleted(getCa1(), getAliasSystemCa1());
709     }
710 
testDeleteUser()711     public void testDeleteUser() throws Exception {
712         store.installCertificate(getCa1());
713         assertRootCa(getCa1(), getAliasUserCa1());
714         assertAliases(getAliasUserCa1());
715 
716         store.deleteCertificateEntry(getAliasUserCa1());
717         assertEmpty();
718         assertDeleted(getCa1(), getAliasUserCa1());
719         assertNoTombstone(getAliasUserCa1());
720     }
721 
testDeleteSystem()722     public void testDeleteSystem() throws Exception {
723         install(getCa1(), getAliasSystemCa1());
724         assertRootCa(getCa1(), getAliasSystemCa1());
725         assertAliases(getAliasSystemCa1());
726 
727         store.deleteCertificateEntry(getAliasSystemCa1());
728         assertEmpty();
729         assertDeleted(getCa1(), getAliasSystemCa1());
730 
731         // deleting again should not change anything
732         store.deleteCertificateEntry(getAliasSystemCa1());
733         assertEmpty();
734         assertDeleted(getCa1(), getAliasSystemCa1());
735     }
736 
testGetLoopedCert()737     public void testGetLoopedCert() throws Exception {
738         install(getCertLoopEe(), getAliasCertLoopEe());
739         install(getCertLoopCa1(), getAliasCertLoopCa1());
740         install(getCertLoopCa2(), getAliasCertLoopCa2());
741 
742         ExecutorService executor = Executors.newSingleThreadExecutor();
743         Future<List<X509Certificate>> future = executor
744                 .submit(new Callable<List<X509Certificate>>() {
745                     @Override
746                     public List<X509Certificate> call() throws Exception {
747                         return store.getCertificateChain(getCertLoopEe());
748                     }
749                 });
750         executor.shutdown();
751         final List<X509Certificate> certs;
752         try {
753             certs = future.get(10, TimeUnit.SECONDS);
754         } catch (TimeoutException e) {
755             fail("Could not finish building chain; possibly confused by loops");
756             return; // Not actually reached.
757         }
758         assertEquals(3, certs.size());
759         assertEquals(getCertLoopEe(), certs.get(0));
760         assertEquals(getCertLoopCa1(), certs.get(1));
761         assertEquals(getCertLoopCa2(), certs.get(2));
762     }
763 
testIsUserAddedCertificate()764     public void testIsUserAddedCertificate() throws Exception {
765         assertFalse(store.isUserAddedCertificate(getCa1()));
766         assertFalse(store.isUserAddedCertificate(getCa2()));
767         install(getCa1(), getAliasSystemCa1());
768         assertFalse(store.isUserAddedCertificate(getCa1()));
769         assertFalse(store.isUserAddedCertificate(getCa2()));
770         install(getCa1(), getAliasUserCa1());
771         assertTrue(store.isUserAddedCertificate(getCa1()));
772         assertFalse(store.isUserAddedCertificate(getCa2()));
773         install(getCa2(), getAliasUserCa2());
774         assertTrue(store.isUserAddedCertificate(getCa1()));
775         assertTrue(store.isUserAddedCertificate(getCa2()));
776         store.deleteCertificateEntry(getAliasUserCa1());
777         assertFalse(store.isUserAddedCertificate(getCa1()));
778         assertTrue(store.isUserAddedCertificate(getCa2()));
779         store.deleteCertificateEntry(getAliasUserCa2());
780         assertFalse(store.isUserAddedCertificate(getCa1()));
781         assertFalse(store.isUserAddedCertificate(getCa2()));
782     }
783 
testSystemCaCertsUseCorrectFileNames()784     public void testSystemCaCertsUseCorrectFileNames() throws Exception {
785         TrustedCertificateStore store = new TrustedCertificateStore();
786 
787         // Assert that all the certificates in the system cacerts directory are stored in files with
788         // expected names.
789         CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
790         File dir = new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts");
791         int systemCertFileCount = 0;
792         for (File actualFile : listFilesNoNull(dir)) {
793             if (!actualFile.isFile()) {
794                 continue;
795             }
796             systemCertFileCount++;
797             X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(
798                     new ByteArrayInputStream(readFully(actualFile)));
799 
800             File expectedFile = store.getCertificateFile(dir, cert);
801             assertEquals("System certificate stored in the wrong file",
802                     expectedFile.getAbsolutePath(), actualFile.getAbsolutePath());
803 
804             // The two statements below indirectly assert that the certificate can be looked up
805             // from a file (hopefully the same one as the expectedFile above). As opposed to
806             // getCertifiacteFile above, these are the actual methods used when verifying chain of
807             // trust. Thus, we assert that they work as expected for all system certificates.
808             assertNotNull("Issuer certificate not found for system certificate " + actualFile,
809                     store.findIssuer(cert));
810             assertNotNull("Trust anchor not found for system certificate " + actualFile,
811                     store.getTrustAnchor(cert));
812         }
813 
814         // Assert that all files corresponding to all system certs/aliases known to the store are
815         // present.
816         int systemCertAliasCount = 0;
817         for (String alias : store.aliases()) {
818             if (!TrustedCertificateStore.isSystem(alias)) {
819                 continue;
820             }
821             systemCertAliasCount++;
822             // Checking that the certificate is stored in a file is extraneous given the current
823             // implementation of the class under test. We do it just in case the implementation
824             // changes.
825             X509Certificate cert = (X509Certificate) store.getCertificate(alias);
826             File expectedFile = store.getCertificateFile(dir, cert);
827             if (!expectedFile.isFile()) {
828                 fail("Missing certificate file for alias " + alias
829                         + ": " + expectedFile.getAbsolutePath());
830             }
831         }
832 
833         assertEquals("Number of system cert files and aliases doesn't match",
834                 systemCertFileCount, systemCertAliasCount);
835     }
836 
testMultipleIssuers()837     public void testMultipleIssuers() throws Exception {
838         Set<X509Certificate> result;
839         install(getMultipleIssuersCa1(), getAliasMultipleIssuersCa1());
840         result = store.findAllIssuers(getMultipleIssuersEe());
841         assertEquals("Unexpected number of issuers found", 1, result.size());
842         assertTrue("findAllIssuers does not contain expected issuer",
843                 result.contains(getMultipleIssuersCa1()));
844         install(getMultipleIssuersCa1Cross(), getAliasMultipleIssuersCa1Cross());
845         result = store.findAllIssuers(getMultipleIssuersEe());
846         assertEquals("findAllIssuers did not return all issuers", 2, result.size());
847         assertTrue("findAllIssuers does not contain CA1",
848                 result.contains(getMultipleIssuersCa1()));
849         assertTrue("findAllIssuers does not contain CA1 signed by CA2",
850                 result.contains(getMultipleIssuersCa1Cross()));
851     }
852 
listFilesNoNull(File dir)853     private static File[] listFilesNoNull(File dir) {
854         File[] files = dir.listFiles();
855         return (files != null) ? files : new File[0];
856     }
857 
readFully(File file)858     private static byte[] readFully(File file) throws IOException {
859         InputStream in = null;
860         try {
861             in = new FileInputStream(file);
862             ByteArrayOutputStream out = new ByteArrayOutputStream();
863             byte[] buf = new byte[16384];
864             int chunkSize;
865             while ((chunkSize = in.read(buf)) != -1) {
866                 out.write(buf, 0, chunkSize);
867             }
868             return out.toByteArray();
869         } finally {
870             if (in != null) {
871                 in.close();
872             }
873         }
874     }
875 
assertRootCa(X509Certificate x, String alias)876     private void assertRootCa(X509Certificate x, String alias) {
877         assertIntermediateCa(x, alias);
878         assertEquals(x, store.findIssuer(x));
879     }
880 
assertTrusted(X509Certificate x, String alias)881     private void assertTrusted(X509Certificate x, String alias) {
882         assertEquals(x, store.getCertificate(alias));
883         assertEquals(file(alias).lastModified(), store.getCreationDate(alias).getTime());
884         assertTrue(store.containsAlias(alias));
885         assertEquals(x, store.getTrustAnchor(x));
886     }
887 
assertIntermediateCa(X509Certificate x, String alias)888     private void assertIntermediateCa(X509Certificate x, String alias) {
889         assertTrusted(x, alias);
890         assertEquals(alias, store.getCertificateAlias(x));
891     }
892 
assertMasked(X509Certificate x, String alias)893     private void assertMasked(X509Certificate x, String alias) {
894         assertTrusted(x, alias);
895         assertFalse(alias.equals(store.getCertificateAlias(x)));
896     }
897 
assertDeleted(X509Certificate x, String alias)898     private void assertDeleted(X509Certificate x, String alias) {
899         assertNull(store.getCertificate(alias));
900         assertFalse(store.containsAlias(alias));
901         assertNull(store.getCertificateAlias(x));
902         assertNull(store.getTrustAnchor(x));
903         assertEquals(store.allSystemAliases().contains(alias),
904                      store.getCertificate(alias, true) != null);
905     }
906 
assertTombstone(String alias)907     private void assertTombstone(String alias) {
908         assertTrue(TrustedCertificateStore.isUser(alias));
909         File file = file(alias);
910         assertTrue(file.exists());
911         assertEquals(0, file.length());
912     }
913 
assertNoTombstone(String alias)914     private void assertNoTombstone(String alias) {
915         assertTrue(TrustedCertificateStore.isUser(alias));
916         assertFalse(file(alias).exists());
917     }
918 
assertAliases(String... aliases)919     private void assertAliases(String... aliases) {
920         Set<String> expected = new HashSet<String>(Arrays.asList(aliases));
921         Set<String> actual = new HashSet<String>();
922         for (String alias : store.aliases()) {
923             boolean system = TrustedCertificateStore.isSystem(alias);
924             boolean user = TrustedCertificateStore.isUser(alias);
925             if (system || user) {
926                 assertEquals(system, store.allSystemAliases().contains(alias));
927                 assertEquals(user, store.userAliases().contains(alias));
928                 actual.add(alias);
929             } else {
930                 throw new AssertionError(alias);
931             }
932         }
933         assertEquals(expected, actual);
934     }
935 
936     /**
937      * format a certificate alias
938      */
alias(boolean user, X509Certificate x, int index)939     private static String alias(boolean user, X509Certificate x, int index) {
940         String prefix = user ? "user:" : "system:";
941 
942         X500Principal subject = x.getSubjectX500Principal();
943         int intHash = NativeCrypto.X509_NAME_hash_old(subject);
944         String strHash = Hex.intToHexString(intHash, 8);
945 
946         return prefix + strHash + '.' + index;
947     }
948 
949     /**
950      * Install certificate under specified alias
951      */
install(X509Certificate x, String alias)952     private void install(X509Certificate x, String alias) {
953         try {
954             File file = file(alias);
955             file.getParentFile().mkdirs();
956             OutputStream out = new FileOutputStream(file);
957             out.write(x.getEncoded());
958             out.close();
959         } catch (Exception e) {
960             throw new RuntimeException(e);
961         }
962     }
963 
964     /**
965      * Compute file for an alias
966      */
file(String alias)967     private File file(String alias) {
968         File dir;
969         if (TrustedCertificateStore.isSystem(alias)) {
970             dir = dirSystem;
971         } else if (TrustedCertificateStore.isUser(alias)) {
972             dir = dirAdded;
973         } else {
974             throw new IllegalArgumentException(alias);
975         }
976 
977         int index = alias.lastIndexOf(":");
978         if (index == -1) {
979             throw new IllegalArgumentException(alias);
980         }
981         String filename = alias.substring(index+1);
982 
983         return new File(dir, filename);
984     }
985 }
986