XmlConfigTests.java revision 5a1078f40dd511901c33ccf78be6e2d5081d6637
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 17package android.security.net.config; 18 19import android.content.Context; 20import android.test.AndroidTestCase; 21import android.test.MoreAsserts; 22import android.util.ArraySet; 23import android.util.Pair; 24import java.io.IOException; 25import java.net.Socket; 26import java.net.URL; 27import java.security.KeyStore; 28import java.security.Provider; 29import java.security.Security; 30import java.security.cert.X509Certificate; 31import java.util.ArrayList; 32import java.util.Collections; 33import java.util.Set; 34import javax.net.ssl.HttpsURLConnection; 35import javax.net.ssl.SSLContext; 36import javax.net.ssl.SSLHandshakeException; 37import javax.net.ssl.TrustManager; 38import javax.net.ssl.TrustManagerFactory; 39 40public class XmlConfigTests extends AndroidTestCase { 41 42 private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA"; 43 44 public void testEmptyConfigFile() throws Exception { 45 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config); 46 ApplicationConfig appConfig = new ApplicationConfig(source); 47 assertFalse(appConfig.hasPerDomainConfigs()); 48 NetworkSecurityConfig config = appConfig.getConfigForHostname(""); 49 assertNotNull(config); 50 // Check defaults. 51 assertTrue(config.isCleartextTrafficPermitted()); 52 assertFalse(config.isHstsEnforced()); 53 assertFalse(config.getTrustAnchors().isEmpty()); 54 PinSet pinSet = config.getPins(); 55 assertTrue(pinSet.pins.isEmpty()); 56 // Try some connections. 57 SSLContext context = TestUtils.getSSLContext(source); 58 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 59 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); 60 TestUtils.assertUrlConnectionSucceeds(context, "google.com", 443); 61 } 62 63 public void testEmptyAnchors() throws Exception { 64 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust); 65 ApplicationConfig appConfig = new ApplicationConfig(source); 66 assertFalse(appConfig.hasPerDomainConfigs()); 67 NetworkSecurityConfig config = appConfig.getConfigForHostname(""); 68 assertNotNull(config); 69 // Check defaults. 70 assertTrue(config.isCleartextTrafficPermitted()); 71 assertFalse(config.isHstsEnforced()); 72 assertTrue(config.getTrustAnchors().isEmpty()); 73 PinSet pinSet = config.getPins(); 74 assertTrue(pinSet.pins.isEmpty()); 75 SSLContext context = TestUtils.getSSLContext(source); 76 TestUtils.assertConnectionFails(context, "android.com", 443); 77 TestUtils.assertConnectionFails(context, "developer.android.com", 443); 78 TestUtils.assertUrlConnectionFails(context, "google.com", 443); 79 } 80 81 public void testBasicDomainConfig() throws Exception { 82 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1); 83 ApplicationConfig appConfig = new ApplicationConfig(source); 84 assertTrue(appConfig.hasPerDomainConfigs()); 85 NetworkSecurityConfig config = appConfig.getConfigForHostname(""); 86 assertNotNull(config); 87 // Check defaults. 88 assertTrue(config.isCleartextTrafficPermitted()); 89 assertFalse(config.isHstsEnforced()); 90 assertTrue(config.getTrustAnchors().isEmpty()); 91 PinSet pinSet = config.getPins(); 92 assertTrue(pinSet.pins.isEmpty()); 93 // Check android.com. 94 config = appConfig.getConfigForHostname("android.com"); 95 assertTrue(config.isCleartextTrafficPermitted()); 96 assertFalse(config.isHstsEnforced()); 97 assertFalse(config.getTrustAnchors().isEmpty()); 98 pinSet = config.getPins(); 99 assertTrue(pinSet.pins.isEmpty()); 100 // Try connections. 101 SSLContext context = TestUtils.getSSLContext(source); 102 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 103 TestUtils.assertConnectionFails(context, "developer.android.com", 443); 104 TestUtils.assertUrlConnectionFails(context, "google.com", 443); 105 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 106 } 107 108 public void testBasicPinning() throws Exception { 109 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1); 110 ApplicationConfig appConfig = new ApplicationConfig(source); 111 assertTrue(appConfig.hasPerDomainConfigs()); 112 // Check android.com. 113 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 114 PinSet pinSet = config.getPins(); 115 assertFalse(pinSet.pins.isEmpty()); 116 // Try connections. 117 SSLContext context = TestUtils.getSSLContext(source); 118 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 119 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 120 TestUtils.assertConnectionSucceeds(context, "google.com", 443); 121 } 122 123 public void testExpiredPin() throws Exception { 124 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin); 125 ApplicationConfig appConfig = new ApplicationConfig(source); 126 assertTrue(appConfig.hasPerDomainConfigs()); 127 // Check android.com. 128 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 129 PinSet pinSet = config.getPins(); 130 assertFalse(pinSet.pins.isEmpty()); 131 // Try connections. 132 SSLContext context = TestUtils.getSSLContext(source); 133 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 134 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 135 } 136 137 public void testOverridesPins() throws Exception { 138 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins); 139 ApplicationConfig appConfig = new ApplicationConfig(source); 140 assertTrue(appConfig.hasPerDomainConfigs()); 141 // Check android.com. 142 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 143 PinSet pinSet = config.getPins(); 144 assertFalse(pinSet.pins.isEmpty()); 145 // Try connections. 146 SSLContext context = TestUtils.getSSLContext(source); 147 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 148 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 149 } 150 151 public void testBadPin() throws Exception { 152 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin); 153 ApplicationConfig appConfig = new ApplicationConfig(source); 154 assertTrue(appConfig.hasPerDomainConfigs()); 155 // Check android.com. 156 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 157 PinSet pinSet = config.getPins(); 158 assertFalse(pinSet.pins.isEmpty()); 159 // Try connections. 160 SSLContext context = TestUtils.getSSLContext(source); 161 TestUtils.assertConnectionFails(context, "android.com", 443); 162 TestUtils.assertUrlConnectionFails(context, "android.com", 443); 163 TestUtils.assertConnectionSucceeds(context, "google.com", 443); 164 } 165 166 public void testMultipleDomains() throws Exception { 167 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains); 168 ApplicationConfig appConfig = new ApplicationConfig(source); 169 assertTrue(appConfig.hasPerDomainConfigs()); 170 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 171 assertTrue(config.isCleartextTrafficPermitted()); 172 assertFalse(config.isHstsEnforced()); 173 assertFalse(config.getTrustAnchors().isEmpty()); 174 PinSet pinSet = config.getPins(); 175 assertTrue(pinSet.pins.isEmpty()); 176 // Both android.com and google.com should use the same config 177 NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com"); 178 assertEquals(config, other); 179 // Try connections. 180 SSLContext context = TestUtils.getSSLContext(source); 181 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 182 TestUtils.assertConnectionSucceeds(context, "google.com", 443); 183 TestUtils.assertConnectionFails(context, "developer.android.com", 443); 184 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 185 } 186 187 public void testMultipleDomainConfigs() throws Exception { 188 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs); 189 ApplicationConfig appConfig = new ApplicationConfig(source); 190 assertTrue(appConfig.hasPerDomainConfigs()); 191 // Should be two different config objects 192 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 193 NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com"); 194 MoreAsserts.assertNotEqual(config, other); 195 // Try connections. 196 SSLContext context = TestUtils.getSSLContext(source); 197 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 198 TestUtils.assertConnectionSucceeds(context, "google.com", 443); 199 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 200 } 201 202 public void testIncludeSubdomains() throws Exception { 203 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains); 204 ApplicationConfig appConfig = new ApplicationConfig(source); 205 assertTrue(appConfig.hasPerDomainConfigs()); 206 // Try connections. 207 SSLContext context = TestUtils.getSSLContext(source); 208 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 209 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); 210 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 211 TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443); 212 TestUtils.assertConnectionFails(context, "google.com", 443); 213 } 214 215 public void testAttributes() throws Exception { 216 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes); 217 ApplicationConfig appConfig = new ApplicationConfig(source); 218 assertFalse(appConfig.hasPerDomainConfigs()); 219 NetworkSecurityConfig config = appConfig.getConfigForHostname(""); 220 assertTrue(config.isHstsEnforced()); 221 assertFalse(config.isCleartextTrafficPermitted()); 222 } 223 224 public void testResourcePemCertificateSource() throws Exception { 225 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem); 226 ApplicationConfig appConfig = new ApplicationConfig(source); 227 // Check android.com. 228 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 229 assertTrue(config.isCleartextTrafficPermitted()); 230 assertFalse(config.isHstsEnforced()); 231 assertEquals(2, config.getTrustAnchors().size()); 232 // Try connections. 233 SSLContext context = TestUtils.getSSLContext(source); 234 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 235 TestUtils.assertConnectionFails(context, "developer.android.com", 443); 236 TestUtils.assertUrlConnectionFails(context, "google.com", 443); 237 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 238 } 239 240 public void testResourceDerCertificateSource() throws Exception { 241 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der); 242 ApplicationConfig appConfig = new ApplicationConfig(source); 243 // Check android.com. 244 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 245 assertTrue(config.isCleartextTrafficPermitted()); 246 assertFalse(config.isHstsEnforced()); 247 assertEquals(2, config.getTrustAnchors().size()); 248 // Try connections. 249 SSLContext context = TestUtils.getSSLContext(source); 250 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 251 TestUtils.assertConnectionFails(context, "developer.android.com", 443); 252 TestUtils.assertUrlConnectionFails(context, "google.com", 443); 253 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); 254 } 255 256 public void testNestedDomainConfigs() throws Exception { 257 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains); 258 ApplicationConfig appConfig = new ApplicationConfig(source); 259 assertTrue(appConfig.hasPerDomainConfigs()); 260 NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); 261 NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com"); 262 MoreAsserts.assertNotEqual(parent, child); 263 MoreAsserts.assertEmpty(parent.getPins().pins); 264 MoreAsserts.assertNotEmpty(child.getPins().pins); 265 // Check that the child inherited the cleartext value and anchors. 266 assertFalse(child.isCleartextTrafficPermitted()); 267 MoreAsserts.assertNotEmpty(child.getTrustAnchors()); 268 // Test connections. 269 SSLContext context = TestUtils.getSSLContext(source); 270 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 271 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); 272 } 273 274 public void testNestedDomainConfigsOverride() throws Exception { 275 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override); 276 ApplicationConfig appConfig = new ApplicationConfig(source); 277 assertTrue(appConfig.hasPerDomainConfigs()); 278 NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); 279 NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com"); 280 MoreAsserts.assertNotEqual(parent, child); 281 assertTrue(parent.isCleartextTrafficPermitted()); 282 assertFalse(child.isCleartextTrafficPermitted()); 283 } 284 285 public void testDebugOverridesDisabled() throws Exception { 286 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, false); 287 ApplicationConfig appConfig = new ApplicationConfig(source); 288 NetworkSecurityConfig config = appConfig.getConfigForHostname(""); 289 Set<TrustAnchor> anchors = config.getTrustAnchors(); 290 MoreAsserts.assertEmpty(anchors); 291 SSLContext context = TestUtils.getSSLContext(source); 292 TestUtils.assertConnectionFails(context, "android.com", 443); 293 TestUtils.assertConnectionFails(context, "developer.android.com", 443); 294 } 295 296 public void testBasicDebugOverrides() throws Exception { 297 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, true); 298 ApplicationConfig appConfig = new ApplicationConfig(source); 299 NetworkSecurityConfig config = appConfig.getConfigForHostname(""); 300 Set<TrustAnchor> anchors = config.getTrustAnchors(); 301 MoreAsserts.assertNotEmpty(anchors); 302 for (TrustAnchor anchor : anchors) { 303 assertTrue(anchor.overridesPins); 304 } 305 SSLContext context = TestUtils.getSSLContext(source); 306 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 307 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); 308 } 309 310 public void testDebugOverridesWithDomain() throws Exception { 311 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true); 312 ApplicationConfig appConfig = new ApplicationConfig(source); 313 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 314 Set<TrustAnchor> anchors = config.getTrustAnchors(); 315 boolean foundDebugCA = false; 316 for (TrustAnchor anchor : anchors) { 317 if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) { 318 foundDebugCA = true; 319 assertTrue(anchor.overridesPins); 320 } 321 } 322 assertTrue(foundDebugCA); 323 SSLContext context = TestUtils.getSSLContext(source); 324 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 325 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); 326 } 327 328 public void testDebugInherit() throws Exception { 329 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true); 330 ApplicationConfig appConfig = new ApplicationConfig(source); 331 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com"); 332 Set<TrustAnchor> anchors = config.getTrustAnchors(); 333 boolean foundDebugCA = false; 334 for (TrustAnchor anchor : anchors) { 335 if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) { 336 foundDebugCA = true; 337 assertTrue(anchor.overridesPins); 338 } 339 } 340 assertTrue(foundDebugCA); 341 assertTrue(anchors.size() > 1); 342 SSLContext context = TestUtils.getSSLContext(source); 343 TestUtils.assertConnectionSucceeds(context, "android.com", 443); 344 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); 345 } 346 347 private void testBadConfig(int configId) throws Exception { 348 try { 349 XmlConfigSource source = new XmlConfigSource(getContext(), configId); 350 ApplicationConfig appConfig = new ApplicationConfig(source); 351 appConfig.getConfigForHostname("android.com"); 352 fail("Bad config " + getContext().getResources().getResourceName(configId) 353 + " did not fail to parse"); 354 } catch (RuntimeException e) { 355 MoreAsserts.assertAssignableFrom(XmlConfigSource.ParserException.class, 356 e.getCause()); 357 } 358 } 359 360 public void testBadConfig0() throws Exception { 361 testBadConfig(R.xml.bad_config0); 362 } 363 364 public void testBadConfig1() throws Exception { 365 testBadConfig(R.xml.bad_config1); 366 } 367 368 public void testBadConfig2() throws Exception { 369 testBadConfig(R.xml.bad_config2); 370 } 371 372 public void testBadConfig3() throws Exception { 373 testBadConfig(R.xml.bad_config3); 374 } 375 376 public void testBadConfig4() throws Exception { 377 testBadConfig(R.xml.bad_config4); 378 } 379 380 public void testBadConfig5() throws Exception { 381 testBadConfig(R.xml.bad_config4); 382 } 383 384 public void testTrustManagerKeystore() throws Exception { 385 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, true); 386 ApplicationConfig appConfig = new ApplicationConfig(source); 387 Provider provider = new NetworkSecurityConfigProvider(); 388 TrustManagerFactory tmf = 389 TrustManagerFactory.getInstance("PKIX", provider); 390 KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); 391 keystore.load(null); 392 int i = 0; 393 for (X509Certificate cert : SystemCertificateSource.getInstance().getCertificates()) { 394 keystore.setEntry(String.valueOf(i), 395 new KeyStore.TrustedCertificateEntry(cert), 396 null); 397 i++; 398 } 399 tmf.init(keystore); 400 TrustManager[] tms = tmf.getTrustManagers(); 401 SSLContext context = SSLContext.getInstance("TLS"); 402 context.init(null, tms, null); 403 TestUtils.assertConnectionSucceeds(context, "android.com" , 443); 404 } 405} 406