Loading core/java/android/security/net/config/Pin.java +20 −0 Original line number Diff line number Diff line Loading @@ -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; Loading core/java/android/security/net/config/XmlConfigSource.java 0 → 100644 +310 −0 Original line number Diff line number Diff line 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); } } } tests/NetworkSecurityConfigTest/AndroidManifest.xml +2 −2 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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> tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der 0 → 100644 +1.44 KiB File added.No diff preview for this file type. View file tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem 0 → 100644 +35 −0 Original line number Diff line number Diff line -----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----- Loading
core/java/android/security/net/config/Pin.java +20 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
core/java/android/security/net/config/XmlConfigSource.java 0 → 100644 +310 −0 Original line number Diff line number Diff line 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); } } }
tests/NetworkSecurityConfigTest/AndroidManifest.xml +2 −2 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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>
tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der 0 → 100644 +1.44 KiB File added.No diff preview for this file type. View file
tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem 0 → 100644 +35 −0 Original line number Diff line number Diff line -----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-----