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