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.Principal; 31 import java.security.cert.CertificateException; 32 import java.security.cert.CertPathValidatorException; 33 import java.security.cert.CertStore; 34 import java.security.cert.CertStoreException; 35 import java.security.cert.PKIXBuilderParameters; 36 import java.security.cert.PKIXCertPathChecker; 37 import java.security.cert.PKIXParameters; 38 import java.security.cert.PKIXReason; 39 import java.security.cert.TrustAnchor; 40 import java.security.cert.X509Certificate; 41 import java.security.cert.X509CertSelector; 42 import java.util.ArrayList; 43 import java.util.Collection; 44 import java.util.Collections; 45 import java.util.Comparator; 46 import java.util.HashSet; 47 import java.util.Iterator; 48 import java.util.List; 49 import java.util.LinkedList; 50 import java.util.Set; 51 52 import javax.security.auth.x500.X500Principal; 53 54 import sun.security.provider.certpath.PKIX.BuilderParams; 55 import sun.security.util.Debug; 56 import sun.security.x509.Extension; 57 import static sun.security.x509.PKIXExtensions.*; 58 import sun.security.x509.X500Name; 59 import sun.security.x509.X509CertImpl; 60 import sun.security.x509.PolicyMappingsExtension; 61 62 /** 63 * This class represents a reverse builder, which is able to retrieve 64 * matching certificates from CertStores and verify a particular certificate 65 * against a ReverseState. 66 * 67 * @since 1.4 68 * @author Sean Mullan 69 * @author Yassir Elley 70 */ 71 72 class ReverseBuilder extends Builder { 73 74 private Debug debug = Debug.getInstance("certpath"); 75 76 private final Set<String> initPolicies; 77 78 /** 79 * Initialize the builder with the input parameters. 80 * 81 * @param params the parameter set used to build a certification path 82 */ 83 ReverseBuilder(BuilderParams buildParams) { 84 super(buildParams); 85 86 Set<String> initialPolicies = buildParams.initialPolicies(); 87 initPolicies = new HashSet<String>(); 88 if (initialPolicies.isEmpty()) { 89 // if no initialPolicies are specified by user, set 90 // initPolicies to be anyPolicy by default 91 initPolicies.add(PolicyChecker.ANY_POLICY); 92 } else { 93 initPolicies.addAll(initialPolicies); 94 } 95 } 96 97 /** 98 * Retrieves all certs from the specified CertStores that satisfy the 99 * requirements specified in the parameters and the current 100 * PKIX state (name constraints, policy constraints, etc). 101 * 102 * @param currentState the current state. 103 * Must be an instance of <code>ReverseState</code> 104 * @param certStores list of CertStores 105 */ 106 @Override 107 Collection<X509Certificate> getMatchingCerts 108 (State currState, List<CertStore> certStores) 109 throws CertStoreException, CertificateException, IOException 110 { 111 ReverseState currentState = (ReverseState) currState; 112 113 if (debug != null) 114 debug.println("In ReverseBuilder.getMatchingCerts."); 115 116 /* 117 * The last certificate could be an EE or a CA certificate 118 * (we may be building a partial certification path or 119 * establishing trust in a CA). 120 * 121 * Try the EE certs before the CA certs. It will be more 122 * common to build a path to an end entity. 123 */ 124 Collection<X509Certificate> certs = 125 getMatchingEECerts(currentState, certStores); 126 certs.addAll(getMatchingCACerts(currentState, certStores)); 127 128 return certs; 129 } 130 131 /* 132 * Retrieves all end-entity certificates which satisfy constraints 133 * and requirements specified in the parameters and PKIX state. 134 */ 135 private Collection<X509Certificate> getMatchingEECerts 136 (ReverseState currentState, List<CertStore> certStores) 137 throws CertStoreException, CertificateException, IOException { 138 139 /* 140 * Compose a CertSelector to filter out 141 * certs which do not satisfy requirements. 142 * 143 * First, retrieve clone of current target cert constraints, and 144 * then add more selection criteria based on current validation state. 145 */ 146 X509CertSelector sel = (X509CertSelector) targetCertConstraints.clone(); 147 148 /* 149 * Match on issuer (subject of previous cert) 150 */ 151 sel.setIssuer(currentState.subjectDN); 152 153 /* 154 * Match on certificate validity date. 155 */ 156 sel.setCertificateValid(buildParams.date()); 157 158 /* 159 * Policy processing optimizations 160 */ 161 if (currentState.explicitPolicy == 0) 162 sel.setPolicy(getMatchingPolicies()); 163 164 /* 165 * If previous cert has a subject key identifier extension, 166 * use it to match on authority key identifier extension. 167 */ 168 /*if (currentState.subjKeyId != null) { 169 AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension( 170 (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID), 171 null, null); 172 sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue()); 173 }*/ 174 175 /* 176 * Require EE certs 177 */ 178 sel.setBasicConstraints(-2); 179 180 /* Retrieve matching certs from CertStores */ 181 HashSet<X509Certificate> eeCerts = new HashSet<>(); 182 addMatchingCerts(sel, certStores, eeCerts, true); 183 184 if (debug != null) { 185 debug.println("ReverseBuilder.getMatchingEECerts got " 186 + eeCerts.size() + " certs."); 187 } 188 return eeCerts; 189 } 190 191 /* 192 * Retrieves all CA certificates which satisfy constraints 193 * and requirements specified in the parameters and PKIX state. 194 */ 195 private Collection<X509Certificate> getMatchingCACerts 196 (ReverseState currentState, List<CertStore> certStores) 197 throws CertificateException, CertStoreException, IOException { 198 199 /* 200 * Compose a CertSelector to filter out 201 * certs which do not satisfy requirements. 202 */ 203 X509CertSelector sel = new X509CertSelector(); 204 205 /* 206 * Match on issuer (subject of previous cert) 207 */ 208 sel.setIssuer(currentState.subjectDN); 209 210 /* 211 * Match on certificate validity date. 212 */ 213 sel.setCertificateValid(buildParams.date()); 214 215 /* 216 * Match on target subject name (checks that current cert's 217 * name constraints permit it to certify target). 218 * (4 is the integer type for DIRECTORY name). 219 */ 220 byte[] subject = targetCertConstraints.getSubjectAsBytes(); 221 if (subject != null) { 222 sel.addPathToName(4, subject); 223 } else { 224 X509Certificate cert = targetCertConstraints.getCertificate(); 225 if (cert != null) { 226 sel.addPathToName(4, 227 cert.getSubjectX500Principal().getEncoded()); 228 } 229 } 230 231 /* 232 * Policy processing optimizations 233 */ 234 if (currentState.explicitPolicy == 0) 235 sel.setPolicy(getMatchingPolicies()); 236 237 /* 238 * If previous cert has a subject key identifier extension, 239 * use it to match on authority key identifier extension. 240 */ 241 /*if (currentState.subjKeyId != null) { 242 AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension( 243 (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID), 244 null, null); 245 sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue()); 246 }*/ 247 248 /* 249 * Require CA certs 250 */ 251 sel.setBasicConstraints(0); 252 253 /* Retrieve matching certs from CertStores */ 254 ArrayList<X509Certificate> reverseCerts = new ArrayList<>(); 255 addMatchingCerts(sel, certStores, reverseCerts, true); 256 257 /* Sort remaining certs using name constraints */ 258 Collections.sort(reverseCerts, new PKIXCertComparator()); 259 260 if (debug != null) 261 debug.println("ReverseBuilder.getMatchingCACerts got " + 262 reverseCerts.size() + " certs."); 263 return reverseCerts; 264 } 265 266 /* 267 * This inner class compares 2 PKIX certificates according to which 268 * should be tried first when building a path to the target. For 269 * now, the algorithm is to look at name constraints in each cert and those 270 * which constrain the path closer to the target should be 271 * ranked higher. Later, we may want to consider other components, 272 * such as key identifiers. 273 */ 274 class PKIXCertComparator implements Comparator<X509Certificate> { 275 276 private Debug debug = Debug.getInstance("certpath"); 277 278 @Override 279 public int compare(X509Certificate cert1, X509Certificate cert2) { 280 281 /* 282 * if either cert certifies the target, always 283 * put at head of list. 284 */ 285 X500Principal targetSubject = buildParams.targetSubject(); 286 if (cert1.getSubjectX500Principal().equals(targetSubject)) { 287 return -1; 288 } 289 if (cert2.getSubjectX500Principal().equals(targetSubject)) { 290 return 1; 291 } 292 293 int targetDist1; 294 int targetDist2; 295 try { 296 X500Name targetSubjectName = X500Name.asX500Name(targetSubject); 297 targetDist1 = Builder.targetDistance( 298 null, cert1, targetSubjectName); 299 targetDist2 = Builder.targetDistance( 300 null, cert2, targetSubjectName); 301 } catch (IOException e) { 302 if (debug != null) { 303 debug.println("IOException in call to Builder.targetDistance"); 304 e.printStackTrace(); 305 } 306 throw new ClassCastException 307 ("Invalid target subject distinguished name"); 308 } 309 310 if (targetDist1 == targetDist2) 311 return 0; 312 313 if (targetDist1 == -1) 314 return 1; 315 316 if (targetDist1 < targetDist2) 317 return -1; 318 319 return 1; 320 } 321 } 322 323 /** 324 * Verifies a matching certificate. 325 * 326 * This method executes any of the validation steps in the PKIX path validation 327 * algorithm which were not satisfied via filtering out non-compliant 328 * certificates with certificate matching rules. 329 * 330 * If the last certificate is being verified (the one whose subject 331 * matches the target subject, then the steps in Section 6.1.4 of the 332 * Certification Path Validation algorithm are NOT executed, 333 * regardless of whether or not the last cert is an end-entity 334 * cert or not. This allows callers to certify CA certs as 335 * well as EE certs. 336 * 337 * @param cert the certificate to be verified 338 * @param currentState the current state against which the cert is verified 339 * @param certPathList the certPathList generated thus far 340 */ 341 @Override 342 void verifyCert(X509Certificate cert, State currState, 343 List<X509Certificate> certPathList) 344 throws GeneralSecurityException 345 { 346 if (debug != null) { 347 debug.println("ReverseBuilder.verifyCert(SN: " 348 + Debug.toHexString(cert.getSerialNumber()) 349 + "\n Subject: " + cert.getSubjectX500Principal() + ")"); 350 } 351 352 ReverseState currentState = (ReverseState) currState; 353 354 /* we don't perform any validation of the trusted cert */ 355 if (currentState.isInitial()) { 356 return; 357 } 358 359 // Don't bother to verify untrusted certificate more. 360 currentState.untrustedChecker.check(cert, 361 Collections.<String>emptySet()); 362 363 /* 364 * check for looping - abort a loop if 365 * ((we encounter the same certificate twice) AND 366 * ((policyMappingInhibited = true) OR (no policy mapping 367 * extensions can be found between the occurrences of the same 368 * certificate))) 369 * in order to facilitate the check to see if there are 370 * any policy mapping extensions found between the occurrences 371 * of the same certificate, we reverse the certpathlist first 372 */ 373 if ((certPathList != null) && (!certPathList.isEmpty())) { 374 List<X509Certificate> reverseCertList = new ArrayList<>(); 375 for (X509Certificate c : certPathList) { 376 reverseCertList.add(0, c); 377 } 378 379 boolean policyMappingFound = false; 380 for (X509Certificate cpListCert : reverseCertList) { 381 X509CertImpl cpListCertImpl = X509CertImpl.toImpl(cpListCert); 382 PolicyMappingsExtension policyMappingsExt = 383 cpListCertImpl.getPolicyMappingsExtension(); 384 if (policyMappingsExt != null) { 385 policyMappingFound = true; 386 } 387 if (debug != null) 388 debug.println("policyMappingFound = " + policyMappingFound); 389 if (cert.equals(cpListCert)) { 390 if ((buildParams.policyMappingInhibited()) || 391 (!policyMappingFound)){ 392 if (debug != null) 393 debug.println("loop detected!!"); 394 throw new CertPathValidatorException("loop detected"); 395 } 396 } 397 } 398 } 399 400 /* check if target cert */ 401 boolean finalCert = cert.getSubjectX500Principal().equals(buildParams.targetSubject()); 402 403 /* check if CA cert */ 404 boolean caCert = (cert.getBasicConstraints() != -1 ? true : false); 405 406 /* if there are more certs to follow, verify certain constraints */ 407 if (!finalCert) { 408 409 /* check if CA cert */ 410 if (!caCert) 411 throw new CertPathValidatorException("cert is NOT a CA cert"); 412 413 /* If the certificate was not self-issued, verify that 414 * remainingCerts is greater than zero 415 */ 416 if ((currentState.remainingCACerts <= 0) && !X509CertImpl.isSelfIssued(cert)) { 417 throw new CertPathValidatorException 418 ("pathLenConstraint violated, path too long", null, 419 null, -1, PKIXReason.PATH_TOO_LONG); 420 } 421 422 /* 423 * Check keyUsage extension (only if CA cert and not final cert) 424 */ 425 KeyChecker.verifyCAKeyUsage(cert); 426 427 } else { 428 429 /* 430 * If final cert, check that it satisfies specified target 431 * constraints 432 */ 433 if (targetCertConstraints.match(cert) == false) { 434 throw new CertPathValidatorException("target certificate " + 435 "constraints check failed"); 436 } 437 } 438 439 /* 440 * Check revocation. 441 */ 442 if (buildParams.revocationEnabled() && currentState.revChecker != null) { 443 currentState.revChecker.check(cert, Collections.<String>emptySet()); 444 } 445 446 /* Check name constraints if this is not a self-issued cert */ 447 if (finalCert || !X509CertImpl.isSelfIssued(cert)){ 448 if (currentState.nc != null) { 449 try { 450 if (!currentState.nc.verify(cert)){ 451 throw new CertPathValidatorException 452 ("name constraints check failed", null, null, -1, 453 PKIXReason.INVALID_NAME); 454 } 455 } catch (IOException ioe) { 456 throw new CertPathValidatorException(ioe); 457 } 458 } 459 } 460 461 /* 462 * Check policy 463 */ 464 X509CertImpl certImpl = X509CertImpl.toImpl(cert); 465 currentState.rootNode = PolicyChecker.processPolicies 466 (currentState.certIndex, initPolicies, 467 currentState.explicitPolicy, currentState.policyMapping, 468 currentState.inhibitAnyPolicy, 469 buildParams.policyQualifiersRejected(), currentState.rootNode, 470 certImpl, finalCert); 471 472 /* 473 * Check CRITICAL private extensions 474 */ 475 Set<String> unresolvedCritExts = cert.getCriticalExtensionOIDs(); 476 if (unresolvedCritExts == null) { 477 unresolvedCritExts = Collections.<String>emptySet(); 478 } 479 480 /* 481 * Check that the signature algorithm is not disabled. 482 */ 483 currentState.algorithmChecker.check(cert, unresolvedCritExts); 484 485 for (PKIXCertPathChecker checker : currentState.userCheckers) { 486 checker.check(cert, unresolvedCritExts); 487 } 488 489 /* 490 * Look at the remaining extensions and remove any ones we have 491 * already checked. If there are any left, throw an exception! 492 */ 493 if (!unresolvedCritExts.isEmpty()) { 494 unresolvedCritExts.remove(BasicConstraints_Id.toString()); 495 unresolvedCritExts.remove(NameConstraints_Id.toString()); 496 unresolvedCritExts.remove(CertificatePolicies_Id.toString()); 497 unresolvedCritExts.remove(PolicyMappings_Id.toString()); 498 unresolvedCritExts.remove(PolicyConstraints_Id.toString()); 499 unresolvedCritExts.remove(InhibitAnyPolicy_Id.toString()); 500 unresolvedCritExts.remove(SubjectAlternativeName_Id.toString()); 501 unresolvedCritExts.remove(KeyUsage_Id.toString()); 502 unresolvedCritExts.remove(ExtendedKeyUsage_Id.toString()); 503 504 if (!unresolvedCritExts.isEmpty()) 505 throw new CertPathValidatorException 506 ("Unrecognized critical extension(s)", null, null, -1, 507 PKIXReason.UNRECOGNIZED_CRIT_EXT); 508 } 509 510 /* 511 * Check signature. 512 */ 513 if (buildParams.sigProvider() != null) { 514 cert.verify(currentState.pubKey, buildParams.sigProvider()); 515 } else { 516 cert.verify(currentState.pubKey); 517 } 518 } 519 520 /** 521 * Verifies whether the input certificate completes the path. 522 * This checks whether the cert is the target certificate. 523 * 524 * @param cert the certificate to test 525 * @return a boolean value indicating whether the cert completes the path. 526 */ 527 @Override 528 boolean isPathCompleted(X509Certificate cert) { 529 return cert.getSubjectX500Principal().equals(buildParams.targetSubject()); 530 } 531 532 /** Adds the certificate to the certPathList 533 * 534 * @param cert the certificate to be added 535 * @param certPathList the certification path list 536 */ 537 @Override 538 void addCertToPath(X509Certificate cert, 539 LinkedList<X509Certificate> certPathList) { 540 certPathList.addLast(cert); 541 } 542 543 /** Removes final certificate from the certPathList 544 * 545 * @param certPathList the certification path list 546 */ 547 @Override 548 void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) { 549 certPathList.removeLast(); 550 } 551 } 552