16bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker/*
26bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * Copyright (C) 2015 The Android Open Source Project
36bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker *
46bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * Licensed under the Apache License, Version 2.0 (the "License");
56bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * you may not use this file except in compliance with the License.
66bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * You may obtain a copy of the License at
76bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker *
86bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker *      http://www.apache.org/licenses/LICENSE-2.0
96bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker *
106bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * Unless required by applicable law or agreed to in writing, software
116bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * distributed under the License is distributed on an "AS IS" BASIS,
126bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * See the License for the specific language governing permissions and
146bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * limitations under the License.
156bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker */
166bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
176bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerpackage android.security.net.config;
186bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
196bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport com.android.org.conscrypt.TrustManagerImpl;
206bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
216bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport android.util.ArrayMap;
226bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.io.IOException;
230f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubakerimport java.net.Socket;
246bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.security.cert.CertificateException;
256bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.security.cert.X509Certificate;
266bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.security.GeneralSecurityException;
276bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.security.KeyStore;
286bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.security.MessageDigest;
296bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.util.List;
306bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.util.Map;
316bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubakerimport java.util.Set;
326bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
330f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubakerimport javax.net.ssl.SSLEngine;
340f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubakerimport javax.net.ssl.X509ExtendedTrustManager;
356bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
366bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker/**
370f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker * {@link X509ExtendedTrustManager} that implements the trust anchor and pinning for a
386bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * given {@link NetworkSecurityConfig}.
396bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker * @hide
406bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker */
410f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubakerpublic class NetworkSecurityTrustManager extends X509ExtendedTrustManager {
426bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    // TODO: Replace this with a general X509TrustManager and use duck-typing.
436bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    private final TrustManagerImpl mDelegate;
446bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    private final NetworkSecurityConfig mNetworkSecurityConfig;
45efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker    private final Object mIssuersLock = new Object();
46efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker
47efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker    private X509Certificate[] mIssuers;
486bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
496bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    public NetworkSecurityTrustManager(NetworkSecurityConfig config) {
506bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        if (config == null) {
516bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            throw new NullPointerException("config must not be null");
526bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        }
536bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        mNetworkSecurityConfig = config;
546bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        try {
55725fefb38a4cb0ab89de439f8131d6c46ccd8b17Chad Brubaker            TrustedCertificateStoreAdapter certStore = new TrustedCertificateStoreAdapter(config);
56725fefb38a4cb0ab89de439f8131d6c46ccd8b17Chad Brubaker            // Provide an empty KeyStore since TrustManagerImpl doesn't support null KeyStores.
57725fefb38a4cb0ab89de439f8131d6c46ccd8b17Chad Brubaker            // TrustManagerImpl will use certStore to lookup certificates.
586bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
596bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            store.load(null);
60725fefb38a4cb0ab89de439f8131d6c46ccd8b17Chad Brubaker            mDelegate = new TrustManagerImpl(store, null, certStore);
616bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        } catch (GeneralSecurityException | IOException e) {
626bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            throw new RuntimeException(e);
636bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        }
646bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    }
656bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
666bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    @Override
676bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    public void checkClientTrusted(X509Certificate[] chain, String authType)
686bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            throws CertificateException {
69bdd13f02bcb6d8620b76eec18bfcce9250939734Chad Brubaker        mDelegate.checkClientTrusted(chain, authType);
706bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    }
716bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
726bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    @Override
730f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    public void checkClientTrusted(X509Certificate[] certs, String authType, Socket socket)
740f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker            throws CertificateException {
750f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker        mDelegate.checkClientTrusted(certs, authType, socket);
760f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    }
770f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker
780f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    @Override
790f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    public void checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
800f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker            throws CertificateException {
810f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker        mDelegate.checkClientTrusted(certs, authType, engine);
820f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    }
830f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker
840f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    @Override
856bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    public void checkServerTrusted(X509Certificate[] certs, String authType)
866bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            throws CertificateException {
870f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker        checkServerTrusted(certs, authType, (String) null);
880f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    }
890f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker
900f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    @Override
910f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    public void checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)
920f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker            throws CertificateException {
930f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker        List<X509Certificate> trustedChain =
940f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker                mDelegate.getTrustedChainForServer(certs, authType, socket);
950f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker        checkPins(trustedChain);
960f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    }
970f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker
980f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    @Override
990f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker    public void checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
1000f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker            throws CertificateException {
1010f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker        List<X509Certificate> trustedChain =
1020f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker                mDelegate.getTrustedChainForServer(certs, authType, engine);
1030f5d386a769cafa3f4faf93f17acb22503b6fd3eChad Brubaker        checkPins(trustedChain);
10493962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker    }
10593962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker
10693962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker    /**
10793962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker     * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
10893962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker     * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
10993962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker     * modify without modifying those callers.
11093962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker     */
11193962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker    public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
11293962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker            String host) throws CertificateException {
11393962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker        List<X509Certificate> trustedChain = mDelegate.checkServerTrusted(certs, authType, host);
1146bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        checkPins(trustedChain);
11593962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker        return trustedChain;
11693962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker    }
11793962c2f0118bcda78ffde7e0b6be73da7ab2500Chad Brubaker
1186bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    private void checkPins(List<X509Certificate> chain) throws CertificateException {
1196bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        PinSet pinSet = mNetworkSecurityConfig.getPins();
1206bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        if (pinSet.pins.isEmpty()
1216bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                || System.currentTimeMillis() > pinSet.expirationTime
1226bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                || !isPinningEnforced(chain)) {
1236bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            return;
1246bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        }
1256bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        Set<String> pinAlgorithms = pinSet.getPinAlgorithms();
1266bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        Map<String, MessageDigest> digestMap = new ArrayMap<String, MessageDigest>(
1276bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                pinAlgorithms.size());
1286bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        for (int i = chain.size() - 1; i >= 0 ; i--) {
1296bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            X509Certificate cert = chain.get(i);
1306bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            byte[] encodedSPKI = cert.getPublicKey().getEncoded();
1316bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            for (String algorithm : pinAlgorithms) {
1326bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                MessageDigest md = digestMap.get(algorithm);
1336bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                if (md == null) {
1346bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                    try {
1356bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                        md = MessageDigest.getInstance(algorithm);
1366bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                    } catch (GeneralSecurityException e) {
1376bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                        throw new RuntimeException(e);
1386bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                    }
1396bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                    digestMap.put(algorithm, md);
1406bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                }
1416bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) {
1426bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                    return;
1436bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker                }
1446bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            }
1456bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        }
1466bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
1476bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        // TODO: Throw a subclass of CertificateException which indicates a pinning failure.
1486bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        throw new CertificateException("Pin verification failed");
1496bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    }
1506bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
1516bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    private boolean isPinningEnforced(List<X509Certificate> chain) throws CertificateException {
1526bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        if (chain.isEmpty()) {
1536bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            return false;
1546bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        }
1556bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        X509Certificate anchorCert = chain.get(chain.size() - 1);
156d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker        TrustAnchor chainAnchor =
157d3af9620817220d737fdb532c1ae1032bdd65e11Chad Brubaker                mNetworkSecurityConfig.findTrustAnchorBySubjectAndPublicKey(anchorCert);
1586bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        if (chainAnchor == null) {
1596bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker            throw new CertificateException("Trusted chain does not end in a TrustAnchor");
1606bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        }
1616bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker        return !chainAnchor.overridesPins;
1626bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    }
1636bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker
1646bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    @Override
1656bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    public X509Certificate[] getAcceptedIssuers() {
166efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker        // TrustManagerImpl only looks at the provided KeyStore and not the TrustedCertificateStore
167efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker        // for getAcceptedIssuers, so implement it here instead of delegating.
168efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker        synchronized (mIssuersLock) {
169efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker            if (mIssuers == null) {
170efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker                Set<TrustAnchor> anchors = mNetworkSecurityConfig.getTrustAnchors();
171efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker                X509Certificate[] issuers = new X509Certificate[anchors.size()];
172efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker                int i = 0;
173efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker                for (TrustAnchor anchor : anchors) {
174efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker                    issuers[i++] = anchor.certificate;
175efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker                }
176efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker                mIssuers = issuers;
177efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker            }
178efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker            return mIssuers.clone();
179efb4f91bdbbd274a120f100337e802e39582fd80Chad Brubaker        }
1806bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker    }
181bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker
182bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker    public void handleTrustStorageUpdate() {
183bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker        synchronized (mIssuersLock) {
184bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker            mIssuers = null;
185bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker            mDelegate.handleTrustStorageUpdate();
186bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker        }
187bf9a82a6433701aa2f02761f3a7c425ffef4fa09Chad Brubaker    }
1886bc1e3966c4890ee3d47b5e527b800f2700ed627Chad Brubaker}
189