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 }