This source file includes following definitions.
- JNINamespace
- onReceive
- checkServerTrusted
- checkServerTrusted
- SuppressLint
- checkServerTrusted
- ensureInitialized
- buildSystemTrustRootSet
- createTrustManager
- reloadTestTrustManager
- reloadDefaultTrustManager
- createCertificateFromBytes
- addTestRootCertificate
- clearTestRootCertificates
- verifyKeyUsage
- verifyServerCertificates
- setDisableNativeCodeForTest
- nativeNotifyKeyChainChanged
- nativeRecordCertVerifyCapabilitiesHistogram
- nativeGetApplicationContext
package org.chromium.net;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.http.X509TrustManagerExtensions;
import android.os.Build;
import android.security.KeyChain;
import android.util.Log;
import android.util.Pair;
import org.chromium.base.JNINamespace;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
@JNINamespace("net")
public class X509Util {
private static final String TAG = "X509Util";
private static final class TrustStorageListener extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
try {
reloadDefaultTrustManager();
}
catch (CertificateException e) {
Log.e(TAG, "Unable to reload the default TrustManager", e);
}
catch (KeyStoreException e) {
Log.e(TAG, "Unable to reload the default TrustManager", e);
}
catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Unable to reload the default TrustManager", e);
}
}
}
}
private static interface X509TrustManagerImplementation {
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
String authType,
String host) throws CertificateException;
}
private static final class X509TrustManagerIceCreamSandwich implements
X509TrustManagerImplementation {
private final X509TrustManager mTrustManager;
public X509TrustManagerIceCreamSandwich(X509TrustManager trustManager) {
mTrustManager = trustManager;
}
@Override
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
String authType,
String host) throws CertificateException {
mTrustManager.checkServerTrusted(chain, authType);
return Collections.<X509Certificate>emptyList();
}
}
private static final class X509TrustManagerJellyBean implements X509TrustManagerImplementation {
private final X509TrustManagerExtensions mTrustManagerExtensions;
@SuppressLint("NewApi")
public X509TrustManagerJellyBean(X509TrustManager trustManager) {
mTrustManagerExtensions = new X509TrustManagerExtensions(trustManager);
}
@Override
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
String authType,
String host) throws CertificateException {
return mTrustManagerExtensions.checkServerTrusted(chain, authType, host);
}
}
private static CertificateFactory sCertificateFactory;
private static final String OID_TLS_SERVER_AUTH = "1.3.6.1.5.5.7.3.1";
private static final String OID_ANY_EKU = "2.5.29.37.0";
private static final String OID_SERVER_GATED_NETSCAPE = "2.16.840.1.113730.4.1";
private static final String OID_SERVER_GATED_MICROSOFT = "1.3.6.1.4.1.311.10.3.3";
private static X509TrustManagerImplementation sDefaultTrustManager;
private static TrustStorageListener sTrustStorageListener;
private static X509TrustManagerImplementation sTestTrustManager;
private static KeyStore sTestKeyStore;
private static Set<Pair<X500Principal, PublicKey>> sSystemTrustRoots;
private static boolean sLoadedSystemTrustRoots;
private static final Object sLock = new Object();
private static boolean sDisableNativeCodeForTest = false;
private static void ensureInitialized() throws CertificateException,
KeyStoreException, NoSuchAlgorithmException {
synchronized (sLock) {
if (sCertificateFactory == null) {
sCertificateFactory = CertificateFactory.getInstance("X.509");
}
if (sDefaultTrustManager == null) {
sDefaultTrustManager = X509Util.createTrustManager(null);
}
if (!sLoadedSystemTrustRoots) {
try {
sSystemTrustRoots = buildSystemTrustRootSet();
} catch (KeyStoreException e) {
Log.w(TAG, "Could not load system trust root set", e);
}
if (!sDisableNativeCodeForTest)
nativeRecordCertVerifyCapabilitiesHistogram(sSystemTrustRoots != null);
sLoadedSystemTrustRoots = true;
}
if (sTestKeyStore == null) {
sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try {
sTestKeyStore.load(null);
} catch (IOException e) {
}
}
if (sTestTrustManager == null) {
sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
}
if (!sDisableNativeCodeForTest && sTrustStorageListener == null) {
sTrustStorageListener = new TrustStorageListener();
nativeGetApplicationContext().registerReceiver(sTrustStorageListener,
new IntentFilter(KeyChain.ACTION_STORAGE_CHANGED));
}
}
}
private static Set<Pair<X500Principal, PublicKey>> buildSystemTrustRootSet() throws
CertificateException, KeyStoreException, NoSuchAlgorithmException {
KeyStore systemKeyStore = KeyStore.getInstance("AndroidCAStore");
try {
systemKeyStore.load(null);
} catch (IOException e) {
}
Set<Pair<X500Principal, PublicKey>> roots = new HashSet<Pair<X500Principal, PublicKey>>();
Enumeration<String> aliases = systemKeyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (!alias.startsWith("system:"))
continue;
Certificate cert = systemKeyStore.getCertificate(alias);
if (cert != null && cert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate)cert;
roots.add(new Pair<X500Principal, PublicKey>(x509Cert.getSubjectX500Principal(),
x509Cert.getPublicKey()));
}
}
return roots;
}
private static X509TrustManagerImplementation createTrustManager(KeyStore keyStore) throws
KeyStoreException, NoSuchAlgorithmException {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(keyStore);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return new X509TrustManagerJellyBean((X509TrustManager) tm);
} else {
return new X509TrustManagerIceCreamSandwich((X509TrustManager) tm);
}
} catch (IllegalArgumentException e) {
Log.e(TAG, "Error creating trust manager: " + e);
}
}
}
return null;
}
private static void reloadTestTrustManager() throws KeyStoreException,
NoSuchAlgorithmException {
sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
}
private static void reloadDefaultTrustManager() throws KeyStoreException,
NoSuchAlgorithmException, CertificateException {
sDefaultTrustManager = null;
sSystemTrustRoots = null;
sLoadedSystemTrustRoots = false;
nativeNotifyKeyChainChanged();
ensureInitialized();
}
public static X509Certificate createCertificateFromBytes(byte[] derBytes) throws
CertificateException, KeyStoreException, NoSuchAlgorithmException {
ensureInitialized();
return (X509Certificate) sCertificateFactory.generateCertificate(
new ByteArrayInputStream(derBytes));
}
public static void addTestRootCertificate(byte[] rootCertBytes) throws CertificateException,
KeyStoreException, NoSuchAlgorithmException {
ensureInitialized();
X509Certificate rootCert = createCertificateFromBytes(rootCertBytes);
synchronized (sLock) {
sTestKeyStore.setCertificateEntry(
"root_cert_" + Integer.toString(sTestKeyStore.size()), rootCert);
reloadTestTrustManager();
}
}
public static void clearTestRootCertificates() throws NoSuchAlgorithmException,
CertificateException, KeyStoreException {
ensureInitialized();
synchronized (sLock) {
try {
sTestKeyStore.load(null);
reloadTestTrustManager();
} catch (IOException e) {
}
}
}
static boolean verifyKeyUsage(X509Certificate certificate) throws CertificateException {
List<String> ekuOids;
try {
ekuOids = certificate.getExtendedKeyUsage();
} catch (NullPointerException e) {
return false;
}
if (ekuOids == null)
return true;
for (String ekuOid : ekuOids) {
if (ekuOid.equals(OID_TLS_SERVER_AUTH) ||
ekuOid.equals(OID_ANY_EKU) ||
ekuOid.equals(OID_SERVER_GATED_NETSCAPE) ||
ekuOid.equals(OID_SERVER_GATED_MICROSOFT)) {
return true;
}
}
return false;
}
public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain,
String authType,
String host)
throws KeyStoreException, NoSuchAlgorithmException {
if (certChain == null || certChain.length == 0 || certChain[0] == null) {
throw new IllegalArgumentException("Expected non-null and non-empty certificate " +
"chain passed as |certChain|. |certChain|=" + Arrays.deepToString(certChain));
}
try {
ensureInitialized();
} catch (CertificateException e) {
return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
}
X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
try {
for (int i = 0; i < certChain.length; ++i) {
serverCertificates[i] = createCertificateFromBytes(certChain[i]);
}
} catch (CertificateException e) {
return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_UNABLE_TO_PARSE);
}
try {
serverCertificates[0].checkValidity();
if (!verifyKeyUsage(serverCertificates[0])) {
return new AndroidCertVerifyResult(
CertVerifyStatusAndroid.VERIFY_INCORRECT_KEY_USAGE);
}
} catch (CertificateExpiredException e) {
return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_EXPIRED);
} catch (CertificateNotYetValidException e) {
return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_NOT_YET_VALID);
} catch (CertificateException e) {
return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
}
synchronized (sLock) {
List<X509Certificate> verifiedChain;
try {
verifiedChain = sDefaultTrustManager.checkServerTrusted(serverCertificates,
authType, host);
} catch (CertificateException eDefaultManager) {
try {
verifiedChain = sTestTrustManager.checkServerTrusted(serverCertificates,
authType, host);
} catch (CertificateException eTestManager) {
Log.i(TAG, "Failed to validate the certificate chain, error: " +
eDefaultManager.getMessage());
return new AndroidCertVerifyResult(
CertVerifyStatusAndroid.VERIFY_NO_TRUSTED_ROOT);
}
}
boolean isIssuedByKnownRoot = false;
if (sSystemTrustRoots != null && verifiedChain.size() > 0) {
X509Certificate root = verifiedChain.get(verifiedChain.size() - 1);
isIssuedByKnownRoot = sSystemTrustRoots.contains(
new Pair<X500Principal, PublicKey>(root.getSubjectX500Principal(),
root.getPublicKey()));
}
return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_OK,
isIssuedByKnownRoot, verifiedChain);
}
}
public static void setDisableNativeCodeForTest(boolean disabled) {
sDisableNativeCodeForTest = disabled;
}
private static native void nativeNotifyKeyChainChanged();
private static native void nativeRecordCertVerifyCapabilitiesHistogram(
boolean foundSystemTrustRoots);
private static native Context nativeGetApplicationContext();
}