Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 // License from Apache Harmony:
     18 /*
     19  *  Licensed to the Apache Software Foundation (ASF) under one or more
     20  *  contributor license agreements.  See the NOTICE file distributed with
     21  *  this work for additional information regarding copyright ownership.
     22  *  The ASF licenses this file to You under the Apache License, Version 2.0
     23  *  (the "License"); you may not use this file except in compliance with
     24  *  the License.  You may obtain a copy of the License at
     25  *
     26  *     http://www.apache.org/licenses/LICENSE-2.0
     27  *
     28  *  Unless required by applicable law or agreed to in writing, software
     29  *  distributed under the License is distributed on an "AS IS" BASIS,
     30  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     31  *  See the License for the specific language governing permissions and
     32  *  limitations under the License.
     33  */
     34 
     35 package org.conscrypt;
     36 
     37 import java.lang.reflect.InvocationTargetException;
     38 import java.lang.reflect.Method;
     39 import java.net.Socket;
     40 import java.security.InvalidAlgorithmParameterException;
     41 import java.security.KeyStore;
     42 import java.security.KeyStoreException;
     43 import java.security.cert.CertPath;
     44 import java.security.cert.CertPathValidator;
     45 import java.security.cert.CertPathValidatorException;
     46 import java.security.cert.Certificate;
     47 import java.security.cert.CertificateException;
     48 import java.security.cert.CertificateFactory;
     49 import java.security.cert.CertificateParsingException;
     50 import java.security.cert.PKIXCertPathChecker;
     51 import java.security.cert.PKIXParameters;
     52 import java.security.cert.PKIXRevocationChecker;
     53 import java.security.cert.PKIXRevocationChecker.Option;
     54 import java.security.cert.TrustAnchor;
     55 import java.security.cert.X509Certificate;
     56 import java.util.ArrayList;
     57 import java.util.Arrays;
     58 import java.util.Collection;
     59 import java.util.Collections;
     60 import java.util.Comparator;
     61 import java.util.Enumeration;
     62 import java.util.HashSet;
     63 import java.util.List;
     64 import java.util.Locale;
     65 import java.util.Set;
     66 import javax.net.ssl.HostnameVerifier;
     67 import javax.net.ssl.HttpsURLConnection;
     68 import javax.net.ssl.SSLEngine;
     69 import javax.net.ssl.SSLParameters;
     70 import javax.net.ssl.SSLSession;
     71 import javax.net.ssl.SSLSocket;
     72 import javax.net.ssl.X509ExtendedTrustManager;
     73 import org.conscrypt.ct.CTLogStore;
     74 import org.conscrypt.ct.CTLogStoreImpl;
     75 import org.conscrypt.ct.CTPolicy;
     76 import org.conscrypt.ct.CTPolicyImpl;
     77 import org.conscrypt.ct.CTVerificationResult;
     78 import org.conscrypt.ct.CTVerifier;
     79 
     80 /**
     81  *
     82  * TrustManager implementation. The implementation is based on CertPathValidator
     83  * PKIX and CertificateFactory X509 implementations. This implementations should
     84  * be provided by some certification provider.
     85  *
     86  * @see javax.net.ssl.X509ExtendedTrustManager
     87  */
     88 public final class TrustManagerImpl extends X509ExtendedTrustManager {
     89 
     90     /**
     91      * Comparator used for ordering trust anchors during certificate path building.
     92      */
     93     private static final TrustAnchorComparator TRUST_ANCHOR_COMPARATOR =
     94             new TrustAnchorComparator();
     95 
     96     /**
     97      * The AndroidCAStore if non-null, null otherwise.
     98      */
     99     private final KeyStore rootKeyStore;
    100 
    101     /**
    102      * The CertPinManager, which validates the chain against a host-to-pin mapping
    103      */
    104     private CertPinManager pinManager;
    105 
    106     /**
    107      * The backing store for the AndroidCAStore if non-null. This will
    108      * be null when the rootKeyStore is null, implying we are not
    109      * using the AndroidCAStore.
    110      */
    111     private final TrustedCertificateStore trustedCertificateStore;
    112 
    113     private final CertPathValidator validator;
    114 
    115     /**
    116      * An index of TrustAnchor instances that we've seen.
    117      */
    118     private final TrustedCertificateIndex trustedCertificateIndex;
    119 
    120     /**
    121      * An index of intermediate certificates that we've seen. These certificates are NOT implicitly
    122      * trusted and must still form a valid chain to an anchor.
    123      */
    124     private final TrustedCertificateIndex intermediateIndex;
    125 
    126     /**
    127      * This is lazily initialized in the AndroidCAStore case since it
    128      * forces us to bring all the CAs into memory. In the
    129      * non-AndroidCAStore, we initialize this as part of the
    130      * constructor.
    131      */
    132     private final X509Certificate[] acceptedIssuers;
    133 
    134     private final Exception err;
    135     private final CertificateFactory factory;
    136     private final CertBlacklist blacklist;
    137     private CTVerifier ctVerifier;
    138     private CTPolicy ctPolicy;
    139 
    140     // Forces CT verification to always to done. For tests.
    141     private boolean ctEnabledOverride;
    142 
    143     /**
    144      * Creates X509TrustManager based on a keystore
    145      *
    146      * @param keyStore
    147      */
    148     public TrustManagerImpl(KeyStore keyStore) {
    149         this(keyStore, null);
    150     }
    151 
    152     public TrustManagerImpl(KeyStore keyStore, CertPinManager manager) {
    153         this(keyStore, manager, null);
    154     }
    155 
    156     public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
    157                             TrustedCertificateStore certStore) {
    158         this(keyStore, manager, certStore, null);
    159     }
    160 
    161     public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
    162                             TrustedCertificateStore certStore,
    163                             CertBlacklist blacklist) {
    164         this(keyStore, manager, certStore, blacklist, null, null, null);
    165     }
    166 
    167     /**
    168      * For testing only.
    169      */
    170     public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
    171             TrustedCertificateStore certStore, CertBlacklist blacklist, CTLogStore ctLogStore,
    172             CTVerifier ctVerifier, CTPolicy ctPolicy) {
    173         CertPathValidator validatorLocal = null;
    174         CertificateFactory factoryLocal = null;
    175         KeyStore rootKeyStoreLocal = null;
    176         TrustedCertificateStore trustedCertificateStoreLocal = null;
    177         TrustedCertificateIndex trustedCertificateIndexLocal = null;
    178         X509Certificate[] acceptedIssuersLocal = null;
    179         Exception errLocal = null;
    180         try {
    181             validatorLocal = CertPathValidator.getInstance("PKIX");
    182             factoryLocal = CertificateFactory.getInstance("X509");
    183 
    184             // if we have an AndroidCAStore, we will lazily load CAs
    185             if ("AndroidCAStore".equals(keyStore.getType())) {
    186                 rootKeyStoreLocal = keyStore;
    187                 trustedCertificateStoreLocal =
    188                     (certStore != null) ? certStore : new TrustedCertificateStore();
    189                 acceptedIssuersLocal = null;
    190                 trustedCertificateIndexLocal = new TrustedCertificateIndex();
    191             } else {
    192                 rootKeyStoreLocal = null;
    193                 trustedCertificateStoreLocal = certStore;
    194                 acceptedIssuersLocal = acceptedIssuers(keyStore);
    195                 trustedCertificateIndexLocal
    196                         = new TrustedCertificateIndex(trustAnchors(acceptedIssuersLocal));
    197             }
    198 
    199         } catch (Exception e) {
    200             errLocal = e;
    201         }
    202 
    203         if (blacklist == null) {
    204             blacklist = CertBlacklist.getDefault();
    205         }
    206         if (ctLogStore == null) {
    207             ctLogStore = new CTLogStoreImpl();
    208         }
    209 
    210         if (ctPolicy == null) {
    211             ctPolicy = new CTPolicyImpl(ctLogStore, 2);
    212         }
    213 
    214         this.pinManager = manager;
    215         this.rootKeyStore = rootKeyStoreLocal;
    216         this.trustedCertificateStore = trustedCertificateStoreLocal;
    217         this.validator = validatorLocal;
    218         this.factory = factoryLocal;
    219         this.trustedCertificateIndex = trustedCertificateIndexLocal;
    220         this.intermediateIndex = new TrustedCertificateIndex();
    221         this.acceptedIssuers = acceptedIssuersLocal;
    222         this.err = errLocal;
    223         this.blacklist = blacklist;
    224         this.ctVerifier = new CTVerifier(ctLogStore);
    225         this.ctPolicy = ctPolicy;
    226     }
    227 
    228     private static X509Certificate[] acceptedIssuers(KeyStore ks) {
    229         try {
    230             // Note that unlike the PKIXParameters code to create a Set of
    231             // TrustAnchors from a KeyStore, this version takes from both
    232             // TrustedCertificateEntry and PrivateKeyEntry, not just
    233             // TrustedCertificateEntry, which is why TrustManagerImpl
    234             // cannot just use an PKIXParameters(KeyStore)
    235             // constructor.
    236 
    237             // TODO remove duplicates if same cert is found in both a
    238             // PrivateKeyEntry and TrustedCertificateEntry
    239             List<X509Certificate> trusted = new ArrayList<X509Certificate>();
    240             for (Enumeration<String> en = ks.aliases(); en.hasMoreElements();) {
    241                 final String alias = en.nextElement();
    242                 final X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
    243                 if (cert != null) {
    244                     trusted.add(cert);
    245                 }
    246             }
    247             return trusted.toArray(new X509Certificate[trusted.size()]);
    248         } catch (KeyStoreException e) {
    249             return new X509Certificate[0];
    250         }
    251     }
    252 
    253     private static Set<TrustAnchor> trustAnchors(X509Certificate[] certs) {
    254         Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(certs.length);
    255         for (X509Certificate cert : certs) {
    256             trustAnchors.add(new TrustAnchor(cert, null));
    257         }
    258         return trustAnchors;
    259     }
    260 
    261     @Override
    262     public void checkClientTrusted(X509Certificate[] chain, String authType)
    263             throws CertificateException {
    264         checkTrusted(chain, authType, null, null, true /* client auth */);
    265     }
    266 
    267     /**
    268      * For backward compatibility with older Android API that used String for the hostname only.
    269      */
    270     public List<X509Certificate> checkClientTrusted(X509Certificate[] chain, String authType,
    271             String hostname) throws CertificateException {
    272         return checkTrusted(chain, null /* ocspData */, null /* tlsSctData */, authType, hostname,
    273                 true);
    274     }
    275 
    276     private static SSLSession getHandshakeSessionOrThrow(SSLSocket sslSocket)
    277             throws CertificateException {
    278         SSLSession session = sslSocket.getHandshakeSession();
    279         if (session == null) {
    280             throw new CertificateException("Not in handshake; no session available");
    281         }
    282         return session;
    283     }
    284 
    285     @Override
    286     public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
    287             throws CertificateException {
    288         SSLSession session = null;
    289         SSLParameters parameters = null;
    290         if (socket instanceof SSLSocket) {
    291             SSLSocket sslSocket = (SSLSocket) socket;
    292             session = getHandshakeSessionOrThrow(sslSocket);
    293             parameters = sslSocket.getSSLParameters();
    294         }
    295         checkTrusted(chain, authType, session, parameters, true /* client auth */);
    296     }
    297 
    298     @Override
    299     public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
    300             throws CertificateException {
    301         SSLSession session = engine.getHandshakeSession();
    302         if (session == null) {
    303             throw new CertificateException("Not in handshake; no session available");
    304         }
    305         checkTrusted(chain, authType, session, engine.getSSLParameters(), true /* client auth */);
    306     }
    307 
    308     @Override
    309     public void checkServerTrusted(X509Certificate[] chain, String authType)
    310             throws CertificateException {
    311         checkTrusted(chain, authType, null, null, false /* client auth */);
    312     }
    313 
    314     /**
    315      * For backward compatibility with older Android API that used String for the hostname only.
    316      */
    317     public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
    318             String hostname) throws CertificateException {
    319         return checkTrusted(chain, null /* ocspData */, null /* tlsSctData */, authType, hostname,
    320                 false);
    321     }
    322 
    323     /**
    324      * Returns the full trusted certificate chain found from {@code certs}.
    325      *
    326      * Throws {@link CertificateException} when no trusted chain can be found from {@code certs}.
    327      */
    328     public List<X509Certificate> getTrustedChainForServer(X509Certificate[] certs,
    329             String authType, Socket socket) throws CertificateException {
    330         SSLSession session = null;
    331         SSLParameters parameters = null;
    332         if (socket instanceof SSLSocket) {
    333             SSLSocket sslSocket = (SSLSocket) socket;
    334             session = getHandshakeSessionOrThrow(sslSocket);
    335             parameters = sslSocket.getSSLParameters();
    336         }
    337         return checkTrusted(certs, authType, session, parameters, false /* client auth */);
    338     }
    339 
    340     /**
    341      * Returns the full trusted certificate chain found from {@code certs}.
    342      *
    343      * Throws {@link CertificateException} when no trusted chain can be found from {@code certs}.
    344      */
    345     public List<X509Certificate> getTrustedChainForServer(X509Certificate[] certs,
    346             String authType, SSLEngine engine) throws CertificateException {
    347         SSLSession session = engine.getHandshakeSession();
    348         if (session == null) {
    349             throw new CertificateException("Not in handshake; no session available");
    350         }
    351         return checkTrusted(certs, authType, session, engine.getSSLParameters(),
    352                 false /* client auth */);
    353     }
    354 
    355     @Override
    356     public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
    357             throws CertificateException {
    358         getTrustedChainForServer(chain, authType, socket);
    359     }
    360 
    361     @Override
    362     public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
    363             throws CertificateException {
    364         getTrustedChainForServer(chain, authType, engine);
    365     }
    366 
    367     public boolean isUserAddedCertificate(X509Certificate cert) {
    368         if (trustedCertificateStore == null) {
    369             return false;
    370         } else {
    371             return trustedCertificateStore.isUserAddedCertificate(cert);
    372         }
    373     }
    374 
    375     /**
    376      * Validates whether a server is trusted. If session is given and non-null
    377      * it also checks if chain is pinned appropriately for that peer host. If
    378      * null, it does not check for pinned certs. The return value is a list of
    379      * the certificates used for making the trust decision.
    380      */
    381     public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
    382             SSLSession session) throws CertificateException {
    383         return checkTrusted(chain, authType, session, null, false /* client auth */);
    384     }
    385 
    386     public void handleTrustStorageUpdate() {
    387         if (acceptedIssuers == null) {
    388             trustedCertificateIndex.reset();
    389         } else {
    390             trustedCertificateIndex.reset(trustAnchors(acceptedIssuers));
    391         }
    392     }
    393 
    394     private List<X509Certificate> checkTrusted(X509Certificate[] certs, String authType,
    395             SSLSession session, SSLParameters parameters, boolean clientAuth)
    396                     throws CertificateException {
    397         byte[] ocspData = null;
    398         byte[] tlsSctData = null;
    399         String hostname = null;
    400         if (session != null) {
    401             hostname = session.getPeerHost();
    402             ocspData = getOcspDataFromSession(session);
    403             tlsSctData = getTlsSctDataFromSession(session);
    404         }
    405 
    406         if (session != null && parameters != null) {
    407             String identificationAlgorithm = parameters.getEndpointIdentificationAlgorithm();
    408             if (identificationAlgorithm != null
    409                     && "HTTPS".equals(identificationAlgorithm.toUpperCase(Locale.US))) {
    410                 HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
    411                 if (!verifier.verify(hostname, session)) {
    412                     throw new CertificateException("No subjectAltNames on the certificate match");
    413                 }
    414             }
    415         }
    416         return checkTrusted(certs, ocspData, tlsSctData, authType, hostname, clientAuth);
    417     }
    418 
    419     private byte[] getOcspDataFromSession(SSLSession session) {
    420         List<byte[]> ocspResponses = null;
    421         if (session instanceof AbstractOpenSSLSession) {
    422             AbstractOpenSSLSession opensslSession = (AbstractOpenSSLSession) session;
    423             ocspResponses = opensslSession.getStatusResponses();
    424         } else {
    425             Method m_getResponses;
    426             try {
    427                 m_getResponses = session.getClass().getDeclaredMethod("getStatusResponses");
    428                 m_getResponses.setAccessible(true);
    429                 Object rawResponses = m_getResponses.invoke(session);
    430                 if (rawResponses instanceof List) {
    431                     ocspResponses = (List<byte[]>) rawResponses;
    432                 }
    433             } catch (NoSuchMethodException | SecurityException | IllegalAccessException
    434                     | IllegalArgumentException ignored) {
    435             } catch (InvocationTargetException e) {
    436                 throw new RuntimeException(e.getCause());
    437             }
    438         }
    439 
    440         if (ocspResponses == null || ocspResponses.isEmpty()) {
    441             return null;
    442         }
    443 
    444         return ocspResponses.get(0);
    445     }
    446 
    447     private byte[] getTlsSctDataFromSession(SSLSession session) {
    448         if (session instanceof AbstractOpenSSLSession) {
    449             AbstractOpenSSLSession opensslSession = (AbstractOpenSSLSession) session;
    450             return opensslSession.getTlsSctData();
    451         }
    452 
    453         byte[] data = null;
    454         try {
    455             Method m_getTlsSctData = session.getClass().getDeclaredMethod("getTlsSctData");
    456             m_getTlsSctData.setAccessible(true);
    457             Object rawData = m_getTlsSctData.invoke(session);
    458             if (rawData instanceof byte[]) {
    459                 data = (byte[]) rawData;
    460             }
    461         } catch (NoSuchMethodException | SecurityException | IllegalAccessException
    462                 | IllegalArgumentException ignored) {
    463         } catch (InvocationTargetException e) {
    464             throw new RuntimeException(e.getCause());
    465         }
    466         return data;
    467     }
    468 
    469     private List<X509Certificate> checkTrusted(X509Certificate[] certs, byte[] ocspData,
    470             byte[] tlsSctData, String authType, String host, boolean clientAuth)
    471             throws CertificateException {
    472         if (certs == null || certs.length == 0 || authType == null || authType.length() == 0) {
    473             throw new IllegalArgumentException("null or zero-length parameter");
    474         }
    475         if (err != null) {
    476             throw new CertificateException(err);
    477         }
    478         Set<X509Certificate> used = new HashSet<X509Certificate>();
    479         ArrayList<X509Certificate> untrustedChain = new ArrayList<X509Certificate>();
    480         ArrayList<TrustAnchor> trustedChain = new ArrayList<TrustAnchor>();
    481         // Initialize the chain to contain the leaf certificate. This potentially could be a trust
    482         // anchor. If the leaf is a trust anchor we still continue with path building to build the
    483         // complete trusted chain for additional validation such as certificate pinning.
    484         X509Certificate leaf = certs[0];
    485         TrustAnchor leafAsAnchor = findTrustAnchorBySubjectAndPublicKey(leaf);
    486         if (leafAsAnchor != null) {
    487             trustedChain.add(leafAsAnchor);
    488             used.add(leafAsAnchor.getTrustedCert());
    489         } else {
    490             untrustedChain.add(leaf);
    491         }
    492         used.add(leaf);
    493         return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
    494                 untrustedChain, trustedChain, used);
    495     }
    496 
    497     /**
    498      * Recursively build certificate chains until a valid chain is found or all possible paths are
    499      * exhausted.
    500      *
    501      * The chain is built in two sections, the complete trusted path is the the combination of
    502      * {@code untrustedChain} and {@code trustAnchorChain}. The chain begins at the leaf
    503      * certificate and ends in the final trusted root certificate.
    504      *
    505      * @param certs the bag of certs provided by the peer. No order is assumed.
    506      * @param host the host being connected to.
    507      * @param clientAuth if a client is being authorized instead of a server.
    508      * @param untrustedChain the untrusted section of the chain built so far. Must be mutable.
    509      * @param trustAnchorChain the trusted section of the chain built so far. Must be mutable.
    510      * @param used the set certificates used so far in path building. Must be mutable.
    511      *
    512      * @return The entire valid chain starting with the leaf certificate. This is the
    513      * concatenation of untrustedChain and trustAnchorChain.
    514      *
    515      * @throws CertificateException If no valid chain could be constructed. Note that there may be
    516      * multiple reasons why no valid chain exists and there is no guarantee that the most severe is
    517      * reported in this exception. As such applications MUST NOT use the specifics of this error
    518      * for trust decisions (e.g. showing the user a click through page based on the specific error).
    519      */
    520     private List<X509Certificate> checkTrustedRecursive(X509Certificate[] certs, byte[] ocspData,
    521             byte[] tlsSctData, String host, boolean clientAuth,
    522             ArrayList<X509Certificate> untrustedChain, ArrayList<TrustAnchor> trustAnchorChain,
    523             Set<X509Certificate> used) throws CertificateException {
    524         CertificateException lastException = null;
    525         X509Certificate current;
    526         if (trustAnchorChain.isEmpty()) {
    527             current = untrustedChain.get(untrustedChain.size() - 1);
    528         } else {
    529             current = trustAnchorChain.get(trustAnchorChain.size() - 1).getTrustedCert();
    530         }
    531 
    532         // Check that the certificate isn't blacklisted.
    533         checkBlacklist(current);
    534 
    535         // 1. If the current certificate in the chain is self-signed verify the chain as is.
    536         if (current.getIssuerDN().equals(current.getSubjectDN())) {
    537             return verifyChain(untrustedChain, trustAnchorChain, host, clientAuth, ocspData,
    538                     tlsSctData);
    539         }
    540 
    541         // 2. Try building a chain via any trust anchors that issued the current certificate.
    542         // Note that we do not stop at the first trust anchor since it is possible that the trust
    543         // anchor is not self-signed and its issuer may be needed for additional validation such as
    544         // certificate pinning. In the common case the first trust anchor will be self-signed or
    545         // its issuer's certificate will be missing.
    546         Set<TrustAnchor> anchors = findAllTrustAnchorsByIssuerAndSignature(current);
    547         boolean seenIssuer = false;
    548         for (TrustAnchor anchor : sortPotentialAnchors(anchors)) {
    549             X509Certificate anchorCert = anchor.getTrustedCert();
    550             // Avoid using certificates that have already been used.
    551             if (used.contains(anchorCert)) {
    552                 continue;
    553             }
    554             seenIssuer = true;
    555             used.add(anchorCert);
    556             trustAnchorChain.add(anchor);
    557             try {
    558                 return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
    559                         untrustedChain, trustAnchorChain, used);
    560             } catch (CertificateException ex) {
    561                 lastException = ex;
    562             }
    563             // Could not form a valid chain via this certificate, remove it from this chain.
    564             trustAnchorChain.remove(trustAnchorChain.size() - 1);
    565             used.remove(anchorCert);
    566         }
    567 
    568         // 3. If we were unable to find additional trusted issuers, verify the current chain.
    569         // This may happen if the root of trust is not self-signed and the issuer is not
    570         // present in the trusted set.
    571         if (!trustAnchorChain.isEmpty()) {
    572             if (!seenIssuer) {
    573                 return verifyChain(untrustedChain, trustAnchorChain, host, clientAuth, ocspData,
    574                         tlsSctData);
    575             }
    576 
    577             // Otherwise all chains based on the current trust anchor were rejected, fail.
    578             throw lastException;
    579         }
    580 
    581         // 4. Use the certificates provided by the peer to grow the chain.
    582         // Ignore the first certificate, as that is the leaf certificate.
    583         for (int i = 1; i < certs.length; i++) {
    584             X509Certificate candidateIssuer = certs[i];
    585             // Avoid using certificates that have already been used.
    586             if (used.contains(candidateIssuer)) {
    587                 continue;
    588             }
    589             if (current.getIssuerDN().equals(candidateIssuer.getSubjectDN())) {
    590                 // Check the strength and validity of the certificate to prune bad certificates
    591                 // early.
    592                 try {
    593                     candidateIssuer.checkValidity();
    594                     ChainStrengthAnalyzer.checkCert(candidateIssuer);
    595                 } catch (CertificateException ex) {
    596                     lastException = new CertificateException("Unacceptable certificate: "
    597                             + candidateIssuer.getSubjectX500Principal(), ex);
    598                     continue;
    599                 }
    600                 used.add(candidateIssuer);
    601                 untrustedChain.add(candidateIssuer);
    602                 try {
    603                     return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
    604                             untrustedChain, trustAnchorChain, used);
    605                 } catch (CertificateException ex) {
    606                     lastException = ex;
    607                 }
    608                 // Could not form a valid chain via this certificate, remove it from this chain.
    609                 used.remove(candidateIssuer);
    610                 untrustedChain.remove(untrustedChain.size() - 1);
    611             }
    612         }
    613 
    614         // 5. Finally try the cached intermediates to handle server that failed to send them.
    615         Set<TrustAnchor> intermediateAnchors =
    616                 intermediateIndex.findAllByIssuerAndSignature(current);
    617         for (TrustAnchor intermediate : sortPotentialAnchors(intermediateAnchors)) {
    618             X509Certificate intermediateCert = intermediate.getTrustedCert();
    619             // Avoid using certificates that have already been used.
    620             if (used.contains(intermediateCert)) {
    621                 continue;
    622             }
    623             used.add(intermediateCert);
    624             untrustedChain.add(intermediateCert);
    625             try {
    626                 return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
    627                         untrustedChain, trustAnchorChain, used);
    628             } catch (CertificateException ex) {
    629                 lastException = ex;
    630             }
    631             // Could not form a valid chain via this certificate, remove it from this chain.
    632             untrustedChain.remove(untrustedChain.size() - 1);
    633             used.remove(intermediateCert);
    634         }
    635 
    636         // 6. We were unable to build a valid chain, throw the last error encountered.
    637         if (lastException != null) {
    638             throw lastException;
    639         }
    640 
    641         // 7. If no errors were encountered above then verifyChain was never called because it was
    642         // not possible to build a valid chain to a trusted certificate.
    643         CertPath certPath = factory.generateCertPath(untrustedChain);
    644         throw new CertificateException(new CertPathValidatorException(
    645                 "Trust anchor for certification path not found.", null, certPath, -1));
    646     }
    647 
    648     private List<X509Certificate> verifyChain(List<X509Certificate> untrustedChain,
    649             List<TrustAnchor> trustAnchorChain, String host, boolean clientAuth, byte[] ocspData,
    650             byte[] tlsSctData)
    651             throws CertificateException {
    652         // build the cert path from the list of certs sans trust anchors
    653         // TODO: check whether this is slow and should be replaced by a minimalistic CertPath impl
    654         // since we already have built the path.
    655         CertPath certPath = factory.generateCertPath(untrustedChain);
    656 
    657         // Check that there are at least some trust anchors
    658         if (trustAnchorChain.isEmpty()) {
    659             throw new CertificateException(new CertPathValidatorException(
    660                     "Trust anchor for certification path not found.", null, certPath, -1));
    661         }
    662 
    663         List<X509Certificate> wholeChain = new ArrayList<X509Certificate>();
    664         wholeChain.addAll(untrustedChain);
    665         for (TrustAnchor anchor : trustAnchorChain) {
    666             wholeChain.add(anchor.getTrustedCert());
    667         }
    668 
    669         if (pinManager != null) {
    670             pinManager.checkChainPinning(host, wholeChain);
    671         }
    672         // Check whole chain against the blacklist
    673         for (X509Certificate cert : wholeChain) {
    674             checkBlacklist(cert);
    675         }
    676 
    677         // Check CT (if required).
    678         if (!clientAuth &&
    679                 (ctEnabledOverride || (host != null && Platform.isCTVerificationRequired(host)))) {
    680             checkCT(host, wholeChain, ocspData, tlsSctData);
    681         }
    682 
    683         if (untrustedChain.isEmpty()) {
    684             // The chain consists of only trust anchors, skip the validator
    685             return wholeChain;
    686         }
    687 
    688         ChainStrengthAnalyzer.check(untrustedChain);
    689 
    690         // Validate the untrusted part of the chain
    691         try {
    692             Set<TrustAnchor> anchorSet = new HashSet<TrustAnchor>();
    693             // We know that untrusted chains to the first trust anchor, only add that.
    694             anchorSet.add(trustAnchorChain.get(0));
    695             PKIXParameters params = new PKIXParameters(anchorSet);
    696             params.setRevocationEnabled(false);
    697             X509Certificate endPointCert = untrustedChain.get(0);
    698             setOcspResponses(params, endPointCert, ocspData);
    699             params.addCertPathChecker(
    700                     new ExtendedKeyUsagePKIXCertPathChecker(clientAuth, endPointCert));
    701             validator.validate(certPath, params);
    702         } catch (InvalidAlgorithmParameterException e) {
    703             throw new CertificateException("Chain validation failed", e);
    704         } catch (CertPathValidatorException e) {
    705             throw new CertificateException("Chain validation failed", e);
    706         }
    707         // Add intermediate CAs to the index to tolerate sites
    708         // that assume that the browser will have cached these.
    709         // http://b/3404902
    710         for (int i = 1; i < untrustedChain.size(); i++) {
    711             intermediateIndex.index(untrustedChain.get(i));
    712         }
    713         return wholeChain;
    714     }
    715 
    716     private void checkBlacklist(X509Certificate cert) throws CertificateException {
    717         if (blacklist.isPublicKeyBlackListed(cert.getPublicKey())) {
    718             throw new CertificateException("Certificate blacklisted by public key: " + cert);
    719         }
    720     }
    721 
    722     private void checkCT(String host, List<X509Certificate> chain, byte[] ocspData, byte[] tlsData)
    723             throws CertificateException {
    724         CTVerificationResult result =
    725                 ctVerifier.verifySignedCertificateTimestamps(chain, tlsData, ocspData);
    726 
    727         if (!ctPolicy.doesResultConformToPolicy(result, host,
    728                     chain.toArray(new X509Certificate[chain.size()]))) {
    729             throw new CertificateException(
    730                     "Certificate chain does not conform to required transparency policy.");
    731         }
    732     }
    733 
    734     /**
    735      * Sets the OCSP response data that was possibly stapled to the TLS response.
    736      */
    737     private void setOcspResponses(PKIXParameters params, X509Certificate cert, byte[] ocspData) {
    738         if (ocspData == null) {
    739             return;
    740         }
    741 
    742         PKIXRevocationChecker revChecker = null;
    743         List<PKIXCertPathChecker> checkers = new ArrayList<>(params.getCertPathCheckers());
    744         for (PKIXCertPathChecker checker : checkers) {
    745             if (checker instanceof PKIXRevocationChecker) {
    746                 revChecker = (PKIXRevocationChecker) checker;
    747                 break;
    748             }
    749         }
    750 
    751         if (revChecker == null) {
    752             // Only new CertPathValidatorSpi instances will support the
    753             // revocation checker API.
    754             try {
    755                 revChecker = (PKIXRevocationChecker) validator.getRevocationChecker();
    756             } catch (UnsupportedOperationException e) {
    757                 return;
    758             }
    759 
    760             checkers.add(revChecker);
    761 
    762             /*
    763              * If we add a new revocation checker, we should set the option for
    764              * end-entity verification only. Otherwise the CertPathValidator will
    765              * throw an exception when it can't verify the entire chain.
    766              */
    767             revChecker.setOptions(Collections.singleton(Option.ONLY_END_ENTITY));
    768         }
    769 
    770         revChecker.setOcspResponses(Collections.singletonMap(cert, ocspData));
    771         params.setCertPathCheckers(checkers);
    772     }
    773 
    774     /**
    775      * Sort potential anchors so that the most preferred for use come first.
    776      *
    777      * @see CertificatePriorityComparator
    778      */
    779     private static Collection<TrustAnchor> sortPotentialAnchors(Set<TrustAnchor> anchors) {
    780         if (anchors.size() <= 1) {
    781             return anchors;
    782         }
    783         List<TrustAnchor> sortedAnchors = new ArrayList<TrustAnchor>(anchors);
    784         Collections.sort(sortedAnchors, TRUST_ANCHOR_COMPARATOR);
    785         return sortedAnchors;
    786     }
    787 
    788 
    789     /**
    790      * Comparator for sorting {@link TrustAnchor}s using a {@link CertificateComparator}.
    791      */
    792     private static class TrustAnchorComparator implements Comparator<TrustAnchor> {
    793         private static final CertificatePriorityComparator CERT_COMPARATOR =
    794                 new CertificatePriorityComparator();
    795         @Override
    796         public int compare(TrustAnchor lhs, TrustAnchor rhs) {
    797             X509Certificate lhsCert = lhs.getTrustedCert();
    798             X509Certificate rhsCert = rhs.getTrustedCert();
    799             return CERT_COMPARATOR.compare(lhsCert, rhsCert);
    800         }
    801     }
    802 
    803     /**
    804      * If an EKU extension is present in the end-entity certificate,
    805      * it MUST contain an appropriate key usage. For servers, this
    806      * includes anyExtendedKeyUsage, serverAuth, or the historical
    807      * Server Gated Cryptography options of nsSGC or msSGC.  For
    808      * clients, this includes anyExtendedKeyUsage and clientAuth.
    809      */
    810     private static class ExtendedKeyUsagePKIXCertPathChecker extends PKIXCertPathChecker {
    811 
    812         private static final String EKU_OID = "2.5.29.37";
    813 
    814         private static final String EKU_anyExtendedKeyUsage = "2.5.29.37.0";
    815         private static final String EKU_clientAuth = "1.3.6.1.5.5.7.3.2";
    816         private static final String EKU_serverAuth = "1.3.6.1.5.5.7.3.1";
    817         private static final String EKU_nsSGC = "2.16.840.1.113730.4.1";
    818         private static final String EKU_msSGC = "1.3.6.1.4.1.311.10.3.3";
    819 
    820         private static final Set<String> SUPPORTED_EXTENSIONS
    821                 = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(EKU_OID)));
    822 
    823         private final boolean clientAuth;
    824         private final X509Certificate leaf;
    825 
    826         private ExtendedKeyUsagePKIXCertPathChecker(boolean clientAuth, X509Certificate leaf) {
    827             this.clientAuth = clientAuth;
    828             this.leaf = leaf;
    829         }
    830 
    831         @Override
    832         public void init(boolean forward) throws CertPathValidatorException {
    833         }
    834 
    835         @Override
    836         public boolean isForwardCheckingSupported() {
    837             return true;
    838         }
    839 
    840         @Override
    841         public Set<String> getSupportedExtensions() {
    842             return SUPPORTED_EXTENSIONS;
    843         }
    844 
    845         @Override
    846         public void check(Certificate c, Collection<String> unresolvedCritExts)
    847                 throws CertPathValidatorException {
    848             // We only want to validate the EKU on the leaf certificate.
    849             if (c != leaf) {
    850                 return;
    851             }
    852             List<String> ekuOids;
    853             try {
    854                 ekuOids = leaf.getExtendedKeyUsage();
    855             } catch (CertificateParsingException e) {
    856                 // A malformed EKU is bad news, consider it fatal.
    857                 throw new CertPathValidatorException(e);
    858             }
    859             // We are here to check EKU, but there is none.
    860             if (ekuOids == null) {
    861                 return;
    862             }
    863 
    864             boolean goodExtendedKeyUsage = false;
    865             for (String ekuOid : ekuOids) {
    866                 // anyExtendedKeyUsage for clients and servers
    867                 if (ekuOid.equals(EKU_anyExtendedKeyUsage)) {
    868                     goodExtendedKeyUsage = true;
    869                     break;
    870                 }
    871 
    872                 // clients
    873                 if (clientAuth) {
    874                     if (ekuOid.equals(EKU_clientAuth)) {
    875                         goodExtendedKeyUsage = true;
    876                         break;
    877                     }
    878                     continue;
    879                 }
    880 
    881                 // servers
    882                 if (ekuOid.equals(EKU_serverAuth)) {
    883                     goodExtendedKeyUsage = true;
    884                     break;
    885                 }
    886                 if (ekuOid.equals(EKU_nsSGC)) {
    887                     goodExtendedKeyUsage = true;
    888                     break;
    889                 }
    890                 if (ekuOid.equals(EKU_msSGC)) {
    891                     goodExtendedKeyUsage = true;
    892                     break;
    893                 }
    894             }
    895             if (goodExtendedKeyUsage) {
    896                 // Mark extendedKeyUsage as resolved if present.
    897                 unresolvedCritExts.remove(EKU_OID);
    898             } else {
    899                 throw new CertPathValidatorException("End-entity certificate does not have a valid "
    900                                                      + "extendedKeyUsage.");
    901             }
    902         }
    903     }
    904 
    905     /**
    906      * Find all possible issuing trust anchors of {@code cert}.
    907      */
    908     private Set<TrustAnchor> findAllTrustAnchorsByIssuerAndSignature(X509Certificate cert) {
    909         Set<TrustAnchor> indexedAnchors =
    910                 trustedCertificateIndex.findAllByIssuerAndSignature(cert);
    911         if (!indexedAnchors.isEmpty() || trustedCertificateStore == null) {
    912             return indexedAnchors;
    913         }
    914         Set<X509Certificate> storeAnchors = trustedCertificateStore.findAllIssuers(cert);
    915         if (storeAnchors.isEmpty()) {
    916             return indexedAnchors;
    917         }
    918         Set<TrustAnchor> result = new HashSet<TrustAnchor>(storeAnchors.size());
    919         for (X509Certificate storeCert : storeAnchors) {
    920             result.add(trustedCertificateIndex.index(storeCert));
    921         }
    922         return result;
    923     }
    924 
    925     /**
    926      * Check the trustedCertificateIndex for the cert to see if it is
    927      * already trusted and failing that check the KeyStore if it is
    928      * available.
    929      */
    930     private TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
    931         TrustAnchor trustAnchor = trustedCertificateIndex.findBySubjectAndPublicKey(cert);
    932         if (trustAnchor != null) {
    933             return trustAnchor;
    934         }
    935         if (trustedCertificateStore == null) {
    936             // not trusted and no TrustedCertificateStore to check
    937             return null;
    938         }
    939         // probe KeyStore for a cert. AndroidCAStore stores its
    940         // contents hashed by cert subject on the filesystem to make
    941         // this faster than scanning all key store entries.
    942         X509Certificate systemCert = trustedCertificateStore.getTrustAnchor(cert);
    943         if (systemCert != null) {
    944             // Don't index the system certificate here, that way the only place that adds anchors to
    945             // the index are findAllTrustAnchorsByIssuerAndSignature.
    946             // This allows findAllTrustAnchorsByIssuerAndSignature to avoid checking the
    947             // TrustedCertificateStore if the TrustedCertificateIndex contains any issuers for the
    948             // certificate because it will have cached all certificates contained in the
    949             // TrustedCertificateStore.
    950             return new TrustAnchor(systemCert, null);
    951         }
    952         return null;
    953     }
    954 
    955     @Override
    956     public X509Certificate[] getAcceptedIssuers() {
    957         return (acceptedIssuers != null) ? acceptedIssuers.clone() : acceptedIssuers(rootKeyStore);
    958     }
    959 
    960     public void setCTEnabledOverride(boolean enabled) {
    961         this.ctEnabledOverride = enabled;
    962     }
    963 
    964     // Replace the CTVerifier. For testing only.
    965     public void setCTVerifier(CTVerifier verifier) {
    966         this.ctVerifier = verifier;
    967     }
    968 
    969     // Replace the CTPolicy. For testing only.
    970     public void setCTPolicy(CTPolicy policy) {
    971         this.ctPolicy = policy;
    972     }
    973 }
    974