summaryrefslogtreecommitdiff
path: root/tests/NetworkSecurityConfigTest
diff options
context:
space:
mode:
authorChad Brubaker <cbrubaker@google.com>2015-10-23 15:33:56 -0700
committerChad Brubaker <cbrubaker@google.com>2015-11-04 14:31:18 -0800
commit6bc1e3966c4890ee3d47b5e527b800f2700ed627 (patch)
tree4a4d5e03e7cca93d342bb6ea6fc7fdfef75587dd /tests/NetworkSecurityConfigTest
parent5d562d4d2fe5697468e5b93b183ed8ea42e115a2 (diff)
Add initial network security config implementation
Initial implementation of a unified application wide static network security configuration. This currently encompases: * Trust decisions such as what trust anchors to use as well as static certificate pinning. * Policy on what to do with cleartext traffic. In order to prevent issues due to interplay of various components in an application and their potentially different security requirements configuration can be specified at a per-domain granularity in addition to application wide defaults. This change contains the internal data structures and trust management code, hooking these up in application startup will come in a future commit. Change-Id: I53ce5ba510a4221d58839e61713262a8f4c6699c
Diffstat (limited to 'tests/NetworkSecurityConfigTest')
-rw-r--r--tests/NetworkSecurityConfigTest/Android.mk15
-rw-r--r--tests/NetworkSecurityConfigTest/AndroidManifest.xml29
-rw-r--r--tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java241
-rw-r--r--tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java33
-rw-r--r--tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java39
5 files changed, 357 insertions, 0 deletions
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
new file mode 100644
index 000000000000..a63162d9ba09
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := NetworkSecurityConfigTests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/NetworkSecurityConfigTest/AndroidManifest.xml b/tests/NetworkSecurityConfigTest/AndroidManifest.xml
new file mode 100644
index 000000000000..811a3f4f4f80
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.tests"
+ android:sharedUserId="android.uid.system">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.security.tests"
+ android:label="ANSC Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
new file mode 100644
index 000000000000..9a1fe151a2dc
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.app.Activity;
+import android.test.ActivityUnitTestCase;
+import android.util.ArraySet;
+import android.util.Pair;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+
+public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
+
+ public NetworkSecurityConfigTests() {
+ super(Activity.class);
+ }
+
+ // SHA-256 of the G2 intermediate CA for android.com (as of 10/2015).
+ private static final byte[] G2_SPKI_SHA256
+ = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776");
+
+ private static byte[] hexToBytes(String s) {
+ int len = s.length();
+ byte[] data = new byte[len / 2];
+ for (int i = 0; i < len; i += 2) {
+ data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(
+ s.charAt(i + 1), 16));
+ }
+ return data;
+ }
+
+ private void assertConnectionFails(SSLContext context, String host, int port)
+ throws Exception {
+ try {
+ Socket s = context.getSocketFactory().createSocket(host, port);
+ s.getInputStream();
+ fail("Expected connection to " + host + ":" + port + " to fail.");
+ } catch (SSLHandshakeException expected) {
+ }
+ }
+
+ private void assertConnectionSucceeds(SSLContext context, String host, int port)
+ throws Exception {
+ Socket s = context.getSocketFactory().createSocket(host, port);
+ s.getInputStream();
+ }
+
+ private void assertUrlConnectionFails(SSLContext context, String host, int port)
+ throws Exception {
+ URL url = new URL("https://" + host + ":" + port);
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setSSLSocketFactory(context.getSocketFactory());
+ try {
+ connection.getInputStream();
+ fail("Connection to " + host + ":" + port + " expected to fail");
+ } catch (SSLHandshakeException expected) {
+ // ignored.
+ }
+ }
+
+ private void assertUrlConnectionSucceeds(SSLContext context, String host, int port)
+ throws Exception {
+ URL url = new URL("https://" + host + ":" + port);
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+ connection.setSSLSocketFactory(context.getSocketFactory());
+ connection.getInputStream();
+ }
+
+ private SSLContext getSSLContext(ConfigSource source) throws Exception {
+ ApplicationConfig config = new ApplicationConfig(source);
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, new TrustManager[] {config.getTrustManager()}, null);
+ return context;
+ }
+
+
+ /**
+ * Return a NetworkSecurityConfig that has an empty TrustAnchor set. This should always cause a
+ * SSLHandshakeException when used for a connection.
+ */
+ private NetworkSecurityConfig getEmptyConfig() {
+ return new NetworkSecurityConfig(true, false,
+ new PinSet(new ArraySet<Pin>(), -1),
+ new ArrayList<CertificatesEntryRef>());
+ }
+
+ private NetworkSecurityConfig getSystemStoreConfig() {
+ ArrayList<CertificatesEntryRef> defaultSource = new ArrayList<CertificatesEntryRef>();
+ defaultSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
+ return new NetworkSecurityConfig(true, false, new PinSet(new ArraySet<Pin>(),
+ -1), defaultSource);
+ }
+
+ public void testEmptyConfig() throws Exception {
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ ConfigSource testSource =
+ new TestConfigSource(domainMap, getEmptyConfig());
+ SSLContext context = getSSLContext(testSource);
+ assertConnectionFails(context, "android.com", 443);
+ }
+
+ public void testEmptyPerNetworkSecurityConfig() throws Exception {
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), getEmptyConfig()));
+ ArrayList<CertificatesEntryRef> defaultSource = new ArrayList<CertificatesEntryRef>();
+ defaultSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
+ NetworkSecurityConfig defaultConfig = new NetworkSecurityConfig(true, false,
+ new PinSet(new ArraySet<Pin>(), -1),
+ defaultSource);
+ SSLContext context = getSSLContext(new TestConfigSource(domainMap, defaultConfig));
+ assertConnectionFails(context, "android.com", 443);
+ assertConnectionSucceeds(context, "google.com", 443);
+ }
+
+ public void testBadPin() throws Exception {
+ ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
+ systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", new byte[0]));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
+ new PinSet(pins, Long.MAX_VALUE),
+ systemSource);
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = getSSLContext(new TestConfigSource(domainMap, getSystemStoreConfig()));
+ assertConnectionFails(context, "android.com", 443);
+ assertConnectionSucceeds(context, "google.com", 443);
+ }
+
+ public void testGoodPin() throws Exception {
+ ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
+ systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
+ new PinSet(pins, Long.MAX_VALUE),
+ systemSource);
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ assertConnectionSucceeds(context, "android.com", 443);
+ assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testOverridePins() throws Exception {
+ // Use a bad pin + granting the system CA store the ability to override pins.
+ ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
+ systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), true));
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", new byte[0]));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
+ new PinSet(pins, Long.MAX_VALUE),
+ systemSource);
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ assertConnectionSucceeds(context, "android.com", 443);
+ }
+
+ public void testMostSpecificNetworkSecurityConfig() throws Exception {
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), getEmptyConfig()));
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("developer.android.com", false), getSystemStoreConfig()));
+ SSLContext context
+ = getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ assertConnectionFails(context, "android.com", 443);
+ assertConnectionSucceeds(context, "developer.android.com", 443);
+ }
+
+ public void testSubdomainIncluded() throws Exception {
+ // First try connecting to a subdomain of a domain entry that includes subdomains.
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), getSystemStoreConfig()));
+ SSLContext context
+ = getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ assertConnectionSucceeds(context, "developer.android.com", 443);
+ // Now try without including subdomains.
+ domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", false), getSystemStoreConfig()));
+ context = getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ assertConnectionFails(context, "developer.android.com", 443);
+ }
+
+ public void testWithUrlConnection() throws Exception {
+ ArrayList<CertificatesEntryRef> systemSource = new ArrayList<CertificatesEntryRef>();
+ systemSource.add(new CertificatesEntryRef(new SystemCertificateSource(), false));
+ ArraySet<Pin> pins = new ArraySet<Pin>();
+ pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+ NetworkSecurityConfig domain = new NetworkSecurityConfig(true, false,
+ new PinSet(pins, Long.MAX_VALUE),
+ systemSource);
+ ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+ = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+ domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+ new Domain("android.com", true), domain));
+ SSLContext context
+ = getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+ assertUrlConnectionSucceeds(context, "android.com", 443);
+ assertUrlConnectionSucceeds(context, "developer.android.com", 443);
+ assertUrlConnectionFails(context, "google.com", 443);
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
new file mode 100644
index 000000000000..92eadc06cd49
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.util.Set;
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public class TestCertificateSource implements CertificateSource {
+
+ private final Set<X509Certificate> mCertificates;
+ public TestCertificateSource(Set<X509Certificate> certificates) {
+ mCertificates = certificates;
+ }
+
+ public Set<X509Certificate> getCertificates() {
+ return mCertificates;
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java
new file mode 100644
index 000000000000..609f481a312c
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.Pair;
+import java.util.Set;
+
+/** @hide */
+public class TestConfigSource implements ConfigSource {
+ private final Set<Pair<Domain, NetworkSecurityConfig>> mConfigs;
+ private final NetworkSecurityConfig mDefaultConfig;
+ public TestConfigSource(Set<Pair<Domain, NetworkSecurityConfig>> configs,
+ NetworkSecurityConfig defaultConfig) {
+ mConfigs = configs;
+ mDefaultConfig = defaultConfig;
+ }
+
+ public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+ return mConfigs;
+ }
+
+ public NetworkSecurityConfig getDefaultConfig() {
+ return mDefaultConfig;
+ }
+}