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.math.BigInteger; 29 import java.util.Arrays; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.Date; 34 import java.util.List; 35 import java.util.HashSet; 36 import java.util.Set; 37 import java.util.Iterator; 38 import java.security.InvalidAlgorithmParameterException; 39 import java.security.NoSuchAlgorithmException; 40 import java.security.PublicKey; 41 import java.security.cert.*; 42 import java.security.cert.CertPathValidatorException.BasicReason; 43 import java.security.interfaces.DSAPublicKey; 44 import javax.security.auth.x500.X500Principal; 45 import sun.security.util.Debug; 46 import sun.security.x509.AccessDescription; 47 import sun.security.x509.AuthorityInfoAccessExtension; 48 import sun.security.x509.CRLDistributionPointsExtension; 49 import sun.security.x509.DistributionPoint; 50 import sun.security.x509.GeneralName; 51 import sun.security.x509.GeneralNames; 52 import sun.security.x509.PKIXExtensions; 53 import sun.security.x509.X500Name; 54 import sun.security.x509.X509CertImpl; 55 import sun.security.x509.X509CRLEntryImpl; 56 57 /** 58 * CrlRevocationChecker is a <code>PKIXCertPathChecker</code> that checks 59 * revocation status information on a PKIX certificate using CRLs obtained 60 * from one or more <code>CertStores</code>. This is based on section 6.3 61 * of RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt). 62 * 63 * @since 1.4 64 * @author Seth Proctor 65 * @author Steve Hanna 66 */ 67 class CrlRevocationChecker extends PKIXCertPathChecker { 68 69 private static final Debug debug = Debug.getInstance("certpath"); 70 private final TrustAnchor mAnchor; 71 private final List<CertStore> mStores; 72 private final String mSigProvider; 73 private final Date mCurrentTime; 74 private PublicKey mPrevPubKey; 75 private boolean mCRLSignFlag; 76 private HashSet<X509CRL> mPossibleCRLs; 77 private HashSet<X509CRL> mApprovedCRLs; 78 private final PKIXParameters mParams; 79 private static final boolean [] mCrlSignUsage = 80 { false, false, false, false, false, false, true }; 81 private static final boolean[] ALL_REASONS = 82 {true, true, true, true, true, true, true, true, true}; 83 private boolean mOnlyEECert = false; 84 85 // Maximum clock skew in milliseconds (15 minutes) allowed when checking 86 // validity of CRLs 87 private static final long MAX_CLOCK_SKEW = 900000; 88 89 /** 90 * Creates a <code>CrlRevocationChecker</code>. 91 * 92 * @param anchor anchor selected to validate the target certificate 93 * @param params <code>PKIXParameters</code> to be used for 94 * finding certificates and CRLs, etc. 95 */ 96 CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params) 97 throws CertPathValidatorException 98 { 99 this(anchor, params, null); 100 } 101 102 /** 103 * Creates a <code>CrlRevocationChecker</code>, allowing 104 * extra certificates to be supplied beyond those contained 105 * in the <code>PKIXParameters</code>. 106 * 107 * @param anchor anchor selected to validate the target certificate 108 * @param params <code>PKIXParameters</code> to be used for 109 * finding certificates and CRLs, etc. 110 * @param certs a <code>Collection</code> of certificates 111 * that may be useful, beyond those available 112 * through <code>params</code> (<code>null</code> 113 * if none) 114 */ 115 CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, 116 Collection<X509Certificate> certs) throws CertPathValidatorException 117 { 118 this(anchor, params, certs, false); 119 } 120 121 CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, 122 Collection<X509Certificate> certs, boolean onlyEECert) 123 throws CertPathValidatorException { 124 mAnchor = anchor; 125 mParams = params; 126 mStores = new ArrayList<CertStore>(params.getCertStores()); 127 mSigProvider = params.getSigProvider(); 128 if (certs != null) { 129 try { 130 mStores.add(CertStore.getInstance("Collection", 131 new CollectionCertStoreParameters(certs))); 132 } catch (Exception e) { 133 // should never occur but not necessarily fatal, so log it, 134 // ignore and continue 135 if (debug != null) { 136 debug.println("CrlRevocationChecker: " + 137 "error creating Collection CertStore: " + e); 138 } 139 } 140 } 141 Date testDate = params.getDate(); 142 mCurrentTime = (testDate != null ? testDate : new Date()); 143 mOnlyEECert = onlyEECert; 144 init(false); 145 } 146 147 /** 148 * Initializes the internal state of the checker from parameters 149 * specified in the constructor 150 */ 151 public void init(boolean forward) throws CertPathValidatorException 152 { 153 if (!forward) { 154 if (mAnchor != null) { 155 if (mAnchor.getCAPublicKey() != null) { 156 mPrevPubKey = mAnchor.getCAPublicKey(); 157 } else { 158 mPrevPubKey = mAnchor.getTrustedCert().getPublicKey(); 159 } 160 } else { 161 mPrevPubKey = null; 162 } 163 mCRLSignFlag = true; 164 } else { 165 throw new CertPathValidatorException("forward checking " 166 + "not supported"); 167 } 168 } 169 170 public boolean isForwardCheckingSupported() { 171 return false; 172 } 173 174 public Set<String> getSupportedExtensions() { 175 return null; 176 } 177 178 /** 179 * Performs the revocation status check on the certificate using 180 * its internal state. 181 * 182 * @param cert the Certificate 183 * @param unresolvedCritExts a Collection of the unresolved critical 184 * extensions 185 * @exception CertPathValidatorException Exception thrown if 186 * certificate does not verify 187 */ 188 public void check(Certificate cert, Collection<String> unresolvedCritExts) 189 throws CertPathValidatorException 190 { 191 X509Certificate currCert = (X509Certificate) cert; 192 verifyRevocationStatus(currCert, mPrevPubKey, mCRLSignFlag, true); 193 194 // Make new public key if parameters are missing 195 PublicKey cKey = currCert.getPublicKey(); 196 if (cKey instanceof DSAPublicKey && 197 ((DSAPublicKey)cKey).getParams() == null) { 198 // cKey needs to inherit DSA parameters from prev key 199 cKey = BasicChecker.makeInheritedParamsKey(cKey, mPrevPubKey); 200 } 201 mPrevPubKey = cKey; 202 mCRLSignFlag = certCanSignCrl(currCert); 203 } 204 205 /** 206 * Performs the revocation status check on the certificate using 207 * the provided state variables, as well as the constant internal 208 * data. 209 * 210 * @param currCert the Certificate 211 * @param prevKey the previous PublicKey in the chain 212 * @param signFlag a boolean as returned from the last call, or true 213 * if this is the first cert in the chain 214 * @return a boolean specifying if the cert is allowed to vouch for the 215 * validity of a CRL for the next iteration 216 * @exception CertPathValidatorException Exception thrown if 217 * certificate does not verify. 218 */ 219 public boolean check(X509Certificate currCert, PublicKey prevKey, 220 boolean signFlag) throws CertPathValidatorException 221 { 222 verifyRevocationStatus(currCert, prevKey, signFlag, true); 223 return certCanSignCrl(currCert); 224 } 225 226 /** 227 * Checks that a cert can be used to verify a CRL. 228 * 229 * @param currCert an X509Certificate to check 230 * @return a boolean specifying if the cert is allowed to vouch for the 231 * validity of a CRL 232 */ 233 static boolean certCanSignCrl(X509Certificate currCert) { 234 // if the cert doesn't include the key usage ext, or 235 // the key usage ext asserts cRLSigning, return true, 236 // otherwise return false. 237 boolean[] kbools = currCert.getKeyUsage(); 238 if (kbools != null) { 239 return kbools[6]; 240 } 241 return false; 242 } 243 244 /** 245 * Internal method to start the verification of a cert 246 */ 247 private void verifyRevocationStatus(X509Certificate currCert, 248 PublicKey prevKey, boolean signFlag, boolean allowSeparateKey) 249 throws CertPathValidatorException 250 { 251 verifyRevocationStatus(currCert, prevKey, signFlag, 252 allowSeparateKey, null, mParams.getTrustAnchors()); 253 } 254 255 /** 256 * Internal method to start the verification of a cert 257 * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> 258 * whose revocation status depends on the 259 * non-revoked status of this cert. To avoid 260 * circular dependencies, we assume they're 261 * revoked while checking the revocation 262 * status of this cert. 263 * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s 264 */ 265 private void verifyRevocationStatus(X509Certificate currCert, 266 PublicKey prevKey, boolean signFlag, boolean allowSeparateKey, 267 Set<X509Certificate> stackedCerts, 268 Set<TrustAnchor> trustAnchors) throws CertPathValidatorException { 269 270 String msg = "revocation status"; 271 if (debug != null) { 272 debug.println("CrlRevocationChecker.verifyRevocationStatus()" + 273 " ---checking " + msg + "..."); 274 } 275 276 if (mOnlyEECert && currCert.getBasicConstraints() != -1) { 277 if (debug != null) { 278 debug.println("Skipping revocation check, not end entity cert"); 279 } 280 return; 281 } 282 283 // reject circular dependencies - RFC 3280 is not explicit on how 284 // to handle this, so we feel it is safest to reject them until 285 // the issue is resolved in the PKIX WG. 286 if ((stackedCerts != null) && stackedCerts.contains(currCert)) { 287 if (debug != null) { 288 debug.println("CrlRevocationChecker.verifyRevocationStatus()" + 289 " circular dependency"); 290 } 291 throw new CertPathValidatorException 292 ("Could not determine revocation status", null, null, -1, 293 BasicReason.UNDETERMINED_REVOCATION_STATUS); 294 } 295 296 // init the state for this run 297 mPossibleCRLs = new HashSet<X509CRL>(); 298 mApprovedCRLs = new HashSet<X509CRL>(); 299 boolean[] reasonsMask = new boolean[9]; 300 301 try { 302 X509CRLSelector sel = new X509CRLSelector(); 303 sel.setCertificateChecking(currCert); 304 CertPathHelper.setDateAndTime(sel, mCurrentTime, MAX_CLOCK_SKEW); 305 306 for (CertStore mStore : mStores) { 307 for (java.security.cert.CRL crl : mStore.getCRLs(sel)) { 308 mPossibleCRLs.add((X509CRL)crl); 309 } 310 } 311 // all CRLs returned by the DP Fetcher have also been verified 312 mApprovedCRLs.addAll(DistributionPointFetcher.getCRLs(sel, signFlag, 313 prevKey, mSigProvider, mStores, reasonsMask, trustAnchors, 314 mParams.getDate())); 315 } catch (Exception e) { 316 if (debug != null) { 317 debug.println("CrlRevocationChecker.verifyRevocationStatus() " 318 + "unexpected exception: " + e.getMessage()); 319 } 320 throw new CertPathValidatorException(e); 321 } 322 323 if (debug != null) { 324 debug.println("CrlRevocationChecker.verifyRevocationStatus() " + 325 "crls.size() = " + mPossibleCRLs.size()); 326 } 327 if (!mPossibleCRLs.isEmpty()) { 328 // Now that we have a list of possible CRLs, see which ones can 329 // be approved 330 mApprovedCRLs.addAll(verifyPossibleCRLs(mPossibleCRLs, currCert, 331 signFlag, prevKey, reasonsMask, trustAnchors)); 332 } 333 if (debug != null) { 334 debug.println("CrlRevocationChecker.verifyRevocationStatus() " + 335 "approved crls.size() = " + mApprovedCRLs.size()); 336 } 337 338 // make sure that we have at least one CRL that _could_ cover 339 // the certificate in question and all reasons are covered 340 if (mApprovedCRLs.isEmpty() || 341 !Arrays.equals(reasonsMask, ALL_REASONS)) { 342 if (allowSeparateKey) { 343 verifyWithSeparateSigningKey(currCert, prevKey, signFlag, 344 stackedCerts); 345 return; 346 } else { 347 throw new CertPathValidatorException 348 ("Could not determine revocation status", null, null, -1, 349 BasicReason.UNDETERMINED_REVOCATION_STATUS); 350 } 351 } 352 353 // See if the cert is in the set of approved crls. 354 if (debug != null) { 355 BigInteger sn = currCert.getSerialNumber(); 356 debug.println("CrlRevocationChecker.verifyRevocationStatus() " + 357 "starting the final sweep..."); 358 debug.println("CrlRevocationChecker.verifyRevocationStatus" + 359 " cert SN: " + sn.toString()); 360 } 361 362 CRLReason reasonCode = CRLReason.UNSPECIFIED; 363 X509CRLEntryImpl entry = null; 364 for (X509CRL crl : mApprovedCRLs) { 365 X509CRLEntry e = crl.getRevokedCertificate(currCert); 366 if (e != null) { 367 try { 368 entry = X509CRLEntryImpl.toImpl(e); 369 } catch (CRLException ce) { 370 throw new CertPathValidatorException(ce); 371 } 372 if (debug != null) { 373 debug.println("CrlRevocationChecker.verifyRevocationStatus" 374 + " CRL entry: " + entry.toString()); 375 } 376 377 /* 378 * Abort CRL validation and throw exception if there are any 379 * unrecognized critical CRL entry extensions (see section 380 * 5.3 of RFC 3280). 381 */ 382 Set<String> unresCritExts = entry.getCriticalExtensionOIDs(); 383 if (unresCritExts != null && !unresCritExts.isEmpty()) { 384 /* remove any that we will process */ 385 unresCritExts.remove 386 (PKIXExtensions.ReasonCode_Id.toString()); 387 unresCritExts.remove 388 (PKIXExtensions.CertificateIssuer_Id.toString()); 389 if (!unresCritExts.isEmpty()) { 390 if (debug != null) { 391 debug.println("Unrecognized " 392 + "critical extension(s) in revoked CRL entry: " 393 + unresCritExts); 394 } 395 throw new CertPathValidatorException 396 ("Could not determine revocation status", null, null, 397 -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); 398 } 399 } 400 401 reasonCode = entry.getRevocationReason(); 402 if (reasonCode == null) { 403 reasonCode = CRLReason.UNSPECIFIED; 404 } 405 Throwable t = new CertificateRevokedException 406 (entry.getRevocationDate(), reasonCode, 407 crl.getIssuerX500Principal(), entry.getExtensions()); 408 throw new CertPathValidatorException(t.getMessage(), t, 409 null, -1, BasicReason.REVOKED); 410 } 411 } 412 } 413 414 /** 415 * We have a cert whose revocation status couldn't be verified by 416 * a CRL issued by the cert that issued the CRL. See if we can 417 * find a valid CRL issued by a separate key that can verify the 418 * revocation status of this certificate. 419 * <p> 420 * Note that this does not provide support for indirect CRLs, 421 * only CRLs signed with a different key (but the same issuer 422 * name) as the certificate being checked. 423 * 424 * @param currCert the <code>X509Certificate</code> to be checked 425 * @param prevKey the <code>PublicKey</code> that failed 426 * @param signFlag <code>true</code> if that key was trusted to sign CRLs 427 * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> 428 * whose revocation status depends on the 429 * non-revoked status of this cert. To avoid 430 * circular dependencies, we assume they're 431 * revoked while checking the revocation 432 * status of this cert. 433 * @throws CertPathValidatorException if the cert's revocation status 434 * cannot be verified successfully with another key 435 */ 436 private void verifyWithSeparateSigningKey(X509Certificate currCert, 437 PublicKey prevKey, boolean signFlag, Set<X509Certificate> stackedCerts) 438 throws CertPathValidatorException { 439 String msg = "revocation status"; 440 if (debug != null) { 441 debug.println( 442 "CrlRevocationChecker.verifyWithSeparateSigningKey()" + 443 " ---checking " + msg + "..."); 444 } 445 446 // reject circular dependencies - RFC 3280 is not explicit on how 447 // to handle this, so we feel it is safest to reject them until 448 // the issue is resolved in the PKIX WG. 449 if ((stackedCerts != null) && stackedCerts.contains(currCert)) { 450 if (debug != null) { 451 debug.println( 452 "CrlRevocationChecker.verifyWithSeparateSigningKey()" + 453 " circular dependency"); 454 } 455 throw new CertPathValidatorException 456 ("Could not determine revocation status", null, null, 457 -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); 458 } 459 460 // If prevKey wasn't trusted, maybe we just didn't have the right 461 // path to it. Don't rule that key out. 462 if (!signFlag) { 463 prevKey = null; 464 } 465 466 // Try to find another key that might be able to sign 467 // CRLs vouching for this cert. 468 buildToNewKey(currCert, prevKey, stackedCerts); 469 } 470 471 /** 472 * Tries to find a CertPath that establishes a key that can be 473 * used to verify the revocation status of a given certificate. 474 * Ignores keys that have previously been tried. Throws a 475 * CertPathValidatorException if no such key could be found. 476 * 477 * @param currCert the <code>X509Certificate</code> to be checked 478 * @param prevKey the <code>PublicKey</code> of the certificate whose key 479 * cannot be used to vouch for the CRL and should be ignored 480 * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> 481 * whose revocation status depends on the 482 * establishment of this path. 483 * @throws CertPathValidatorException on failure 484 */ 485 private void buildToNewKey(X509Certificate currCert, 486 PublicKey prevKey, Set<X509Certificate> stackedCerts) 487 throws CertPathValidatorException { 488 489 if (debug != null) { 490 debug.println("CrlRevocationChecker.buildToNewKey()" + 491 " starting work"); 492 } 493 Set<PublicKey> badKeys = new HashSet<PublicKey>(); 494 if (prevKey != null) { 495 badKeys.add(prevKey); 496 } 497 X509CertSelector certSel = new RejectKeySelector(badKeys); 498 certSel.setSubject(currCert.getIssuerX500Principal()); 499 certSel.setKeyUsage(mCrlSignUsage); 500 501 Set<TrustAnchor> newAnchors = 502 (mAnchor == null ? mParams.getTrustAnchors() : 503 Collections.singleton(mAnchor)); 504 505 PKIXBuilderParameters builderParams; 506 if (mParams instanceof PKIXBuilderParameters) { 507 builderParams = (PKIXBuilderParameters) mParams.clone(); 508 builderParams.setTargetCertConstraints(certSel); 509 // Policy qualifiers must be rejected, since we don't have 510 // any way to convey them back to the application. 511 builderParams.setPolicyQualifiersRejected(true); 512 try { 513 builderParams.setTrustAnchors(newAnchors); 514 } catch (InvalidAlgorithmParameterException iape) { 515 throw new RuntimeException(iape); // should never occur 516 } 517 } else { 518 // It's unfortunate that there's no easy way to make a 519 // PKIXBuilderParameters object from a PKIXParameters 520 // object. This might miss some things if parameters 521 // are added in the future or the validatorParams object 522 // is a custom class derived from PKIXValidatorParameters. 523 try { 524 builderParams = new PKIXBuilderParameters(newAnchors, certSel); 525 } catch (InvalidAlgorithmParameterException iape) { 526 throw new RuntimeException(iape); // should never occur 527 } 528 builderParams.setInitialPolicies(mParams.getInitialPolicies()); 529 builderParams.setCertStores(mStores); 530 builderParams.setExplicitPolicyRequired 531 (mParams.isExplicitPolicyRequired()); 532 builderParams.setPolicyMappingInhibited 533 (mParams.isPolicyMappingInhibited()); 534 builderParams.setAnyPolicyInhibited(mParams.isAnyPolicyInhibited()); 535 // Policy qualifiers must be rejected, since we don't have 536 // any way to convey them back to the application. 537 // That's the default, so no need to write code. 538 builderParams.setDate(mParams.getDate()); 539 builderParams.setCertPathCheckers(mParams.getCertPathCheckers()); 540 builderParams.setSigProvider(mParams.getSigProvider()); 541 } 542 543 // Skip revocation during this build to detect circular 544 // references. But check revocation afterwards, using the 545 // key (or any other that works). 546 builderParams.setRevocationEnabled(false); 547 548 // check for AuthorityInformationAccess extension 549 if (Builder.USE_AIA == true) { 550 X509CertImpl currCertImpl = null; 551 try { 552 currCertImpl = X509CertImpl.toImpl(currCert); 553 } catch (CertificateException ce) { 554 // ignore but log it 555 if (debug != null) { 556 debug.println("CrlRevocationChecker.buildToNewKey: " + 557 "error decoding cert: " + ce); 558 } 559 } 560 AuthorityInfoAccessExtension aiaExt = null; 561 if (currCertImpl != null) { 562 aiaExt = currCertImpl.getAuthorityInfoAccessExtension(); 563 } 564 if (aiaExt != null) { 565 List<AccessDescription> adList = aiaExt.getAccessDescriptions(); 566 if (adList != null) { 567 for (AccessDescription ad : adList) { 568 CertStore cs = URICertStore.getInstance(ad); 569 if (cs != null) { 570 if (debug != null) { 571 debug.println("adding AIAext CertStore"); 572 } 573 builderParams.addCertStore(cs); 574 } 575 } 576 } 577 } 578 } 579 580 CertPathBuilder builder = null; 581 try { 582 builder = CertPathBuilder.getInstance("PKIX"); 583 } catch (NoSuchAlgorithmException nsae) { 584 throw new CertPathValidatorException(nsae); 585 } 586 while (true) { 587 try { 588 if (debug != null) { 589 debug.println("CrlRevocationChecker.buildToNewKey()" + 590 " about to try build ..."); 591 } 592 PKIXCertPathBuilderResult cpbr = 593 (PKIXCertPathBuilderResult) builder.build(builderParams); 594 595 if (debug != null) { 596 debug.println("CrlRevocationChecker.buildToNewKey()" + 597 " about to check revocation ..."); 598 } 599 // Now check revocation of all certs in path, assuming that 600 // the stackedCerts are revoked. 601 if (stackedCerts == null) { 602 stackedCerts = new HashSet<X509Certificate>(); 603 } 604 stackedCerts.add(currCert); 605 TrustAnchor ta = cpbr.getTrustAnchor(); 606 PublicKey prevKey2 = ta.getCAPublicKey(); 607 if (prevKey2 == null) { 608 prevKey2 = ta.getTrustedCert().getPublicKey(); 609 } 610 boolean signFlag = true; 611 List<? extends Certificate> cpList = 612 cpbr.getCertPath().getCertificates(); 613 try { 614 for (int i = cpList.size()-1; i >= 0; i-- ) { 615 X509Certificate cert = (X509Certificate) cpList.get(i); 616 617 if (debug != null) { 618 debug.println("CrlRevocationChecker.buildToNewKey()" 619 + " index " + i + " checking " + cert); 620 } 621 verifyRevocationStatus(cert, prevKey2, signFlag, true, 622 stackedCerts, newAnchors); 623 signFlag = certCanSignCrl(cert); 624 prevKey2 = cert.getPublicKey(); 625 } 626 } catch (CertPathValidatorException cpve) { 627 // ignore it and try to get another key 628 badKeys.add(cpbr.getPublicKey()); 629 continue; 630 } 631 632 if (debug != null) { 633 debug.println("CrlRevocationChecker.buildToNewKey()" + 634 " got key " + cpbr.getPublicKey()); 635 } 636 // Now check revocation on the current cert using that key. 637 // If it doesn't check out, try to find a different key. 638 // And if we can't find a key, then return false. 639 PublicKey newKey = cpbr.getPublicKey(); 640 try { 641 verifyRevocationStatus(currCert, newKey, true, false); 642 // If that passed, the cert is OK! 643 return; 644 } catch (CertPathValidatorException cpve) { 645 // If it is revoked, rethrow exception 646 if (cpve.getReason() == BasicReason.REVOKED) { 647 throw cpve; 648 } 649 // Otherwise, ignore the exception and 650 // try to get another key. 651 } 652 badKeys.add(newKey); 653 } catch (InvalidAlgorithmParameterException iape) { 654 throw new CertPathValidatorException(iape); 655 } catch (CertPathBuilderException cpbe) { 656 throw new CertPathValidatorException 657 ("Could not determine revocation status", null, null, 658 -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); 659 } 660 } 661 } 662 663 /* 664 * This inner class extends the X509CertSelector to add an additional 665 * check to make sure the subject public key isn't on a particular list. 666 * This class is used by buildToNewKey() to make sure the builder doesn't 667 * end up with a CertPath to a public key that has already been rejected. 668 */ 669 private static class RejectKeySelector extends X509CertSelector { 670 private final Set<PublicKey> badKeySet; 671 672 /** 673 * Creates a new <code>RejectKeySelector</code>. 674 * 675 * @param badPublicKeys a <code>Set</code> of 676 * <code>PublicKey</code>s that 677 * should be rejected (or <code>null</code> 678 * if no such check should be done) 679 */ 680 RejectKeySelector(Set<PublicKey> badPublicKeys) { 681 this.badKeySet = badPublicKeys; 682 } 683 684 /** 685 * Decides whether a <code>Certificate</code> should be selected. 686 * 687 * @param cert the <code>Certificate</code> to be checked 688 * @return <code>true</code> if the <code>Certificate</code> should be 689 * selected, <code>false</code> otherwise 690 */ 691 public boolean match(Certificate cert) { 692 if (!super.match(cert)) 693 return(false); 694 695 if (badKeySet.contains(cert.getPublicKey())) { 696 if (debug != null) 697 debug.println("RejectCertSelector.match: bad key"); 698 return false; 699 } 700 701 if (debug != null) 702 debug.println("RejectCertSelector.match: returning true"); 703 return true; 704 } 705 706 /** 707 * Return a printable representation of the <code>CertSelector</code>. 708 * 709 * @return a <code>String</code> describing the contents of the 710 * <code>CertSelector</code> 711 */ 712 public String toString() { 713 StringBuilder sb = new StringBuilder(); 714 sb.append("RejectCertSelector: [\n"); 715 sb.append(super.toString()); 716 sb.append(badKeySet); 717 sb.append("]"); 718 return sb.toString(); 719 } 720 } 721 722 /** 723 * Internal method that verifies a set of possible_crls, 724 * and sees if each is approved, based on the cert. 725 * 726 * @param crls a set of possible CRLs to test for acceptability 727 * @param cert the certificate whose revocation status is being checked 728 * @param signFlag <code>true</code> if prevKey was trusted to sign CRLs 729 * @param prevKey the public key of the issuer of cert 730 * @param reasonsMask the reason code mask 731 * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s> 732 * @return a collection of approved crls (or an empty collection) 733 */ 734 private Collection<X509CRL> verifyPossibleCRLs(Set<X509CRL> crls, 735 X509Certificate cert, boolean signFlag, PublicKey prevKey, 736 boolean[] reasonsMask, 737 Set<TrustAnchor> trustAnchors) throws CertPathValidatorException { 738 739 try { 740 X509CertImpl certImpl = X509CertImpl.toImpl(cert); 741 if (debug != null) { 742 debug.println("CRLRevocationChecker.verifyPossibleCRLs: " + 743 "Checking CRLDPs for " 744 + certImpl.getSubjectX500Principal()); 745 } 746 CRLDistributionPointsExtension ext = 747 certImpl.getCRLDistributionPointsExtension(); 748 List<DistributionPoint> points = null; 749 if (ext == null) { 750 // assume a DP with reasons and CRLIssuer fields omitted 751 // and a DP name of the cert issuer. 752 // TODO add issuerAltName too 753 X500Name certIssuer = (X500Name)certImpl.getIssuerDN(); 754 DistributionPoint point = new DistributionPoint 755 (new GeneralNames().add(new GeneralName(certIssuer)), 756 null, null); 757 points = Collections.singletonList(point); 758 } else { 759 points = (List<DistributionPoint>)ext.get( 760 CRLDistributionPointsExtension.POINTS); 761 } 762 Set<X509CRL> results = new HashSet<X509CRL>(); 763 for (Iterator<DistributionPoint> t = points.iterator(); 764 t.hasNext() && !Arrays.equals(reasonsMask, ALL_REASONS); ) { 765 DistributionPoint point = t.next(); 766 for (X509CRL crl : crls) { 767 if (DistributionPointFetcher.verifyCRL(certImpl, point, crl, 768 reasonsMask, signFlag, prevKey, mSigProvider, 769 trustAnchors, mStores, mParams.getDate())) { 770 results.add(crl); 771 } 772 } 773 } 774 return results; 775 } catch (Exception e) { 776 if (debug != null) { 777 debug.println("Exception while verifying CRL: "+e.getMessage()); 778 e.printStackTrace(); 779 } 780 return Collections.emptySet(); 781 } 782 } 783 }