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