Home | History | Annotate | Download | only in jsse
      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.apache.harmony.xnet.provider.jsse;
     18 
     19 import java.io.File;
     20 import java.io.FileOutputStream;
     21 import java.io.OutputStream;
     22 import java.security.KeyStore;
     23 import java.security.PrivateKey;
     24 import java.security.PublicKey;
     25 import java.security.cert.X509Certificate;
     26 import java.util.Arrays;
     27 import java.util.Collections;
     28 import java.util.Enumeration;
     29 import java.util.HashSet;
     30 import java.util.NoSuchElementException;
     31 import java.util.Set;
     32 import javax.security.auth.x500.X500Principal;
     33 import junit.framework.TestCase;
     34 import libcore.java.security.TestKeyStore;
     35 
     36 public class TrustedCertificateStoreTest extends TestCase {
     37 
     38     private static final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
     39     private static final File DIR_TEST = new File(DIR_TEMP, "test");
     40     private static final File DIR_SYSTEM = new File(DIR_TEST, "system");
     41     private static final File DIR_ADDED = new File(DIR_TEST, "added");
     42     private static final File DIR_DELETED = new File(DIR_TEST, "removed");
     43 
     44     private static X509Certificate CA1;
     45     private static X509Certificate CA2;
     46 
     47     private static KeyStore.PrivateKeyEntry PRIVATE;
     48     private static X509Certificate[] CHAIN;
     49 
     50     private static X509Certificate CA3_WITH_CA1_SUBJECT;
     51     private static String ALIAS_SYSTEM_CA1;
     52     private static String ALIAS_SYSTEM_CA2;
     53     private static String ALIAS_USER_CA1;
     54     private static String ALIAS_USER_CA2;
     55 
     56     private static String ALIAS_SYSTEM_CHAIN0;
     57     private static String ALIAS_SYSTEM_CHAIN1;
     58     private static String ALIAS_SYSTEM_CHAIN2;
     59     private static String ALIAS_USER_CHAIN0;
     60     private static String ALIAS_USER_CHAIN1;
     61     private static String ALIAS_USER_CHAIN2;
     62 
     63     private static String ALIAS_SYSTEM_CA3;
     64     private static String ALIAS_SYSTEM_CA3_COLLISION;
     65     private static String ALIAS_USER_CA3;
     66     private static String ALIAS_USER_CA3_COLLISION;
     67 
     68     private static X509Certificate getCa1() {
     69         initCerts();
     70         return CA1;
     71     }
     72     private static X509Certificate getCa2() {
     73         initCerts();
     74         return CA2;
     75     }
     76 
     77     private static KeyStore.PrivateKeyEntry getPrivate() {
     78         initCerts();
     79         return PRIVATE;
     80     }
     81     private static X509Certificate[] getChain() {
     82         initCerts();
     83         return CHAIN;
     84     }
     85 
     86     private static X509Certificate getCa3WithCa1Subject() {
     87         initCerts();
     88         return CA3_WITH_CA1_SUBJECT;
     89     }
     90 
     91     private static String getAliasSystemCa1() {
     92         initCerts();
     93         return ALIAS_SYSTEM_CA1;
     94     }
     95     private static String getAliasSystemCa2() {
     96         initCerts();
     97         return ALIAS_SYSTEM_CA2;
     98     }
     99     private static String getAliasUserCa1() {
    100         initCerts();
    101         return ALIAS_USER_CA1;
    102     }
    103     private static String getAliasUserCa2() {
    104         initCerts();
    105         return ALIAS_USER_CA2;
    106     }
    107 
    108     private static String getAliasSystemChain0() {
    109         initCerts();
    110         return ALIAS_SYSTEM_CHAIN0;
    111     }
    112     private static String getAliasSystemChain1() {
    113         initCerts();
    114         return ALIAS_SYSTEM_CHAIN1;
    115     }
    116     private static String getAliasSystemChain2() {
    117         initCerts();
    118         return ALIAS_SYSTEM_CHAIN2;
    119     }
    120     private static String getAliasUserChain0() {
    121         initCerts();
    122         return ALIAS_USER_CHAIN0;
    123     }
    124     private static String getAliasUserChain1() {
    125         initCerts();
    126         return ALIAS_USER_CHAIN1;
    127     }
    128     private static String getAliasUserChain2() {
    129         initCerts();
    130         return ALIAS_USER_CHAIN2;
    131     }
    132 
    133     private static String getAliasSystemCa3() {
    134         initCerts();
    135         return ALIAS_SYSTEM_CA3;
    136     }
    137     private static String getAliasSystemCa3Collision() {
    138         initCerts();
    139         return ALIAS_SYSTEM_CA3_COLLISION;
    140     }
    141     private static String getAliasUserCa3() {
    142         initCerts();
    143         return ALIAS_USER_CA3;
    144     }
    145     private static String getAliasUserCa3Collision() {
    146         initCerts();
    147         return ALIAS_USER_CA3_COLLISION;
    148     }
    149 
    150     /**
    151      * Lazily create shared test certificates.
    152      */
    153     private static synchronized void initCerts() {
    154         if (CA1 != null) {
    155             return;
    156         }
    157         try {
    158             CA1 = TestKeyStore.getClient().getRootCertificate("RSA");
    159             CA2 = TestKeyStore.getClientCA2().getRootCertificate("RSA");
    160             PRIVATE = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
    161             CHAIN = (X509Certificate[]) PRIVATE.getCertificateChain();
    162             CA3_WITH_CA1_SUBJECT = new TestKeyStore.Builder()
    163                     .aliasPrefix("unused")
    164                     .subject(CA1.getSubjectX500Principal())
    165                     .ca(true)
    166                     .build().getRootCertificate("RSA");
    167 
    168 
    169             ALIAS_SYSTEM_CA1 = alias(false, CA1, 0);
    170             ALIAS_SYSTEM_CA2 = alias(false, CA2, 0);
    171             ALIAS_USER_CA1 = alias(true, CA1, 0);
    172             ALIAS_USER_CA2 = alias(true, CA2, 0);
    173 
    174             ALIAS_SYSTEM_CHAIN0 = alias(false, getChain()[0], 0);
    175             ALIAS_SYSTEM_CHAIN1 = alias(false, getChain()[1], 0);
    176             ALIAS_SYSTEM_CHAIN2 = alias(false, getChain()[2], 0);
    177             ALIAS_USER_CHAIN0 = alias(true, getChain()[0], 0);
    178             ALIAS_USER_CHAIN1 = alias(true, getChain()[1], 0);
    179             ALIAS_USER_CHAIN2 = alias(true, getChain()[2], 0);
    180 
    181             ALIAS_SYSTEM_CA3 = alias(false, CA3_WITH_CA1_SUBJECT, 0);
    182             ALIAS_SYSTEM_CA3_COLLISION = alias(false, CA3_WITH_CA1_SUBJECT, 1);
    183             ALIAS_USER_CA3 = alias(true, CA3_WITH_CA1_SUBJECT, 0);
    184             ALIAS_USER_CA3_COLLISION = alias(true, CA3_WITH_CA1_SUBJECT, 1);
    185         } catch (Exception e) {
    186             throw new RuntimeException(e);
    187         }
    188     }
    189 
    190     private TrustedCertificateStore store;
    191 
    192     @Override protected void setUp() {
    193         setupStore();
    194     }
    195 
    196     private void setupStore() {
    197         DIR_SYSTEM.mkdirs();
    198         createStore();
    199     }
    200 
    201     private void createStore() {
    202         store = new TrustedCertificateStore(DIR_SYSTEM, DIR_ADDED, DIR_DELETED);
    203     }
    204 
    205     @Override protected void tearDown() {
    206         cleanStore();
    207     }
    208 
    209     private void cleanStore() {
    210         for (File dir : new File[] { DIR_SYSTEM, DIR_ADDED, DIR_DELETED, DIR_TEST }) {
    211             File[] files = dir.listFiles();
    212             if (files == null) {
    213                 continue;
    214             }
    215             for (File file : files) {
    216                 assertTrue(file.delete());
    217             }
    218         }
    219         store = null;
    220     }
    221 
    222     private void resetStore() {
    223         cleanStore();
    224         setupStore();
    225     }
    226 
    227     public void testEmptyDirectories() throws Exception {
    228         assertEmpty();
    229     }
    230 
    231     public void testOneSystemOneDeleted() throws Exception {
    232         install(getCa1(), getAliasSystemCa1());
    233         store.deleteCertificateEntry(getAliasSystemCa1());
    234         assertEmpty();
    235         assertDeleted(getCa1(), getAliasSystemCa1());
    236     }
    237 
    238     public void testTwoSystemTwoDeleted() throws Exception {
    239         install(getCa1(), getAliasSystemCa1());
    240         store.deleteCertificateEntry(getAliasSystemCa1());
    241         install(getCa2(), getAliasSystemCa2());
    242         store.deleteCertificateEntry(getAliasSystemCa2());
    243         assertEmpty();
    244         assertDeleted(getCa1(), getAliasSystemCa1());
    245         assertDeleted(getCa2(), getAliasSystemCa2());
    246     }
    247 
    248     public void testPartialFileIsIgnored() throws Exception {
    249         File file = file(getAliasSystemCa1());
    250         OutputStream os = new FileOutputStream(file);
    251         os.write(0);
    252         os.close();
    253         assertTrue(file.exists());
    254         assertEmpty();
    255         assertTrue(file.exists());
    256     }
    257 
    258     private void assertEmpty() throws Exception {
    259         try {
    260             store.getCertificate(null);
    261             fail();
    262         } catch (NullPointerException expected) {
    263         }
    264         assertNull(store.getCertificate(""));
    265 
    266         try {
    267             store.getCreationDate(null);
    268             fail();
    269         } catch (NullPointerException expected) {
    270         }
    271         assertNull(store.getCreationDate(""));
    272 
    273         Set<String> s = store.aliases();
    274         assertNotNull(s);
    275         assertTrue(s.isEmpty());
    276         assertAliases();
    277 
    278         Set<String> u = store.userAliases();
    279         assertNotNull(u);
    280         assertTrue(u.isEmpty());
    281 
    282         try {
    283             store.containsAlias(null);
    284             fail();
    285         } catch (NullPointerException expected) {
    286         }
    287         assertFalse(store.containsAlias(""));
    288 
    289         assertNull(store.getCertificateAlias(null));
    290         assertNull(store.getCertificateAlias(getCa1()));
    291 
    292         try {
    293             store.isTrustAnchor(null);
    294             fail();
    295         } catch (NullPointerException expected) {
    296         }
    297         assertFalse(store.isTrustAnchor(getCa1()));
    298 
    299         try {
    300             store.findIssuer(null);
    301             fail();
    302         } catch (NullPointerException expected) {
    303         }
    304         assertNull(store.findIssuer(getCa1()));
    305 
    306         try {
    307             store.installCertificate(null);
    308             fail();
    309         } catch (NullPointerException expected) {
    310         }
    311 
    312         store.deleteCertificateEntry(null);
    313         store.deleteCertificateEntry("");
    314 
    315         String[] userFiles = DIR_ADDED.list();
    316         assertTrue(userFiles == null || userFiles.length == 0);
    317     }
    318 
    319     public void testTwoSystem() throws Exception {
    320         testTwo(getCa1(), getAliasSystemCa1(),
    321                 getCa2(), getAliasSystemCa2());
    322     }
    323 
    324     public void testTwoUser() throws Exception {
    325         testTwo(getCa1(), getAliasUserCa1(),
    326                 getCa2(), getAliasUserCa2());
    327     }
    328 
    329     public void testOneSystemOneUser() throws Exception {
    330         testTwo(getCa1(), getAliasSystemCa1(),
    331                 getCa2(), getAliasUserCa2());
    332     }
    333 
    334     public void testTwoSystemSameSubject() throws Exception {
    335         testTwo(getCa1(), getAliasSystemCa1(),
    336                 getCa3WithCa1Subject(), getAliasSystemCa3Collision());
    337     }
    338 
    339     public void testTwoUserSameSubject() throws Exception {
    340         testTwo(getCa1(), getAliasUserCa1(),
    341                 getCa3WithCa1Subject(), getAliasUserCa3Collision());
    342 
    343         store.deleteCertificateEntry(getAliasUserCa1());
    344         assertDeleted(getCa1(), getAliasUserCa1());
    345         assertTombstone(getAliasUserCa1());
    346         assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3Collision());
    347         assertAliases(getAliasUserCa3Collision());
    348 
    349         store.deleteCertificateEntry(getAliasUserCa3Collision());
    350         assertDeleted(getCa3WithCa1Subject(), getAliasUserCa3Collision());
    351         assertNoTombstone(getAliasUserCa3Collision());
    352         assertNoTombstone(getAliasUserCa1());
    353         assertEmpty();
    354     }
    355 
    356     public void testOneSystemOneUserSameSubject() throws Exception {
    357         testTwo(getCa1(), getAliasSystemCa1(),
    358                 getCa3WithCa1Subject(), getAliasUserCa3());
    359         testTwo(getCa1(), getAliasUserCa1(),
    360                 getCa3WithCa1Subject(), getAliasSystemCa3());
    361     }
    362 
    363     private void testTwo(X509Certificate x1, String alias1,
    364                          X509Certificate x2, String alias2) {
    365         install(x1, alias1);
    366         install(x2, alias2);
    367         assertRootCa(x1, alias1);
    368         assertRootCa(x2, alias2);
    369         assertAliases(alias1, alias2);
    370     }
    371 
    372 
    373     public void testOneSystemOneUserOneDeleted() throws Exception {
    374         install(getCa1(), getAliasSystemCa1());
    375         store.installCertificate(getCa2());
    376         store.deleteCertificateEntry(getAliasSystemCa1());
    377         assertDeleted(getCa1(), getAliasSystemCa1());
    378         assertRootCa(getCa2(), getAliasUserCa2());
    379         assertAliases(getAliasUserCa2());
    380     }
    381 
    382     public void testOneSystemOneUserOneDeletedSameSubject() throws Exception {
    383         install(getCa1(), getAliasSystemCa1());
    384         store.installCertificate(getCa3WithCa1Subject());
    385         store.deleteCertificateEntry(getAliasSystemCa1());
    386         assertDeleted(getCa1(), getAliasSystemCa1());
    387         assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3());
    388         assertAliases(getAliasUserCa3());
    389     }
    390 
    391     public void testUserMaskingSystem() throws Exception {
    392         install(getCa1(), getAliasSystemCa1());
    393         install(getCa1(), getAliasUserCa1());
    394         assertMasked(getCa1(), getAliasSystemCa1());
    395         assertRootCa(getCa1(), getAliasUserCa1());
    396         assertAliases(getAliasSystemCa1(), getAliasUserCa1());
    397     }
    398 
    399     public void testChain() throws Exception {
    400         testChain(getAliasSystemChain1(), getAliasSystemChain2());
    401         testChain(getAliasSystemChain1(), getAliasUserChain2());
    402         testChain(getAliasUserChain1(), getAliasSystemCa1());
    403         testChain(getAliasUserChain1(), getAliasUserChain2());
    404     }
    405 
    406     private void testChain(String alias1, String alias2) throws Exception {
    407         install(getChain()[1], alias1);
    408         install(getChain()[2], alias2);
    409         assertIntermediateCa(getChain()[1], alias1);
    410         assertRootCa(getChain()[2], alias2);
    411         assertAliases(alias1, alias2);
    412         assertEquals(getChain()[2], store.findIssuer(getChain()[1]));
    413         assertEquals(getChain()[1], store.findIssuer(getChain()[0]));
    414         resetStore();
    415     }
    416 
    417     public void testMissingSystemDirectory() throws Exception {
    418         cleanStore();
    419         createStore();
    420         assertEmpty();
    421     }
    422 
    423     public void testWithExistingUserDirectories() throws Exception {
    424         DIR_ADDED.mkdirs();
    425         DIR_DELETED.mkdirs();
    426         install(getCa1(), getAliasSystemCa1());
    427         assertRootCa(getCa1(), getAliasSystemCa1());
    428         assertAliases(getAliasSystemCa1());
    429     }
    430 
    431     public void testIsTrustAnchorWithReissuedgetCa() throws Exception {
    432         PublicKey publicKey = getPrivate().getCertificate().getPublicKey();
    433         PrivateKey privateKey = getPrivate().getPrivateKey();
    434         String name = "CN=CA4";
    435         X509Certificate ca1 = TestKeyStore.createCa(publicKey, privateKey, name);
    436         Thread.sleep(1 * 1000); // wait to ensure CAs vary by expiration
    437         X509Certificate ca2 = TestKeyStore.createCa(publicKey, privateKey, name);
    438         assertFalse(ca1.equals(ca2));
    439 
    440         String systemAlias = alias(false, ca1, 0);
    441         install(ca1, systemAlias);
    442         assertRootCa(ca1, systemAlias);
    443         assertTrue(store.isTrustAnchor(ca2));
    444         assertEquals(ca1, store.findIssuer(ca2));
    445         resetStore();
    446 
    447         String userAlias = alias(true, ca1, 0);
    448         store.installCertificate(ca1);
    449         assertRootCa(ca1, userAlias);
    450         assertTrue(store.isTrustAnchor(ca2));
    451         assertEquals(ca1, store.findIssuer(ca2));
    452         resetStore();
    453     }
    454 
    455     public void testInstallEmpty() throws Exception {
    456         store.installCertificate(getCa1());
    457         assertRootCa(getCa1(), getAliasUserCa1());
    458         assertAliases(getAliasUserCa1());
    459 
    460         // reinstalling should not change anything
    461         store.installCertificate(getCa1());
    462         assertRootCa(getCa1(), getAliasUserCa1());
    463         assertAliases(getAliasUserCa1());
    464     }
    465 
    466     public void testInstallEmptySystemExists() throws Exception {
    467         install(getCa1(), getAliasSystemCa1());
    468         assertRootCa(getCa1(), getAliasSystemCa1());
    469         assertAliases(getAliasSystemCa1());
    470 
    471         // reinstalling should not affect system CA
    472         store.installCertificate(getCa1());
    473         assertRootCa(getCa1(), getAliasSystemCa1());
    474         assertAliases(getAliasSystemCa1());
    475 
    476     }
    477 
    478     public void testInstallEmptyDeletedSystemExists() throws Exception {
    479         install(getCa1(), getAliasSystemCa1());
    480         store.deleteCertificateEntry(getAliasSystemCa1());
    481         assertEmpty();
    482         assertDeleted(getCa1(), getAliasSystemCa1());
    483 
    484         // installing should restore deleted system CA
    485         store.installCertificate(getCa1());
    486         assertRootCa(getCa1(), getAliasSystemCa1());
    487         assertAliases(getAliasSystemCa1());
    488     }
    489 
    490     public void testDeleteEmpty() throws Exception {
    491         store.deleteCertificateEntry(getAliasSystemCa1());
    492         assertEmpty();
    493         assertDeleted(getCa1(), getAliasSystemCa1());
    494     }
    495 
    496     public void testDeleteUser() throws Exception {
    497         store.installCertificate(getCa1());
    498         assertRootCa(getCa1(), getAliasUserCa1());
    499         assertAliases(getAliasUserCa1());
    500 
    501         store.deleteCertificateEntry(getAliasUserCa1());
    502         assertEmpty();
    503         assertDeleted(getCa1(), getAliasUserCa1());
    504         assertNoTombstone(getAliasUserCa1());
    505     }
    506 
    507     public void testDeleteSystem() throws Exception {
    508         install(getCa1(), getAliasSystemCa1());
    509         assertRootCa(getCa1(), getAliasSystemCa1());
    510         assertAliases(getAliasSystemCa1());
    511 
    512         store.deleteCertificateEntry(getAliasSystemCa1());
    513         assertEmpty();
    514         assertDeleted(getCa1(), getAliasSystemCa1());
    515 
    516         // deleting again should not change anything
    517         store.deleteCertificateEntry(getAliasSystemCa1());
    518         assertEmpty();
    519         assertDeleted(getCa1(), getAliasSystemCa1());
    520     }
    521 
    522     private void assertRootCa(X509Certificate x, String alias) {
    523         assertIntermediateCa(x, alias);
    524         assertEquals(x, store.findIssuer(x));
    525     }
    526 
    527     private void assertTrusted(X509Certificate x, String alias) {
    528         assertEquals(x, store.getCertificate(alias));
    529         assertEquals(file(alias).lastModified(), store.getCreationDate(alias).getTime());
    530         assertTrue(store.containsAlias(alias));
    531         assertTrue(store.isTrustAnchor(x));
    532     }
    533 
    534     private void assertIntermediateCa(X509Certificate x, String alias) {
    535         assertTrusted(x, alias);
    536         assertEquals(alias, store.getCertificateAlias(x));
    537     }
    538 
    539     private void assertMasked(X509Certificate x, String alias) {
    540         assertTrusted(x, alias);
    541         assertFalse(alias.equals(store.getCertificateAlias(x)));
    542     }
    543 
    544     private void assertDeleted(X509Certificate x, String alias) {
    545         assertNull(store.getCertificate(alias));
    546         assertFalse(store.containsAlias(alias));
    547         assertNull(store.getCertificateAlias(x));
    548         assertFalse(store.isTrustAnchor(x));
    549         assertEquals(store.allSystemAliases().contains(alias),
    550                      store.getCertificate(alias, true) != null);
    551     }
    552 
    553     private void assertTombstone(String alias) {
    554         assertTrue(TrustedCertificateStore.isUser(alias));
    555         File file = file(alias);
    556         assertTrue(file.exists());
    557         assertEquals(0, file.length());
    558     }
    559 
    560     private void assertNoTombstone(String alias) {
    561         assertTrue(TrustedCertificateStore.isUser(alias));
    562         assertFalse(file(alias).exists());
    563     }
    564 
    565     private void assertAliases(String... aliases) {
    566         Set<String> expected = new HashSet<String>(Arrays.asList(aliases));
    567         Set<String> actual = new HashSet<String>();
    568         for (String alias : store.aliases()) {
    569             boolean system = TrustedCertificateStore.isSystem(alias);
    570             boolean user = TrustedCertificateStore.isUser(alias);
    571             if (system || user) {
    572                 assertEquals(system, store.allSystemAliases().contains(alias));
    573                 assertEquals(user, store.userAliases().contains(alias));
    574                 actual.add(alias);
    575             } else {
    576                 throw new AssertionError(alias);
    577             }
    578         }
    579         assertEquals(expected, actual);
    580     }
    581 
    582     /**
    583      * format a certificate alias
    584      */
    585     private static String alias(boolean user, X509Certificate x, int index) {
    586         String prefix = user ? "user:" : "system:";
    587 
    588         X500Principal subject = x.getSubjectX500Principal();
    589         int intHash = NativeCrypto.X509_NAME_hash_old(subject);
    590         String strHash = IntegralToString.intToHexString(intHash, false, 8);
    591 
    592         return prefix + strHash + '.' + index;
    593     }
    594 
    595     /**
    596      * Install certificate under specified alias
    597      */
    598     private static void install(X509Certificate x, String alias) {
    599         try {
    600             File file = file(alias);
    601             file.getParentFile().mkdirs();
    602             OutputStream out = new FileOutputStream(file);
    603             out.write(x.getEncoded());
    604             out.close();
    605         } catch (Exception e) {
    606             throw new RuntimeException(e);
    607         }
    608     }
    609 
    610     /**
    611      * Compute file for an alias
    612      */
    613     private static File file(String alias) {
    614         File dir;
    615         if (TrustedCertificateStore.isSystem(alias)) {
    616             dir = DIR_SYSTEM;
    617         } else if (TrustedCertificateStore.isUser(alias)) {
    618             dir = DIR_ADDED;
    619         } else {
    620             throw new IllegalArgumentException(alias);
    621         }
    622 
    623         int index = alias.lastIndexOf(":");
    624         if (index == -1) {
    625             throw new IllegalArgumentException(alias);
    626         }
    627         String filename = alias.substring(index+1);
    628 
    629         return new File(dir, filename);
    630     }
    631 }
    632