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; 2201e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubakerimport android.util.Log; 23d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport android.util.Pair; 24d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.BufferedInputStream; 25d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.File; 26d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.FileInputStream; 27d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.InputStream; 28d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.io.IOException; 29d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.Certificate; 30d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.CertificateException; 31d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.CertificateFactory; 32d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.security.cert.X509Certificate; 33aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubakerimport java.util.Collections; 34d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport java.util.Set; 35d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport libcore.io.IoUtils; 36d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 377845e44c0cff3ec6a9ea0e5059fa1f7019ef0eb7Chad Brubakerimport com.android.org.conscrypt.Hex; 38d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport com.android.org.conscrypt.NativeCrypto; 39d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 40d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerimport javax.security.auth.x500.X500Principal; 41d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 42d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker/** 43d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * {@link CertificateSource} based on a directory where certificates are stored as individual files 44d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * named after a hash of their SubjectName for more efficient lookups. 45d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker * @hide 46d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker */ 47d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubakerabstract class DirectoryCertificateSource implements CertificateSource { 4801e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker private static final String LOG_TAG = "DirectoryCertificateSrc"; 49d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private final File mDir; 50d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private final Object mLock = new Object(); 51d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private final CertificateFactory mCertFactory; 52d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 53d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private Set<X509Certificate> mCertificates; 54d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 55d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker protected DirectoryCertificateSource(File caDir) { 56d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker mDir = caDir; 57d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker try { 58d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker mCertFactory = CertificateFactory.getInstance("X.509"); 59d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } catch (CertificateException e) { 60d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); 61d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 62d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 63d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 64d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker protected abstract boolean isCertMarkedAsRemoved(String caFile); 65d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 66d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker @Override 67d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker public Set<X509Certificate> getCertificates() { 68d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker // TODO: loading all of these is wasteful, we should instead use a keystore style API. 69d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker synchronized (mLock) { 70d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (mCertificates != null) { 71d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return mCertificates; 72d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 73d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 74d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker Set<X509Certificate> certs = new ArraySet<X509Certificate>(); 75d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (mDir.isDirectory()) { 76d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker for (String caFile : mDir.list()) { 77d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (isCertMarkedAsRemoved(caFile)) { 78d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker continue; 79d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 80d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker X509Certificate cert = readCertificate(caFile); 81d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (cert != null) { 82d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker certs.add(cert); 83d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 84d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 85d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 86d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker mCertificates = certs; 87d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return mCertificates; 88d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 89d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 90d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 91d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker @Override 92d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker public X509Certificate findBySubjectAndPublicKey(final X509Certificate cert) { 93d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return findCert(cert.getSubjectX500Principal(), new CertSelector() { 94d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker @Override 95d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker public boolean match(X509Certificate ca) { 96d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return ca.getPublicKey().equals(cert.getPublicKey()); 97d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 98d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker }); 99d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 100d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 101fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker @Override 102fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker public X509Certificate findByIssuerAndSignature(final X509Certificate cert) { 103fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker return findCert(cert.getIssuerX500Principal(), new CertSelector() { 104fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker @Override 105fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker public boolean match(X509Certificate ca) { 106fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker try { 107fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker cert.verify(ca.getPublicKey()); 108fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker return true; 109fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } catch (Exception e) { 110fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker return false; 111fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } 112fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } 113fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker }); 114fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker } 115fa9beebb83abe38fa04c14dc628bc5c1b4b068cdChad Brubaker 116aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker @Override 117aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker public Set<X509Certificate> findAllByIssuerAndSignature(final X509Certificate cert) { 118aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return findCerts(cert.getIssuerX500Principal(), new CertSelector() { 119aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker @Override 120aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker public boolean match(X509Certificate ca) { 121aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker try { 122aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker cert.verify(ca.getPublicKey()); 123aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return true; 124aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } catch (Exception e) { 125aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return false; 126aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 127aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 128aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker }); 129aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 130aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker 131bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker @Override 132bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker public void handleTrustStorageUpdate() { 133bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker synchronized (mLock) { 134bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker mCertificates = null; 135bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker } 136bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker } 137bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker 138d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private static interface CertSelector { 139d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker boolean match(X509Certificate cert); 140d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 141d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 142aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker private Set<X509Certificate> findCerts(X500Principal subj, CertSelector selector) { 143aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker String hash = getHash(subj); 144aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker Set<X509Certificate> certs = null; 145aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker for (int index = 0; index >= 0; index++) { 146aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker String fileName = hash + "." + index; 147aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (!new File(mDir, fileName).exists()) { 148aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker break; 149aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 150aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (isCertMarkedAsRemoved(fileName)) { 151aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker continue; 152aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 153aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker X509Certificate cert = readCertificate(fileName); 15401e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker if (cert == null) { 15501e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker continue; 15601e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker } 157aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (!subj.equals(cert.getSubjectX500Principal())) { 158aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker continue; 159aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 160aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (selector.match(cert)) { 161aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker if (certs == null) { 162aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker certs = new ArraySet<X509Certificate>(); 163aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 164aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker certs.add(cert); 165aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 166aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 167aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker return certs != null ? certs : Collections.<X509Certificate>emptySet(); 168aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker } 169aa6c3c3e252252b80c3900bd4c1ff27d37265c6dChad Brubaker 170d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private X509Certificate findCert(X500Principal subj, CertSelector selector) { 171d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker String hash = getHash(subj); 172d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker for (int index = 0; index >= 0; index++) { 173d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker String fileName = hash + "." + index; 174d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (!new File(mDir, fileName).exists()) { 175d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker break; 176d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 177d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (isCertMarkedAsRemoved(fileName)) { 178d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker continue; 179d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 180d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker X509Certificate cert = readCertificate(fileName); 18101e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker if (cert == null) { 18201e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker continue; 18301e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker } 184d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (!subj.equals(cert.getSubjectX500Principal())) { 185d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker continue; 186d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 187d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker if (selector.match(cert)) { 188d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return cert; 189d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 190d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 191d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return null; 192d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 193d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 194d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private String getHash(X500Principal name) { 195d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker int hash = NativeCrypto.X509_NAME_hash_old(name); 1967845e44c0cff3ec6a9ea0e5059fa1f7019ef0eb7Chad Brubaker return Hex.intToHexString(hash, 8); 197d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 198d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker 199d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker private X509Certificate readCertificate(String file) { 200d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker InputStream is = null; 201d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker try { 202d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker is = new BufferedInputStream(new FileInputStream(new File(mDir, file))); 203d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return (X509Certificate) mCertFactory.generateCertificate(is); 204d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } catch (CertificateException | IOException e) { 20501e9682cab6a554e5e83bb98db7d81691e0a2ca7Chad Brubaker Log.e(LOG_TAG, "Failed to read certificate from " + file, e); 206d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker return null; 207d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } finally { 208d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker IoUtils.closeQuietly(is); 209d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 210d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker } 211d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker} 212