1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 package org.mozilla.gecko;
7 
8 import java.io.IOException;
9 
10 import java.security.KeyStore;
11 import java.security.KeyStoreException;
12 import java.security.NoSuchAlgorithmException;
13 
14 import java.security.cert.Certificate;
15 import java.security.cert.CertificateEncodingException;
16 import java.security.cert.CertificateException;
17 
18 import java.util.ArrayList;
19 import java.util.Enumeration;
20 
21 import org.mozilla.gecko.annotation.WrapForJNI;
22 
23 import android.util.Log;
24 
25 // This class implements the functionality needed to find third-party root
26 // certificates that have been added to the android CA store.
27 public class EnterpriseRoots {
28     private static final String LOGTAG = "EnterpriseRoots";
29 
30     // Gecko calls this function from C++ to find third-party root certificates
31     // it can use as trust anchors for TLS connections.
32     @WrapForJNI
gatherEnterpriseRoots()33     private static byte[][] gatherEnterpriseRoots() {
34 
35         // The KeyStore "AndroidCAStore" contains the certificates we're
36         // interested in.
37         final KeyStore ks;
38         try {
39             ks = KeyStore.getInstance("AndroidCAStore");
40         } catch (final KeyStoreException kse) {
41             Log.e(LOGTAG, "getInstance() failed", kse);
42             return new byte[0][0];
43         }
44         try {
45             ks.load(null);
46         } catch (final CertificateException ce) {
47             Log.e(LOGTAG, "load() failed", ce);
48             return new byte[0][0];
49         } catch (final IOException ioe) {
50             Log.e(LOGTAG, "load() failed", ioe);
51             return new byte[0][0];
52         } catch (final NoSuchAlgorithmException nsae) {
53             Log.e(LOGTAG, "load() failed", nsae);
54             return new byte[0][0];
55         }
56         // Given the KeyStore, we get an identifier for each object in it. For
57         // each one that is a Certificate, we try to distinguish between
58         // entries that shipped with the OS and entries that were added by the
59         // user or an administrator. The former we ignore and the latter we
60         // collect in an array of byte arrays and return.
61         final Enumeration<String> aliases;
62         try {
63             aliases = ks.aliases();
64         } catch (final KeyStoreException kse) {
65             Log.e(LOGTAG, "aliases() failed", kse);
66             return new byte[0][0];
67         }
68         final ArrayList<byte[]> roots = new ArrayList<byte[]>();
69         while (aliases.hasMoreElements()) {
70             final String alias = aliases.nextElement();
71             final boolean isCertificate;
72             try {
73                 isCertificate = ks.isCertificateEntry(alias);
74             } catch (final KeyStoreException kse) {
75                 Log.e(LOGTAG, "isCertificateEntry() failed", kse);
76                 continue;
77             }
78             // Built-in certificate aliases start with "system:", whereas
79             // 3rd-party certificate aliases start with "user:". It's
80             // unfortunate to be relying on this implementation detail, but
81             // there appears to be no other way to differentiate between the
82             // two.
83             if (isCertificate && alias.startsWith("user:")) {
84                 final Certificate certificate;
85                 try {
86                     certificate = ks.getCertificate(alias);
87                 } catch (final KeyStoreException kse) {
88                     Log.e(LOGTAG, "getCertificate() failed", kse);
89                     continue;
90                 }
91                 try {
92                     roots.add(certificate.getEncoded());
93                 } catch (final CertificateEncodingException cee) {
94                     Log.e(LOGTAG, "getEncoded() failed", cee);
95                 }
96             }
97         }
98         Log.d(LOGTAG, "found " + roots.size() + " enterprise roots");
99         return roots.toArray(new byte[0][0]);
100     }
101 }
102