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