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