1 /* 2 * Copyright (c) 2000, 2013, 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 26 package sun.security.provider.certpath; 27 28 import java.io.IOException; 29 import java.security.GeneralSecurityException; 30 import java.security.InvalidKeyException; 31 import java.security.PublicKey; 32 import java.security.cert.CertificateException; 33 import java.security.cert.CertPathValidatorException; 34 import java.security.cert.PKIXReason; 35 import java.security.cert.CertStore; 36 import java.security.cert.CertStoreException; 37 import java.security.cert.PKIXBuilderParameters; 38 import java.security.cert.PKIXCertPathChecker; 39 import java.security.cert.TrustAnchor; 40 import java.security.cert.X509Certificate; 41 import java.security.cert.X509CertSelector; 42 import java.util.*; 43 import javax.security.auth.x500.X500Principal; 44 45 import sun.security.provider.certpath.PKIX.BuilderParams; 46 import sun.security.util.Debug; 47 import sun.security.x509.AccessDescription; 48 import sun.security.x509.AuthorityInfoAccessExtension; 49 import static sun.security.x509.PKIXExtensions.*; 50 import sun.security.x509.X500Name; 51 import 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 */ 62 class 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(): ca is target"); 213 } 214 215 if (caTargetSelector == null) { 216 caTargetSelector = 217 (X509CertSelector) targetCertConstraints.clone(); 218 219 /* 220 * Since we don't check the validity period of trusted 221 * certificates, please don't set the certificate valid 222 * criterion unless the trusted certificate matching is 223 * completed. 224 */ 225 226 /* 227 * Policy processing optimizations 228 */ 229 if (buildParams.explicitPolicyRequired()) 230 caTargetSelector.setPolicy(getMatchingPolicies()); 231 } 232 233 sel = caTargetSelector; 234 } else { 235 236 if (caSelector == null) { 237 caSelector = new AdaptableX509CertSelector(); 238 239 /* 240 * Since we don't check the validity period of trusted 241 * certificates, please don't set the certificate valid 242 * criterion unless the trusted certificate matching is 243 * completed. 244 */ 245 246 /* 247 * Policy processing optimizations 248 */ 249 if (buildParams.explicitPolicyRequired()) 250 caSelector.setPolicy(getMatchingPolicies()); 251 } 252 253 /* 254 * Match on subject (issuer of previous cert) 255 */ 256 caSelector.setSubject(currentState.issuerDN); 257 258 /* 259 * Match on subjectNamesTraversed (both DNs and AltNames) 260 * (checks that current cert's name constraints permit it 261 * to certify all the DNs and AltNames that have been traversed) 262 */ 263 CertPathHelper.setPathToNames 264 (caSelector, currentState.subjectNamesTraversed); 265 266 /* 267 * Facilitate certification path construction with authority 268 * key identifier and subject key identifier. 269 */ 270 AuthorityKeyIdentifierExtension akidext = 271 currentState.cert.getAuthorityKeyIdentifierExtension(); 272 caSelector.parseAuthorityKeyIdentifierExtension(akidext); 273 274 /* 275 * check the validity period 276 */ 277 caSelector.setValidityPeriod(currentState.cert.getNotBefore(), 278 currentState.cert.getNotAfter()); 279 280 sel = caSelector; 281 } 282 283 /* 284 * For compatibility, conservatively, we don't check the path 285 * length constraint of trusted anchors. Please don't set the 286 * basic constraints criterion unless the trusted certificate 287 * matching is completed. 288 */ 289 sel.setBasicConstraints(-1); 290 291 for (X509Certificate trustedCert : trustedCerts) { 292 if (sel.match(trustedCert)) { 293 if (debug != null) { 294 debug.println("ForwardBuilder.getMatchingCACerts: " 295 + "found matching trust anchor"); 296 } 297 if (caCerts.add(trustedCert) && !searchAllCertStores) { 298 return; 299 } 300 } 301 } 302 303 /* 304 * The trusted certificate matching is completed. We need to match 305 * on certificate validity date. 306 */ 307 sel.setCertificateValid(buildParams.date()); 308 309 /* 310 * Require CA certs with a pathLenConstraint that allows 311 * at least as many CA certs that have already been traversed 312 */ 313 sel.setBasicConstraints(currentState.traversedCACerts); 314 315 /* 316 * If we have already traversed as many CA certs as the maxPathLength 317 * will allow us to, then we don't bother looking through these 318 * certificate pairs. If maxPathLength has a value of -1, this 319 * means it is unconstrained, so we always look through the 320 * certificate pairs. 321 */ 322 if (currentState.isInitial() || 323 (buildParams.maxPathLength() == -1) || 324 (buildParams.maxPathLength() > currentState.traversedCACerts)) 325 { 326 if (addMatchingCerts(sel, certStores, 327 caCerts, searchAllCertStores) 328 && !searchAllCertStores) { 329 return; 330 } 331 } 332 333 if (!currentState.isInitial() && Builder.USE_AIA) { 334 // check for AuthorityInformationAccess extension 335 AuthorityInfoAccessExtension aiaExt = 336 currentState.cert.getAuthorityInfoAccessExtension(); 337 if (aiaExt != null) { 338 getCerts(aiaExt, caCerts); 339 } 340 } 341 342 if (debug != null) { 343 int numCerts = caCerts.size() - initialSize; 344 debug.println("ForwardBuilder.getMatchingCACerts: found " + 345 numCerts + " CA certs"); 346 } 347 } 348 349 /** 350 * Download Certificates from the given AIA and add them to the 351 * specified Collection. 352 */ 353 // cs.getCertificates(caSelector) returns a collection of X509Certificate's 354 // because of the selector, so the cast is safe 355 @SuppressWarnings("unchecked") 356 private boolean getCerts(AuthorityInfoAccessExtension aiaExt, 357 Collection<X509Certificate> certs) 358 { 359 if (Builder.USE_AIA == false) { 360 return false; 361 } 362 List<AccessDescription> adList = aiaExt.getAccessDescriptions(); 363 if (adList == null || adList.isEmpty()) { 364 return false; 365 } 366 367 boolean add = false; 368 for (AccessDescription ad : adList) { 369 CertStore cs = URICertStore.getInstance(ad); 370 if (cs != null) { 371 try { 372 if (certs.addAll((Collection<X509Certificate>) 373 cs.getCertificates(caSelector))) { 374 add = true; 375 if (!searchAllCertStores) { 376 return true; 377 } 378 } 379 } catch (CertStoreException cse) { 380 if (debug != null) { 381 debug.println("exception getting certs from CertStore:"); 382 cse.printStackTrace(); 383 } 384 } 385 } 386 } 387 return add; 388 } 389 390 /** 391 * This inner class compares 2 PKIX certificates according to which 392 * should be tried first when building a path from the target. 393 * The preference order is as follows: 394 * 395 * Given trusted certificate(s): 396 * Subject:ou=D,ou=C,o=B,c=A 397 * 398 * Preference order for current cert: 399 * 400 * 1) Issuer matches a trusted subject 401 * Issuer: ou=D,ou=C,o=B,c=A 402 * 403 * 2) Issuer is a descendant of a trusted subject (in order of 404 * number of links to the trusted subject) 405 * a) Issuer: ou=E,ou=D,ou=C,o=B,c=A [links=1] 406 * b) Issuer: ou=F,ou=E,ou=D,ou=C,ou=B,c=A [links=2] 407 * 408 * 3) Issuer is an ancestor of a trusted subject (in order of number of 409 * links to the trusted subject) 410 * a) Issuer: ou=C,o=B,c=A [links=1] 411 * b) Issuer: o=B,c=A [links=2] 412 * 413 * 4) Issuer is in the same namespace as a trusted subject (in order of 414 * number of links to the trusted subject) 415 * a) Issuer: ou=G,ou=C,o=B,c=A [links=2] 416 * b) Issuer: ou=H,o=B,c=A [links=3] 417 * 418 * 5) Issuer is an ancestor of certificate subject (in order of number 419 * of links to the certificate subject) 420 * a) Issuer: ou=K,o=J,c=A 421 * Subject: ou=L,ou=K,o=J,c=A 422 * b) Issuer: o=J,c=A 423 * Subject: ou=L,ou=K,0=J,c=A 424 * 425 * 6) Any other certificates 426 */ 427 static class PKIXCertComparator implements Comparator<X509Certificate> { 428 429 final static String METHOD_NME = "PKIXCertComparator.compare()"; 430 431 private final Set<X500Principal> trustedSubjectDNs; 432 433 PKIXCertComparator(Set<X500Principal> trustedSubjectDNs) { 434 this.trustedSubjectDNs = trustedSubjectDNs; 435 } 436 437 /** 438 * @param oCert1 First X509Certificate to be compared 439 * @param oCert2 Second X509Certificate to be compared 440 * @return -1 if oCert1 is preferable to oCert2, or 441 * if oCert1 and oCert2 are equally preferable (in this 442 * case it doesn't matter which is preferable, but we don't 443 * return 0 because the comparator would behave strangely 444 * when used in a SortedSet). 445 * 1 if oCert2 is preferable to oCert1 446 * 0 if oCert1.equals(oCert2). We only return 0 if the 447 * certs are equal so that this comparator behaves 448 * correctly when used in a SortedSet. 449 * @throws ClassCastException if either argument is not of type 450 * X509Certificate 451 */ 452 @Override 453 public int compare(X509Certificate oCert1, X509Certificate oCert2) { 454 455 // if certs are the same, return 0 456 if (oCert1.equals(oCert2)) return 0; 457 458 X500Principal cIssuer1 = oCert1.getIssuerX500Principal(); 459 X500Principal cIssuer2 = oCert2.getIssuerX500Principal(); 460 X500Name cIssuer1Name = X500Name.asX500Name(cIssuer1); 461 X500Name cIssuer2Name = X500Name.asX500Name(cIssuer2); 462 463 if (debug != null) { 464 debug.println(METHOD_NME + " o1 Issuer: " + cIssuer1); 465 debug.println(METHOD_NME + " o2 Issuer: " + cIssuer2); 466 } 467 468 /* If one cert's issuer matches a trusted subject, then it is 469 * preferable. 470 */ 471 if (debug != null) { 472 debug.println(METHOD_NME + " MATCH TRUSTED SUBJECT TEST..."); 473 } 474 475 boolean m1 = trustedSubjectDNs.contains(cIssuer1); 476 boolean m2 = trustedSubjectDNs.contains(cIssuer2); 477 if (debug != null) { 478 debug.println(METHOD_NME + " m1: " + m1); 479 debug.println(METHOD_NME + " m2: " + m2); 480 } 481 if (m1 && m2) { 482 return -1; 483 } else if (m1) { 484 return -1; 485 } else if (m2) { 486 return 1; 487 } 488 489 /* If one cert's issuer is a naming descendant of a trusted subject, 490 * then it is preferable, in order of increasing naming distance. 491 */ 492 if (debug != null) { 493 debug.println(METHOD_NME + " NAMING DESCENDANT TEST..."); 494 } 495 for (X500Principal tSubject : trustedSubjectDNs) { 496 X500Name tSubjectName = X500Name.asX500Name(tSubject); 497 int distanceTto1 = 498 Builder.distance(tSubjectName, cIssuer1Name, -1); 499 int distanceTto2 = 500 Builder.distance(tSubjectName, cIssuer2Name, -1); 501 if (debug != null) { 502 debug.println(METHOD_NME +" distanceTto1: " + distanceTto1); 503 debug.println(METHOD_NME +" distanceTto2: " + distanceTto2); 504 } 505 if (distanceTto1 > 0 || distanceTto2 > 0) { 506 if (distanceTto1 == distanceTto2) { 507 return -1; 508 } else if (distanceTto1 > 0 && distanceTto2 <= 0) { 509 return -1; 510 } else if (distanceTto1 <= 0 && distanceTto2 > 0) { 511 return 1; 512 } else if (distanceTto1 < distanceTto2) { 513 return -1; 514 } else { // distanceTto1 > distanceTto2 515 return 1; 516 } 517 } 518 } 519 520 /* If one cert's issuer is a naming ancestor of a trusted subject, 521 * then it is preferable, in order of increasing naming distance. 522 */ 523 if (debug != null) { 524 debug.println(METHOD_NME + " NAMING ANCESTOR TEST..."); 525 } 526 for (X500Principal tSubject : trustedSubjectDNs) { 527 X500Name tSubjectName = X500Name.asX500Name(tSubject); 528 529 int distanceTto1 = Builder.distance 530 (tSubjectName, cIssuer1Name, Integer.MAX_VALUE); 531 int distanceTto2 = Builder.distance 532 (tSubjectName, cIssuer2Name, Integer.MAX_VALUE); 533 if (debug != null) { 534 debug.println(METHOD_NME +" distanceTto1: " + distanceTto1); 535 debug.println(METHOD_NME +" distanceTto2: " + distanceTto2); 536 } 537 if (distanceTto1 < 0 || distanceTto2 < 0) { 538 if (distanceTto1 == distanceTto2) { 539 return -1; 540 } else if (distanceTto1 < 0 && distanceTto2 >= 0) { 541 return -1; 542 } else if (distanceTto1 >= 0 && distanceTto2 < 0) { 543 return 1; 544 } else if (distanceTto1 > distanceTto2) { 545 return -1; 546 } else { 547 return 1; 548 } 549 } 550 } 551 552 /* If one cert's issuer is in the same namespace as a trusted 553 * subject, then it is preferable, in order of increasing naming 554 * distance. 555 */ 556 if (debug != null) { 557 debug.println(METHOD_NME +" SAME NAMESPACE AS TRUSTED TEST..."); 558 } 559 for (X500Principal tSubject : trustedSubjectDNs) { 560 X500Name tSubjectName = X500Name.asX500Name(tSubject); 561 X500Name tAo1 = tSubjectName.commonAncestor(cIssuer1Name); 562 X500Name tAo2 = tSubjectName.commonAncestor(cIssuer2Name); 563 if (debug != null) { 564 debug.println(METHOD_NME +" tAo1: " + String.valueOf(tAo1)); 565 debug.println(METHOD_NME +" tAo2: " + String.valueOf(tAo2)); 566 } 567 if (tAo1 != null || tAo2 != null) { 568 if (tAo1 != null && tAo2 != null) { 569 int hopsTto1 = Builder.hops 570 (tSubjectName, cIssuer1Name, Integer.MAX_VALUE); 571 int hopsTto2 = Builder.hops 572 (tSubjectName, cIssuer2Name, Integer.MAX_VALUE); 573 if (debug != null) { 574 debug.println(METHOD_NME +" hopsTto1: " + hopsTto1); 575 debug.println(METHOD_NME +" hopsTto2: " + hopsTto2); 576 } 577 if (hopsTto1 == hopsTto2) { 578 } else if (hopsTto1 > hopsTto2) { 579 return 1; 580 } else { // hopsTto1 < hopsTto2 581 return -1; 582 } 583 } else if (tAo1 == null) { 584 return 1; 585 } else { 586 return -1; 587 } 588 } 589 } 590 591 592 /* If one cert's issuer is an ancestor of that cert's subject, 593 * then it is preferable, in order of increasing naming distance. 594 */ 595 if (debug != null) { 596 debug.println(METHOD_NME+" CERT ISSUER/SUBJECT COMPARISON TEST..."); 597 } 598 X500Principal cSubject1 = oCert1.getSubjectX500Principal(); 599 X500Principal cSubject2 = oCert2.getSubjectX500Principal(); 600 X500Name cSubject1Name = X500Name.asX500Name(cSubject1); 601 X500Name cSubject2Name = X500Name.asX500Name(cSubject2); 602 603 if (debug != null) { 604 debug.println(METHOD_NME + " o1 Subject: " + cSubject1); 605 debug.println(METHOD_NME + " o2 Subject: " + cSubject2); 606 } 607 int distanceStoI1 = Builder.distance 608 (cSubject1Name, cIssuer1Name, Integer.MAX_VALUE); 609 int distanceStoI2 = Builder.distance 610 (cSubject2Name, cIssuer2Name, Integer.MAX_VALUE); 611 if (debug != null) { 612 debug.println(METHOD_NME + " distanceStoI1: " + distanceStoI1); 613 debug.println(METHOD_NME + " distanceStoI2: " + distanceStoI2); 614 } 615 if (distanceStoI2 > distanceStoI1) { 616 return -1; 617 } else if (distanceStoI2 < distanceStoI1) { 618 return 1; 619 } 620 621 /* Otherwise, certs are equally preferable. 622 */ 623 if (debug != null) { 624 debug.println(METHOD_NME + " no tests matched; RETURN 0"); 625 } 626 return -1; 627 } 628 } 629 630 /** 631 * Verifies a matching certificate. 632 * 633 * This method executes the validation steps in the PKIX path 634 * validation algorithm <draft-ietf-pkix-new-part1-08.txt> which were 635 * not satisfied by the selection criteria used by getCertificates() 636 * to find the certs and only the steps that can be executed in a 637 * forward direction (target to trust anchor). Those steps that can 638 * only be executed in a reverse direction are deferred until the 639 * complete path has been built. 640 * 641 * Trust anchor certs are not validated, but are used to verify the 642 * signature and revocation status of the previous cert. 643 * 644 * If the last certificate is being verified (the one whose subject 645 * matches the target subject, then steps in 6.1.4 of the PKIX 646 * Certification Path Validation algorithm are NOT executed, 647 * regardless of whether or not the last cert is an end-entity 648 * cert or not. This allows callers to certify CA certs as 649 * well as EE certs. 650 * 651 * @param cert the certificate to be verified 652 * @param currentState the current state against which the cert is verified 653 * @param certPathList the certPathList generated thus far 654 */ 655 @Override 656 void verifyCert(X509Certificate cert, State currentState, 657 List<X509Certificate> certPathList) 658 throws GeneralSecurityException 659 { 660 if (debug != null) { 661 debug.println("ForwardBuilder.verifyCert(SN: " 662 + Debug.toHexString(cert.getSerialNumber()) 663 + "\n Issuer: " + cert.getIssuerX500Principal() + ")" 664 + "\n Subject: " + cert.getSubjectX500Principal() + ")"); 665 } 666 667 ForwardState currState = (ForwardState)currentState; 668 669 // Don't bother to verify untrusted certificate more. 670 currState.untrustedChecker.check(cert, Collections.<String>emptySet()); 671 672 /* 673 * check for looping - abort a loop if we encounter the same 674 * certificate twice 675 */ 676 if (certPathList != null) { 677 for (X509Certificate cpListCert : certPathList) { 678 if (cert.equals(cpListCert)) { 679 if (debug != null) { 680 debug.println("loop detected!!"); 681 } 682 throw new CertPathValidatorException("loop detected"); 683 } 684 } 685 } 686 687 /* check if trusted cert */ 688 boolean isTrustedCert = trustedCerts.contains(cert); 689 690 /* we don't perform any validation of the trusted cert */ 691 if (!isTrustedCert) { 692 /* 693 * Check CRITICAL private extensions for user checkers that 694 * support forward checking (forwardCheckers) and remove 695 * ones we know how to check. 696 */ 697 Set<String> unresCritExts = cert.getCriticalExtensionOIDs(); 698 if (unresCritExts == null) { 699 unresCritExts = Collections.<String>emptySet(); 700 } 701 for (PKIXCertPathChecker checker : currState.forwardCheckers) { 702 checker.check(cert, unresCritExts); 703 } 704 705 /* 706 * Remove extensions from user checkers that don't support 707 * forward checking. After this step, we will have removed 708 * all extensions that all user checkers are capable of 709 * processing. 710 */ 711 for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) { 712 if (!checker.isForwardCheckingSupported()) { 713 Set<String> supportedExts = checker.getSupportedExtensions(); 714 if (supportedExts != null) { 715 unresCritExts.removeAll(supportedExts); 716 } 717 } 718 } 719 720 /* 721 * Look at the remaining extensions and remove any ones we know how 722 * to check. If there are any left, throw an exception! 723 */ 724 if (!unresCritExts.isEmpty()) { 725 unresCritExts.remove(BasicConstraints_Id.toString()); 726 unresCritExts.remove(NameConstraints_Id.toString()); 727 unresCritExts.remove(CertificatePolicies_Id.toString()); 728 unresCritExts.remove(PolicyMappings_Id.toString()); 729 unresCritExts.remove(PolicyConstraints_Id.toString()); 730 unresCritExts.remove(InhibitAnyPolicy_Id.toString()); 731 unresCritExts.remove(SubjectAlternativeName_Id.toString()); 732 unresCritExts.remove(KeyUsage_Id.toString()); 733 unresCritExts.remove(ExtendedKeyUsage_Id.toString()); 734 735 if (!unresCritExts.isEmpty()) 736 throw new CertPathValidatorException 737 ("Unrecognized critical extension(s)", null, null, -1, 738 PKIXReason.UNRECOGNIZED_CRIT_EXT); 739 } 740 } 741 742 /* 743 * if this is the target certificate (init=true), then we are 744 * not able to do any more verification, so just return 745 */ 746 if (currState.isInitial()) { 747 return; 748 } 749 750 /* we don't perform any validation of the trusted cert */ 751 if (!isTrustedCert) { 752 /* Make sure this is a CA cert */ 753 if (cert.getBasicConstraints() == -1) { 754 throw new CertificateException("cert is NOT a CA cert"); 755 } 756 757 /* 758 * Check keyUsage extension 759 */ 760 KeyChecker.verifyCAKeyUsage(cert); 761 } 762 763 /* 764 * the following checks are performed even when the cert 765 * is a trusted cert, since we are only extracting the 766 * subjectDN, and publicKey from the cert 767 * in order to verify a previous cert 768 */ 769 770 /* 771 * Check signature only if no key requiring key parameters has been 772 * encountered. 773 */ 774 if (!currState.keyParamsNeeded()) { 775 if (buildParams.sigProvider() != null) { 776 (currState.cert).verify(cert.getPublicKey(), 777 buildParams.sigProvider()); 778 } else { 779 (currState.cert).verify(cert.getPublicKey()); 780 } 781 } 782 } 783 784 /** 785 * Verifies whether the input certificate completes the path. 786 * Checks the cert against each trust anchor that was specified, in order, 787 * and returns true as soon as it finds a valid anchor. 788 * Returns true if the cert matches a trust anchor specified as a 789 * certificate or if the cert verifies with a trust anchor that 790 * was specified as a trusted {pubkey, caname} pair. Returns false if none 791 * of the trust anchors are valid for this cert. 792 * 793 * @param cert the certificate to test 794 * @return a boolean value indicating whether the cert completes the path. 795 */ 796 @Override 797 boolean isPathCompleted(X509Certificate cert) { 798 for (TrustAnchor anchor : trustAnchors) { 799 if (anchor.getTrustedCert() != null) { 800 if (cert.equals(anchor.getTrustedCert())) { 801 this.trustAnchor = anchor; 802 return true; 803 } else { 804 continue; 805 } 806 } 807 X500Principal principal = anchor.getCA(); 808 PublicKey publicKey = anchor.getCAPublicKey(); 809 810 if (principal != null && publicKey != null && 811 principal.equals(cert.getSubjectX500Principal())) { 812 if (publicKey.equals(cert.getPublicKey())) { 813 // the cert itself is a trust anchor 814 this.trustAnchor = anchor; 815 return true; 816 } 817 // else, it is a self-issued certificate of the anchor 818 } 819 820 // Check subject/issuer name chaining 821 if (principal == null || 822 !principal.equals(cert.getIssuerX500Principal())) { 823 continue; 824 } 825 826 // skip anchor if it contains a DSA key with no DSA params 827 if (PKIX.isDSAPublicKeyWithoutParams(publicKey)) { 828 continue; 829 } 830 831 /* 832 * Check signature 833 */ 834 try { 835 if (buildParams.sigProvider() != null) { 836 cert.verify(publicKey, buildParams.sigProvider()); 837 } else { 838 cert.verify(publicKey); 839 } 840 } catch (InvalidKeyException ike) { 841 if (debug != null) { 842 debug.println("ForwardBuilder.isPathCompleted() invalid " 843 + "DSA key found"); 844 } 845 continue; 846 } catch (GeneralSecurityException e){ 847 if (debug != null) { 848 debug.println("ForwardBuilder.isPathCompleted() " + 849 "unexpected exception"); 850 e.printStackTrace(); 851 } 852 continue; 853 } 854 855 this.trustAnchor = anchor; 856 return true; 857 } 858 859 return false; 860 } 861 862 /** Adds the certificate to the certPathList 863 * 864 * @param cert the certificate to be added 865 * @param certPathList the certification path list 866 */ 867 @Override 868 void addCertToPath(X509Certificate cert, 869 LinkedList<X509Certificate> certPathList) 870 { 871 certPathList.addFirst(cert); 872 } 873 874 /** Removes final certificate from the certPathList 875 * 876 * @param certPathList the certification path list 877 */ 878 @Override 879 void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) { 880 certPathList.removeFirst(); 881 } 882 } 883