90012a4aa7496f92876e9215db48251b1829ab56
[platform/framework/web/crosswalk.git] / src / net / android / java / src / org / chromium / net / X509Util.java
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
5 package org.chromium.net;
6
7 import android.annotation.SuppressLint;
8 import android.content.BroadcastReceiver;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.content.IntentFilter;
12 import android.net.http.X509TrustManagerExtensions;
13 import android.os.Build;
14 import android.security.KeyChain;
15 import android.util.Log;
16 import android.util.Pair;
17
18 import org.chromium.base.JNINamespace;
19
20 import java.io.ByteArrayInputStream;
21 import java.io.IOException;
22 import java.security.KeyStore;
23 import java.security.KeyStoreException;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.PublicKey;
26 import java.security.cert.Certificate;
27 import java.security.cert.CertificateException;
28 import java.security.cert.CertificateExpiredException;
29 import java.security.cert.CertificateFactory;
30 import java.security.cert.CertificateNotYetValidException;
31 import java.security.cert.X509Certificate;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Enumeration;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38
39 import javax.net.ssl.TrustManager;
40 import javax.net.ssl.TrustManagerFactory;
41 import javax.net.ssl.X509TrustManager;
42 import javax.security.auth.x500.X500Principal;
43
44 /**
45  * Utility functions for verifying X.509 certificates.
46  */
47 @JNINamespace("net")
48 public 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 }