ForwardBuilder.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.security.provider.certpath; 27 28import java.io.IOException; 29import java.security.GeneralSecurityException; 30import java.security.InvalidKeyException; 31import java.security.PublicKey; 32import java.security.cert.CertificateException; 33import java.security.cert.CertPathValidatorException; 34import java.security.cert.PKIXReason; 35import java.security.cert.CertStore; 36import java.security.cert.CertStoreException; 37import java.security.cert.PKIXBuilderParameters; 38import java.security.cert.PKIXCertPathChecker; 39import java.security.cert.TrustAnchor; 40import java.security.cert.X509Certificate; 41import java.security.cert.X509CertSelector; 42import java.util.*; 43import javax.security.auth.x500.X500Principal; 44 45import sun.security.provider.certpath.PKIX.BuilderParams; 46import sun.security.util.Debug; 47import sun.security.x509.AccessDescription; 48import sun.security.x509.AuthorityInfoAccessExtension; 49import static sun.security.x509.PKIXExtensions.*; 50import sun.security.x509.X500Name; 51import sun.security.x509.AuthorityKeyIdentifierExtension; 52 53/** 54 * This class represents a forward builder, which is able to retrieve 55 * matching certificates from CertStores and verify a particular certificate 56 * against a ForwardState. 57 * 58 * @since 1.4 59 * @author Yassir Elley 60 * @author Sean Mullan 61 */ 62class ForwardBuilder extends Builder { 63 64 private static final Debug debug = Debug.getInstance("certpath"); 65 private final Set<X509Certificate> trustedCerts; 66 private final Set<X500Principal> trustedSubjectDNs; 67 private final Set<TrustAnchor> trustAnchors; 68 private X509CertSelector eeSelector; 69 private AdaptableX509CertSelector caSelector; 70 private X509CertSelector caTargetSelector; 71 TrustAnchor trustAnchor; 72 private Comparator<X509Certificate> comparator; 73 private boolean searchAllCertStores = true; 74 75 /** 76 * Initialize the builder with the input parameters. 77 * 78 * @param params the parameter set used to build a certification path 79 */ 80 ForwardBuilder(BuilderParams buildParams, boolean searchAllCertStores) { 81 super(buildParams); 82 83 // populate sets of trusted certificates and subject DNs 84 trustAnchors = buildParams.trustAnchors(); 85 trustedCerts = new HashSet<X509Certificate>(trustAnchors.size()); 86 trustedSubjectDNs = new HashSet<X500Principal>(trustAnchors.size()); 87 for (TrustAnchor anchor : trustAnchors) { 88 X509Certificate trustedCert = anchor.getTrustedCert(); 89 if (trustedCert != null) { 90 trustedCerts.add(trustedCert); 91 trustedSubjectDNs.add(trustedCert.getSubjectX500Principal()); 92 } else { 93 trustedSubjectDNs.add(anchor.getCA()); 94 } 95 } 96 comparator = new PKIXCertComparator(trustedSubjectDNs); 97 this.searchAllCertStores = searchAllCertStores; 98 } 99 100 /** 101 * Retrieves all certs from the specified CertStores that satisfy the 102 * requirements specified in the parameters and the current 103 * PKIX state (name constraints, policy constraints, etc). 104 * 105 * @param currentState the current state. 106 * Must be an instance of <code>ForwardState</code> 107 * @param certStores list of CertStores 108 */ 109 @Override 110 Collection<X509Certificate> getMatchingCerts(State currentState, 111 List<CertStore> certStores) 112 throws CertStoreException, CertificateException, IOException 113 { 114 if (debug != null) { 115 debug.println("ForwardBuilder.getMatchingCerts()..."); 116 } 117 118 ForwardState currState = (ForwardState) currentState; 119 120 /* 121 * We store certs in a Set because we don't want duplicates. 122 * As each cert is added, it is sorted based on the PKIXCertComparator 123 * algorithm. 124 */ 125 Set<X509Certificate> certs = new TreeSet<>(comparator); 126 127 /* 128 * Only look for EE certs if search has just started. 129 */ 130 if (currState.isInitial()) { 131 getMatchingEECerts(currState, certStores, certs); 132 } 133 getMatchingCACerts(currState, certStores, certs); 134 135 return certs; 136 } 137 138 /* 139 * Retrieves all end-entity certificates which satisfy constraints 140 * and requirements specified in the parameters and PKIX state. 141 */ 142 private void getMatchingEECerts(ForwardState currentState, 143 List<CertStore> certStores, 144 Collection<X509Certificate> eeCerts) 145 throws IOException 146 { 147 if (debug != null) { 148 debug.println("ForwardBuilder.getMatchingEECerts()..."); 149 } 150 /* 151 * Compose a certificate matching rule to filter out 152 * certs which don't satisfy constraints 153 * 154 * First, retrieve clone of current target cert constraints, 155 * and then add more selection criteria based on current validation 156 * state. Since selector never changes, cache local copy & reuse. 157 */ 158 if (eeSelector == null) { 159 eeSelector = (X509CertSelector) targetCertConstraints.clone(); 160 161 /* 162 * Match on certificate validity date 163 */ 164 eeSelector.setCertificateValid(buildParams.date()); 165 166 /* 167 * Policy processing optimizations 168 */ 169 if (buildParams.explicitPolicyRequired()) { 170 eeSelector.setPolicy(getMatchingPolicies()); 171 } 172 /* 173 * Require EE certs 174 */ 175 eeSelector.setBasicConstraints(-2); 176 } 177 178 /* Retrieve matching EE certs from CertStores */ 179 addMatchingCerts(eeSelector, certStores, eeCerts, searchAllCertStores); 180 } 181 182 /** 183 * Retrieves all CA certificates which satisfy constraints 184 * and requirements specified in the parameters and PKIX state. 185 */ 186 private void getMatchingCACerts(ForwardState currentState, 187 List<CertStore> certStores, 188 Collection<X509Certificate> caCerts) 189 throws IOException 190 { 191 if (debug != null) { 192 debug.println("ForwardBuilder.getMatchingCACerts()..."); 193 } 194 int initialSize = caCerts.size(); 195 196 /* 197 * Compose a CertSelector to filter out 198 * certs which do not satisfy requirements. 199 */ 200 X509CertSelector sel = null; 201 202 if (currentState.isInitial()) { 203 if (targetCertConstraints.getBasicConstraints() == -2) { 204 // no need to continue: this means we never can match a CA cert 205 return; 206 } 207 208 /* This means a CA is the target, so match on same stuff as 209 * getMatchingEECerts 210 */ 211 if (debug != null) { 212 debug.println("ForwardBuilder.getMatchingCACerts(): " + 213 "the target is a CA"); 214 } 215 216 if (caTargetSelector == null) { 217 caTargetSelector = 218 (X509CertSelector) targetCertConstraints.clone(); 219 220 /* 221 * Since we don't check the validity period of trusted 222 * certificates, please don't set the certificate valid 223 * criterion unless the trusted certificate matching is 224 * completed. 225 */ 226 227 /* 228 * Policy processing optimizations 229 */ 230 if (buildParams.explicitPolicyRequired()) 231 caTargetSelector.setPolicy(getMatchingPolicies()); 232 } 233 234 sel = caTargetSelector; 235 } else { 236 237 if (caSelector == null) { 238 caSelector = new AdaptableX509CertSelector(); 239 240 /* 241 * Since we don't check the validity period of trusted 242 * certificates, please don't set the certificate valid 243 * criterion unless the trusted certificate matching is 244 * completed. 245 */ 246 247 /* 248 * Policy processing optimizations 249 */ 250 if (buildParams.explicitPolicyRequired()) 251 caSelector.setPolicy(getMatchingPolicies()); 252 } 253 254 /* 255 * Match on subject (issuer of previous cert) 256 */ 257 caSelector.setSubject(currentState.issuerDN); 258 259 /* 260 * Match on subjectNamesTraversed (both DNs and AltNames) 261 * (checks that current cert's name constraints permit it 262 * to certify all the DNs and AltNames that have been traversed) 263 */ 264 CertPathHelper.setPathToNames 265 (caSelector, currentState.subjectNamesTraversed); 266 267 /* 268 * Facilitate certification path construction with authority 269 * key identifier and subject key identifier. 270 */ 271 AuthorityKeyIdentifierExtension akidext = 272 currentState.cert.getAuthorityKeyIdentifierExtension(); 273 caSelector.setSkiAndSerialNumber(akidext); 274 275 /* 276 * check the validity period 277 */ 278 caSelector.setValidityPeriod(currentState.cert.getNotBefore(), 279 currentState.cert.getNotAfter()); 280 281 sel = caSelector; 282 } 283 284 /* 285 * For compatibility, conservatively, we don't check the path 286 * length constraint of trusted anchors. Please don't set the 287 * basic constraints criterion unless the trusted certificate 288 * matching is completed. 289 */ 290 sel.setBasicConstraints(-1); 291 292 for (X509Certificate trustedCert : trustedCerts) { 293 if (sel.match(trustedCert)) { 294 if (debug != null) { 295 debug.println("ForwardBuilder.getMatchingCACerts: " + 296 "found matching trust anchor." + 297 "\n SN: " + 298 Debug.toHexString(trustedCert.getSerialNumber()) + 299 "\n Subject: " + 300 trustedCert.getSubjectX500Principal() + 301 "\n Issuer: " + 302 trustedCert.getIssuerX500Principal()); 303 } 304 if (caCerts.add(trustedCert) && !searchAllCertStores) { 305 return; 306 } 307 } 308 } 309 310 /* 311 * The trusted certificate matching is completed. We need to match 312 * on certificate validity date. 313 */ 314 sel.setCertificateValid(buildParams.date()); 315 316 /* 317 * Require CA certs with a pathLenConstraint that allows 318 * at least as many CA certs that have already been traversed 319 */ 320 sel.setBasicConstraints(currentState.traversedCACerts); 321 322 /* 323 * If we have already traversed as many CA certs as the maxPathLength 324 * will allow us to, then we don't bother looking through these 325 * certificate pairs. If maxPathLength has a value of -1, this 326 * means it is unconstrained, so we always look through the 327 * certificate pairs. 328 */ 329 if (currentState.isInitial() || 330 (buildParams.maxPathLength() == -1) || 331 (buildParams.maxPathLength() > currentState.traversedCACerts)) 332 { 333 if (addMatchingCerts(sel, certStores, 334 caCerts, searchAllCertStores) 335 && !searchAllCertStores) { 336 return; 337 } 338 } 339 340 if (!currentState.isInitial() && Builder.USE_AIA) { 341 // check for AuthorityInformationAccess extension 342 AuthorityInfoAccessExtension aiaExt = 343 currentState.cert.getAuthorityInfoAccessExtension(); 344 if (aiaExt != null) { 345 getCerts(aiaExt, caCerts); 346 } 347 } 348 349 if (debug != null) { 350 int numCerts = caCerts.size() - initialSize; 351 debug.println("ForwardBuilder.getMatchingCACerts: found " + 352 numCerts + " CA certs"); 353 } 354 } 355 356 /** 357 * Download Certificates from the given AIA and add them to the 358 * specified Collection. 359 */ 360 // cs.getCertificates(caSelector) returns a collection of X509Certificate's 361 // because of the selector, so the cast is safe 362 @SuppressWarnings("unchecked") 363 private boolean getCerts(AuthorityInfoAccessExtension aiaExt, 364 Collection<X509Certificate> certs) 365 { 366 if (Builder.USE_AIA == false) { 367 return false; 368 } 369 List<AccessDescription> adList = aiaExt.getAccessDescriptions(); 370 if (adList == null || adList.isEmpty()) { 371 return false; 372 } 373 374 boolean add = false; 375 for (AccessDescription ad : adList) { 376 CertStore cs = URICertStore.getInstance(ad); 377 if (cs != null) { 378 try { 379 if (certs.addAll((Collection<X509Certificate>) 380 cs.getCertificates(caSelector))) { 381 add = true; 382 if (!searchAllCertStores) { 383 return true; 384 } 385 } 386 } catch (CertStoreException cse) { 387 if (debug != null) { 388 debug.println("exception getting certs from CertStore:"); 389 cse.printStackTrace(); 390 } 391 } 392 } 393 } 394 return add; 395 } 396 397 /** 398 * This inner class compares 2 PKIX certificates according to which 399 * should be tried first when building a path from the target. 400 * The preference order is as follows: 401 * 402 * Given trusted certificate(s): 403 * Subject:ou=D,ou=C,o=B,c=A 404 * 405 * Preference order for current cert: 406 * 407 * 1) Issuer matches a trusted subject 408 * Issuer: ou=D,ou=C,o=B,c=A 409 * 410 * 2) Issuer is a descendant of a trusted subject (in order of 411 * number of links to the trusted subject) 412 * a) Issuer: ou=E,ou=D,ou=C,o=B,c=A [links=1] 413 * b) Issuer: ou=F,ou=E,ou=D,ou=C,ou=B,c=A [links=2] 414 * 415 * 3) Issuer is an ancestor of a trusted subject (in order of number of 416 * links to the trusted subject) 417 * a) Issuer: ou=C,o=B,c=A [links=1] 418 * b) Issuer: o=B,c=A [links=2] 419 * 420 * 4) Issuer is in the same namespace as a trusted subject (in order of 421 * number of links to the trusted subject) 422 * a) Issuer: ou=G,ou=C,o=B,c=A [links=2] 423 * b) Issuer: ou=H,o=B,c=A [links=3] 424 * 425 * 5) Issuer is an ancestor of certificate subject (in order of number 426 * of links to the certificate subject) 427 * a) Issuer: ou=K,o=J,c=A 428 * Subject: ou=L,ou=K,o=J,c=A 429 * b) Issuer: o=J,c=A 430 * Subject: ou=L,ou=K,0=J,c=A 431 * 432 * 6) Any other certificates 433 */ 434 static class PKIXCertComparator implements Comparator<X509Certificate> { 435 436 static final String METHOD_NME = "PKIXCertComparator.compare()"; 437 438 private final Set<X500Principal> trustedSubjectDNs; 439 440 PKIXCertComparator(Set<X500Principal> trustedSubjectDNs) { 441 this.trustedSubjectDNs = trustedSubjectDNs; 442 } 443 444 /** 445 * @param oCert1 First X509Certificate to be compared 446 * @param oCert2 Second X509Certificate to be compared 447 * @return -1 if oCert1 is preferable to oCert2, or 448 * if oCert1 and oCert2 are equally preferable (in this 449 * case it doesn't matter which is preferable, but we don't 450 * return 0 because the comparator would behave strangely 451 * when used in a SortedSet). 452 * 1 if oCert2 is preferable to oCert1 453 * 0 if oCert1.equals(oCert2). We only return 0 if the 454 * certs are equal so that this comparator behaves 455 * correctly when used in a SortedSet. 456 * @throws ClassCastException if either argument is not of type 457 * X509Certificate 458 */ 459 @Override 460 public int compare(X509Certificate oCert1, X509Certificate oCert2) { 461 462 // if certs are the same, return 0 463 if (oCert1.equals(oCert2)) return 0; 464 465 X500Principal cIssuer1 = oCert1.getIssuerX500Principal(); 466 X500Principal cIssuer2 = oCert2.getIssuerX500Principal(); 467 X500Name cIssuer1Name = X500Name.asX500Name(cIssuer1); 468 X500Name cIssuer2Name = X500Name.asX500Name(cIssuer2); 469 470 if (debug != null) { 471 debug.println(METHOD_NME + " o1 Issuer: " + cIssuer1); 472 debug.println(METHOD_NME + " o2 Issuer: " + cIssuer2); 473 } 474 475 /* If one cert's issuer matches a trusted subject, then it is 476 * preferable. 477 */ 478 if (debug != null) { 479 debug.println(METHOD_NME + " MATCH TRUSTED SUBJECT TEST..."); 480 } 481 482 boolean m1 = trustedSubjectDNs.contains(cIssuer1); 483 boolean m2 = trustedSubjectDNs.contains(cIssuer2); 484 if (debug != null) { 485 debug.println(METHOD_NME + " m1: " + m1); 486 debug.println(METHOD_NME + " m2: " + m2); 487 } 488 if (m1 && m2) { 489 return -1; 490 } else if (m1) { 491 return -1; 492 } else if (m2) { 493 return 1; 494 } 495 496 /* If one cert's issuer is a naming descendant of a trusted subject, 497 * then it is preferable, in order of increasing naming distance. 498 */ 499 if (debug != null) { 500 debug.println(METHOD_NME + " NAMING DESCENDANT TEST..."); 501 } 502 for (X500Principal tSubject : trustedSubjectDNs) { 503 X500Name tSubjectName = X500Name.asX500Name(tSubject); 504 int distanceTto1 = 505 Builder.distance(tSubjectName, cIssuer1Name, -1); 506 int distanceTto2 = 507 Builder.distance(tSubjectName, cIssuer2Name, -1); 508 if (debug != null) { 509 debug.println(METHOD_NME +" distanceTto1: " + distanceTto1); 510 debug.println(METHOD_NME +" distanceTto2: " + distanceTto2); 511 } 512 if (distanceTto1 > 0 || distanceTto2 > 0) { 513 if (distanceTto1 == distanceTto2) { 514 return -1; 515 } else if (distanceTto1 > 0 && distanceTto2 <= 0) { 516 return -1; 517 } else if (distanceTto1 <= 0 && distanceTto2 > 0) { 518 return 1; 519 } else if (distanceTto1 < distanceTto2) { 520 return -1; 521 } else { // distanceTto1 > distanceTto2 522 return 1; 523 } 524 } 525 } 526 527 /* If one cert's issuer is a naming ancestor of a trusted subject, 528 * then it is preferable, in order of increasing naming distance. 529 */ 530 if (debug != null) { 531 debug.println(METHOD_NME + " NAMING ANCESTOR TEST..."); 532 } 533 for (X500Principal tSubject : trustedSubjectDNs) { 534 X500Name tSubjectName = X500Name.asX500Name(tSubject); 535 536 int distanceTto1 = Builder.distance 537 (tSubjectName, cIssuer1Name, Integer.MAX_VALUE); 538 int distanceTto2 = Builder.distance 539 (tSubjectName, cIssuer2Name, Integer.MAX_VALUE); 540 if (debug != null) { 541 debug.println(METHOD_NME +" distanceTto1: " + distanceTto1); 542 debug.println(METHOD_NME +" distanceTto2: " + distanceTto2); 543 } 544 if (distanceTto1 < 0 || distanceTto2 < 0) { 545 if (distanceTto1 == distanceTto2) { 546 return -1; 547 } else if (distanceTto1 < 0 && distanceTto2 >= 0) { 548 return -1; 549 } else if (distanceTto1 >= 0 && distanceTto2 < 0) { 550 return 1; 551 } else if (distanceTto1 > distanceTto2) { 552 return -1; 553 } else { 554 return 1; 555 } 556 } 557 } 558 559 /* If one cert's issuer is in the same namespace as a trusted 560 * subject, then it is preferable, in order of increasing naming 561 * distance. 562 */ 563 if (debug != null) { 564 debug.println(METHOD_NME +" SAME NAMESPACE AS TRUSTED TEST..."); 565 } 566 for (X500Principal tSubject : trustedSubjectDNs) { 567 X500Name tSubjectName = X500Name.asX500Name(tSubject); 568 X500Name tAo1 = tSubjectName.commonAncestor(cIssuer1Name); 569 X500Name tAo2 = tSubjectName.commonAncestor(cIssuer2Name); 570 if (debug != null) { 571 debug.println(METHOD_NME +" tAo1: " + String.valueOf(tAo1)); 572 debug.println(METHOD_NME +" tAo2: " + String.valueOf(tAo2)); 573 } 574 if (tAo1 != null || tAo2 != null) { 575 if (tAo1 != null && tAo2 != null) { 576 int hopsTto1 = Builder.hops 577 (tSubjectName, cIssuer1Name, Integer.MAX_VALUE); 578 int hopsTto2 = Builder.hops 579 (tSubjectName, cIssuer2Name, Integer.MAX_VALUE); 580 if (debug != null) { 581 debug.println(METHOD_NME +" hopsTto1: " + hopsTto1); 582 debug.println(METHOD_NME +" hopsTto2: " + hopsTto2); 583 } 584 if (hopsTto1 == hopsTto2) { 585 } else if (hopsTto1 > hopsTto2) { 586 return 1; 587 } else { // hopsTto1 < hopsTto2 588 return -1; 589 } 590 } else if (tAo1 == null) { 591 return 1; 592 } else { 593 return -1; 594 } 595 } 596 } 597 598 599 /* If one cert's issuer is an ancestor of that cert's subject, 600 * then it is preferable, in order of increasing naming distance. 601 */ 602 if (debug != null) { 603 debug.println(METHOD_NME+" CERT ISSUER/SUBJECT COMPARISON TEST..."); 604 } 605 X500Principal cSubject1 = oCert1.getSubjectX500Principal(); 606 X500Principal cSubject2 = oCert2.getSubjectX500Principal(); 607 X500Name cSubject1Name = X500Name.asX500Name(cSubject1); 608 X500Name cSubject2Name = X500Name.asX500Name(cSubject2); 609 610 if (debug != null) { 611 debug.println(METHOD_NME + " o1 Subject: " + cSubject1); 612 debug.println(METHOD_NME + " o2 Subject: " + cSubject2); 613 } 614 int distanceStoI1 = Builder.distance 615 (cSubject1Name, cIssuer1Name, Integer.MAX_VALUE); 616 int distanceStoI2 = Builder.distance 617 (cSubject2Name, cIssuer2Name, Integer.MAX_VALUE); 618 if (debug != null) { 619 debug.println(METHOD_NME + " distanceStoI1: " + distanceStoI1); 620 debug.println(METHOD_NME + " distanceStoI2: " + distanceStoI2); 621 } 622 if (distanceStoI2 > distanceStoI1) { 623 return -1; 624 } else if (distanceStoI2 < distanceStoI1) { 625 return 1; 626 } 627 628 /* Otherwise, certs are equally preferable. 629 */ 630 if (debug != null) { 631 debug.println(METHOD_NME + " no tests matched; RETURN 0"); 632 } 633 return -1; 634 } 635 } 636 637 /** 638 * Verifies a matching certificate. 639 * 640 * This method executes the validation steps in the PKIX path 641 * validation algorithm <draft-ietf-pkix-new-part1-08.txt> which were 642 * not satisfied by the selection criteria used by getCertificates() 643 * to find the certs and only the steps that can be executed in a 644 * forward direction (target to trust anchor). Those steps that can 645 * only be executed in a reverse direction are deferred until the 646 * complete path has been built. 647 * 648 * Trust anchor certs are not validated, but are used to verify the 649 * signature and revocation status of the previous cert. 650 * 651 * If the last certificate is being verified (the one whose subject 652 * matches the target subject, then steps in 6.1.4 of the PKIX 653 * Certification Path Validation algorithm are NOT executed, 654 * regardless of whether or not the last cert is an end-entity 655 * cert or not. This allows callers to certify CA certs as 656 * well as EE certs. 657 * 658 * @param cert the certificate to be verified 659 * @param currentState the current state against which the cert is verified 660 * @param certPathList the certPathList generated thus far 661 */ 662 @Override 663 void verifyCert(X509Certificate cert, State currentState, 664 List<X509Certificate> certPathList) 665 throws GeneralSecurityException 666 { 667 if (debug != null) { 668 debug.println("ForwardBuilder.verifyCert(SN: " 669 + Debug.toHexString(cert.getSerialNumber()) 670 + "\n Issuer: " + cert.getIssuerX500Principal() + ")" 671 + "\n Subject: " + cert.getSubjectX500Principal() + ")"); 672 } 673 674 ForwardState currState = (ForwardState)currentState; 675 676 // Don't bother to verify untrusted certificate more. 677 currState.untrustedChecker.check(cert, Collections.<String>emptySet()); 678 679 /* 680 * check for looping - abort a loop if we encounter the same 681 * certificate twice 682 */ 683 if (certPathList != null) { 684 for (X509Certificate cpListCert : certPathList) { 685 if (cert.equals(cpListCert)) { 686 if (debug != null) { 687 debug.println("loop detected!!"); 688 } 689 throw new CertPathValidatorException("loop detected"); 690 } 691 } 692 } 693 694 /* check if trusted cert */ 695 boolean isTrustedCert = trustedCerts.contains(cert); 696 697 /* we don't perform any validation of the trusted cert */ 698 if (!isTrustedCert) { 699 /* 700 * Check CRITICAL private extensions for user checkers that 701 * support forward checking (forwardCheckers) and remove 702 * ones we know how to check. 703 */ 704 Set<String> unresCritExts = cert.getCriticalExtensionOIDs(); 705 if (unresCritExts == null) { 706 unresCritExts = Collections.<String>emptySet(); 707 } 708 for (PKIXCertPathChecker checker : currState.forwardCheckers) { 709 checker.check(cert, unresCritExts); 710 } 711 712 /* 713 * Remove extensions from user checkers that don't support 714 * forward checking. After this step, we will have removed 715 * all extensions that all user checkers are capable of 716 * processing. 717 */ 718 for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) { 719 if (!checker.isForwardCheckingSupported()) { 720 Set<String> supportedExts = checker.getSupportedExtensions(); 721 if (supportedExts != null) { 722 unresCritExts.removeAll(supportedExts); 723 } 724 } 725 } 726 727 /* 728 * Look at the remaining extensions and remove any ones we know how 729 * to check. If there are any left, throw an exception! 730 */ 731 if (!unresCritExts.isEmpty()) { 732 unresCritExts.remove(BasicConstraints_Id.toString()); 733 unresCritExts.remove(NameConstraints_Id.toString()); 734 unresCritExts.remove(CertificatePolicies_Id.toString()); 735 unresCritExts.remove(PolicyMappings_Id.toString()); 736 unresCritExts.remove(PolicyConstraints_Id.toString()); 737 unresCritExts.remove(InhibitAnyPolicy_Id.toString()); 738 unresCritExts.remove(SubjectAlternativeName_Id.toString()); 739 unresCritExts.remove(KeyUsage_Id.toString()); 740 unresCritExts.remove(ExtendedKeyUsage_Id.toString()); 741 742 if (!unresCritExts.isEmpty()) 743 throw new CertPathValidatorException 744 ("Unrecognized critical extension(s)", null, null, -1, 745 PKIXReason.UNRECOGNIZED_CRIT_EXT); 746 } 747 } 748 749 /* 750 * if this is the target certificate (init=true), then we are 751 * not able to do any more verification, so just return 752 */ 753 if (currState.isInitial()) { 754 return; 755 } 756 757 /* we don't perform any validation of the trusted cert */ 758 if (!isTrustedCert) { 759 /* Make sure this is a CA cert */ 760 if (cert.getBasicConstraints() == -1) { 761 throw new CertificateException("cert is NOT a CA cert"); 762 } 763 764 /* 765 * Check keyUsage extension 766 */ 767 KeyChecker.verifyCAKeyUsage(cert); 768 } 769 770 /* 771 * the following checks are performed even when the cert 772 * is a trusted cert, since we are only extracting the 773 * subjectDN, and publicKey from the cert 774 * in order to verify a previous cert 775 */ 776 777 /* 778 * Check signature only if no key requiring key parameters has been 779 * encountered. 780 */ 781 if (!currState.keyParamsNeeded()) { 782 (currState.cert).verify(cert.getPublicKey(), 783 buildParams.sigProvider()); 784 } 785 } 786 787 /** 788 * Verifies whether the input certificate completes the path. 789 * Checks the cert against each trust anchor that was specified, in order, 790 * and returns true as soon as it finds a valid anchor. 791 * Returns true if the cert matches a trust anchor specified as a 792 * certificate or if the cert verifies with a trust anchor that 793 * was specified as a trusted {pubkey, caname} pair. Returns false if none 794 * of the trust anchors are valid for this cert. 795 * 796 * @param cert the certificate to test 797 * @return a boolean value indicating whether the cert completes the path. 798 */ 799 @Override 800 boolean isPathCompleted(X509Certificate cert) { 801 for (TrustAnchor anchor : trustAnchors) { 802 if (anchor.getTrustedCert() != null) { 803 if (cert.equals(anchor.getTrustedCert())) { 804 this.trustAnchor = anchor; 805 return true; 806 } else { 807 continue; 808 } 809 } 810 X500Principal principal = anchor.getCA(); 811 PublicKey publicKey = anchor.getCAPublicKey(); 812 813 if (principal != null && publicKey != null && 814 principal.equals(cert.getSubjectX500Principal())) { 815 if (publicKey.equals(cert.getPublicKey())) { 816 // the cert itself is a trust anchor 817 this.trustAnchor = anchor; 818 return true; 819 } 820 // else, it is a self-issued certificate of the anchor 821 } 822 823 // Check subject/issuer name chaining 824 if (principal == null || 825 !principal.equals(cert.getIssuerX500Principal())) { 826 continue; 827 } 828 829 // skip anchor if it contains a DSA key with no DSA params 830 if (PKIX.isDSAPublicKeyWithoutParams(publicKey)) { 831 continue; 832 } 833 834 /* 835 * Check signature 836 */ 837 try { 838 cert.verify(publicKey, buildParams.sigProvider()); 839 } catch (InvalidKeyException ike) { 840 if (debug != null) { 841 debug.println("ForwardBuilder.isPathCompleted() invalid " 842 + "DSA key found"); 843 } 844 continue; 845 } catch (GeneralSecurityException e){ 846 if (debug != null) { 847 debug.println("ForwardBuilder.isPathCompleted() " + 848 "unexpected exception"); 849 e.printStackTrace(); 850 } 851 continue; 852 } 853 854 this.trustAnchor = anchor; 855 return true; 856 } 857 858 return false; 859 } 860 861 /** Adds the certificate to the certPathList 862 * 863 * @param cert the certificate to be added 864 * @param certPathList the certification path list 865 */ 866 @Override 867 void addCertToPath(X509Certificate cert, 868 LinkedList<X509Certificate> certPathList) 869 { 870 certPathList.addFirst(cert); 871 } 872 873 /** Removes final certificate from the certPathList 874 * 875 * @param certPathList the certification path list 876 */ 877 @Override 878 void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) { 879 certPathList.removeFirst(); 880 } 881} 882