blob: 047be162e6425a0c633e4ea3abb7eb5c76813e59 [file] [log] [blame]
Chad Brubaker6bc1e392015-10-23 15:33:56 -07001/*
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
17package android.security.net.config;
18
19import android.app.Activity;
Chad Brubaker5ac2ea12017-10-18 10:35:04 -070020import android.content.pm.ApplicationInfo;
Chad Brubaker32d2a102016-02-23 16:01:55 -080021import android.os.Build;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070022import android.test.ActivityUnitTestCase;
23import android.util.ArraySet;
24import android.util.Pair;
Chad Brubaker32d2a102016-02-23 16:01:55 -080025import java.io.ByteArrayInputStream;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070026import java.io.IOException;
27import java.net.Socket;
28import java.net.URL;
Chad Brubaker32d2a102016-02-23 16:01:55 -080029import java.security.cert.Certificate;
30import java.security.cert.CertificateFactory;
31import java.security.cert.X509Certificate;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070032import java.util.ArrayList;
33import java.util.Collections;
Chad Brubaker32d2a102016-02-23 16:01:55 -080034import java.util.HashSet;
35import java.util.Set;
Chad Brubaker6bc1e392015-10-23 15:33:56 -070036import javax.net.ssl.HttpsURLConnection;
37import javax.net.ssl.SSLContext;
38import javax.net.ssl.SSLHandshakeException;
39import javax.net.ssl.TrustManager;
40
Chad Brubaker32d2a102016-02-23 16:01:55 -080041import com.android.org.conscrypt.TrustedCertificateStore;
42
Chad Brubaker6bc1e392015-10-23 15:33:56 -070043public 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
Chad Brubaker32d2a102016-02-23 16:01:55 -080053 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
Chad Brubaker6bc1e392015-10-23 15:33:56 -070098 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
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700108
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700109 /**
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() {
Chad Brubaker80a73f52015-11-04 23:36:07 -0800114 return new NetworkSecurityConfig.Builder().build();
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700115 }
116
117 private NetworkSecurityConfig getSystemStoreConfig() {
Chad Brubaker80a73f52015-11-04 23:36:07 -0800118 return new NetworkSecurityConfig.Builder()
119 .addCertificatesEntryRef(
120 new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
121 .build();
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700122 }
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());
Chad Brubaker5f967022015-11-04 23:55:29 -0800129 SSLContext context = TestUtils.getSSLContext(testSource);
130 TestUtils.assertConnectionFails(context, "android.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700131 }
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()));
Chad Brubaker80a73f52015-11-04 23:36:07 -0800138 NetworkSecurityConfig defaultConfig = getSystemStoreConfig();
Chad Brubaker5f967022015-11-04 23:55:29 -0800139 SSLContext context = TestUtils.getSSLContext(new TestConfigSource(domainMap, defaultConfig));
140 TestUtils.assertConnectionFails(context, "android.com", 443);
141 TestUtils.assertConnectionSucceeds(context, "google.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700142 }
143
144 public void testBadPin() throws Exception {
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700145 ArraySet<Pin> pins = new ArraySet<Pin>();
146 pins.add(new Pin("SHA-256", new byte[0]));
Chad Brubaker80a73f52015-11-04 23:36:07 -0800147 NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
148 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
149 .addCertificatesEntryRef(
150 new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
151 .build();
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700152 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
Chad Brubaker5f967022015-11-04 23:55:29 -0800157 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getSystemStoreConfig()));
158 TestUtils.assertConnectionFails(context, "android.com", 443);
159 TestUtils.assertConnectionSucceeds(context, "google.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700160 }
161
162 public void testGoodPin() throws Exception {
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700163 ArraySet<Pin> pins = new ArraySet<Pin>();
164 pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
Chad Brubaker80a73f52015-11-04 23:36:07 -0800165 NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
166 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
167 .addCertificatesEntryRef(
168 new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
169 .build();
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700170 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
Chad Brubaker5f967022015-11-04 23:55:29 -0800175 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
176 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
177 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700178 }
179
180 public void testOverridePins() throws Exception {
181 // Use a bad pin + granting the system CA store the ability to override pins.
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700182 ArraySet<Pin> pins = new ArraySet<Pin>();
183 pins.add(new Pin("SHA-256", new byte[0]));
Chad Brubaker80a73f52015-11-04 23:36:07 -0800184 NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
185 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
186 .addCertificatesEntryRef(
187 new CertificatesEntryRef(SystemCertificateSource.getInstance(), true))
188 .build();
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700189 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
Chad Brubaker5f967022015-11-04 23:55:29 -0800194 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
195 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700196 }
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
Chad Brubaker5f967022015-11-04 23:55:29 -0800206 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
207 TestUtils.assertConnectionFails(context, "android.com", 443);
208 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700209 }
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
Chad Brubaker5f967022015-11-04 23:55:29 -0800218 = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
219 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700220 // 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()));
Chad Brubaker5f967022015-11-04 23:55:29 -0800224 context = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
225 TestUtils.assertConnectionFails(context, "developer.android.com", 443);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700226 }
227
Chad Brubaker80a73f52015-11-04 23:36:07 -0800228 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()
Chad Brubaker5ac2ea12017-10-18 10:35:04 -0700231 .setParent(NetworkSecurityConfig
232 .getDefaultBuilder(TestUtils.makeApplicationInfo()))
Chad Brubaker80a73f52015-11-04 23:36:07 -0800233 .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
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700248 public void testWithUrlConnection() throws Exception {
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700249 ArraySet<Pin> pins = new ArraySet<Pin>();
250 pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
Chad Brubaker80a73f52015-11-04 23:36:07 -0800251 NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
252 .setPinSet(new PinSet(pins, Long.MAX_VALUE))
253 .addCertificatesEntryRef(
254 new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
255 .build();
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700256 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
Chad Brubaker5f967022015-11-04 23:55:29 -0800261 = 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);
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700265 }
Chad Brubaker32d2a102016-02-23 16:01:55 -0800266
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 =
Chad Brubaker5ac2ea12017-10-18 10:35:04 -0700273 NetworkSecurityConfig
274 .getDefaultBuilder(TestUtils.makeApplicationInfo(Build.VERSION_CODES.M))
275 .build();
Chad Brubaker32d2a102016-02-23 16:01:55 -0800276 NetworkSecurityConfig nConfig =
Chad Brubaker5ac2ea12017-10-18 10:35:04 -0700277 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();
Chad Brubaker32d2a102016-02-23 16:01:55 -0800286 Set<TrustAnchor> preNAnchors = preNConfig.getTrustAnchors();
287 Set<TrustAnchor> nAnchors = nConfig.getTrustAnchors();
Chad Brubaker5ac2ea12017-10-18 10:35:04 -0700288 Set<TrustAnchor> privAnchors = privConfig.getTrustAnchors();
Chad Brubaker32d2a102016-02-23 16:01:55 -0800289 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 }
Chad Brubaker5ac2ea12017-10-18 10:35:04 -0700297 Set<X509Certificate> privCerts = new HashSet<X509Certificate>();
298 for (TrustAnchor anchor : privAnchors) {
299 privCerts.add(anchor.certificate);
300 }
Chad Brubaker32d2a102016-02-23 16:01:55 -0800301 assertTrue(preNCerts.contains(TEST_CA_CERT));
302 assertFalse(nCerts.contains(TEST_CA_CERT));
Chad Brubaker5ac2ea12017-10-18 10:35:04 -0700303 assertFalse(privCerts.contains(TEST_CA_CERT));
Chad Brubaker32d2a102016-02-23 16:01:55 -0800304 } 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 }
Chad Brubaker6bc1e392015-10-23 15:33:56 -0700316}