Merge "Add xml source for network security configuration"
This commit is contained in:
@@ -30,6 +30,26 @@ public final class Pin {
|
||||
this.digest = digest;
|
||||
mHashCode = Arrays.hashCode(digest) ^ digestAlgorithm.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isSupportedDigestAlgorithm(String algorithm) {
|
||||
// Currently only SHA-256 is supported. SHA-512 if/once Chromium networking stack
|
||||
// supports it.
|
||||
return "SHA-256".equalsIgnoreCase(algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static int getDigestLength(String algorithm) {
|
||||
if ("SHA-256".equalsIgnoreCase(algorithm)) {
|
||||
return 32;
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported digest algorithm: " + algorithm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mHashCode;
|
||||
|
||||
310
core/java/android/security/net/config/XmlConfigSource.java
Normal file
310
core/java/android/security/net/config/XmlConfigSource.java
Normal file
@@ -0,0 +1,310 @@
|
||||
package android.security.net.config;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Base64;
|
||||
import android.util.Pair;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link ConfigSource} based on an XML configuration file.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class XmlConfigSource implements ConfigSource {
|
||||
private final Object mLock = new Object();
|
||||
private final int mResourceId;
|
||||
|
||||
private boolean mInitialized;
|
||||
private NetworkSecurityConfig mDefaultConfig;
|
||||
private Set<Pair<Domain, NetworkSecurityConfig>> mDomainMap;
|
||||
private Context mContext;
|
||||
|
||||
public XmlConfigSource(Context context, int resourceId) {
|
||||
mResourceId = resourceId;
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
|
||||
ensureInitialized();
|
||||
return mDomainMap;
|
||||
}
|
||||
|
||||
public NetworkSecurityConfig getDefaultConfig() {
|
||||
ensureInitialized();
|
||||
return mDefaultConfig;
|
||||
}
|
||||
|
||||
private void ensureInitialized() {
|
||||
synchronized (mLock) {
|
||||
if (mInitialized) {
|
||||
return;
|
||||
}
|
||||
try (XmlResourceParser parser = mContext.getResources().getXml(mResourceId)) {
|
||||
parseNetworkSecurityConfig(parser);
|
||||
mContext = null;
|
||||
mInitialized = true;
|
||||
} catch (Resources.NotFoundException | XmlPullParserException | IOException
|
||||
| ParserException e) {
|
||||
throw new RuntimeException("Failed to parse XML configuration from "
|
||||
+ mContext.getResources().getResourceEntryName(mResourceId), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Pin parsePin(XmlResourceParser parser)
|
||||
throws IOException, XmlPullParserException, ParserException {
|
||||
String digestAlgorithm = parser.getAttributeValue(null, "digest");
|
||||
if (!Pin.isSupportedDigestAlgorithm(digestAlgorithm)) {
|
||||
throw new ParserException(parser, "Unsupported pin digest algorithm: "
|
||||
+ digestAlgorithm);
|
||||
}
|
||||
if (parser.next() != XmlPullParser.TEXT) {
|
||||
throw new ParserException(parser, "Missing pin digest");
|
||||
}
|
||||
String digest = parser.getText();
|
||||
byte[] decodedDigest = null;
|
||||
try {
|
||||
decodedDigest = Base64.decode(digest, 0);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ParserException(parser, "Invalid pin digest", e);
|
||||
}
|
||||
int expectedLength = Pin.getDigestLength(digestAlgorithm);
|
||||
if (decodedDigest.length != expectedLength) {
|
||||
throw new ParserException(parser, "digest length " + decodedDigest.length
|
||||
+ " does not match expected length for " + digestAlgorithm + " of "
|
||||
+ expectedLength);
|
||||
}
|
||||
if (parser.next() != XmlPullParser.END_TAG) {
|
||||
throw new ParserException(parser, "pin contains additional elements");
|
||||
}
|
||||
return new Pin(digestAlgorithm, decodedDigest);
|
||||
}
|
||||
|
||||
private PinSet parsePinSet(XmlResourceParser parser)
|
||||
throws IOException, XmlPullParserException, ParserException {
|
||||
String expirationDate = parser.getAttributeValue(null, "expiration");
|
||||
long expirationTimestampMilis = Long.MAX_VALUE;
|
||||
if (expirationDate != null) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
sdf.setLenient(false);
|
||||
Date date = sdf.parse(expirationDate);
|
||||
if (date == null) {
|
||||
throw new ParserException(parser, "Invalid expiration date in pin-set");
|
||||
}
|
||||
expirationTimestampMilis = date.getTime();
|
||||
} catch (ParseException e) {
|
||||
throw new ParserException(parser, "Invalid expiration date in pin-set", e);
|
||||
}
|
||||
}
|
||||
|
||||
int outerDepth = parser.getDepth();
|
||||
Set<Pin> pins = new ArraySet<>();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
String tagName = parser.getName();
|
||||
if (tagName.equals("pin")) {
|
||||
pins.add(parsePin(parser));
|
||||
} else {
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
}
|
||||
}
|
||||
return new PinSet(pins, expirationTimestampMilis);
|
||||
}
|
||||
|
||||
private Domain parseDomain(XmlResourceParser parser, Set<String> seenDomains)
|
||||
throws IOException, XmlPullParserException, ParserException {
|
||||
boolean includeSubdomains =
|
||||
parser.getAttributeBooleanValue(null, "includeSubdomains", false);
|
||||
if (parser.next() != XmlPullParser.TEXT) {
|
||||
throw new ParserException(parser, "Domain name missing");
|
||||
}
|
||||
String domain = parser.getText().toLowerCase(Locale.US);
|
||||
if (parser.next() != XmlPullParser.END_TAG) {
|
||||
throw new ParserException(parser, "domain contains additional elements");
|
||||
}
|
||||
// Domains are matched using a most specific match, so don't allow duplicates.
|
||||
// includeSubdomains isn't relevant here, both android.com + subdomains and android.com
|
||||
// match for android.com equally. Do not allow any duplicates period.
|
||||
if (!seenDomains.add(domain)) {
|
||||
throw new ParserException(parser, domain + " has already been specified");
|
||||
}
|
||||
return new Domain(domain, includeSubdomains);
|
||||
}
|
||||
|
||||
private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser)
|
||||
throws IOException, XmlPullParserException, ParserException {
|
||||
boolean overridePins = parser.getAttributeBooleanValue(null, "overridePins", false);
|
||||
int sourceId = parser.getAttributeResourceValue(null, "src", -1);
|
||||
String sourceString = parser.getAttributeValue(null, "src");
|
||||
CertificateSource source = null;
|
||||
if (sourceString == null) {
|
||||
throw new ParserException(parser, "certificates element missing src attribute");
|
||||
}
|
||||
if (sourceId != -1) {
|
||||
// TODO: Cache ResourceCertificateSources by sourceId
|
||||
source = new ResourceCertificateSource(sourceId, mContext);
|
||||
} else if ("system".equals(sourceString)) {
|
||||
source = SystemCertificateSource.getInstance();
|
||||
} else if ("user".equals(sourceString)) {
|
||||
source = UserCertificateSource.getInstance();
|
||||
} else {
|
||||
throw new ParserException(parser, "Unknown certificates src. "
|
||||
+ "Should be one of system|user|@resourceVal");
|
||||
}
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
return new CertificatesEntryRef(source, overridePins);
|
||||
}
|
||||
|
||||
private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser)
|
||||
throws IOException, XmlPullParserException, ParserException {
|
||||
int outerDepth = parser.getDepth();
|
||||
List<CertificatesEntryRef> anchors = new ArrayList<>();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
String tagName = parser.getName();
|
||||
if (tagName.equals("certificates")) {
|
||||
anchors.add(parseCertificatesEntry(parser));
|
||||
} else {
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
}
|
||||
}
|
||||
return anchors;
|
||||
}
|
||||
|
||||
private Pair<NetworkSecurityConfig.Builder, Set<Domain>> parseConfigEntry(
|
||||
XmlResourceParser parser, Set<String> seenDomains, boolean baseConfig)
|
||||
throws IOException, XmlPullParserException, ParserException {
|
||||
NetworkSecurityConfig.Builder builder = new NetworkSecurityConfig.Builder();
|
||||
Set<Domain> domains = new ArraySet<>();
|
||||
boolean seenPinSet = false;
|
||||
boolean seenTrustAnchors = false;
|
||||
String configName = parser.getName();
|
||||
int outerDepth = parser.getDepth();
|
||||
// Parse config attributes. Only set values that are present, config inheritence will
|
||||
// handle the rest.
|
||||
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||
String name = parser.getAttributeName(i);
|
||||
if ("hstsEnforced".equals(name)) {
|
||||
builder.setHstsEnforced(
|
||||
parser.getAttributeBooleanValue(i,
|
||||
NetworkSecurityConfig.DEFAULT_HSTS_ENFORCED));
|
||||
} else if ("cleartextTrafficPermitted".equals(name)) {
|
||||
builder.setCleartextTrafficPermitted(
|
||||
parser.getAttributeBooleanValue(i,
|
||||
NetworkSecurityConfig.DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED));
|
||||
}
|
||||
}
|
||||
// Parse the config elements.
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
String tagName = parser.getName();
|
||||
// TODO: Support nested domain-config entries.
|
||||
if ("domain".equals(tagName)) {
|
||||
if (baseConfig) {
|
||||
throw new ParserException(parser, "domain element not allowed in base-config");
|
||||
}
|
||||
Domain domain = parseDomain(parser, seenDomains);
|
||||
domains.add(domain);
|
||||
} else if ("trust-anchors".equals(tagName)) {
|
||||
if (seenTrustAnchors) {
|
||||
throw new ParserException(parser,
|
||||
"Multiple trust-anchor elements not allowed");
|
||||
}
|
||||
builder.addCertificatesEntryRefs(parseTrustAnchors(parser));
|
||||
seenTrustAnchors = true;
|
||||
} else if ("pin-set".equals(tagName)) {
|
||||
if (baseConfig) {
|
||||
throw new ParserException(parser,
|
||||
"pin-set element not allowed in base-config");
|
||||
}
|
||||
if (seenPinSet) {
|
||||
throw new ParserException(parser, "Multiple pin-set elements not allowed");
|
||||
}
|
||||
builder.setPinSet(parsePinSet(parser));
|
||||
seenPinSet = true;
|
||||
} else {
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
}
|
||||
}
|
||||
if (!baseConfig && domains.isEmpty()) {
|
||||
throw new ParserException(parser, "No domain elements in domain-config");
|
||||
}
|
||||
return new Pair<>(builder, domains);
|
||||
}
|
||||
|
||||
private void parseNetworkSecurityConfig(XmlResourceParser parser)
|
||||
throws IOException, XmlPullParserException, ParserException {
|
||||
Set<String> seenDomains = new ArraySet<>();
|
||||
List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>();
|
||||
NetworkSecurityConfig.Builder baseConfigBuilder = null;
|
||||
boolean seenDebugOverrides = false;
|
||||
boolean seenBaseConfig = false;
|
||||
|
||||
XmlUtils.beginDocument(parser, "network-security-config");
|
||||
int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
// TODO: support debug-override.
|
||||
if ("base-config".equals(parser.getName())) {
|
||||
if (seenBaseConfig) {
|
||||
throw new ParserException(parser, "Only one base-config allowed");
|
||||
}
|
||||
seenBaseConfig = true;
|
||||
baseConfigBuilder = parseConfigEntry(parser, seenDomains, true).first;
|
||||
} else if ("domain-config".equals(parser.getName())) {
|
||||
builders.add(parseConfigEntry(parser, seenDomains, false));
|
||||
} else {
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
}
|
||||
}
|
||||
|
||||
// Use the platform default as the parent of the base config for any values not provided
|
||||
// there. If there is no base config use the platform default.
|
||||
NetworkSecurityConfig.Builder platformDefaultBuilder =
|
||||
NetworkSecurityConfig.getDefaultBuilder();
|
||||
if (baseConfigBuilder != null) {
|
||||
baseConfigBuilder.setParent(platformDefaultBuilder);
|
||||
} else {
|
||||
baseConfigBuilder = platformDefaultBuilder;
|
||||
}
|
||||
// Build the per-domain config mapping.
|
||||
Set<Pair<Domain, NetworkSecurityConfig>> configs = new ArraySet<>();
|
||||
|
||||
for (Pair<NetworkSecurityConfig.Builder, Set<Domain>> entry : builders) {
|
||||
NetworkSecurityConfig.Builder builder = entry.first;
|
||||
Set<Domain> domains = entry.second;
|
||||
// Use the base-config for inheriting any unset values in the domain-config entry.
|
||||
builder.setParent(baseConfigBuilder);
|
||||
NetworkSecurityConfig config = builder.build();
|
||||
for (Domain domain : domains) {
|
||||
configs.add(new Pair<>(domain, config));
|
||||
}
|
||||
}
|
||||
mDefaultConfig = baseConfigBuilder.build();
|
||||
mDomainMap = configs;
|
||||
}
|
||||
|
||||
public static class ParserException extends Exception {
|
||||
|
||||
public ParserException(XmlPullParser parser, String message, Throwable cause) {
|
||||
super(message + " at: " + parser.getPositionDescription(), cause);
|
||||
}
|
||||
|
||||
public ParserException(XmlPullParser parser, String message) {
|
||||
this(parser, message, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.security.tests"
|
||||
package="android.security.net.config"
|
||||
android:sharedUserId="android.uid.system">
|
||||
|
||||
<application>
|
||||
@@ -23,7 +23,7 @@
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="android.security.tests"
|
||||
android:targetPackage="android.security.net.config"
|
||||
android:label="ANSC Tests">
|
||||
</instrumentation>
|
||||
</manifest>
|
||||
|
||||
BIN
tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
Normal file
BIN
tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
Normal file
Binary file not shown.
35
tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
Normal file
35
tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
Normal file
@@ -0,0 +1,35 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
|
||||
MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
|
||||
aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
|
||||
WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
|
||||
AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
|
||||
OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
|
||||
T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
|
||||
JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
|
||||
Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
|
||||
PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
|
||||
aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
|
||||
TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
|
||||
LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
|
||||
BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
|
||||
dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
|
||||
AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
|
||||
NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
|
||||
b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
|
||||
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
|
||||
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
|
||||
MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
|
||||
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
|
||||
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
|
||||
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
|
||||
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
|
||||
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
|
||||
CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
|
||||
2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
|
||||
2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
|
||||
-----END CERTIFICATE-----
|
||||
5
tests/NetworkSecurityConfigTest/res/xml/attributes.xml
Normal file
5
tests/NetworkSecurityConfigTest/res/xml/attributes.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="false" hstsEnforced="true">
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
10
tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml
Normal file
10
tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<pin-set>
|
||||
<!-- Bad pin digest -->
|
||||
<pin digest="I am probably not an algorithm">1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
|
||||
</pin-set>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
10
tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml
Normal file
10
tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<pin-set>
|
||||
<!-- Unknown pin digest -->
|
||||
<pin>1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
|
||||
</pin-set>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
10
tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml
Normal file
10
tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<pin-set>
|
||||
<!-- empty digest -->
|
||||
<pin digest="SHA-256"></pin>
|
||||
</pin-set>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
10
tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml
Normal file
10
tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
</domain-config>
|
||||
<domain-config>
|
||||
<!-- Same domain name used in two configs -->
|
||||
<domain>android.com</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
7
tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml
Normal file
7
tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<!-- domains are not allowed in base-config -->
|
||||
<domain>android.com</domain>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
8
tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml
Normal file
8
tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<!-- pins are not allowed in base-config -->
|
||||
<pin-set>
|
||||
</pin-set>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
9
tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml
Normal file
9
tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<pin-set>
|
||||
<pin digest="SHA-256">1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
|
||||
</pin-set>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
14
tests/NetworkSecurityConfigTest/res/xml/domain1.xml
Normal file
14
tests/NetworkSecurityConfigTest/res/xml/domain1.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
3
tests/NetworkSecurityConfigTest/res/xml/empty_config.xml
Normal file
3
tests/NetworkSecurityConfigTest/res/xml/empty_config.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
</network-security-config>
|
||||
7
tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml
Normal file
7
tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
10
tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml
Normal file
10
tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<!-- Invalid pin that has expired -->
|
||||
<pin-set expiration="2015-01-01">
|
||||
<pin digest="SHA-256">aaaaaaaaaaa2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
|
||||
</pin-set>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
21
tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml
Normal file
21
tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
<domain-config>
|
||||
<domain>google.com</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
15
tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml
Normal file
15
tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<domain>google.com</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
12
tests/NetworkSecurityConfigTest/res/xml/override_pins.xml
Normal file
12
tests/NetworkSecurityConfigTest/res/xml/override_pins.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<pin-set>
|
||||
<pin digest="SHA-256">aaaaaaaaIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
|
||||
</pin-set>
|
||||
<trust-anchors>
|
||||
<certificates src="system" overridePins="true" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
9
tests/NetworkSecurityConfigTest/res/xml/pins1.xml
Normal file
9
tests/NetworkSecurityConfigTest/res/xml/pins1.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<pin-set>
|
||||
<pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
|
||||
</pin-set>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="@raw/ca_certs_der" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="@raw/ca_certs_pem" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
14
tests/NetworkSecurityConfigTest/res/xml/subdomains.xml
Normal file
14
tests/NetworkSecurityConfigTest/res/xml/subdomains.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config>
|
||||
<domain includeSubdomains="true">android.com</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
@@ -50,49 +50,6 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -115,8 +72,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
= new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
|
||||
ConfigSource testSource =
|
||||
new TestConfigSource(domainMap, getEmptyConfig());
|
||||
SSLContext context = getSSLContext(testSource);
|
||||
assertConnectionFails(context, "android.com", 443);
|
||||
SSLContext context = TestUtils.getSSLContext(testSource);
|
||||
TestUtils.assertConnectionFails(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testEmptyPerNetworkSecurityConfig() throws Exception {
|
||||
@@ -125,9 +82,9 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
|
||||
new Domain("android.com", true), getEmptyConfig()));
|
||||
NetworkSecurityConfig defaultConfig = getSystemStoreConfig();
|
||||
SSLContext context = getSSLContext(new TestConfigSource(domainMap, defaultConfig));
|
||||
assertConnectionFails(context, "android.com", 443);
|
||||
assertConnectionSucceeds(context, "google.com", 443);
|
||||
SSLContext context = TestUtils.getSSLContext(new TestConfigSource(domainMap, defaultConfig));
|
||||
TestUtils.assertConnectionFails(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "google.com", 443);
|
||||
}
|
||||
|
||||
public void testBadPin() throws Exception {
|
||||
@@ -143,9 +100,9 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
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);
|
||||
= TestUtils.getSSLContext(new TestConfigSource(domainMap, getSystemStoreConfig()));
|
||||
TestUtils.assertConnectionFails(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "google.com", 443);
|
||||
}
|
||||
|
||||
public void testGoodPin() throws Exception {
|
||||
@@ -161,9 +118,9 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
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);
|
||||
= TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
|
||||
}
|
||||
|
||||
public void testOverridePins() throws Exception {
|
||||
@@ -180,8 +137,8 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
|
||||
new Domain("android.com", true), domain));
|
||||
SSLContext context
|
||||
= getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
|
||||
assertConnectionSucceeds(context, "android.com", 443);
|
||||
= TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testMostSpecificNetworkSecurityConfig() throws Exception {
|
||||
@@ -192,9 +149,9 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
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);
|
||||
= TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
|
||||
TestUtils.assertConnectionFails(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
|
||||
}
|
||||
|
||||
public void testSubdomainIncluded() throws Exception {
|
||||
@@ -204,14 +161,14 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
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);
|
||||
= TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
|
||||
TestUtils.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);
|
||||
context = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
|
||||
TestUtils.assertConnectionFails(context, "developer.android.com", 443);
|
||||
}
|
||||
|
||||
public void testConfigBuilderUsesParents() throws Exception {
|
||||
@@ -246,9 +203,9 @@ public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
|
||||
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);
|
||||
= TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionFails(context, "google.com", 443);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.net.Socket;
|
||||
import java.net.URL;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public final class TestUtils extends Assert {
|
||||
|
||||
private TestUtils() {
|
||||
}
|
||||
|
||||
public static 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) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertConnectionSucceeds(SSLContext context, String host, int port)
|
||||
throws Exception {
|
||||
Socket s = context.getSocketFactory().createSocket(host, port);
|
||||
s.getInputStream();
|
||||
}
|
||||
|
||||
public static 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.
|
||||
}
|
||||
}
|
||||
|
||||
public static 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();
|
||||
}
|
||||
|
||||
public static 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
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 XmlConfigTests extends AndroidTestCase {
|
||||
|
||||
public void testEmptyConfigFile() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertFalse(appConfig.hasPerDomainConfigs());
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("");
|
||||
assertNotNull(config);
|
||||
// Check defaults.
|
||||
assertTrue(config.isCleartextTrafficPermitted());
|
||||
assertFalse(config.isHstsEnforced());
|
||||
assertFalse(config.getTrustAnchors().isEmpty());
|
||||
PinSet pinSet = config.getPins();
|
||||
assertTrue(pinSet.pins.isEmpty());
|
||||
// Try some connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "google.com", 443);
|
||||
}
|
||||
|
||||
public void testEmptyAnchors() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertFalse(appConfig.hasPerDomainConfigs());
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("");
|
||||
assertNotNull(config);
|
||||
// Check defaults.
|
||||
assertTrue(config.isCleartextTrafficPermitted());
|
||||
assertFalse(config.isHstsEnforced());
|
||||
assertTrue(config.getTrustAnchors().isEmpty());
|
||||
PinSet pinSet = config.getPins();
|
||||
assertTrue(pinSet.pins.isEmpty());
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionFails(context, "android.com", 443);
|
||||
TestUtils.assertConnectionFails(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionFails(context, "google.com", 443);
|
||||
}
|
||||
|
||||
public void testBasicDomainConfig() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("");
|
||||
assertNotNull(config);
|
||||
// Check defaults.
|
||||
assertTrue(config.isCleartextTrafficPermitted());
|
||||
assertFalse(config.isHstsEnforced());
|
||||
assertTrue(config.getTrustAnchors().isEmpty());
|
||||
PinSet pinSet = config.getPins();
|
||||
assertTrue(pinSet.pins.isEmpty());
|
||||
// Check android.com.
|
||||
config = appConfig.getConfigForHostname("android.com");
|
||||
assertTrue(config.isCleartextTrafficPermitted());
|
||||
assertFalse(config.isHstsEnforced());
|
||||
assertFalse(config.getTrustAnchors().isEmpty());
|
||||
pinSet = config.getPins();
|
||||
assertTrue(pinSet.pins.isEmpty());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionFails(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionFails(context, "google.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testBasicPinning() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
// Check android.com.
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
PinSet pinSet = config.getPins();
|
||||
assertFalse(pinSet.pins.isEmpty());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "google.com", 443);
|
||||
}
|
||||
|
||||
public void testExpiredPin() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
// Check android.com.
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
PinSet pinSet = config.getPins();
|
||||
assertFalse(pinSet.pins.isEmpty());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testOverridesPins() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
// Check android.com.
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
PinSet pinSet = config.getPins();
|
||||
assertFalse(pinSet.pins.isEmpty());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testBadPin() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
// Check android.com.
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
PinSet pinSet = config.getPins();
|
||||
assertFalse(pinSet.pins.isEmpty());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionFails(context, "android.com", 443);
|
||||
TestUtils.assertUrlConnectionFails(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "google.com", 443);
|
||||
}
|
||||
|
||||
public void testMultipleDomains() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
assertTrue(config.isCleartextTrafficPermitted());
|
||||
assertFalse(config.isHstsEnforced());
|
||||
assertFalse(config.getTrustAnchors().isEmpty());
|
||||
PinSet pinSet = config.getPins();
|
||||
assertTrue(pinSet.pins.isEmpty());
|
||||
// Both android.com and google.com should use the same config
|
||||
NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
|
||||
assertEquals(config, other);
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "google.com", 443);
|
||||
TestUtils.assertConnectionFails(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testMultipleDomainConfigs() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
// Should be two different config objects
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
|
||||
MoreAsserts.assertNotEqual(config, other);
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "google.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testIncludeSubdomains() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertTrue(appConfig.hasPerDomainConfigs());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
|
||||
TestUtils.assertConnectionFails(context, "google.com", 443);
|
||||
}
|
||||
|
||||
public void testAttributes() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
assertFalse(appConfig.hasPerDomainConfigs());
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("");
|
||||
assertTrue(config.isHstsEnforced());
|
||||
assertFalse(config.isCleartextTrafficPermitted());
|
||||
}
|
||||
|
||||
public void testResourcePemCertificateSource() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
// Check android.com.
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
assertTrue(config.isCleartextTrafficPermitted());
|
||||
assertFalse(config.isHstsEnforced());
|
||||
assertEquals(2, config.getTrustAnchors().size());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionFails(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionFails(context, "google.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
public void testResourceDerCertificateSource() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
// Check android.com.
|
||||
NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
|
||||
assertTrue(config.isCleartextTrafficPermitted());
|
||||
assertFalse(config.isHstsEnforced());
|
||||
assertEquals(2, config.getTrustAnchors().size());
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertConnectionFails(context, "developer.android.com", 443);
|
||||
TestUtils.assertUrlConnectionFails(context, "google.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
|
||||
private void testBadConfig(int configId) throws Exception {
|
||||
try {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), configId);
|
||||
ApplicationConfig appConfig = new ApplicationConfig(source);
|
||||
appConfig.getConfigForHostname("android.com");
|
||||
fail("Bad config " + getContext().getResources().getResourceName(configId)
|
||||
+ " did not fail to parse");
|
||||
} catch (RuntimeException e) {
|
||||
MoreAsserts.assertAssignableFrom(XmlConfigSource.ParserException.class,
|
||||
e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public void testBadConfig0() throws Exception {
|
||||
testBadConfig(R.xml.bad_config0);
|
||||
}
|
||||
|
||||
public void testBadConfig1() throws Exception {
|
||||
testBadConfig(R.xml.bad_config1);
|
||||
}
|
||||
|
||||
public void testBadConfig2() throws Exception {
|
||||
testBadConfig(R.xml.bad_config2);
|
||||
}
|
||||
|
||||
public void testBadConfig3() throws Exception {
|
||||
testBadConfig(R.xml.bad_config3);
|
||||
}
|
||||
|
||||
public void testBadConfig4() throws Exception {
|
||||
testBadConfig(R.xml.bad_config4);
|
||||
}
|
||||
|
||||
public void testBadConfig5() throws Exception {
|
||||
testBadConfig(R.xml.bad_config4);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user