1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.net;
7 import android.annotation.SuppressLint;
8 import android.content.BroadcastReceiver;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.content.IntentFilter;
12 import android.net.http.X509TrustManagerExtensions;
13 import android.os.Build;
14 import android.security.KeyChain;
15 import android.util.Log;
16 import android.util.Pair;
18 import org.chromium.base.JNINamespace;
20 import java.io.ByteArrayInputStream;
21 import java.io.IOException;
22 import java.security.KeyStore;
23 import java.security.KeyStoreException;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.PublicKey;
26 import java.security.cert.Certificate;
27 import java.security.cert.CertificateException;
28 import java.security.cert.CertificateExpiredException;
29 import java.security.cert.CertificateFactory;
30 import java.security.cert.CertificateNotYetValidException;
31 import java.security.cert.X509Certificate;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Enumeration;
35 import java.util.HashSet;
36 import java.util.List;
39 import javax.net.ssl.TrustManager;
40 import javax.net.ssl.TrustManagerFactory;
41 import javax.net.ssl.X509TrustManager;
42 import javax.security.auth.x500.X500Principal;
45 * Utility functions for verifying X.509 certificates.
48 public class X509Util {
50 private static final String TAG = "X509Util";
52 private static final class TrustStorageListener extends BroadcastReceiver {
53 @Override public void onReceive(Context context, Intent intent) {
54 if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
56 reloadDefaultTrustManager();
58 catch (CertificateException e) {
59 Log.e(TAG, "Unable to reload the default TrustManager", e);
61 catch (KeyStoreException e) {
62 Log.e(TAG, "Unable to reload the default TrustManager", e);
64 catch (NoSuchAlgorithmException e) {
65 Log.e(TAG, "Unable to reload the default TrustManager", e);
72 * Interface that wraps one of X509TrustManager or
73 * X509TrustManagerExtensions to support platforms before the latter was
76 private static interface X509TrustManagerImplementation {
77 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
79 String host) throws CertificateException;
82 private static final class X509TrustManagerIceCreamSandwich implements
83 X509TrustManagerImplementation {
84 private final X509TrustManager mTrustManager;
86 public X509TrustManagerIceCreamSandwich(X509TrustManager trustManager) {
87 mTrustManager = trustManager;
91 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
93 String host) throws CertificateException {
94 mTrustManager.checkServerTrusted(chain, authType);
95 return Collections.<X509Certificate>emptyList();
99 private static final class X509TrustManagerJellyBean implements X509TrustManagerImplementation {
100 private final X509TrustManagerExtensions mTrustManagerExtensions;
102 @SuppressLint("NewApi")
103 public X509TrustManagerJellyBean(X509TrustManager trustManager) {
104 mTrustManagerExtensions = new X509TrustManagerExtensions(trustManager);
108 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain,
110 String host) throws CertificateException {
111 return mTrustManagerExtensions.checkServerTrusted(chain, authType, host);
115 private static CertificateFactory sCertificateFactory;
117 private static final String OID_TLS_SERVER_AUTH = "1.3.6.1.5.5.7.3.1";
118 private static final String OID_ANY_EKU = "2.5.29.37.0";
119 // Server-Gated Cryptography (necessary to support a few legacy issuers):
121 private static final String OID_SERVER_GATED_NETSCAPE = "2.16.840.1.113730.4.1";
123 private static final String OID_SERVER_GATED_MICROSOFT = "1.3.6.1.4.1.311.10.3.3";
126 * Trust manager backed up by the read-only system certificate store.
128 private static X509TrustManagerImplementation sDefaultTrustManager;
131 * BroadcastReceiver that listens to change in the system keystore to invalidate certificate
134 private static TrustStorageListener sTrustStorageListener;
137 * Trust manager backed up by a custom certificate store. We need such manager to plant test
138 * root CA to the trust store in testing.
140 private static X509TrustManagerImplementation sTestTrustManager;
141 private static KeyStore sTestKeyStore;
144 * Hash set of the subject and public key of system roots. This is used to
145 * determine whether a chain ends at a well-known root or not.
147 * Querying the system KeyStore for the root directly doesn't work as the
148 * root of the verified chain may be the server's version of a root rather
149 * than the system one. For instance, the server may send a certificate
150 * signed by another CA, while the system store contains a self-signed root
151 * with the same subject and SPKI. The chain will terminate at that root
152 * but X509TrustManagerExtensions will return the server's version.
154 private static Set<Pair<X500Principal, PublicKey>> sSystemTrustRoots;
157 * True if the system trust roots were initialized. (sSystemTrustRoots may
158 * still be null if system trust roots cannot be distinguished from
159 * user-installed ones.)
161 private static boolean sLoadedSystemTrustRoots;
164 * Lock object used to synchronize all calls that modify or depend on the trust managers.
166 private static final Object sLock = new Object();
169 * Allow disabling registering the observer and recording histograms for the certificate
170 * changes. Net unit tests do not load native libraries which prevent this to succeed. Moreover,
171 * the system does not allow to interact with the certificate store without user interaction.
173 private static boolean sDisableNativeCodeForTest = false;
176 * Ensures that the trust managers and certificate factory are initialized.
178 private static void ensureInitialized() throws CertificateException,
179 KeyStoreException, NoSuchAlgorithmException {
180 synchronized (sLock) {
181 if (sCertificateFactory == null) {
182 sCertificateFactory = CertificateFactory.getInstance("X.509");
184 if (sDefaultTrustManager == null) {
185 sDefaultTrustManager = X509Util.createTrustManager(null);
187 if (!sLoadedSystemTrustRoots) {
189 sSystemTrustRoots = buildSystemTrustRootSet();
190 } catch (KeyStoreException e) {
191 // If the device does not have an "AndroidCAStore" KeyStore, don't make the
192 // failure fatal. Instead default conservatively to setting isIssuedByKnownRoot
193 // to false everywhere.
194 Log.w(TAG, "Could not load system trust root set", e);
196 if (!sDisableNativeCodeForTest)
197 nativeRecordCertVerifyCapabilitiesHistogram(sSystemTrustRoots != null);
198 sLoadedSystemTrustRoots = true;
200 if (sTestKeyStore == null) {
201 sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
203 sTestKeyStore.load(null);
204 } catch (IOException e) {
205 // No IO operation is attempted.
208 if (sTestTrustManager == null) {
209 sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
211 if (!sDisableNativeCodeForTest && sTrustStorageListener == null) {
212 sTrustStorageListener = new TrustStorageListener();
213 nativeGetApplicationContext().registerReceiver(sTrustStorageListener,
214 new IntentFilter(KeyChain.ACTION_STORAGE_CHANGED));
219 private static Set<Pair<X500Principal, PublicKey>> buildSystemTrustRootSet() throws
220 CertificateException, KeyStoreException, NoSuchAlgorithmException {
221 // Load the Android CA store.
222 KeyStore systemKeyStore = KeyStore.getInstance("AndroidCAStore");
224 systemKeyStore.load(null);
225 } catch (IOException e) {
226 // No IO operation is attempted.
229 // System trust roots have prefix of "system:".
230 Set<Pair<X500Principal, PublicKey>> roots = new HashSet<Pair<X500Principal, PublicKey>>();
231 Enumeration<String> aliases = systemKeyStore.aliases();
232 while (aliases.hasMoreElements()) {
233 String alias = aliases.nextElement();
234 if (!alias.startsWith("system:"))
236 Certificate cert = systemKeyStore.getCertificate(alias);
237 if (cert != null && cert instanceof X509Certificate) {
238 X509Certificate x509Cert = (X509Certificate)cert;
239 roots.add(new Pair<X500Principal, PublicKey>(x509Cert.getSubjectX500Principal(),
240 x509Cert.getPublicKey()));
247 * Creates a X509TrustManagerImplementation backed up by the given key
248 * store. When null is passed as a key store, system default trust store is
250 * @throws KeyStoreException, NoSuchAlgorithmException on error initializing the TrustManager.
252 private static X509TrustManagerImplementation createTrustManager(KeyStore keyStore) throws
253 KeyStoreException, NoSuchAlgorithmException {
254 String algorithm = TrustManagerFactory.getDefaultAlgorithm();
255 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
258 for (TrustManager tm : tmf.getTrustManagers()) {
259 if (tm instanceof X509TrustManager) {
261 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
262 return new X509TrustManagerJellyBean((X509TrustManager) tm);
264 return new X509TrustManagerIceCreamSandwich((X509TrustManager) tm);
266 } catch (IllegalArgumentException e) {
267 Log.e(TAG, "Error creating trust manager: " + e);
275 * After each modification of test key store, trust manager has to be generated again.
277 private static void reloadTestTrustManager() throws KeyStoreException,
278 NoSuchAlgorithmException {
279 sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
283 * After each modification by the system of the key store, trust manager has to be regenerated.
285 private static void reloadDefaultTrustManager() throws KeyStoreException,
286 NoSuchAlgorithmException, CertificateException {
287 sDefaultTrustManager = null;
288 sSystemTrustRoots = null;
289 sLoadedSystemTrustRoots = false;
290 nativeNotifyKeyChainChanged();
295 * Convert a DER encoded certificate to an X509Certificate.
297 public static X509Certificate createCertificateFromBytes(byte[] derBytes) throws
298 CertificateException, KeyStoreException, NoSuchAlgorithmException {
300 return (X509Certificate) sCertificateFactory.generateCertificate(
301 new ByteArrayInputStream(derBytes));
304 public static void addTestRootCertificate(byte[] rootCertBytes) throws CertificateException,
305 KeyStoreException, NoSuchAlgorithmException {
307 X509Certificate rootCert = createCertificateFromBytes(rootCertBytes);
308 synchronized (sLock) {
309 sTestKeyStore.setCertificateEntry(
310 "root_cert_" + Integer.toString(sTestKeyStore.size()), rootCert);
311 reloadTestTrustManager();
315 public static void clearTestRootCertificates() throws NoSuchAlgorithmException,
316 CertificateException, KeyStoreException {
318 synchronized (sLock) {
320 sTestKeyStore.load(null);
321 reloadTestTrustManager();
322 } catch (IOException e) {
323 // No IO operation is attempted.
329 * If an EKU extension is present in the end-entity certificate, it MUST contain either the
330 * anyEKU or serverAuth or netscapeSGC or Microsoft SGC EKUs.
332 * @return true if there is no EKU extension or if any of the EKU extensions is one of the valid
333 * OIDs for web server certificates.
335 * TODO(palmer): This can be removed after the equivalent change is made to the Android default
336 * TrustManager and that change is shipped to a large majority of Android users.
338 static boolean verifyKeyUsage(X509Certificate certificate) throws CertificateException {
339 List<String> ekuOids;
341 ekuOids = certificate.getExtendedKeyUsage();
342 } catch (NullPointerException e) {
343 // getExtendedKeyUsage() can crash due to an Android platform bug. This probably
344 // happens when the EKU extension data is malformed so return false here.
345 // See http://crbug.com/233610
351 for (String ekuOid : ekuOids) {
352 if (ekuOid.equals(OID_TLS_SERVER_AUTH) ||
353 ekuOid.equals(OID_ANY_EKU) ||
354 ekuOid.equals(OID_SERVER_GATED_NETSCAPE) ||
355 ekuOid.equals(OID_SERVER_GATED_MICROSOFT)) {
363 public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain,
366 throws KeyStoreException, NoSuchAlgorithmException {
367 if (certChain == null || certChain.length == 0 || certChain[0] == null) {
368 throw new IllegalArgumentException("Expected non-null and non-empty certificate " +
369 "chain passed as |certChain|. |certChain|=" + Arrays.deepToString(certChain));
375 } catch (CertificateException e) {
376 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
379 X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
381 for (int i = 0; i < certChain.length; ++i) {
382 serverCertificates[i] = createCertificateFromBytes(certChain[i]);
384 } catch (CertificateException e) {
385 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_UNABLE_TO_PARSE);
388 // Expired and not yet valid certificates would be rejected by the trust managers, but the
389 // trust managers report all certificate errors using the general CertificateException. In
390 // order to get more granular error information, cert validity time range is being checked
393 serverCertificates[0].checkValidity();
394 if (!verifyKeyUsage(serverCertificates[0])) {
395 return new AndroidCertVerifyResult(
396 CertVerifyStatusAndroid.VERIFY_INCORRECT_KEY_USAGE);
398 } catch (CertificateExpiredException e) {
399 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_EXPIRED);
400 } catch (CertificateNotYetValidException e) {
401 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_NOT_YET_VALID);
402 } catch (CertificateException e) {
403 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED);
406 synchronized (sLock) {
407 List<X509Certificate> verifiedChain;
409 verifiedChain = sDefaultTrustManager.checkServerTrusted(serverCertificates,
411 } catch (CertificateException eDefaultManager) {
413 verifiedChain = sTestTrustManager.checkServerTrusted(serverCertificates,
415 } catch (CertificateException eTestManager) {
416 // Neither of the trust managers confirms the validity of the certificate chain,
417 // log the error message returned by the system trust manager.
418 Log.i(TAG, "Failed to validate the certificate chain, error: " +
419 eDefaultManager.getMessage());
420 return new AndroidCertVerifyResult(
421 CertVerifyStatusAndroid.VERIFY_NO_TRUSTED_ROOT);
425 boolean isIssuedByKnownRoot = false;
426 if (sSystemTrustRoots != null && verifiedChain.size() > 0) {
427 X509Certificate root = verifiedChain.get(verifiedChain.size() - 1);
428 isIssuedByKnownRoot = sSystemTrustRoots.contains(
429 new Pair<X500Principal, PublicKey>(root.getSubjectX500Principal(),
430 root.getPublicKey()));
433 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_OK,
434 isIssuedByKnownRoot, verifiedChain);
438 public static void setDisableNativeCodeForTest(boolean disabled) {
439 sDisableNativeCodeForTest = disabled;
442 * Notify the native net::CertDatabase instance that the system database has been updated.
444 private static native void nativeNotifyKeyChainChanged();
447 * Record histograms on the platform's certificate verification capabilities.
449 private static native void nativeRecordCertVerifyCapabilitiesHistogram(
450 boolean foundSystemTrustRoots);
453 * Returns the application context.
455 private static native Context nativeGetApplicationContext();