Merge "Expose findTrustAnchorBySubjectAndPublicKey"
am: 6fea66116c
* commit '6fea66116c79ecb5232a1c7ae66ae2a6e62c1c12':
Expose findTrustAnchorBySubjectAndPublicKey
This commit is contained in:
@@ -22,4 +22,5 @@ import java.security.cert.X509Certificate;
|
||||
/** @hide */
|
||||
public interface CertificateSource {
|
||||
Set<X509Certificate> getCertificates();
|
||||
X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ public final class CertificatesEntryRef {
|
||||
mOverridesPins = overridesPins;
|
||||
}
|
||||
|
||||
boolean overridesPins() {
|
||||
return mOverridesPins;
|
||||
}
|
||||
|
||||
public Set<TrustAnchor> getTrustAnchors() {
|
||||
// TODO: cache this [but handle mutable sources]
|
||||
Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
|
||||
@@ -38,4 +42,13 @@ public final class CertificatesEntryRef {
|
||||
}
|
||||
return anchors;
|
||||
}
|
||||
|
||||
public TrustAnchor findBySubjectAndPublicKey(X509Certificate cert) {
|
||||
X509Certificate foundCert = mSource.findBySubjectAndPublicKey(cert);
|
||||
if (foundCert == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TrustAnchor(foundCert, mOverridesPins);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.os.Environment;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Pair;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Set;
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import com.android.org.conscrypt.NativeCrypto;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
/**
|
||||
* {@link CertificateSource} based on a directory where certificates are stored as individual files
|
||||
* named after a hash of their SubjectName for more efficient lookups.
|
||||
* @hide
|
||||
*/
|
||||
abstract class DirectoryCertificateSource implements CertificateSource {
|
||||
private final File mDir;
|
||||
private final Object mLock = new Object();
|
||||
private final CertificateFactory mCertFactory;
|
||||
|
||||
private Set<X509Certificate> mCertificates;
|
||||
|
||||
protected DirectoryCertificateSource(File caDir) {
|
||||
mDir = caDir;
|
||||
try {
|
||||
mCertFactory = CertificateFactory.getInstance("X.509");
|
||||
} catch (CertificateException e) {
|
||||
throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean isCertMarkedAsRemoved(String caFile);
|
||||
|
||||
@Override
|
||||
public Set<X509Certificate> getCertificates() {
|
||||
// TODO: loading all of these is wasteful, we should instead use a keystore style API.
|
||||
synchronized (mLock) {
|
||||
if (mCertificates != null) {
|
||||
return mCertificates;
|
||||
}
|
||||
|
||||
Set<X509Certificate> certs = new ArraySet<X509Certificate>();
|
||||
if (mDir.isDirectory()) {
|
||||
for (String caFile : mDir.list()) {
|
||||
if (isCertMarkedAsRemoved(caFile)) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate cert = readCertificate(caFile);
|
||||
if (cert != null) {
|
||||
certs.add(cert);
|
||||
}
|
||||
}
|
||||
}
|
||||
mCertificates = certs;
|
||||
return mCertificates;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate findBySubjectAndPublicKey(final X509Certificate cert) {
|
||||
return findCert(cert.getSubjectX500Principal(), new CertSelector() {
|
||||
@Override
|
||||
public boolean match(X509Certificate ca) {
|
||||
return ca.getPublicKey().equals(cert.getPublicKey());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static interface CertSelector {
|
||||
boolean match(X509Certificate cert);
|
||||
}
|
||||
|
||||
private X509Certificate findCert(X500Principal subj, CertSelector selector) {
|
||||
String hash = getHash(subj);
|
||||
for (int index = 0; index >= 0; index++) {
|
||||
String fileName = hash + "." + index;
|
||||
if (!new File(mDir, fileName).exists()) {
|
||||
break;
|
||||
}
|
||||
if (isCertMarkedAsRemoved(fileName)) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate cert = readCertificate(fileName);
|
||||
if (!subj.equals(cert.getSubjectX500Principal())) {
|
||||
continue;
|
||||
}
|
||||
if (selector.match(cert)) {
|
||||
return cert;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getHash(X500Principal name) {
|
||||
int hash = NativeCrypto.X509_NAME_hash_old(name);
|
||||
return IntegralToString.intToHexString(hash, false, 8);
|
||||
}
|
||||
|
||||
private X509Certificate readCertificate(String file) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new BufferedInputStream(new FileInputStream(new File(mDir, file)));
|
||||
return (X509Certificate) mCertFactory.generateCertificate(is);
|
||||
} catch (CertificateException | IOException e) {
|
||||
return null;
|
||||
} finally {
|
||||
IoUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
|
||||
import com.android.org.conscrypt.TrustedCertificateIndex;
|
||||
|
||||
/**
|
||||
* {@link CertificateSource} which provides certificates from trusted certificate entries of a
|
||||
* {@link KeyStore}.
|
||||
@@ -31,6 +33,7 @@ import java.util.Set;
|
||||
class KeyStoreCertificateSource implements CertificateSource {
|
||||
private final Object mLock = new Object();
|
||||
private final KeyStore mKeyStore;
|
||||
private TrustedCertificateIndex mIndex;
|
||||
private Set<X509Certificate> mCertificates;
|
||||
|
||||
public KeyStoreCertificateSource(KeyStore ks) {
|
||||
@@ -39,24 +42,42 @@ class KeyStoreCertificateSource implements CertificateSource {
|
||||
|
||||
@Override
|
||||
public Set<X509Certificate> getCertificates() {
|
||||
ensureInitialized();
|
||||
return mCertificates;
|
||||
}
|
||||
|
||||
private void ensureInitialized() {
|
||||
synchronized (mLock) {
|
||||
if (mCertificates != null) {
|
||||
return mCertificates;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
TrustedCertificateIndex localIndex = new TrustedCertificateIndex();
|
||||
Set<X509Certificate> certificates = new ArraySet<>(mKeyStore.size());
|
||||
for (Enumeration<String> en = mKeyStore.aliases(); en.hasMoreElements();) {
|
||||
String alias = en.nextElement();
|
||||
X509Certificate cert = (X509Certificate) mKeyStore.getCertificate(alias);
|
||||
if (cert != null) {
|
||||
certificates.add(cert);
|
||||
localIndex.index(cert);
|
||||
}
|
||||
}
|
||||
mIndex = localIndex;
|
||||
mCertificates = certificates;
|
||||
return mCertificates;
|
||||
} catch (KeyStoreException e) {
|
||||
throw new RuntimeException("Failed to load certificates from KeyStore", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
|
||||
ensureInitialized();
|
||||
java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
|
||||
if (anchor == null) {
|
||||
return null;
|
||||
}
|
||||
return anchor.getTrustedCert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -53,6 +54,19 @@ public final class NetworkSecurityConfig {
|
||||
mHstsEnforced = hstsEnforced;
|
||||
mPins = pins;
|
||||
mCertificatesEntryRefs = certificatesEntryRefs;
|
||||
// Sort the certificates entry refs so that all entries that override pins come before
|
||||
// non-override pin entries. This allows us to handle the case where a certificate is in
|
||||
// multiple entry refs by returning the certificate from the first entry ref.
|
||||
Collections.sort(mCertificatesEntryRefs, new Comparator<CertificatesEntryRef>() {
|
||||
@Override
|
||||
public int compare(CertificatesEntryRef lhs, CertificatesEntryRef rhs) {
|
||||
if (lhs.overridesPins()) {
|
||||
return rhs.overridesPins() ? 0 : -1;
|
||||
} else {
|
||||
return rhs.overridesPins() ? 1 : 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Set<TrustAnchor> getTrustAnchors() {
|
||||
@@ -63,14 +77,15 @@ public final class NetworkSecurityConfig {
|
||||
// Merge trust anchors based on the X509Certificate.
|
||||
// If we see the same certificate in two TrustAnchors, one with overridesPins and one
|
||||
// without, the one with overridesPins wins.
|
||||
// Because mCertificatesEntryRefs is sorted with all overridesPins anchors coming first
|
||||
// this can be simplified to just using the first occurrence of a certificate.
|
||||
Map<X509Certificate, TrustAnchor> anchorMap = new ArrayMap<>();
|
||||
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
|
||||
Set<TrustAnchor> anchors = ref.getTrustAnchors();
|
||||
for (TrustAnchor anchor : anchors) {
|
||||
if (anchor.overridesPins) {
|
||||
anchorMap.put(anchor.certificate, anchor);
|
||||
} else if (!anchorMap.containsKey(anchor.certificate)) {
|
||||
anchorMap.put(anchor.certificate, anchor);
|
||||
X509Certificate cert = anchor.certificate;
|
||||
if (!anchorMap.containsKey(cert)) {
|
||||
anchorMap.put(cert, anchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,6 +123,17 @@ public final class NetworkSecurityConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
|
||||
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
|
||||
TrustAnchor anchor = ref.findBySubjectAndPublicKey(cert);
|
||||
if (anchor != null) {
|
||||
return anchor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
|
||||
*
|
||||
|
||||
@@ -133,14 +133,8 @@ public class NetworkSecurityTrustManager implements X509TrustManager {
|
||||
return false;
|
||||
}
|
||||
X509Certificate anchorCert = chain.get(chain.size() - 1);
|
||||
TrustAnchor chainAnchor = null;
|
||||
// TODO: faster lookup
|
||||
for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
|
||||
if (anchor.certificate.equals(anchorCert)) {
|
||||
chainAnchor = anchor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TrustAnchor chainAnchor =
|
||||
mNetworkSecurityConfig.findTrustAnchorBySubjectAndPublicKey(anchorCert);
|
||||
if (chainAnchor == null) {
|
||||
throw new CertificateException("Trusted chain does not end in a TrustAnchor");
|
||||
}
|
||||
|
||||
@@ -27,26 +27,29 @@ import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import com.android.org.conscrypt.TrustedCertificateIndex;
|
||||
|
||||
/**
|
||||
* {@link CertificateSource} based on certificates contained in an application resource file.
|
||||
* @hide
|
||||
*/
|
||||
public class ResourceCertificateSource implements CertificateSource {
|
||||
private Set<X509Certificate> mCertificates;
|
||||
private final int mResourceId;
|
||||
private Context mContext;
|
||||
private final Object mLock = new Object();
|
||||
private final int mResourceId;
|
||||
|
||||
private Set<X509Certificate> mCertificates;
|
||||
private Context mContext;
|
||||
private TrustedCertificateIndex mIndex;
|
||||
|
||||
public ResourceCertificateSource(int resourceId, Context context) {
|
||||
mResourceId = resourceId;
|
||||
mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<X509Certificate> getCertificates() {
|
||||
private void ensureInitialized() {
|
||||
synchronized (mLock) {
|
||||
if (mCertificates != null) {
|
||||
return mCertificates;
|
||||
return;
|
||||
}
|
||||
Set<X509Certificate> certificates = new ArraySet<X509Certificate>();
|
||||
Collection<? extends Certificate> certs;
|
||||
@@ -61,12 +64,30 @@ public class ResourceCertificateSource implements CertificateSource {
|
||||
} finally {
|
||||
IoUtils.closeQuietly(in);
|
||||
}
|
||||
TrustedCertificateIndex indexLocal = new TrustedCertificateIndex();
|
||||
for (Certificate cert : certs) {
|
||||
certificates.add((X509Certificate) cert);
|
||||
certificates.add((X509Certificate) cert);
|
||||
indexLocal.index((X509Certificate) cert);
|
||||
}
|
||||
mCertificates = certificates;
|
||||
mIndex = indexLocal;
|
||||
mContext = null;
|
||||
return mCertificates;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<X509Certificate> getCertificates() {
|
||||
ensureInitialized();
|
||||
return mCertificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
|
||||
ensureInitialized();
|
||||
java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
|
||||
if (anchor == null) {
|
||||
return null;
|
||||
}
|
||||
return anchor.getTrustedCert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,29 +18,20 @@ package android.security.net.config;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Set;
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
/**
|
||||
* {@link CertificateSource} based on the system trusted CA store.
|
||||
* @hide
|
||||
*/
|
||||
public class SystemCertificateSource implements CertificateSource {
|
||||
public final class SystemCertificateSource extends DirectoryCertificateSource {
|
||||
private static final SystemCertificateSource INSTANCE = new SystemCertificateSource();
|
||||
private Set<X509Certificate> mSystemCerts = null;
|
||||
private final Object mLock = new Object();
|
||||
private final File mUserRemovedCaDir;
|
||||
|
||||
private SystemCertificateSource() {
|
||||
super(new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts"));
|
||||
File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
|
||||
mUserRemovedCaDir = new File(configDir, "cacerts-removed");
|
||||
}
|
||||
|
||||
public static SystemCertificateSource getInstance() {
|
||||
@@ -48,54 +39,7 @@ public class SystemCertificateSource implements CertificateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<X509Certificate> getCertificates() {
|
||||
// TODO: loading all of these is wasteful, we should instead use a keystore style API.
|
||||
synchronized (mLock) {
|
||||
if (mSystemCerts != null) {
|
||||
return mSystemCerts;
|
||||
}
|
||||
CertificateFactory certFactory;
|
||||
try {
|
||||
certFactory = CertificateFactory.getInstance("X.509");
|
||||
} catch (CertificateException e) {
|
||||
throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
|
||||
}
|
||||
|
||||
final String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
|
||||
final File systemCaDir = new File(ANDROID_ROOT + "/etc/security/cacerts");
|
||||
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
|
||||
final File userRemovedCaDir = new File(configDir, "cacerts-removed");
|
||||
// Sanity check
|
||||
if (!systemCaDir.isDirectory()) {
|
||||
throw new AssertionError(systemCaDir + " is not a directory");
|
||||
}
|
||||
|
||||
Set<X509Certificate> systemCerts = new ArraySet<X509Certificate>();
|
||||
for (String caFile : systemCaDir.list()) {
|
||||
// Skip any CAs in the user's deleted directory.
|
||||
if (new File(userRemovedCaDir, caFile).exists()) {
|
||||
continue;
|
||||
}
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new BufferedInputStream(
|
||||
new FileInputStream(new File(systemCaDir, caFile)));
|
||||
systemCerts.add((X509Certificate) certFactory.generateCertificate(is));
|
||||
} catch (CertificateException | IOException e) {
|
||||
// Don't rethrow to be consistent with conscrypt's cert loading code.
|
||||
continue;
|
||||
} finally {
|
||||
IoUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
mSystemCerts = systemCerts;
|
||||
return mSystemCerts;
|
||||
}
|
||||
}
|
||||
|
||||
public void onCertificateStorageChange() {
|
||||
synchronized (mLock) {
|
||||
mSystemCerts = null;
|
||||
}
|
||||
protected boolean isCertMarkedAsRemoved(String caFile) {
|
||||
return new File(mUserRemovedCaDir, caFile).exists();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,29 +18,18 @@ package android.security.net.config;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Set;
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
/**
|
||||
* {@link CertificateSource} based on the user-installed trusted CA store.
|
||||
* @hide
|
||||
*/
|
||||
public class UserCertificateSource implements CertificateSource {
|
||||
public final class UserCertificateSource extends DirectoryCertificateSource {
|
||||
private static final UserCertificateSource INSTANCE = new UserCertificateSource();
|
||||
private Set<X509Certificate> mUserCerts = null;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private UserCertificateSource() {
|
||||
super(new File(
|
||||
Environment.getUserConfigDirectory(UserHandle.myUserId()), "cacerts-added"));
|
||||
}
|
||||
|
||||
public static UserCertificateSource getInstance() {
|
||||
@@ -48,45 +37,7 @@ public class UserCertificateSource implements CertificateSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<X509Certificate> getCertificates() {
|
||||
// TODO: loading all of these is wasteful, we should instead use a keystore style API.
|
||||
synchronized (mLock) {
|
||||
if (mUserCerts != null) {
|
||||
return mUserCerts;
|
||||
}
|
||||
CertificateFactory certFactory;
|
||||
try {
|
||||
certFactory = CertificateFactory.getInstance("X.509");
|
||||
} catch (CertificateException e) {
|
||||
throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
|
||||
}
|
||||
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
|
||||
final File userCaDir = new File(configDir, "cacerts-added");
|
||||
Set<X509Certificate> userCerts = new ArraySet<X509Certificate>();
|
||||
// If the user hasn't added any certificates the directory may not exist.
|
||||
if (userCaDir.isDirectory()) {
|
||||
for (String caFile : userCaDir.list()) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new BufferedInputStream(
|
||||
new FileInputStream(new File(userCaDir, caFile)));
|
||||
userCerts.add((X509Certificate) certFactory.generateCertificate(is));
|
||||
} catch (CertificateException | IOException e) {
|
||||
// Don't rethrow to be consistent with conscrypt's cert loading code.
|
||||
continue;
|
||||
} finally {
|
||||
IoUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
mUserCerts = userCerts;
|
||||
return mUserCerts;
|
||||
}
|
||||
}
|
||||
|
||||
public void onCertificateStorageChange() {
|
||||
synchronized (mLock) {
|
||||
mUserCerts = null;
|
||||
}
|
||||
protected boolean isCertMarkedAsRemoved(String caFile) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
20
tests/NetworkSecurityConfigTest/res/xml/override_dedup.xml
Normal file
20
tests/NetworkSecurityConfigTest/res/xml/override_dedup.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<!-- Entry with a bad pin. Connections to this will only succeed if overridePins is set. -->
|
||||
<domain-config>
|
||||
<domain>android.com</domain>
|
||||
<pin-set>
|
||||
<pin digest="SHA-256">aaaaaaaaIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
|
||||
</pin-set>
|
||||
<trust-anchors>
|
||||
<certificates src="system" overridePins="false" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
<!-- override that contains all of the system CA store. This should completely override the
|
||||
anchors in the domain config-above with ones that have overridePins set. -->
|
||||
<debug-overrides>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
</trust-anchors>
|
||||
</debug-overrides>
|
||||
</network-security-config>
|
||||
@@ -19,15 +19,29 @@ package android.security.net.config;
|
||||
import java.util.Set;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import com.android.org.conscrypt.TrustedCertificateIndex;
|
||||
|
||||
/** @hide */
|
||||
public class TestCertificateSource implements CertificateSource {
|
||||
|
||||
private final Set<X509Certificate> mCertificates;
|
||||
private final TrustedCertificateIndex mIndex = new TrustedCertificateIndex();
|
||||
public TestCertificateSource(Set<X509Certificate> certificates) {
|
||||
mCertificates = certificates;
|
||||
for (X509Certificate cert : certificates) {
|
||||
mIndex.index(cert);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<X509Certificate> getCertificates() {
|
||||
return mCertificates;
|
||||
}
|
||||
|
||||
public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
|
||||
java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
|
||||
if (anchor == null) {
|
||||
return null;
|
||||
}
|
||||
return anchor.getTrustedCert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,4 +402,22 @@ public class XmlConfigTests extends AndroidTestCase {
|
||||
context.init(null, tms, null);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com" , 443);
|
||||
}
|
||||
|
||||
public void testDebugDedup() throws Exception {
|
||||
XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_dedup, true);
|
||||
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());
|
||||
// Check that all TrustAnchors come from the override pins debug source.
|
||||
for (TrustAnchor anchor : config.getTrustAnchors()) {
|
||||
assertTrue(anchor.overridesPins);
|
||||
}
|
||||
// Try connections.
|
||||
SSLContext context = TestUtils.getSSLContext(source);
|
||||
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
|
||||
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user