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