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