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