1d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker/* 2d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * Copyright (C) 2015 The Android Open Source Project 3d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * 4d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * Licensed under the Apache License, Version 2.0 (the "License"); 5d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * you may not use this file except in compliance with the License. 6d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * You may obtain a copy of the License at 7d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * 8d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * http://www.apache.org/licenses/LICENSE-2.0 9d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * 10d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * Unless required by applicable law or agreed to in writing, software 11d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * distributed under the License is distributed on an "AS IS" BASIS, 12d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * See the License for the specific language governing permissions and 14d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * limitations under the License. 15d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker */ 16d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 17d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerpackage android.security.net.config; 18d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 19d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport android.os.Environment; 20d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport android.os.UserHandle; 21d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport android.util.ArraySet; 22d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport android.util.Pair; 23d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.BufferedInputStream; 24d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.File; 25d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.FileInputStream; 26d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.InputStream; 27d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.IOException; 28d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.Certificate; 29d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.CertificateException; 30d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.CertificateFactory; 31d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.X509Certificate; 32aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubakerimport java.util.Collections; 33d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.util.Set; 34d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport libcore.io.IoUtils; 35d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 367845e44c0cff3ec6a9ea0e5059fa1f7019ef0eb7Chad Brubakerimport com.android.org.conscrypt.Hex; 37d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport com.android.org.conscrypt.NativeCrypto; 38d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 39d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport javax.security.auth.x500.X500Principal; 40d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 41d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker/** 42d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * {@link CertificateSource} based on a directory where certificates are stored as individual files 43d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * named after a hash of their SubjectName for more efficient lookups. 44d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * @hide 45d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker */ 46d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerabstract class DirectoryCertificateSource implements CertificateSource { 47d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private final File mDir; 48d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private final Object mLock = new Object(); 49d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private final CertificateFactory mCertFactory; 50d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 51d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private Set<X509Certificate> mCertificates; 52d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 53d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker protected DirectoryCertificateSource(File caDir) { 54d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker mDir = caDir; 55d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker try { 56d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker mCertFactory = CertificateFactory.getInstance("X.509"); 57d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } catch (CertificateException e) { 58d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); 59d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 60d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 61d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 62d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker protected abstract boolean isCertMarkedAsRemoved(String caFile); 63d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 64d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker @Override 65d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker public Set<X509Certificate> getCertificates() { 66d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker // TODO: loading all of these is wasteful, we should instead use a keystore style API. 67d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker synchronized (mLock) { 68d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (mCertificates != null) { 69d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return mCertificates; 70d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 71d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 72d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker Set<X509Certificate> certs = new ArraySet<X509Certificate>(); 73d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (mDir.isDirectory()) { 74d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker for (String caFile : mDir.list()) { 75d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (isCertMarkedAsRemoved(caFile)) { 76d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker continue; 77d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 78d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker X509Certificate cert = readCertificate(caFile); 79d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (cert != null) { 80d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker certs.add(cert); 81d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 82d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 83d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 84d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker mCertificates = certs; 85d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return mCertificates; 86d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 87d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 88d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 89d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker @Override 90d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker public X509Certificate findBySubjectAndPublicKey(final X509Certificate cert) { 91d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return findCert(cert.getSubjectX500Principal(), new CertSelector() { 92d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker @Override 93d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker public boolean match(X509Certificate ca) { 94d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return ca.getPublicKey().equals(cert.getPublicKey()); 95d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 96d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker }); 97d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 98d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 99fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker @Override 100fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker public X509Certificate findByIssuerAndSignature(final X509Certificate cert) { 101fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker return findCert(cert.getIssuerX500Principal(), new CertSelector() { 102fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker @Override 103fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker public boolean match(X509Certificate ca) { 104fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker try { 105fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker cert.verify(ca.getPublicKey()); 106fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker return true; 107fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } catch (Exception e) { 108fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker return false; 109fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } 110fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } 111fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker }); 112fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } 113fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker 114aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker @Override 115aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker public Set<X509Certificate> findAllByIssuerAndSignature(final X509Certificate cert) { 116aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return findCerts(cert.getIssuerX500Principal(), new CertSelector() { 117aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker @Override 118aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker public boolean match(X509Certificate ca) { 119aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker try { 120aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker cert.verify(ca.getPublicKey()); 121aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return true; 122aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } catch (Exception e) { 123aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return false; 124aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 125aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 126aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker }); 127aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 128aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker 129bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker @Override 130bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker public void handleTrustStorageUpdate() { 131bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker synchronized (mLock) { 132bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker mCertificates = null; 133bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker } 134bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker } 135bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker 136d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private static interface CertSelector { 137d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker boolean match(X509Certificate cert); 138d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 139d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 140aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker private Set<X509Certificate> findCerts(X500Principal subj, CertSelector selector) { 141aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker String hash = getHash(subj); 142aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker Set<X509Certificate> certs = null; 143aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker for (int index = 0; index >= 0; index++) { 144aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker String fileName = hash + "." + index; 145aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (!new File(mDir, fileName).exists()) { 146aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker break; 147aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 148aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (isCertMarkedAsRemoved(fileName)) { 149aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker continue; 150aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 151aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker X509Certificate cert = readCertificate(fileName); 152aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (!subj.equals(cert.getSubjectX500Principal())) { 153aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker continue; 154aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 155aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (selector.match(cert)) { 156aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (certs == null) { 157aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker certs = new ArraySet<X509Certificate>(); 158aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 159aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker certs.add(cert); 160aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 161aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 162aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return certs != null ? certs : Collections.<X509Certificate>emptySet(); 163aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 164aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker 165d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private X509Certificate findCert(X500Principal subj, CertSelector selector) { 166d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker String hash = getHash(subj); 167d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker for (int index = 0; index >= 0; index++) { 168d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker String fileName = hash + "." + index; 169d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (!new File(mDir, fileName).exists()) { 170d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker break; 171d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 172d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (isCertMarkedAsRemoved(fileName)) { 173d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker continue; 174d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 175d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker X509Certificate cert = readCertificate(fileName); 176d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (!subj.equals(cert.getSubjectX500Principal())) { 177d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker continue; 178d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 179d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (selector.match(cert)) { 180d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return cert; 181d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 182d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 183d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return null; 184d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 185d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 186d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private String getHash(X500Principal name) { 187d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker int hash = NativeCrypto.X509_NAME_hash_old(name); 1887845e44c0cff3ec6a9ea0e5059fa1f7019ef0eb7Chad Brubaker return Hex.intToHexString(hash, 8); 189d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 190d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 191d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private X509Certificate readCertificate(String file) { 192d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker InputStream is = null; 193d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker try { 194d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker is = new BufferedInputStream(new FileInputStream(new File(mDir, file))); 195d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return (X509Certificate) mCertFactory.generateCertificate(is); 196d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } catch (CertificateException | IOException e) { 197d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return null; 198d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } finally { 199d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker IoUtils.closeQuietly(is); 200d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 201d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 202d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker} 203