/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko; import android.util.Log; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Enumeration; import org.mozilla.gecko.annotation.WrapForJNI; // This class implements the functionality needed to find third-party root // certificates that have been added to the android CA store. public class EnterpriseRoots { private static final String LOGTAG = "EnterpriseRoots"; // Gecko calls this function from C++ to find third-party root certificates // it can use as trust anchors for TLS connections. @WrapForJNI private static byte[][] gatherEnterpriseRoots() { // The KeyStore "AndroidCAStore" contains the certificates we're // interested in. final KeyStore ks; try { ks = KeyStore.getInstance("AndroidCAStore"); } catch (final KeyStoreException kse) { Log.e(LOGTAG, "getInstance() failed", kse); return new byte[0][0]; } try { ks.load(null); } catch (final CertificateException ce) { Log.e(LOGTAG, "load() failed", ce); return new byte[0][0]; } catch (final IOException ioe) { Log.e(LOGTAG, "load() failed", ioe); return new byte[0][0]; } catch (final NoSuchAlgorithmException nsae) { Log.e(LOGTAG, "load() failed", nsae); return new byte[0][0]; } // Given the KeyStore, we get an identifier for each object in it. For // each one that is a Certificate, we try to distinguish between // entries that shipped with the OS and entries that were added by the // user or an administrator. The former we ignore and the latter we // collect in an array of byte arrays and return. final Enumeration aliases; try { aliases = ks.aliases(); } catch (final KeyStoreException kse) { Log.e(LOGTAG, "aliases() failed", kse); return new byte[0][0]; } final ArrayList roots = new ArrayList(); while (aliases.hasMoreElements()) { final String alias = aliases.nextElement(); final boolean isCertificate; try { isCertificate = ks.isCertificateEntry(alias); } catch (final KeyStoreException kse) { Log.e(LOGTAG, "isCertificateEntry() failed", kse); continue; } // Built-in certificate aliases start with "system:", whereas // 3rd-party certificate aliases start with "user:". It's // unfortunate to be relying on this implementation detail, but // there appears to be no other way to differentiate between the // two. if (isCertificate && alias.startsWith("user:")) { final Certificate certificate; try { certificate = ks.getCertificate(alias); } catch (final KeyStoreException kse) { Log.e(LOGTAG, "getCertificate() failed", kse); continue; } try { roots.add(certificate.getEncoded()); } catch (final CertificateEncodingException cee) { Log.e(LOGTAG, "getEncoded() failed", cee); } } } Log.d(LOGTAG, "found " + roots.size() + " enterprise roots"); return roots.toArray(new byte[0][0]); } }