X509Util.java revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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.IOException; 22import java.security.KeyStore; 23import java.security.KeyStoreException; 24import java.security.NoSuchAlgorithmException; 25import java.security.PublicKey; 26import java.security.cert.Certificate; 27import java.security.cert.CertificateException; 28import java.security.cert.CertificateExpiredException; 29import java.security.cert.CertificateFactory; 30import java.security.cert.CertificateNotYetValidException; 31import java.security.cert.X509Certificate; 32import java.util.Arrays; 33import java.util.Collections; 34import java.util.Enumeration; 35import java.util.HashSet; 36import java.util.List; 37import java.util.Set; 38 39import javax.net.ssl.TrustManager; 40import javax.net.ssl.TrustManagerFactory; 41import javax.net.ssl.X509TrustManager; 42import javax.security.auth.x500.X500Principal; 43 44/** 45 * Utility functions for verifying X.509 certificates. 46 */ 47@JNINamespace("net") 48public class X509Util { 49 50 private static final String TAG = "X509Util"; 51 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)) { 55 try { 56 reloadDefaultTrustManager(); 57 } 58 catch (CertificateException e) { 59 Log.e(TAG, "Unable to reload the default TrustManager", e); 60 } 61 catch (KeyStoreException e) { 62 Log.e(TAG, "Unable to reload the default TrustManager", e); 63 } 64 catch (NoSuchAlgorithmException e) { 65 Log.e(TAG, "Unable to reload the default TrustManager", e); 66 } 67 } 68 } 69 } 70 71 /** 72 * Interface that wraps one of X509TrustManager or 73 * X509TrustManagerExtensions to support platforms before the latter was 74 * added. 75 */ 76 private static interface X509TrustManagerImplementation { 77 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, 78 String authType, 79 String host) throws CertificateException; 80 } 81 82 private static final class X509TrustManagerIceCreamSandwich implements 83 X509TrustManagerImplementation { 84 private final X509TrustManager mTrustManager; 85 86 public X509TrustManagerIceCreamSandwich(X509TrustManager trustManager) { 87 mTrustManager = trustManager; 88 } 89 90 @Override 91 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, 92 String authType, 93 String host) throws CertificateException { 94 mTrustManager.checkServerTrusted(chain, authType); 95 return Collections.<X509Certificate>emptyList(); 96 } 97 } 98 99 private static final class X509TrustManagerJellyBean implements X509TrustManagerImplementation { 100 private final X509TrustManagerExtensions mTrustManagerExtensions; 101 102 @SuppressLint("NewApi") 103 public X509TrustManagerJellyBean(X509TrustManager trustManager) { 104 mTrustManagerExtensions = new X509TrustManagerExtensions(trustManager); 105 } 106 107 @Override 108 public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, 109 String authType, 110 String host) throws CertificateException { 111 return mTrustManagerExtensions.checkServerTrusted(chain, authType, host); 112 } 113 } 114 115 private static CertificateFactory sCertificateFactory; 116 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): 120 // Netscape: 121 private static final String OID_SERVER_GATED_NETSCAPE = "2.16.840.1.113730.4.1"; 122 // Microsoft: 123 private static final String OID_SERVER_GATED_MICROSOFT = "1.3.6.1.4.1.311.10.3.3"; 124 125 /** 126 * Trust manager backed up by the read-only system certificate store. 127 */ 128 private static X509TrustManagerImplementation sDefaultTrustManager; 129 130 /** 131 * BroadcastReceiver that listens to change in the system keystore to invalidate certificate 132 * caches. 133 */ 134 private static TrustStorageListener sTrustStorageListener; 135 136 /** 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. 139 */ 140 private static X509TrustManagerImplementation sTestTrustManager; 141 private static KeyStore sTestKeyStore; 142 143 /** 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. 146 * 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. 153 */ 154 private static Set<Pair<X500Principal, PublicKey>> sSystemTrustRoots; 155 156 /** 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.) 160 */ 161 private static boolean sLoadedSystemTrustRoots; 162 163 /** 164 * Lock object used to synchronize all calls that modify or depend on the trust managers. 165 */ 166 private static final Object sLock = new Object(); 167 168 /** 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. 172 */ 173 private static boolean sDisableNativeCodeForTest = false; 174 175 /** 176 * Ensures that the trust managers and certificate factory are initialized. 177 */ 178 private static void ensureInitialized() throws CertificateException, 179 KeyStoreException, NoSuchAlgorithmException { 180 synchronized (sLock) { 181 if (sCertificateFactory == null) { 182 sCertificateFactory = CertificateFactory.getInstance("X.509"); 183 } 184 if (sDefaultTrustManager == null) { 185 sDefaultTrustManager = X509Util.createTrustManager(null); 186 } 187 if (!sLoadedSystemTrustRoots) { 188 try { 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); 195 } 196 if (!sDisableNativeCodeForTest) 197 nativeRecordCertVerifyCapabilitiesHistogram(sSystemTrustRoots != null); 198 sLoadedSystemTrustRoots = true; 199 } 200 if (sTestKeyStore == null) { 201 sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 202 try { 203 sTestKeyStore.load(null); 204 } catch (IOException e) { 205 // No IO operation is attempted. 206 } 207 } 208 if (sTestTrustManager == null) { 209 sTestTrustManager = X509Util.createTrustManager(sTestKeyStore); 210 } 211 if (!sDisableNativeCodeForTest && sTrustStorageListener == null) { 212 sTrustStorageListener = new TrustStorageListener(); 213 nativeGetApplicationContext().registerReceiver(sTrustStorageListener, 214 new IntentFilter(KeyChain.ACTION_STORAGE_CHANGED)); 215 } 216 } 217 } 218 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"); 223 try { 224 systemKeyStore.load(null); 225 } catch (IOException e) { 226 // No IO operation is attempted. 227 } 228 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:")) 235 continue; 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())); 241 } 242 } 243 return roots; 244 } 245 246 /** 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 249 * used. 250 * @throws KeyStoreException, NoSuchAlgorithmException on error initializing the TrustManager. 251 */ 252 private static X509TrustManagerImplementation createTrustManager(KeyStore keyStore) throws 253 KeyStoreException, NoSuchAlgorithmException { 254 String algorithm = TrustManagerFactory.getDefaultAlgorithm(); 255 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); 256 tmf.init(keyStore); 257 258 for (TrustManager tm : tmf.getTrustManagers()) { 259 if (tm instanceof X509TrustManager) { 260 try { 261 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 262 return new X509TrustManagerJellyBean((X509TrustManager) tm); 263 } else { 264 return new X509TrustManagerIceCreamSandwich((X509TrustManager) tm); 265 } 266 } catch (IllegalArgumentException e) { 267 Log.e(TAG, "Error creating trust manager: " + e); 268 } 269 } 270 } 271 return null; 272 } 273 274 /** 275 * After each modification of test key store, trust manager has to be generated again. 276 */ 277 private static void reloadTestTrustManager() throws KeyStoreException, 278 NoSuchAlgorithmException { 279 sTestTrustManager = X509Util.createTrustManager(sTestKeyStore); 280 } 281 282 /** 283 * After each modification by the system of the key store, trust manager has to be regenerated. 284 */ 285 private static void reloadDefaultTrustManager() throws KeyStoreException, 286 NoSuchAlgorithmException, CertificateException { 287 sDefaultTrustManager = null; 288 sSystemTrustRoots = null; 289 sLoadedSystemTrustRoots = false; 290 nativeNotifyKeyChainChanged(); 291 ensureInitialized(); 292 } 293 294 /** 295 * Convert a DER encoded certificate to an X509Certificate. 296 */ 297 public static X509Certificate createCertificateFromBytes(byte[] derBytes) throws 298 CertificateException, KeyStoreException, NoSuchAlgorithmException { 299 ensureInitialized(); 300 return (X509Certificate) sCertificateFactory.generateCertificate( 301 new ByteArrayInputStream(derBytes)); 302 } 303 304 public static void addTestRootCertificate(byte[] rootCertBytes) throws CertificateException, 305 KeyStoreException, NoSuchAlgorithmException { 306 ensureInitialized(); 307 X509Certificate rootCert = createCertificateFromBytes(rootCertBytes); 308 synchronized (sLock) { 309 sTestKeyStore.setCertificateEntry( 310 "root_cert_" + Integer.toString(sTestKeyStore.size()), rootCert); 311 reloadTestTrustManager(); 312 } 313 } 314 315 public static void clearTestRootCertificates() throws NoSuchAlgorithmException, 316 CertificateException, KeyStoreException { 317 ensureInitialized(); 318 synchronized (sLock) { 319 try { 320 sTestKeyStore.load(null); 321 reloadTestTrustManager(); 322 } catch (IOException e) { 323 // No IO operation is attempted. 324 } 325 } 326 } 327 328 /** 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. 331 * 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. 334 * 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. 337 */ 338 static boolean verifyKeyUsage(X509Certificate certificate) throws CertificateException { 339 List<String> ekuOids; 340 try { 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 346 return false; 347 } 348 if (ekuOids == null) 349 return true; 350 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)) { 356 return true; 357 } 358 } 359 360 return false; 361 } 362 363 public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain, 364 String authType, 365 String host) 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)); 370 } 371 372 373 try { 374 ensureInitialized(); 375 } catch (CertificateException e) { 376 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_FAILED); 377 } 378 379 X509Certificate[] serverCertificates = new X509Certificate[certChain.length]; 380 try { 381 for (int i = 0; i < certChain.length; ++i) { 382 serverCertificates[i] = createCertificateFromBytes(certChain[i]); 383 } 384 } catch (CertificateException e) { 385 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_UNABLE_TO_PARSE); 386 } 387 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 391 // separately. 392 try { 393 serverCertificates[0].checkValidity(); 394 if (!verifyKeyUsage(serverCertificates[0])) { 395 return new AndroidCertVerifyResult( 396 CertVerifyStatusAndroid.VERIFY_INCORRECT_KEY_USAGE); 397 } 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); 404 } 405 406 synchronized (sLock) { 407 List<X509Certificate> verifiedChain; 408 try { 409 verifiedChain = sDefaultTrustManager.checkServerTrusted(serverCertificates, 410 authType, host); 411 } catch (CertificateException eDefaultManager) { 412 try { 413 verifiedChain = sTestTrustManager.checkServerTrusted(serverCertificates, 414 authType, host); 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); 422 } 423 } 424 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())); 431 } 432 433 return new AndroidCertVerifyResult(CertVerifyStatusAndroid.VERIFY_OK, 434 isIssuedByKnownRoot, verifiedChain); 435 } 436 } 437 438 public static void setDisableNativeCodeForTest(boolean disabled) { 439 sDisableNativeCodeForTest = disabled; 440 } 441 /** 442 * Notify the native net::CertDatabase instance that the system database has been updated. 443 */ 444 private static native void nativeNotifyKeyChainChanged(); 445 446 /** 447 * Record histograms on the platform's certificate verification capabilities. 448 */ 449 private static native void nativeRecordCertVerifyCapabilitiesHistogram( 450 boolean foundSystemTrustRoots); 451 452 /** 453 * Returns the application context. 454 */ 455 private static native Context nativeGetApplicationContext(); 456 457} 458