• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net;
6 
7 import android.content.BroadcastReceiver;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.IntentFilter;
11 import android.net.http.X509TrustManagerExtensions;
12 import android.os.Build;
13 import android.security.KeyChain;
14 import android.util.Pair;
15 
16 import org.jni_zero.JNINamespace;
17 import org.jni_zero.NativeMethods;
18 
19 import org.chromium.base.ContextUtils;
20 import org.chromium.base.Log;
21 
22 import java.io.ByteArrayInputStream;
23 import java.io.File;
24 import java.io.IOException;
25 import java.security.KeyStore;
26 import java.security.KeyStoreException;
27 import java.security.MessageDigest;
28 import java.security.NoSuchAlgorithmException;
29 import java.security.PublicKey;
30 import java.security.cert.Certificate;
31 import java.security.cert.CertificateEncodingException;
32 import java.security.cert.CertificateException;
33 import java.security.cert.CertificateExpiredException;
34 import java.security.cert.CertificateFactory;
35 import java.security.cert.CertificateNotYetValidException;
36 import java.security.cert.X509Certificate;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Enumeration;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Set;
43 
44 import javax.net.ssl.TrustManager;
45 import javax.net.ssl.TrustManagerFactory;
46 import javax.net.ssl.X509TrustManager;
47 import javax.security.auth.x500.X500Principal;
48 
49 /** Utility functions for interacting with Android's X.509 certificates. */
50 @JNINamespace("net")
51 public class X509Util {
52     private static final String TAG = "X509Util";
53 
54     private static final class TrustStorageListener extends BroadcastReceiver {
55         @Override
onReceive(Context context, Intent intent)56         public void onReceive(Context context, Intent intent) {
57             boolean shouldReloadTrustManager = false;
58             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
59                 if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
60                     shouldReloadTrustManager = true;
61                 } else if (KeyChain.ACTION_KEYCHAIN_CHANGED.equals(intent.getAction())) {
62                     X509UtilJni.get().notifyClientCertStoreChanged();
63                 } else if (KeyChain.ACTION_KEY_ACCESS_CHANGED.equals(intent.getAction())
64                         && !intent.getBooleanExtra(KeyChain.EXTRA_KEY_ACCESSIBLE, false)) {
65                     // We lost access to a client certificate key. Reload all client certificate
66                     // state as we are not currently able to forget an individual identity.
67                     X509UtilJni.get().notifyClientCertStoreChanged();
68                 }
69             } else {
70                 @SuppressWarnings("deprecation")
71                 String action = KeyChain.ACTION_STORAGE_CHANGED;
72                 // Before Android O, KeyChain only emitted a coarse-grained intent. This fires much
73                 // more often than it should (https://crbug.com/381912), but there are no APIs to
74                 // distinguish the various cases.
75                 if (action.equals(intent.getAction())) {
76                     shouldReloadTrustManager = true;
77                     X509UtilJni.get().notifyClientCertStoreChanged();
78                 }
79             }
80 
81             if (shouldReloadTrustManager) {
82                 try {
83                     reloadDefaultTrustManager();
84                 } catch (CertificateException e) {
85                     Log.e(TAG, "Unable to reload the default TrustManager", e);
86                 } catch (KeyStoreException e) {
87                     Log.e(TAG, "Unable to reload the default TrustManager", e);
88                 } catch (NoSuchAlgorithmException e) {
89                     Log.e(TAG, "Unable to reload the default TrustManager", e);
90                 }
91             }
92         }
93     }
94 
checkServerTrustedIgnoringRuntimeException( X509TrustManagerExtensions tm, X509Certificate[] chain, String authType, String host)95     private static List<X509Certificate> checkServerTrustedIgnoringRuntimeException(
96             X509TrustManagerExtensions tm, X509Certificate[] chain, String authType, String host)
97             throws CertificateException {
98         try {
99             return tm.checkServerTrusted(chain, authType, host);
100         } catch (RuntimeException e) {
101             // https://crbug.com/937354: checkServerTrusted() can unexpectedly throw runtime
102             // exceptions, most often within conscrypt while parsing certificates.
103             Log.e(TAG, "checkServerTrusted() unexpectedly threw: %s", e);
104             throw new CertificateException(e);
105         }
106     }
107 
108     private static CertificateFactory sCertificateFactory;
109 
110     private static final String OID_TLS_SERVER_AUTH = "1.3.6.1.5.5.7.3.1";
111     private static final String OID_ANY_EKU = "2.5.29.37.0";
112     // Server-Gated Cryptography (necessary to support a few legacy issuers):
113     //    Netscape:
114     private static final String OID_SERVER_GATED_NETSCAPE = "2.16.840.1.113730.4.1";
115     //    Microsoft:
116     private static final String OID_SERVER_GATED_MICROSOFT = "1.3.6.1.4.1.311.10.3.3";
117 
118     /** Trust manager backed up by the read-only system certificate store. */
119     private static X509TrustManagerExtensions sDefaultTrustManager;
120 
121     /**
122      * BroadcastReceiver that listens to change in the system keystore to invalidate certificate
123      * caches.
124      */
125     private static TrustStorageListener sTrustStorageListener;
126 
127     /**
128      * Trust manager backed up by a custom certificate store. We need such manager to plant test
129      * root CA to the trust store in testing.
130      */
131     private static X509TrustManagerExtensions sTestTrustManager;
132 
133     private static KeyStore sTestKeyStore;
134 
135     /**
136      * The system key store. This is used to determine whether a trust anchor is a system trust
137      * anchor or user-installed.
138      */
139     private static KeyStore sSystemKeyStore;
140 
141     /**
142      * The directory where system certificates are stored. This is used to determine whether a
143      * trust anchor is a system trust anchor or user-installed. The KeyStore API alone is not
144      * sufficient to efficiently query whether a given X500Principal, PublicKey pair is a trust
145      * anchor.
146      */
147     private static File sSystemCertificateDirectory;
148 
149     /**
150      * An in-memory cache of which trust anchors are system trust roots. This avoids reading and
151      * decoding the root from disk on every verification. Mirrors a similar in-memory cache in
152      * Conscrypt's X509TrustManager implementation.
153      */
154     private static Set<Pair<X500Principal, PublicKey>> sSystemTrustAnchorCache;
155 
156     /**
157      * True if the system key store has been loaded. If the "AndroidCAStore" KeyStore instance
158      * was not found, sSystemKeyStore may be null while sLoadedSystemKeyStore is true.
159      */
160     private static boolean sLoadedSystemKeyStore;
161 
162     /** A root that will be installed as a user-trusted root for testing purposes. */
163     private static X509Certificate sTestRoot;
164 
165     /** Lock object used to synchronize all calls that modify or depend on the trust managers. */
166     private static final Object sLock = new Object();
167 
168     /** Ensures that the trust managers and certificate factory are initialized. */
ensureInitialized()169     private static void ensureInitialized()
170             throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
171         synchronized (sLock) {
172             ensureInitializedLocked();
173         }
174     }
175 
176     /**
177      * Ensures that the trust managers and certificate factory are initialized. Must be called with
178      * |sLock| held. Does not initialize test infrastructure.
179      */
180     // FindBugs' static field initialization warnings do not handle methods that are expected to be
181     // called locked.
ensureInitializedLocked()182     private static void ensureInitializedLocked()
183             throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
184         assert Thread.holdsLock(sLock);
185 
186         if (sCertificateFactory == null) {
187             sCertificateFactory = CertificateFactory.getInstance("X.509");
188         }
189         if (sDefaultTrustManager == null) {
190             sDefaultTrustManager = X509Util.createTrustManager(null);
191         }
192         if (!sLoadedSystemKeyStore) {
193             try {
194                 sSystemKeyStore = KeyStore.getInstance("AndroidCAStore");
195                 try {
196                     sSystemKeyStore.load(null);
197                 } catch (IOException e) {
198                     // No IO operation is attempted.
199                 }
200                 sSystemCertificateDirectory =
201                         new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts");
202             } catch (KeyStoreException e) {
203                 // Could not load AndroidCAStore. Continue anyway; isKnownRoot will always
204                 // return false.
205             }
206             sLoadedSystemKeyStore = true;
207         }
208         if (sSystemTrustAnchorCache == null) {
209             sSystemTrustAnchorCache = new HashSet<Pair<X500Principal, PublicKey>>();
210         }
211         if (sTrustStorageListener == null) {
212             sTrustStorageListener = new TrustStorageListener();
213             IntentFilter filter = new IntentFilter();
214             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
215                 filter.addAction(KeyChain.ACTION_KEYCHAIN_CHANGED);
216                 filter.addAction(KeyChain.ACTION_KEY_ACCESS_CHANGED);
217                 filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
218             } else {
219                 @SuppressWarnings("deprecation")
220                 String action = KeyChain.ACTION_STORAGE_CHANGED;
221                 filter.addAction(action);
222             }
223             ContextUtils.registerProtectedBroadcastReceiver(
224                     ContextUtils.getApplicationContext(), sTrustStorageListener, filter);
225         }
226     }
227 
228     /**
229      * Ensures that test  trust managers and certificate factory are initialized. Must be called
230      * with |sLock| held.
231      */
ensureTestInitializedLocked()232     private static void ensureTestInitializedLocked()
233             throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
234         assert Thread.holdsLock(sLock);
235         if (sTestKeyStore == null) {
236             sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
237             try {
238                 sTestKeyStore.load(null);
239             } catch (IOException e) {
240                 // No IO operation is attempted.
241             }
242         }
243         if (sTestTrustManager == null) {
244             sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
245         }
246     }
247 
248     /**
249      * Creates a X509TrustManagerExtensions backed up by the given key
250      * store. When null is passed as a key store, system default trust store is
251      * used. Returns null if no created TrustManager was suitable.
252      * @throws KeyStoreException, NoSuchAlgorithmException on error initializing the TrustManager.
253      */
createTrustManager(KeyStore keyStore)254     private static X509TrustManagerExtensions createTrustManager(KeyStore keyStore)
255             throws KeyStoreException, NoSuchAlgorithmException {
256         String algorithm = TrustManagerFactory.getDefaultAlgorithm();
257         TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
258         tmf.init(keyStore);
259 
260         TrustManager[] trustManagers = null;
261         try {
262             trustManagers = tmf.getTrustManagers();
263         } catch (RuntimeException e) {
264             // https://crbug.com/937354: getTrustManagers() can unexpectedly throw runtime
265             // exceptions, most often while processing the network security config XML file.
266             Log.e(TAG, "TrustManagerFactory.getTrustManagers() unexpectedly threw: %s", e);
267             throw new KeyStoreException(e);
268         }
269 
270         for (TrustManager tm : trustManagers) {
271             if (tm instanceof X509TrustManager) {
272                 try {
273                     return new X509TrustManagerExtensions((X509TrustManager) tm);
274                 } catch (IllegalArgumentException e) {
275                     String className = tm.getClass().getName();
276                     Log.e(TAG, "Error creating trust manager (" + className + "): " + e);
277                 }
278             }
279         }
280         Log.e(TAG, "Could not find suitable trust manager");
281         return null;
282     }
283 
284     /** After each modification of test key store, trust manager has to be generated again. */
reloadTestTrustManager()285     private static void reloadTestTrustManager()
286             throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
287         assert Thread.holdsLock(sLock);
288         ensureTestInitializedLocked();
289 
290         sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
291     }
292 
293     /**
294      * After each modification by the system of the key store, trust manager has to be regenerated.
295      */
reloadDefaultTrustManager()296     private static void reloadDefaultTrustManager()
297             throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
298         synchronized (sLock) {
299             sDefaultTrustManager = null;
300             sSystemTrustAnchorCache = null;
301             ensureInitializedLocked();
302         }
303         X509UtilJni.get().notifyTrustStoreChanged();
304     }
305 
306     /** Convert a DER encoded certificate to an X509Certificate. */
createCertificateFromBytes(byte[] derBytes)307     public static X509Certificate createCertificateFromBytes(byte[] derBytes)
308             throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
309         ensureInitialized();
310         return (X509Certificate)
311                 sCertificateFactory.generateCertificate(new ByteArrayInputStream(derBytes));
312     }
313 
314     /** Add a test root certificate for use by the Android Platform verifier. */
addTestRootCertificate(byte[] rootCertBytes)315     public static void addTestRootCertificate(byte[] rootCertBytes)
316             throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
317         X509Certificate rootCert = createCertificateFromBytes(rootCertBytes);
318         synchronized (sLock) {
319             ensureTestInitializedLocked();
320             // Add the cert to be used by the Android Platform Verifier.
321             sTestKeyStore.setCertificateEntry(
322                     "root_cert_" + Integer.toString(sTestKeyStore.size()), rootCert);
323             reloadTestTrustManager();
324         }
325     }
326 
327     /** Clear test root certificates in use by the Android Platform verifier. */
clearTestRootCertificates()328     public static void clearTestRootCertificates()
329             throws NoSuchAlgorithmException, CertificateException, KeyStoreException {
330         synchronized (sLock) {
331             ensureTestInitializedLocked();
332             try {
333                 sTestKeyStore.load(null);
334                 reloadTestTrustManager();
335             } catch (IOException e) {
336                 // No IO operation is attempted.
337             }
338         }
339     }
340 
341     /** Set a test root certificate for use by CertVerifierBuiltin. */
setTestRootCertificateForBuiltin(byte[] rootCertBytes)342     public static void setTestRootCertificateForBuiltin(byte[] rootCertBytes)
343             throws NoSuchAlgorithmException, CertificateException, KeyStoreException {
344         X509Certificate rootCert = createCertificateFromBytes(rootCertBytes);
345         synchronized (sLock) {
346             // Add the cert to be used by CertVerifierBuiltin.
347             //
348             // This saves the root so it is returned from getUserAddedRoots, for TrustStoreAndroid.
349             // This is done for the Java EmbeddedTestServer implementation and must run before
350             // native code is loaded, when getUserAddedRoots is first run.
351             sTestRoot = rootCert;
352         }
353     }
354 
355     private static final char[] HEX_DIGITS = {
356         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
357     };
358 
hashPrincipal(X500Principal principal)359     private static String hashPrincipal(X500Principal principal) throws NoSuchAlgorithmException {
360         // Android hashes a principal as the first four bytes of its MD5 digest, encoded in
361         // lowercase hex and reversed. Verified in 4.2, 4.3, and 4.4.
362         byte[] digest = MessageDigest.getInstance("MD5").digest(principal.getEncoded());
363         char[] hexChars = new char[8];
364         for (int i = 0; i < 4; i++) {
365             hexChars[2 * i] = HEX_DIGITS[(digest[3 - i] >> 4) & 0xf];
366             hexChars[2 * i + 1] = HEX_DIGITS[digest[3 - i] & 0xf];
367         }
368         return new String(hexChars);
369     }
370 
isKnownRoot(X509Certificate root)371     private static boolean isKnownRoot(X509Certificate root)
372             throws NoSuchAlgorithmException, KeyStoreException {
373         assert Thread.holdsLock(sLock);
374 
375         // Could not find the system key store. Conservatively report false.
376         if (sSystemKeyStore == null) return false;
377 
378         // Check the in-memory cache first; avoid decoding the anchor from disk
379         // if it has been seen before.
380         Pair<X500Principal, PublicKey> key =
381                 new Pair<X500Principal, PublicKey>(
382                         root.getSubjectX500Principal(), root.getPublicKey());
383 
384         if (sSystemTrustAnchorCache.contains(key)) return true;
385 
386         // Note: It is not sufficient to call sSystemKeyStore.getCertificiateAlias. If the server
387         // supplies a copy of a trust anchor, X509TrustManagerExtensions returns the server's
388         // version rather than the system one. getCertificiateAlias will then fail to find an anchor
389         // name. This is fixed upstream in https://android-review.googlesource.com/#/c/91605/
390         //
391         // TODO(davidben): When the change trickles into an Android release, query sSystemKeyStore
392         // directly.
393 
394         // System trust anchors are stored under a hash of the principal. In case of collisions,
395         // a number is appended.
396         String hash = hashPrincipal(root.getSubjectX500Principal());
397         for (int i = 0; true; i++) {
398             String alias = hash + '.' + i;
399             if (!new File(sSystemCertificateDirectory, alias).exists()) break;
400 
401             Certificate anchor = sSystemKeyStore.getCertificate("system:" + alias);
402             // It is possible for this to return null if the user deleted a trust anchor. In
403             // that case, the certificate remains in the system directory but is also added to
404             // another file. Continue iterating as there may be further collisions after the
405             // deleted anchor.
406             if (anchor == null) continue;
407 
408             if (!(anchor instanceof X509Certificate)) {
409                 // This should never happen.
410                 String className = anchor.getClass().getName();
411                 Log.e(TAG, "Anchor " + alias + " not an X509Certificate: " + className);
412                 continue;
413             }
414 
415             // If the subject and public key match, this is a system root.
416             X509Certificate anchorX509 = (X509Certificate) anchor;
417             if (root.getSubjectX500Principal().equals(anchorX509.getSubjectX500Principal())
418                     && root.getPublicKey().equals(anchorX509.getPublicKey())) {
419                 sSystemTrustAnchorCache.add(key);
420                 return true;
421             }
422         }
423 
424         return false;
425     }
426 
427     /**
428      * If an EKU extension is present in the end-entity certificate, it MUST contain either the
429      * anyEKU or serverAuth or netscapeSGC or Microsoft SGC EKUs.
430      *
431      * @return true if there is no EKU extension or if any of the EKU extensions is one of the valid
432      * OIDs for web server certificates.
433      *
434      * TODO(palmer): This can be removed after the equivalent change is made to the Android default
435      * TrustManager and that change is shipped to a large majority of Android users.
436      */
verifyKeyUsage(X509Certificate certificate)437     static boolean verifyKeyUsage(X509Certificate certificate) throws CertificateException {
438         List<String> ekuOids;
439         try {
440             ekuOids = certificate.getExtendedKeyUsage();
441         } catch (NullPointerException e) {
442             // getExtendedKeyUsage() can crash due to an Android platform bug. This probably
443             // happens when the EKU extension data is malformed so return false here.
444             // See http://crbug.com/233610
445             return false;
446         }
447         if (ekuOids == null) return true;
448 
449         for (String ekuOid : ekuOids) {
450             if (ekuOid.equals(OID_TLS_SERVER_AUTH)
451                     || ekuOid.equals(OID_ANY_EKU)
452                     || ekuOid.equals(OID_SERVER_GATED_NETSCAPE)
453                     || ekuOid.equals(OID_SERVER_GATED_MICROSOFT)) {
454                 return true;
455             }
456         }
457 
458         return false;
459     }
460 
461     /**
462      * Get the list of user-added roots.
463      *
464      * @return DER-encoded list of user-added roots.
465      */
getUserAddedRoots()466     public static byte[][] getUserAddedRoots() {
467         List<byte[]> userRootBytes = new ArrayList<byte[]>();
468         synchronized (sLock) {
469             try {
470                 ensureInitialized();
471             } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException e) {
472                 return new byte[0][];
473             }
474 
475             if (sSystemKeyStore == null) {
476                 return new byte[0][];
477             }
478 
479             try {
480                 for (Enumeration<String> aliases = sSystemKeyStore.aliases();
481                         aliases.hasMoreElements(); ) {
482                     String alias = aliases.nextElement();
483                     // We check if its a user added root by looking at the alias; user roots should
484                     // start with 'user:'. Another way of checking this would be to fetch the
485                     // certificate and call X509TrustManagerExtensions.isUserAddedCertificate(), but
486                     // that is imperfect as well because Keystore and X509TrustManagerExtensions
487                     // are actually implemented by two separate systems, and mixing them probably
488                     // works but might not in all cases.
489                     //
490                     // Also, to call X509TrustManagerExtensions.isUserAddedCertificate() we'd need
491                     // to call Keystore.getCertificate on all of the roots, even the system ones.
492                     //
493                     // Since there's no perfect way of doing this we go with the simpler and more
494                     // performant one.
495                     if (alias.startsWith("user:")) {
496                         try {
497                             Certificate anchor = sSystemKeyStore.getCertificate(alias);
498                             if (!(anchor instanceof X509Certificate)) {
499                                 Log.w(TAG, "alias: " + alias + " is not a X509 Cert, skipping");
500                                 continue;
501                             }
502                             X509Certificate anchorX509 = (X509Certificate) anchor;
503                             userRootBytes.add(anchorX509.getEncoded());
504                         } catch (KeyStoreException e) {
505                             Log.e(TAG, "Error reading cert with alias %s, error: %s", alias, e);
506                         } catch (CertificateEncodingException e) {
507                             Log.e(TAG, "Error encoding cert with alias %s, error: %s", alias, e);
508                         }
509                     }
510                 }
511             } catch (KeyStoreException e) {
512                 Log.e(TAG, "Error reading cert aliases: %s", e);
513                 return new byte[0][];
514             }
515 
516             if (sTestRoot != null) {
517                 try {
518                     userRootBytes.add(sTestRoot.getEncoded());
519                 } catch (CertificateEncodingException e) {
520                     Log.e(TAG, "Error encoding test root cert, error %s", e);
521                 }
522             }
523         }
524 
525         return userRootBytes.toArray(new byte[0][]);
526     }
527 
verifyServerCertificates( byte[][] certChain, String authType, String host)528     public static AndroidCertVerifyResult verifyServerCertificates(
529             byte[][] certChain, String authType, String host)
530             throws KeyStoreException, NoSuchAlgorithmException {
531         if (certChain == null || certChain.length == 0 || certChain[0] == null) {
532             throw new IllegalArgumentException(
533                     "Expected non-null and non-empty certificate "
534                             + "chain passed as |certChain|. |certChain|="
535                             + Arrays.deepToString(certChain));
536         }
537 
538         try {
539             ensureInitialized();
540         } catch (CertificateException e) {
541             return new AndroidCertVerifyResult(CertVerifyStatusAndroid.FAILED);
542         }
543 
544         List<X509Certificate> serverCertificatesList = new ArrayList<X509Certificate>();
545         try {
546             serverCertificatesList.add(createCertificateFromBytes(certChain[0]));
547         } catch (CertificateException e) {
548             return new AndroidCertVerifyResult(CertVerifyStatusAndroid.UNABLE_TO_PARSE);
549         }
550         for (int i = 1; i < certChain.length; ++i) {
551             try {
552                 serverCertificatesList.add(createCertificateFromBytes(certChain[i]));
553             } catch (CertificateException e) {
554                 Log.w(TAG, "intermediate " + i + " failed parsing");
555             }
556         }
557         X509Certificate[] serverCertificates =
558                 serverCertificatesList.toArray(new X509Certificate[serverCertificatesList.size()]);
559 
560         // Expired and not yet valid certificates would be rejected by the trust managers, but the
561         // trust managers report all certificate errors using the general CertificateException. In
562         // order to get more granular error information, cert validity time range is being checked
563         // separately.
564         try {
565             serverCertificates[0].checkValidity();
566             if (!verifyKeyUsage(serverCertificates[0])) {
567                 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.INCORRECT_KEY_USAGE);
568             }
569         } catch (CertificateExpiredException e) {
570             return new AndroidCertVerifyResult(CertVerifyStatusAndroid.EXPIRED);
571         } catch (CertificateNotYetValidException e) {
572             return new AndroidCertVerifyResult(CertVerifyStatusAndroid.NOT_YET_VALID);
573         } catch (CertificateException e) {
574             return new AndroidCertVerifyResult(CertVerifyStatusAndroid.FAILED);
575         }
576 
577         synchronized (sLock) {
578             // If no trust manager was found, fail without crashing on the null pointer.
579             if (sDefaultTrustManager == null) {
580                 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.FAILED);
581             }
582 
583             List<X509Certificate> verifiedChain = null;
584             try {
585                 verifiedChain =
586                         checkServerTrustedIgnoringRuntimeException(
587                                 sDefaultTrustManager, serverCertificates, authType, host);
588             } catch (CertificateException eDefaultManager) {
589                 if (sTestTrustManager != null) {
590                     try {
591                         verifiedChain =
592                                 checkServerTrustedIgnoringRuntimeException(
593                                         sTestTrustManager, serverCertificates, authType, host);
594                     } catch (CertificateException eTestManager) {
595                         // See following if block.
596                     }
597                 }
598 
599                 if (verifiedChain == null) {
600                     // Neither of the trust managers confirms the validity of the certificate chain,
601                     // log the error message returned by the system trust manager.
602                     Log.i(
603                             TAG,
604                             "Failed to validate the certificate chain, error: "
605                                     + eDefaultManager.getMessage());
606                     return new AndroidCertVerifyResult(CertVerifyStatusAndroid.NO_TRUSTED_ROOT);
607                 }
608             }
609 
610             boolean isIssuedByKnownRoot = false;
611             if (verifiedChain.size() > 0) {
612                 X509Certificate root = verifiedChain.get(verifiedChain.size() - 1);
613                 isIssuedByKnownRoot = isKnownRoot(root);
614             }
615 
616             return new AndroidCertVerifyResult(
617                     CertVerifyStatusAndroid.OK, isIssuedByKnownRoot, verifiedChain);
618         }
619     }
620 
621     @NativeMethods
622     interface Natives {
623         /**
624          * Notify the native net::CertDatabase instance that the system database has been updated.
625          */
notifyTrustStoreChanged()626         void notifyTrustStoreChanged();
627 
notifyClientCertStoreChanged()628         void notifyClientCertStoreChanged();
629     }
630 }
631