11b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom/*
21b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * Copyright (C) 2011 The Android Open Source Project
31b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *
41b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * Licensed under the Apache License, Version 2.0 (the "License");
51b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * you may not use this file except in compliance with the License.
61b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * You may obtain a copy of the License at
71b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *
81b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *      http://www.apache.org/licenses/LICENSE-2.0
91b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *
101b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * Unless required by applicable law or agreed to in writing, software
111b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * distributed under the License is distributed on an "AS IS" BASIS,
121b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * See the License for the specific language governing permissions and
141b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * limitations under the License.
151b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom */
161b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1738375a4d0b3d34e2babbd2f6a013976c7c439696Kenny Rootpackage org.conscrypt;
181b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
191b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.io.BufferedInputStream;
201b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.io.File;
211b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.io.FileInputStream;
221b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.io.FileOutputStream;
231b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.io.IOException;
241b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.io.InputStream;
251b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.io.OutputStream;
261b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.security.cert.Certificate;
271b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.security.cert.CertificateException;
281b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.security.cert.CertificateFactory;
291b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.security.cert.X509Certificate;
303fb088d79e446063ef743362a030e1cfb80b2178Kenny Rootimport java.util.ArrayList;
311b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.util.Date;
321b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.util.HashSet;
333fb088d79e446063ef743362a030e1cfb80b2178Kenny Rootimport java.util.List;
341b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport java.util.Set;
351b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport javax.security.auth.x500.X500Principal;
361b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstromimport libcore.io.IoUtils;
371b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
381b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom/**
391b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * A source for trusted root certificate authority (CA) certificates
401b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * supporting an immutable system CA directory along with mutable
411b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * directories allowing the user addition of custom CAs and user
42c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * removal of system CAs. This store supports the {@code
43c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * TrustedCertificateKeyStoreSpi} wrapper to allow a traditional
44c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * KeyStore interface for use with {@link
45c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * javax.net.ssl.TrustManagerFactory.init}.
461b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *
471b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * <p>The CAs are accessed via {@code KeyStore} style aliases. Aliases
481b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * are made up of a prefix identifying the source ("system:" vs
491b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * "user:") and a suffix based on the OpenSSL X509_NAME_hash_old
501b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * function of the CA's subject name. For example, the system CA for
511b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification
521b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * Authority" could be represented as "system:7651b327.0". By using
531b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * the subject hash, operations such as {@link #getCertificateAlias
541b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * getCertificateAlias} can be implemented efficiently without
551b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * scanning the entire store.
561b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *
57c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * <p>In addition to supporting the {@code
58c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * TrustedCertificateKeyStoreSpi} implementation, {@code
59c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * TrustedCertificateStore} also provides the additional public
60c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * methods {@link #isTrustAnchor} and {@link #findIssuer} to allow
61c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * efficient lookup operations for CAs again based on the file naming
62c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * convention.
631b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *
641b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * <p>The KeyChainService users the {@link installCertificate} and
651b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * {@link #deleteCertificateEntry} to install user CAs as well as
661b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * delete those user CAs as well as system CAs. The deletion of system
671b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * CAs is performed by placing an exact copy of that CA in the deleted
681b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * directory. Such deletions are intended to persist across upgrades
691b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * but not intended to mask a CA with a matching name or public key
701b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * but is otherwise reissued in a system update. Reinstalling a
711b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * deleted system certificate simply removes the copy from the deleted
721b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * directory, reenabling the original in the system directory.
731b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom *
741b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * <p>Note that the default mutable directory is created by init via
751b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * configuration in the system/core/rootdir/init.rc file. The
76d8e6e701b29c32484b062933fa905601ce638513Brian Carlstrom * directive "mkdir /data/misc/keychain 0775 system system"
77d8e6e701b29c32484b062933fa905601ce638513Brian Carlstrom * ensures that its owner and group are the system uid and system
78d8e6e701b29c32484b062933fa905601ce638513Brian Carlstrom * gid and that it is world readable but only writable by the system
791b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom * user.
801b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom */
811b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrompublic final class TrustedCertificateStore {
821b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
831b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private static final String PREFIX_SYSTEM = "system:";
841b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private static final String PREFIX_USER = "user:";
851b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
861b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public static final boolean isSystem(String alias) {
871b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return alias.startsWith(PREFIX_SYSTEM);
881b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
891b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public static final boolean isUser(String alias) {
901b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return alias.startsWith(PREFIX_USER);
911b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
921b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
931b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private static final File CA_CERTS_DIR_SYSTEM;
941b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private static final File CA_CERTS_DIR_ADDED;
951b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private static final File CA_CERTS_DIR_DELETED;
961b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private static final CertificateFactory CERT_FACTORY;
971b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    static {
981b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
991b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        String ANDROID_DATA = System.getenv("ANDROID_DATA");
1001b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        CA_CERTS_DIR_SYSTEM = new File(ANDROID_ROOT + "/etc/security/cacerts");
1011b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        CA_CERTS_DIR_ADDED = new File(ANDROID_DATA + "/misc/keychain/cacerts-added");
1021b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        CA_CERTS_DIR_DELETED = new File(ANDROID_DATA + "/misc/keychain/cacerts-removed");
1031b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1041b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        try {
1051b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            CERT_FACTORY = CertificateFactory.getInstance("X509");
1061b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        } catch (CertificateException e) {
1071b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            throw new AssertionError(e);
1081b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1091b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
1101b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1111b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private final File systemDir;
1121b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private final File addedDir;
1131b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private final File deletedDir;
1141b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1151b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public TrustedCertificateStore() {
1161b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        this(CA_CERTS_DIR_SYSTEM, CA_CERTS_DIR_ADDED, CA_CERTS_DIR_DELETED);
1171b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
1181b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1191b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public TrustedCertificateStore(File systemDir, File addedDir, File deletedDir) {
1201b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        this.systemDir = systemDir;
1211b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        this.addedDir = addedDir;
1221b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        this.deletedDir = deletedDir;
1231b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
1241b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1251b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public Certificate getCertificate(String alias) {
1265f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        return getCertificate(alias, false);
1275f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    }
1285f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom
1295f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    public Certificate getCertificate(String alias, boolean includeDeletedSystem) {
1305f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom
1311b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File file = fileForAlias(alias);
1321b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (file == null || (isUser(alias) && isTombstone(file))) {
1331b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
1341b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1351b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        X509Certificate cert = readCertificate(file);
1365f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        if (cert == null || (isSystem(alias)
1375f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom                             && !includeDeletedSystem
1385f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom                             && isDeletedSystemCertificate(cert))) {
1391b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // skip malformed certs as well as deleted system ones
1401b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
1411b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1421b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return cert;
1431b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
1441b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1451b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private File fileForAlias(String alias) {
1461b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (alias == null) {
1471b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            throw new NullPointerException("alias == null");
1481b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1491b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File file;
1501b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (isSystem(alias)) {
1511b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            file = new File(systemDir, alias.substring(PREFIX_SYSTEM.length()));
1521b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        } else if (isUser(alias)) {
1531b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            file = new File(addedDir, alias.substring(PREFIX_USER.length()));
1541b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        } else {
1551b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
1561b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1571b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (!file.exists() || isTombstone(file)) {
1581b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // silently elide tombstones
1591b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
1601b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1611b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return file;
1621b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
1631b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1641b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private boolean isTombstone(File file) {
1651b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return file.length() == 0;
1661b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
1671b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1681b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private X509Certificate readCertificate(File file) {
1691b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (!file.isFile()) {
1701b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
1711b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1721b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        InputStream is = null;
1731b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        try {
1741b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            is = new BufferedInputStream(new FileInputStream(file));
1751b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return (X509Certificate) CERT_FACTORY.generateCertificate(is);
1761b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        } catch (IOException e) {
1771b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
1781b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        } catch (CertificateException e) {
1791b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // reading a cert while its being installed can lead to this.
1801b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // just pretend like its not available yet.
1811b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
1821b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        } finally {
1831b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            IoUtils.closeQuietly(is);
1841b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
1851b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
1861b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
1871b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private void writeCertificate(File file, X509Certificate cert)
1881b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            throws IOException, CertificateException {
1891b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File dir = file.getParentFile();
1901b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        dir.mkdirs();
1911b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        dir.setReadable(true, false);
1921b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        dir.setExecutable(true, false);
1931b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        OutputStream os = null;
1941b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        try {
1951b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            os = new FileOutputStream(file);
1961b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            os.write(cert.getEncoded());
1971b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        } finally {
198eda571883445b108e7f9e7337e2d80f1d8329fc8Brian Carlstrom            IoUtils.closeQuietly(os);
1991b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2001b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        file.setReadable(true, false);
2011b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
2021b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
2031b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private boolean isDeletedSystemCertificate(X509Certificate x) {
2041b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return getCertificateFile(deletedDir, x).exists();
2051b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
2061b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
2071b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public Date getCreationDate(String alias) {
2081b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        // containsAlias check ensures the later fileForAlias result
2091b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        // was not a deleted system cert.
2101b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (!containsAlias(alias)) {
2111b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
2121b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2131b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File file = fileForAlias(alias);
2141b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (file == null) {
2151b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
2161b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2171b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        long time = file.lastModified();
2181b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (time == 0) {
2191b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
2201b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2211b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return new Date(time);
2221b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
2231b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
2241b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public Set<String> aliases() {
2251b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        Set<String> result = new HashSet<String>();
2261b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        addAliases(result, PREFIX_USER, addedDir);
2271b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        addAliases(result, PREFIX_SYSTEM, systemDir);
2281b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return result;
2291b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
2301b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
2315f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    public Set<String> userAliases() {
2325f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        Set<String> result = new HashSet<String>();
2335f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        addAliases(result, PREFIX_USER, addedDir);
2345f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        return result;
2355f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    }
2365f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom
2371b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private void addAliases(Set<String> result, String prefix, File dir) {
2381b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        String[] files = dir.list();
2391b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (files == null) {
2401b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
2411b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2421b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        for (String filename : files) {
2431b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            String alias = prefix + filename;
2441b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (containsAlias(alias)) {
2451b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                result.add(alias);
2461b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
2471b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2481b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
2491b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
2505f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    public Set<String> allSystemAliases() {
2515f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        Set<String> result = new HashSet<String>();
2525f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        String[] files = systemDir.list();
2535f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        if (files == null) {
2545f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom            return result;
2555f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        }
2565f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        for (String filename : files) {
2575f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom            String alias = PREFIX_SYSTEM + filename;
2585f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom            if (containsAlias(alias, true)) {
2595f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom                result.add(alias);
2605f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom            }
2615f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        }
2625f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        return result;
2635f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    }
2645f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom
2651b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public boolean containsAlias(String alias) {
2665f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        return containsAlias(alias, false);
2675f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    }
2685f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom
2695f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom    private boolean containsAlias(String alias, boolean includeDeletedSystem) {
2705f7beb162c46a281b272d11fec6fe23b8e0796c3Brian Carlstrom        return getCertificate(alias, includeDeletedSystem) != null;
2711b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
2721b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
2731b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public String getCertificateAlias(Certificate c) {
2741b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (c == null || !(c instanceof X509Certificate)) {
2751b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
2761b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2771b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        X509Certificate x = (X509Certificate) c;
2781b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File user = getCertificateFile(addedDir, x);
2791b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (user.exists()) {
2801b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return PREFIX_USER + user.getName();
2811b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2821b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (isDeletedSystemCertificate(x)) {
2831b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return null;
2841b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2851b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File system = getCertificateFile(systemDir, x);
2861b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (system.exists()) {
2871b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return PREFIX_SYSTEM + system.getName();
2881b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
2891b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return null;
2901b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
2911b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
2921b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    /**
2935c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra     * Returns true to indicate that the certificate was added by the
2945c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra     * user, false otherwise.
2955c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra     */
2965c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra    public boolean isUserAddedCertificate(X509Certificate cert) {
2975c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra        return getCertificateFile(addedDir, cert).exists();
2985c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra    }
2995c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra
3005c9add3e84fd426fafbec289738f1f09c49aaf90Geremy Condra    /**
3011b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * Returns a File for where the certificate is found if it exists
3021b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * or where it should be installed if it does not exist. The
3031b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * caller can disambiguate these cases by calling {@code
3041b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * File.exists()} on the result.
3051b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     */
3061b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private File getCertificateFile(File dir, final X509Certificate x) {
3071b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        // compare X509Certificate.getEncoded values
3081b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        CertSelector selector = new CertSelector() {
3091b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            @Override public boolean match(X509Certificate cert) {
3101b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                return cert.equals(x);
3111b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
3121b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        };
3131b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return findCert(dir, x.getSubjectX500Principal(), selector, File.class);
3141b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
3151b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
3161b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    /**
3171b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * This non-{@code KeyStoreSpi} public interface is used by {@code
3181b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * TrustManagerImpl} to locate a CA certificate with the same name
3191b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * and public key as the provided {@code X509Certificate}. We
3201b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * match on the name and public key and not the entire certificate
3211b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * since a CA may be reissued with the same name and PublicKey but
3221b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * with other differences (for example when switching signature
3231b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * from md2WithRSAEncryption to SHA1withRSA)
3241b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     */
3251b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public boolean isTrustAnchor(final X509Certificate c) {
3261b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        // compare X509Certificate.getPublicKey values
3271b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        CertSelector selector = new CertSelector() {
3281b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            @Override public boolean match(X509Certificate ca) {
3291b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                return ca.getPublicKey().equals(c.getPublicKey());
3301b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
3311b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        };
3321b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        boolean user = findCert(addedDir,
3331b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                                c.getSubjectX500Principal(),
3341b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                                selector,
3351b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                                Boolean.class);
3361b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (user) {
3371b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return true;
3381b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
3391b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        X509Certificate system = findCert(systemDir,
3401b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                                          c.getSubjectX500Principal(),
3411b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                                          selector,
3421b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                                          X509Certificate.class);
3431b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return system != null && !isDeletedSystemCertificate(system);
3441b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
3451b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
3461b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    /**
3471b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * This non-{@code KeyStoreSpi} public interface is used by {@code
3481b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * TrustManagerImpl} to locate the CA certificate that signed the
3491b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * provided {@code X509Certificate}.
3501b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     */
3511b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public X509Certificate findIssuer(final X509Certificate c) {
3521b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        // match on verified issuer of Certificate
3531b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        CertSelector selector = new CertSelector() {
3541b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            @Override public boolean match(X509Certificate ca) {
3551b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                try {
3561b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    c.verify(ca.getPublicKey());
3571b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    return true;
3581b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                } catch (Exception e) {
3591b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    return false;
3601b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                }
3611b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
3621b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        };
363bd7005d38883b9917b6452bbbadbda14fd141dadBrian Carlstrom        X500Principal issuer = c.getIssuerX500Principal();
364bd7005d38883b9917b6452bbbadbda14fd141dadBrian Carlstrom        X509Certificate user = findCert(addedDir, issuer, selector, X509Certificate.class);
3651b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (user != null) {
3661b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return user;
3671b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
368bd7005d38883b9917b6452bbbadbda14fd141dadBrian Carlstrom        X509Certificate system = findCert(systemDir, issuer, selector, X509Certificate.class);
3691b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (system != null && !isDeletedSystemCertificate(system)) {
3701b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return system;
3711b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
3721b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return null;
3731b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
3741b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
3756a71da569514b7b3dc8041ea06daf21313826b80Kenny Root    private static boolean isSelfIssuedCertificate(OpenSSLX509Certificate cert) {
3766a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        final long ctx = cert.getContext();
3776a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        return NativeCrypto.X509_check_issued(ctx, ctx) == 0;
3783fb088d79e446063ef743362a030e1cfb80b2178Kenny Root    }
3793fb088d79e446063ef743362a030e1cfb80b2178Kenny Root
3806a71da569514b7b3dc8041ea06daf21313826b80Kenny Root    /**
3816a71da569514b7b3dc8041ea06daf21313826b80Kenny Root     * Converts the {@code cert} to the internal OpenSSL X.509 format so we can
3826a71da569514b7b3dc8041ea06daf21313826b80Kenny Root     * run {@link NativeCrypto} methods on it.
3836a71da569514b7b3dc8041ea06daf21313826b80Kenny Root     */
3846a71da569514b7b3dc8041ea06daf21313826b80Kenny Root    private static OpenSSLX509Certificate convertToOpenSSLIfNeeded(X509Certificate cert)
3856a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            throws CertificateException {
3866a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        if (cert == null) {
3873fb088d79e446063ef743362a030e1cfb80b2178Kenny Root            return null;
3883fb088d79e446063ef743362a030e1cfb80b2178Kenny Root        }
3893fb088d79e446063ef743362a030e1cfb80b2178Kenny Root
3906a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        if (cert instanceof OpenSSLX509Certificate) {
3916a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            return (OpenSSLX509Certificate) cert;
3923fb088d79e446063ef743362a030e1cfb80b2178Kenny Root        }
3933fb088d79e446063ef743362a030e1cfb80b2178Kenny Root
3946a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        try {
3956a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            return OpenSSLX509Certificate.fromX509Der(cert.getEncoded());
3966a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        } catch (Exception e) {
3976a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            throw new CertificateException(e);
3983fb088d79e446063ef743362a030e1cfb80b2178Kenny Root        }
3993fb088d79e446063ef743362a030e1cfb80b2178Kenny Root    }
4003fb088d79e446063ef743362a030e1cfb80b2178Kenny Root
4013fb088d79e446063ef743362a030e1cfb80b2178Kenny Root    /**
4023fb088d79e446063ef743362a030e1cfb80b2178Kenny Root     * Attempt to build a certificate chain from the supplied {@code leaf}
4033fb088d79e446063ef743362a030e1cfb80b2178Kenny Root     * argument through the chain of issuers as high up as known. If the chain
4043fb088d79e446063ef743362a030e1cfb80b2178Kenny Root     * can't be completed, the most complete chain available will be returned.
4053fb088d79e446063ef743362a030e1cfb80b2178Kenny Root     * This means that a list with only the {@code leaf} certificate is returned
4063fb088d79e446063ef743362a030e1cfb80b2178Kenny Root     * if no issuer certificates could be found.
4076a71da569514b7b3dc8041ea06daf21313826b80Kenny Root     *
4086a71da569514b7b3dc8041ea06daf21313826b80Kenny Root     * @throws CertificateException if there was a problem parsing the
4096a71da569514b7b3dc8041ea06daf21313826b80Kenny Root     *             certificates
4103fb088d79e446063ef743362a030e1cfb80b2178Kenny Root     */
4116a71da569514b7b3dc8041ea06daf21313826b80Kenny Root    public List<X509Certificate> getCertificateChain(X509Certificate leaf)
4126a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            throws CertificateException {
4136a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        final List<OpenSSLX509Certificate> chain = new ArrayList<OpenSSLX509Certificate>();
4146a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        chain.add(convertToOpenSSLIfNeeded(leaf));
4153fb088d79e446063ef743362a030e1cfb80b2178Kenny Root
4163fb088d79e446063ef743362a030e1cfb80b2178Kenny Root        for (int i = 0; true; i++) {
4176a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            OpenSSLX509Certificate cert = chain.get(i);
4186a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            if (isSelfIssuedCertificate(cert)) {
4193fb088d79e446063ef743362a030e1cfb80b2178Kenny Root                break;
4203fb088d79e446063ef743362a030e1cfb80b2178Kenny Root            }
4216a71da569514b7b3dc8041ea06daf21313826b80Kenny Root            OpenSSLX509Certificate issuer = convertToOpenSSLIfNeeded(findIssuer(cert));
4223fb088d79e446063ef743362a030e1cfb80b2178Kenny Root            if (issuer == null) {
4233fb088d79e446063ef743362a030e1cfb80b2178Kenny Root                break;
4243fb088d79e446063ef743362a030e1cfb80b2178Kenny Root            }
4253fb088d79e446063ef743362a030e1cfb80b2178Kenny Root            chain.add(issuer);
4263fb088d79e446063ef743362a030e1cfb80b2178Kenny Root        }
4273fb088d79e446063ef743362a030e1cfb80b2178Kenny Root
4286a71da569514b7b3dc8041ea06daf21313826b80Kenny Root        return new ArrayList<X509Certificate>(chain);
4293fb088d79e446063ef743362a030e1cfb80b2178Kenny Root    }
4303fb088d79e446063ef743362a030e1cfb80b2178Kenny Root
4311b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    // like java.security.cert.CertSelector but with X509Certificate and without cloning
4321b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private static interface CertSelector {
4331b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        public boolean match(X509Certificate cert);
4341b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
4351b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
4361b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private <T> T findCert(
4371b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            File dir, X500Principal subject, CertSelector selector, Class<T> desiredReturnType) {
4381b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
4391b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        String hash = hash(subject);
4401b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        for (int index = 0; true; index++) {
4411b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            File file = file(dir, hash, index);
4421b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (!file.isFile()) {
4431b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                // could not find a match, no file exists, bail
4441b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                if (desiredReturnType == Boolean.class) {
4451b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    return (T) Boolean.FALSE;
4461b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                }
4471b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                if (desiredReturnType == File.class) {
4481b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    // we return file so that caller that wants to
4491b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    // write knows what the next available has
4501b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    // location is
4511b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    return (T) file;
4521b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                }
4531b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                return null;
4541b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
4551b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (isTombstone(file)) {
4561b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                continue;
4571b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
4581b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            X509Certificate cert = readCertificate(file);
4591b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (cert == null) {
4601b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                // skip problem certificates
4611b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                continue;
4621b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
4631b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (selector.match(cert)) {
4641b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                if (desiredReturnType == X509Certificate.class) {
4651b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    return (T) cert;
4661b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                }
4671b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                if (desiredReturnType == Boolean.class) {
4681b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    return (T) Boolean.TRUE;
4691b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                }
4701b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                if (desiredReturnType == File.class) {
4711b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    return (T) file;
4721b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                }
4731b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                throw new AssertionError();
4741b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
4751b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
4761b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
4771b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
4781b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private String hash(X500Principal name) {
4791b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        int hash = NativeCrypto.X509_NAME_hash_old(name);
4801b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return IntegralToString.intToHexString(hash, false, 8);
4811b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
4821b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
4831b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private File file(File dir, String hash, int index) {
4841b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        return new File(dir, hash + '.' + index);
4851b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
4861b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
4871b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    /**
4881b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * This non-{@code KeyStoreSpi} public interface is used by the
4891b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * {@code KeyChainService} to install new CA certificates. It
4901b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * silently ignores the certificate if it already exists in the
4911b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * store.
4921b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     */
4931b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public void installCertificate(X509Certificate cert) throws IOException, CertificateException {
4941b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (cert == null) {
4951b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            throw new NullPointerException("cert == null");
4961b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
4971b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File system = getCertificateFile(systemDir, cert);
4981b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (system.exists()) {
4991b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            File deleted = getCertificateFile(deletedDir, cert);
5001b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (deleted.exists()) {
5011b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                // we have a system cert that was marked deleted.
5021b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                // remove the deleted marker to expose the original
5031b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                if (!deleted.delete()) {
5041b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                    throw new IOException("Could not remove " + deleted);
5051b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                }
5061b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                return;
5071b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
5081b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // otherwise we just have a dup of an existing system cert.
5091b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // return taking no further action.
5101b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
5111b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5121b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File user = getCertificateFile(addedDir, cert);
5131b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (user.exists()) {
5141b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // we have an already installed user cert, bail.
5151b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
5161b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5171b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        // install the user cert
5181b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        writeCertificate(user, cert);
5191b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
5201b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
5211b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    /**
5221b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     * This could be considered the implementation of {@code
523c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom     * TrustedCertificateKeyStoreSpi.engineDeleteEntry} but we
524c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom     * consider {@code TrustedCertificateKeyStoreSpi} to be read
525c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom     * only. Instead, this is used by the {@code KeyChainService} to
526c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom     * delete CA certificates.
5271b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom     */
5281b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    public void deleteCertificateEntry(String alias) throws IOException, CertificateException {
5291b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (alias == null) {
5301b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
5311b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5321b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        File file = fileForAlias(alias);
5331b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (file == null) {
5341b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
5351b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5361b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (isSystem(alias)) {
5371b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            X509Certificate cert = readCertificate(file);
5381b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (cert == null) {
5391b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                // skip problem certificates
5401b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                return;
5411b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
5421b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            File deleted = getCertificateFile(deletedDir, cert);
5431b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (deleted.exists()) {
5441b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                // already deleted system certificate
5451b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                return;
5461b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
5471b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // write copy of system cert to marked as deleted
5481b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            writeCertificate(deleted, cert);
5491b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
5501b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5511b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (isUser(alias)) {
5521b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // truncate the file to make a tombstone by opening and closing.
5531b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            // we need ensure that we don't leave a gap before a valid cert.
5541b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            new FileOutputStream(file).close();
5551b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            removeUnnecessaryTombstones(alias);
5561b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
5571b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5581b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        // non-existant user cert, nothing to delete
5591b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
5601b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
5611b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    private void removeUnnecessaryTombstones(String alias) throws IOException {
5621b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (!isUser(alias)) {
5631b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            throw new AssertionError(alias);
5641b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5651b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        int dotIndex = alias.lastIndexOf('.');
5661b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (dotIndex == -1) {
5671b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            throw new AssertionError(alias);
5681b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5691b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
5701b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        String hash = alias.substring(PREFIX_USER.length(), dotIndex);
5711b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        int lastTombstoneIndex = Integer.parseInt(alias.substring(dotIndex + 1));
5721b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
5731b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        if (file(addedDir, hash, lastTombstoneIndex + 1).exists()) {
5741b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            return;
5751b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5761b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        while (lastTombstoneIndex >= 0) {
5771b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            File file = file(addedDir, hash, lastTombstoneIndex);
5781b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (!isTombstone(file)) {
5791b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                break;
5801b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
5811b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            if (!file.delete()) {
5821b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom                throw new IOException("Could not remove " + file);
5831b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            }
5841b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom            lastTombstoneIndex--;
5851b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
5861b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
5871b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom}
588