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