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. 4 5package org.chromium.net; 6 7import android.annotation.SuppressLint; 8import android.content.BroadcastReceiver; 9import android.content.Context; 10import android.content.Intent; 11import android.content.IntentFilter; 12import android.net.http.X509TrustManagerExtensions; 13import android.os.Build; 14import android.security.KeyChain; 15import android.util.Log; 16import android.util.Pair; 17 18import org.chromium.base.JNINamespace; 19 20import java.io.ByteArrayInputStream; 21import java.io.File; 22import java.io.IOException; 23import java.security.KeyStore; 24import java.security.KeyStoreException; 25import java.security.MessageDigest; 26import java.security.NoSuchAlgorithmException; 27import java.security.PublicKey; 28import java.security.cert.Certificate; 29import java.security.cert.CertificateException; 30import java.security.cert.CertificateExpiredException; 31import java.security.cert.CertificateFactory; 32import java.security.cert.CertificateNotYetValidException; 33import java.security.cert.X509Certificate; 34import java.util.Arrays; 35import java.util.Collections; 36import java.util.HashSet; 37import java.util.List; 38import java.util.Set; 39 40import javax.net.ssl.TrustManager; 41import javax.net.ssl.TrustManagerFactory; 42import javax.net.ssl.X509TrustManager; 43import javax.security.auth.x500.X500Principal; 44 45/** 46 * Utility functions for verifying X.509 certificates. 47 */ 48@JNINamespace("net") 49public class X509Util { 50 51 private static final String TAG = "X509Util"; 52 53 private static final class TrustStorageListener extends BroadcastReceiver { 54 @Override public void onReceive(Context context, Intent intent) { 55 if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { 56 try { 57 reloadDefaultTrustManager(); 58 } catch (CertificateException e) { 59 Log.e(TAG, "Unable to reload the default TrustManager", e); 60 } catch (KeyStoreException e) { 61 Log.e(TAG, "Unable to reload the default TrustManager", e); 62 } catch (NoSuchAlgorithmException e) { 63 Log.e(TAG, "Unable to reload the default TrustManager", e); 64 } 65 } 66 } 67 } 68 69 /** 70 * Interface that wraps one of X509TrustManager or 71 * X509TrustManagerExtensions to support platforms before the latter was 72 * added. 73 */ 74 private static interface X509TrustManagerImplementation { 75 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, 76 String authType, 77 String host) throws CertificateException; 78 } 79 80 private static final class X509TrustManagerIceCreamSandwich implements 81 X509TrustManagerImplementation { 82 private final X509TrustManager mTrustManager; 83 84 public X509TrustManagerIceCreamSandwich(X509TrustManager trustManager) { 85 mTrustManager = trustManager; 86 } 87 88 @Override 89 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, 90 String authType, 91 String host) throws CertificateException { 92 mTrustManager.checkServerTrusted(chain, authType); 93 return Collections.<X509Certificate>emptyList(); 94 } 95 } 96 97 private static final class X509TrustManagerJellyBean implements X509TrustManagerImplementation { 98 private final X509TrustManagerExtensions mTrustManagerExtensions; 99 100 @SuppressLint("NewApi") 101 public X509TrustManagerJellyBean(X509TrustManager trustManager) { 102 mTrustManagerExtensions = new X509TrustManagerExtensions(trustManager); 103 } 104 105 @Override 106 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, 107 String authType, 108 String host) throws CertificateException { 109 return mTrustManagerExtensions.checkServerTrusted(chain, authType, host); 110 } 111 } 112 113 private static CertificateFactory sCertificateFactory; 114 115 private static final String OID_TLS_SERVER_AUTH = "1.3.6.1.5.5.7.3.1"; 116 private static final String OID_ANY_EKU = "2.5.29.37.0"; 117 // Server-Gated Cryptography (necessary to support a few legacy issuers): 118 // Netscape: 119 private static final String OID_SERVER_GATED_NETSCAPE = "2.16.840.1.113730.4.1"; 120 // Microsoft: 121 private static final String OID_SERVER_GATED_MICROSOFT = "1.3.6.1.4.1.311.10.3.3"; 122 123 /** 124 * Trust manager backed up by the read-only system certificate store. 125 */ 126 private static X509TrustManagerImplementation sDefaultTrustManager; 127 128 /** 129 * BroadcastReceiver that listens to change in the system keystore to invalidate certificate 130 * caches. 131 */ 132 private static TrustStorageListener sTrustStorageListener; 133 134 /** 135 * Trust manager backed up by a custom certificate store. We need such manager to plant test 136 * root CA to the trust store in testing. 137 */ 138 private static X509TrustManagerImplementation sTestTrustManager; 139 private static KeyStore sTestKeyStore; 140 141 /** 142 * The system key store. This is used to determine whether a trust anchor is a system trust 143 * anchor or user-installed. 144 */ 145 private static KeyStore sSystemKeyStore; 146 147 /** 148 * The directory where system certificates are stored. This is used to determine whether a 149 * trust anchor is a system trust anchor or user-installed. The KeyStore API alone is not 150 * sufficient to efficiently query whether a given X500Principal, PublicKey pair is a trust 151 * anchor. 152 */ 153 private static File sSystemCertificateDirectory; 154 155 /** 156 * An in-memory cache of which trust anchors are system trust roots. This avoids reading and 157 * decoding the root from disk on every verification. Mirrors a similar in-memory cache in 158 * Conscrypt's X509TrustManager implementation. 159 */ 160 private static Set<Pair<X500Principal, PublicKey>> sSystemTrustAnchorCache; 161 162 /** 163 * True if the system key store has been loaded. If the "AndroidCAStore" KeyStore instance 164 * was not found, sSystemKeyStore may be null while sLoadedSystemKeyStore is true. 165 */ 166 private static boolean sLoadedSystemKeyStore; 167 168 /** 169 * Lock object used to synchronize all calls that modify or depend on the trust managers. 170 */ 171 private static final Object sLock = new Object(); 172 173 /** 174 * Allow disabling registering the observer and recording histograms for the certificate 175 * changes. Net unit tests do not load native libraries which prevent this to succeed. Moreover, 176 * the system does not allow to interact with the certificate store without user interaction. 177 */ 178 private static boolean sDisableNativeCodeForTest = false; 179 180 /** 181 * Ensures that the trust managers and certificate factory are initialized. 182 */ 183 private static void ensureInitialized() throws CertificateException, 184 KeyStoreException, NoSuchAlgorithmException { 185 synchronized (sLock) { 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 if (!sDisableNativeCodeForTest) 207 nativeRecordCertVerifyCapabilitiesHistogram(sSystemKeyStore != null); 208 sLoadedSystemKeyStore = true; 209 } 210 if (sSystemTrustAnchorCache == null) { 211 sSystemTrustAnchorCache = new HashSet<Pair<X500Principal, PublicKey>>(); 212 } 213 if (sTestKeyStore == null) { 214 sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 215 try { 216 sTestKeyStore.load(null); 217 } catch (IOException e) { 218 // No IO operation is attempted. 219 } 220 } 221 if (sTestTrustManager == null) { 222 sTestTrustManager = X509Util.createTrustManager(sTestKeyStore); 223 } 224 if (!sDisableNativeCodeForTest && sTrustStorageListener == null) { 225 sTrustStorageListener = new TrustStorageListener(); 226 nativeGetApplicationContext().registerReceiver(sTrustStorageListener, 227 new IntentFilter(KeyChain.ACTION_STORAGE_CHANGED)); 228 } 229 } 230 } 231 232 /** 233 * Creates a X509TrustManagerImplementation backed up by the given key 234 * store. When null is passed as a key store, system default trust store is 235 * used. Returns null if no created TrustManager was suitable. 236 * @throws KeyStoreException, NoSuchAlgorithmException on error initializing the TrustManager. 237 */ 238 private static X509TrustManagerImplementation createTrustManager(KeyStore keyStore) throws 239 KeyStoreException, NoSuchAlgorithmException { 240 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); 241 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); 242 tmf.init(keyStore); 243 244 for (TrustManager tm : tmf.getTrustManagers()) { 245 if (tm instanceof X509TrustManager) { 246 try { 247 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 248 return new X509TrustManagerJellyBean((X509TrustManager) tm); 249 } else { 250 return new X509TrustManagerIceCreamSandwich((X509TrustManager) tm); 251 } 252 } catch (IllegalArgumentException e) { 253 String className = tm.getClass().getName(); 254 Log.e(TAG, "Error creating trust manager (" + className + "): " + e); 255 } 256 } 257 } 258 Log.e(TAG, "Could not find suitable trust manager"); 259 return null; 260 } 261 262 /** 263 * After each modification of test key store, trust manager has to be generated again. 264 */ 265 private static void reloadTestTrustManager() throws KeyStoreException, 266 NoSuchAlgorithmException { 267 sTestTrustManager = X509Util.createTrustManager(sTestKeyStore); 268 } 269 270 /** 271 * After each modification by the system of the key store, trust manager has to be regenerated. 272 */ 273 private static void reloadDefaultTrustManager() throws KeyStoreException, 274 NoSuchAlgorithmException, CertificateException { 275 sDefaultTrustManager = null; 276 sSystemTrustAnchorCache = null; 277 nativeNotifyKeyChainChanged(); 278 ensureInitialized(); 279 } 280 281 /** 282 * Convert a DER encoded certificate to an X509Certificate. 283 */ 284 public static X509Certificate createCertificateFromBytes(byte[] derBytes) throws 285 CertificateException, KeyStoreException, NoSuchAlgorithmException { 286 ensureInitialized(); 287 return (X509Certificate) sCertificateFactory.generateCertificate( 288 new ByteArrayInputStream(derBytes)); 289 } 290 291 public static void addTestRootCertificate(byte[] rootCertBytes) throws CertificateException, 292 KeyStoreException, NoSuchAlgorithmException { 293 ensureInitialized(); 294 X509Certificate rootCert = createCertificateFromBytes(rootCertBytes); 295 synchronized (sLock) { 296 sTestKeyStore.setCertificateEntry( 297 "root_cert_" + Integer.toString(sTestKeyStore.size()), rootCert); 298 reloadTestTrustManager(); 299 } 300 } 301 302 public static void clearTestRootCertificates() throws NoSuchAlgorithmException, 303 CertificateException, KeyStoreException { 304 ensureInitialized(); 305 synchronized (sLock) { 306 try { 307 sTestKeyStore.load(null); 308 reloadTestTrustManager(); 309 } catch (IOException e) { 310 // No IO operation is attempted. 311 } 312 } 313 } 314 315 private static final char[] HEX_DIGITS = { 316 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 317 'a', 'b', 'c', 'd', 'e', 'f', 318 }; 319 320 private static String hashPrincipal(X500Principal principal) throws NoSuchAlgorithmException { 321 // Android hashes a principal as the first four bytes of its MD5 digest, encoded in 322 // lowercase hex and reversed. Verified in 4.2, 4.3, and 4.4. 323 byte[] digest = MessageDigest.getInstance("MD5").digest(principal.getEncoded()); 324 char[] hexChars = new char[8]; 325 for (int i = 0; i < 4; i++) { 326 hexChars[2 * i] = HEX_DIGITS[(digest[3 - i] >> 4) & 0xf]; 327 hexChars[2 * i + 1] = HEX_DIGITS[digest[3 - i] & 0xf]; 328 } 329 return new String(hexChars); 330 } 331 332 private static boolean isKnownRoot(X509Certificate root) 333 throws NoSuchAlgorithmException, KeyStoreException { 334 // Could not find the system key store. Conservatively report false. 335 if (sSystemKeyStore == null) 336 return false; 337 338 // Check the in-memory cache first; avoid decoding the anchor from disk 339 // if it has been seen before. 340 Pair<X500Principal, PublicKey> key = 341 new Pair<X500Principal, PublicKey>(root.getSubjectX500Principal(), root.getPublicKey()); 342 if (sSystemTrustAnchorCache.contains(key)) 343 return true; 344 345 // Note: It is not sufficient to call sSystemKeyStore.getCertificiateAlias. If the server 346 // supplies a copy of a trust anchor, X509TrustManagerExtensions returns the server's 347 // version rather than the system one. getCertificiateAlias will then fail to find an anchor 348 // name. This is fixed upstream in https://android-review.googlesource.com/#/c/91605/ 349 // 350 // TODO(davidben): When the change trickles into an Android release, query sSystemKeyStore 351 // directly. 352 353 // System trust anchors are stored under a hash of the principal. In case of collisions, 354 // a number is appended. 355 String hash = hashPrincipal(root.getSubjectX500Principal()); 356 for (int i = 0; true; i++) { 357 String alias = hash + '.' + i; 358 if (!new File(sSystemCertificateDirectory, alias).exists()) 359 break; 360 361 Certificate anchor = sSystemKeyStore.getCertificate("system:" + alias); 362 // It is possible for this to return null if the user deleted a trust anchor. In 363 // that case, the certificate remains in the system directory but is also added to 364 // another file. Continue iterating as there may be further collisions after the 365 // deleted anchor. 366 if (anchor == null) 367 continue; 368 369 if (!(anchor instanceof X509Certificate)) { 370 // This should never happen. 371 String className = anchor.getClass().getName(); 372 Log.e(TAG, "Anchor " + alias + " not an X509Certificate: " + className); 373 continue; 374 } 375 376 // If the subject and public key match, this is a system root. 377 X509Certificate anchorX509 = (X509Certificate) anchor; 378 if (root.getSubjectX500Principal().equals(anchorX509.getSubjectX500Principal()) && 379 root.getPublicKey().equals(anchorX509.getPublicKey())) { 380 sSystemTrustAnchorCache.add(key); 381 return true; 382 } 383 } 384 385 return false; 386 } 387 388 /** 389 * If an EKU extension is present in the end-entity certificate, it MUST contain either the 390 * anyEKU or serverAuth or netscapeSGC or Microsoft SGC EKUs. 391 * 392 * @return true if there is no EKU extension or if any of the EKU extensions is one of the valid 393 * OIDs for web server certificates. 394 * 395 * TODO(palmer): This can be removed after the equivalent change is made to the Android default 396 * TrustManager and that change is shipped to a large majority of Android users. 397 */ 398 static boolean verifyKeyUsage(X509Certificate certificate) throws CertificateException { 399 List<String> ekuOids; 400 try { 401 ekuOids = certificate.getExtendedKeyUsage(); 402 } catch (NullPointerException e) { 403 // getExtendedKeyUsage() can crash due to an Android platform bug. This probably 404 // happens when the EKU extension data is malformed so return false here. 405 // See http://crbug.com/233610 406 return false; 407 } 408 if (ekuOids == null) 409 return true; 410 411 for (String ekuOid : ekuOids) { 412 if (ekuOid.equals(OID_TLS_SERVER_AUTH) || 413 ekuOid.equals(OID_ANY_EKU) || 414 ekuOid.equals(OID_SERVER_GATED_NETSCAPE) || 415 ekuOid.equals(OID_SERVER_GATED_MICROSOFT)) { 416 return true; 417 } 418 } 419 420 return false; 421 } 422 423 public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain, 424 String authType, 425 String host) 426 throws KeyStoreException, NoSuchAlgorithmException { 427 if (certChain == null || certChain.length == 0 || certChain[0] == null) { 428 throw new IllegalArgumentException("Expected non-null and non-empty certificate " + 429 "chain passed as |certChain|. |certChain|=" + Arrays.deepToString(certChain)); 430 } 431 432 433 try { 434 ensureInitialized(); 435 } catch (CertificateException e) { 436 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED); 437 } 438 439 X509Certificate[] serverCertificates = new X509Certificate[certChain.length]; 440 try { 441 for (int i = 0; i < certChain.length; ++i) { 442 serverCertificates[i] = createCertificateFromBytes(certChain[i]); 443 } 444 } catch (CertificateException e) { 445 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_UNABLE_TO_PARSE); 446 } 447 448 // Expired and not yet valid certificates would be rejected by the trust managers, but the 449 // trust managers report all certificate errors using the general CertificateException. In 450 // order to get more granular error information, cert validity time range is being checked 451 // separately. 452 try { 453 serverCertificates[0].checkValidity(); 454 if (!verifyKeyUsage(serverCertificates[0])) { 455 return new AndroidCertVerifyResult( 456 CertVerifyStatusAndroid.VERIFY_INCORRECT_KEY_USAGE); 457 } 458 } catch (CertificateExpiredException e) { 459 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_EXPIRED); 460 } catch (CertificateNotYetValidException e) { 461 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_NOT_YET_VALID); 462 } catch (CertificateException e) { 463 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED); 464 } 465 466 synchronized (sLock) { 467 // If no trust manager was found, fail without crashing on the null pointer. 468 if (sDefaultTrustManager == null) 469 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED); 470 471 List<X509Certificate> verifiedChain; 472 try { 473 verifiedChain = sDefaultTrustManager.checkServerTrusted(serverCertificates, 474 authType, host); 475 } catch (CertificateException eDefaultManager) { 476 try { 477 verifiedChain = sTestTrustManager.checkServerTrusted(serverCertificates, 478 authType, host); 479 } catch (CertificateException eTestManager) { 480 // Neither of the trust managers confirms the validity of the certificate chain, 481 // log the error message returned by the system trust manager. 482 Log.i(TAG, "Failed to validate the certificate chain, error: " + 483 eDefaultManager.getMessage()); 484 return new AndroidCertVerifyResult( 485 CertVerifyStatusAndroid.VERIFY_NO_TRUSTED_ROOT); 486 } 487 } 488 489 boolean isIssuedByKnownRoot = false; 490 if (verifiedChain.size() > 0) { 491 X509Certificate root = verifiedChain.get(verifiedChain.size() - 1); 492 isIssuedByKnownRoot = isKnownRoot(root); 493 } 494 495 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_OK, 496 isIssuedByKnownRoot, verifiedChain); 497 } 498 } 499 500 public static void setDisableNativeCodeForTest(boolean disabled) { 501 sDisableNativeCodeForTest = disabled; 502 } 503 /** 504 * Notify the native net::CertDatabase instance that the system database has been updated. 505 */ 506 private static native void nativeNotifyKeyChainChanged(); 507 508 /** 509 * Record histograms on the platform's certificate verification capabilities. 510 */ 511 private static native void nativeRecordCertVerifyCapabilitiesHistogram( 512 boolean foundSystemTrustRoots); 513 514 /** 515 * Returns the application context. 516 */ 517 private static native Context nativeGetApplicationContext(); 518 519} 520