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