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