Home | History | Annotate | Download | only in config
      1 /*
      2  * Copyright (C) 2015 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 android.security.net.config;
     18 
     19 import android.app.Activity;
     20 import android.os.Build;
     21 import android.test.ActivityUnitTestCase;
     22 import android.util.ArraySet;
     23 import android.util.Pair;
     24 import java.io.ByteArrayInputStream;
     25 import java.io.IOException;
     26 import java.net.Socket;
     27 import java.net.URL;
     28 import java.security.cert.Certificate;
     29 import java.security.cert.CertificateFactory;
     30 import java.security.cert.X509Certificate;
     31 import java.util.ArrayList;
     32 import java.util.Collections;
     33 import java.util.HashSet;
     34 import java.util.Set;
     35 import javax.net.ssl.HttpsURLConnection;
     36 import javax.net.ssl.SSLContext;
     37 import javax.net.ssl.SSLHandshakeException;
     38 import javax.net.ssl.TrustManager;
     39 
     40 import com.android.org.conscrypt.TrustedCertificateStore;
     41 
     42 public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
     43 
     44     public NetworkSecurityConfigTests() {
     45         super(Activity.class);
     46     }
     47 
     48     // SHA-256 of the G2 intermediate CA for android.com (as of 10/2015).
     49     private static final byte[] G2_SPKI_SHA256
     50             = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776");
     51 
     52     private static final byte[] TEST_CA_BYTES
     53             = hexToBytes(
     54                     "3082036130820249a003020102020900bd54597d6750ea62300d06092a86"
     55                     + "4886f70d01010b05003047310b3009060355040613025553310b30090603"
     56                     + "5504080c0243413110300e060355040a0c07416e64726f69643119301706"
     57                     + "035504030c104e53436f6e6669672054657374204341301e170d31363032"
     58                     + "32343030313130325a170d3136303332353030313130325a3047310b3009"
     59                     + "060355040613025553310b300906035504080c0243413110300e06035504"
     60                     + "0a0c07416e64726f69643119301706035504030c104e53436f6e66696720"
     61                     + "5465737420434130820122300d06092a864886f70d01010105000382010f"
     62                     + "003082010a0282010100e15ce8fd5794029841e760d68d6e0159c9c67630"
     63                     + "089775bc728d83dae7e29e23fe5f6e113b789f4c5b22f052300ec6d5faa5"
     64                     + "724432e7bac96682792ef6e9617c939c4329dce8788cbdf3a11b621fac9e"
     65                     + "2edbec2d7e5e07296bbb544b89263137a6a31573a2362e05ca8ff9c886bf"
     66                     + "52df4ff93c45475145a40a83f2670e23669220a5a4bf2c6860edb78d3022"
     67                     + "192fb5dc5e8c118f70870f89da292dfe522751462f020ed556653c8b07f8"
     68                     + "89712a6e8196c457a637439e3073d7d917ab55aa51a146826367f7b5922a"
     69                     + "64fb2f95099de21eb98341fa76faa79ffbda123fe5b8adc614b16174e8b0"
     70                     + "dfdac2bbc4d526d2487ad2b009d53996ec23ffbd732112efa66b02030100"
     71                     + "01a350304e301d0603551d0e04160414f66e1a95486c879edd60a5756bc2"
     72                     + "f1f4677e128e301f0603551d23041830168014f66e1a95486c879edd60a5"
     73                     + "756bc2f1f4677e128e300c0603551d13040530030101ff300d06092a8648"
     74                     + "86f70d01010b05000382010100d2856130dccae24e5f8901900d94bc642f"
     75                     + "85466ab7cfa1066399077a168cd4b56603a9e2af9d2e58aec13101e338a4"
     76                     + "8e95e9c7a84d7991f0d381d4965eaada1b80fbbd8277445f449babe64f53"
     77                     + "ba625387460b592a1a97b14b8251115e6610350021a6e716ae22b905f8d4"
     78                     + "eae24e668e71b12ab51fd2f2bb600e074487dec720c3db14dbca504844b6"
     79                     + "933bb0248283ea95464747689c37d706d4839c7d0e9bd86abf98ddce5d36"
     80                     + "8b38bfe5062353e28d5be378827fade1caa6bba3df9cd9ebf83d839eae52"
     81                     + "780181f31973f15f982686ba6d899f7b644fd1f26c8ebb99f4c986faaf4c"
     82                     + "1b9e3d9d391943ce3fb9fa2e631bd66b8ef3d47fd85acf09ea3a30f15f");
     83 
     84     private static final X509Certificate TEST_CA_CERT;
     85 
     86     static {
     87         try {
     88             CertificateFactory factory = CertificateFactory.getInstance("X.509");
     89             Certificate cert = factory.generateCertificate(new ByteArrayInputStream(TEST_CA_BYTES));
     90             TEST_CA_CERT = (X509Certificate) cert;
     91         } catch (Exception e) {
     92             throw new RuntimeException(e);
     93         }
     94     }
     95 
     96 
     97     private static byte[] hexToBytes(String s) {
     98         int len = s.length();
     99         byte[] data = new byte[len / 2];
    100         for (int i = 0; i < len; i += 2) {
    101             data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(
    102                     s.charAt(i + 1), 16));
    103         }
    104         return data;
    105     }
    106 
    107 
    108     /**
    109      * Return a NetworkSecurityConfig that has an empty TrustAnchor set. This should always cause a
    110      * SSLHandshakeException when used for a connection.
    111      */
    112     private NetworkSecurityConfig getEmptyConfig() {
    113         return new NetworkSecurityConfig.Builder().build();
    114     }
    115 
    116     private NetworkSecurityConfig getSystemStoreConfig() {
    117         return new NetworkSecurityConfig.Builder()
    118                 .addCertificatesEntryRef(
    119                         new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
    120                 .build();
    121     }
    122 
    123     public void testEmptyConfig() throws Exception {
    124         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    125                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    126         ConfigSource testSource =
    127                 new TestConfigSource(domainMap, getEmptyConfig());
    128         SSLContext context = TestUtils.getSSLContext(testSource);
    129         TestUtils.assertConnectionFails(context, "android.com", 443);
    130     }
    131 
    132     public void testEmptyPerNetworkSecurityConfig() throws Exception {
    133         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    134                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    135         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    136                 new Domain("android.com", true), getEmptyConfig()));
    137         NetworkSecurityConfig defaultConfig = getSystemStoreConfig();
    138         SSLContext context = TestUtils.getSSLContext(new TestConfigSource(domainMap, defaultConfig));
    139         TestUtils.assertConnectionFails(context, "android.com", 443);
    140         TestUtils.assertConnectionSucceeds(context, "google.com", 443);
    141     }
    142 
    143     public void testBadPin() throws Exception {
    144         ArraySet<Pin> pins = new ArraySet<Pin>();
    145         pins.add(new Pin("SHA-256", new byte[0]));
    146         NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
    147                 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
    148                 .addCertificatesEntryRef(
    149                         new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
    150                 .build();
    151         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    152                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    153         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    154                 new Domain("android.com", true), domain));
    155         SSLContext context
    156                 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getSystemStoreConfig()));
    157         TestUtils.assertConnectionFails(context, "android.com", 443);
    158         TestUtils.assertConnectionSucceeds(context, "google.com", 443);
    159     }
    160 
    161     public void testGoodPin() throws Exception {
    162         ArraySet<Pin> pins = new ArraySet<Pin>();
    163         pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
    164         NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
    165                 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
    166                 .addCertificatesEntryRef(
    167                         new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
    168                 .build();
    169         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    170                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    171         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    172                 new Domain("android.com", true), domain));
    173         SSLContext context
    174                 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
    175         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
    176         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
    177     }
    178 
    179     public void testOverridePins() throws Exception {
    180         // Use a bad pin + granting the system CA store the ability to override pins.
    181         ArraySet<Pin> pins = new ArraySet<Pin>();
    182         pins.add(new Pin("SHA-256", new byte[0]));
    183         NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
    184                 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
    185                 .addCertificatesEntryRef(
    186                         new CertificatesEntryRef(SystemCertificateSource.getInstance(), true))
    187                 .build();
    188         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    189                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    190         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    191                 new Domain("android.com", true), domain));
    192         SSLContext context
    193                 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
    194         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
    195     }
    196 
    197     public void testMostSpecificNetworkSecurityConfig() throws Exception {
    198         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    199                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    200         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    201                 new Domain("android.com", true), getEmptyConfig()));
    202         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    203                 new Domain("developer.android.com", false), getSystemStoreConfig()));
    204         SSLContext context
    205                 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
    206         TestUtils.assertConnectionFails(context, "android.com", 443);
    207         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
    208     }
    209 
    210     public void testSubdomainIncluded() throws Exception {
    211         // First try connecting to a subdomain of a domain entry that includes subdomains.
    212         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    213                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    214         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    215                 new Domain("android.com", true), getSystemStoreConfig()));
    216         SSLContext context
    217                 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
    218         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
    219         // Now try without including subdomains.
    220         domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    221         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    222                 new Domain("android.com", false), getSystemStoreConfig()));
    223         context = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
    224         TestUtils.assertConnectionFails(context, "developer.android.com", 443);
    225     }
    226 
    227     public void testConfigBuilderUsesParents() throws Exception {
    228         // Check that a builder with a parent uses the parent's values when non is set.
    229         NetworkSecurityConfig config = new NetworkSecurityConfig.Builder()
    230                 .setParent(NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.N, 1))
    231                 .build();
    232         assert(!config.getTrustAnchors().isEmpty());
    233     }
    234 
    235     public void testConfigBuilderParentLoop() throws Exception {
    236         NetworkSecurityConfig.Builder config1 = new NetworkSecurityConfig.Builder();
    237         NetworkSecurityConfig.Builder config2 = new NetworkSecurityConfig.Builder();
    238         config1.setParent(config2);
    239         try {
    240             config2.setParent(config1);
    241             fail("Loop in NetworkSecurityConfig parents");
    242         } catch (IllegalArgumentException expected) {
    243         }
    244     }
    245 
    246     public void testWithUrlConnection() throws Exception {
    247         ArraySet<Pin> pins = new ArraySet<Pin>();
    248         pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
    249         NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
    250                 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
    251                 .addCertificatesEntryRef(
    252                         new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
    253                 .build();
    254         ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
    255                 = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
    256         domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
    257                 new Domain("android.com", true), domain));
    258         SSLContext context
    259                 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
    260         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
    261         TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
    262         TestUtils.assertUrlConnectionFails(context, "google.com", 443);
    263     }
    264 
    265     public void testUserAddedCaOptIn() throws Exception {
    266         TrustedCertificateStore store = new TrustedCertificateStore();
    267         try {
    268             // Install the test CA.
    269             store.installCertificate(TEST_CA_CERT);
    270             NetworkSecurityConfig preNConfig =
    271                     NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.M, 1).build();
    272             NetworkSecurityConfig nConfig =
    273                     NetworkSecurityConfig.getDefaultBuilder(Build.VERSION_CODES.N, 1).build();
    274             Set<TrustAnchor> preNAnchors = preNConfig.getTrustAnchors();
    275             Set<TrustAnchor> nAnchors = nConfig.getTrustAnchors();
    276             Set<X509Certificate> preNCerts = new HashSet<X509Certificate>();
    277             for (TrustAnchor anchor : preNAnchors) {
    278                 preNCerts.add(anchor.certificate);
    279             }
    280             Set<X509Certificate> nCerts = new HashSet<X509Certificate>();
    281             for (TrustAnchor anchor : nAnchors) {
    282                 nCerts.add(anchor.certificate);
    283             }
    284             assertTrue(preNCerts.contains(TEST_CA_CERT));
    285             assertFalse(nCerts.contains(TEST_CA_CERT));
    286         } finally {
    287             // Delete the user added CA. We don't know the alias so just delete them all.
    288             for (String alias : store.aliases()) {
    289                 if (store.isUser(alias)) {
    290                     try {
    291                         store.deleteCertificateEntry(alias);
    292                     } catch (Exception ignored) {
    293                     }
    294                 }
    295             }
    296         }
    297     }
    298 }
    299