/* * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.provider.certpath; import java.math.BigInteger;import java.util.Arrays;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.Date;import java.util.List;import java.util.HashSet;import java.util.Set;import java.util.Iterator;import java.security.InvalidAlgorithmParameterException;import java.security.NoSuchAlgorithmException;import java.security.PublicKey;import java.security.cert.*;import java.security.cert.CertPathValidatorException.BasicReason;import java.security.interfaces.DSAPublicKey;import javax.security.auth.x500.X500Principal;import sun.security.util.Debug;import sun.security.x509.AccessDescription;import sun.security.x509.AuthorityInfoAccessExtension;import sun.security.x509.CRLDistributionPointsExtension;import sun.security.x509.DistributionPoint;import sun.security.x509.GeneralName;import sun.security.x509.GeneralNames;import sun.security.x509.PKIXExtensions;import sun.security.x509.X500Name;import sun.security.x509.X509CertImpl;import sun.security.x509.X509CRLEntryImpl; /** * CrlRevocationChecker is a <code>PKIXCertPathChecker</code> that checks * revocation status information on a PKIX certificate using CRLs obtained * from one or more <code>CertStores</code>. This is based on section 6.3 * of RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt). * * @since 1.4 * @author Seth Proctor * @author Steve Hanna */class CrlRevocationChecker extends PKIXCertPathChecker { private static final Debug debug = Debug.getInstance("certpath"); private final TrustAnchor mAnchor; private final List<CertStore> mStores; private final String mSigProvider; private final Date mCurrentTime; private PublicKey mPrevPubKey; private boolean mCRLSignFlag; private HashSet<X509CRL> mPossibleCRLs; private HashSet<X509CRL> mApprovedCRLs; private final PKIXParameters mParams; private static final boolean [] mCrlSignUsage = { false, false, false, false, false, false, true }; private static final boolean[] ALL_REASONS = {true, true, true, true, true, true, true, true, true}; private boolean mOnlyEECert = false; // Maximum clock skew in milliseconds (15 minutes) allowed when checking // validity of CRLs private static final long MAX_CLOCK_SKEW = 900000; /** * Creates a <code>CrlRevocationChecker</code>. * * @param anchor anchor selected to validate the target certificate * @param params <code>PKIXParameters</code> to be used for * finding certificates and CRLs, etc. */ CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params) throws CertPathValidatorException { this(anchor, params, null); } /** * Creates a <code>CrlRevocationChecker</code>, allowing * extra certificates to be supplied beyond those contained * in the <code>PKIXParameters</code>. * * @param anchor anchor selected to validate the target certificate * @param params <code>PKIXParameters</code> to be used for * finding certificates and CRLs, etc. * @param certs a <code>Collection</code> of certificates * that may be useful, beyond those available * through <code>params</code> (<code>null</code> * if none) */ CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, Collection<X509Certificate> certs) throws CertPathValidatorException { this(anchor, params, certs, false); } CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, Collection<X509Certificate> certs, boolean onlyEECert) throws CertPathValidatorException { mAnchor = anchor; mParams = params; mStores = new ArrayList<CertStore>(params.getCertStores()); mSigProvider = params.getSigProvider(); if (certs != null) { try { mStores.add(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certs))); } catch (Exception e) { // should never occur but not necessarily fatal, so log it, // ignore and continue if (debug != null) { debug.println("CrlRevocationChecker: " + "error creating Collection CertStore: " + e); } } } Date testDate = params.getDate(); mCurrentTime = (testDate != null ? testDate : new Date()); mOnlyEECert = onlyEECert; init(false); } /** * Initializes the internal state of the checker from parameters * specified in the constructor */ public void init(boolean forward) throws CertPathValidatorException { if (!forward) { if (mAnchor != null) { if (mAnchor.getCAPublicKey() != null) { mPrevPubKey = mAnchor.getCAPublicKey(); } else { mPrevPubKey = mAnchor.getTrustedCert().getPublicKey(); } } else { mPrevPubKey = null; } mCRLSignFlag = true; } else { throw new CertPathValidatorException("forward checking " + "not supported"); } } public boolean isForwardCheckingSupported() { return false; } public Set<String> getSupportedExtensions() { return null; } /** * Performs the revocation status check on the certificate using * its internal state. * * @param cert the Certificate * @param unresolvedCritExts a Collection of the unresolved critical * extensions * @exception CertPathValidatorException Exception thrown if * certificate does not verify */ public void check(Certificate cert, Collection<String> unresolvedCritExts) throws CertPathValidatorException { X509Certificate currCert = (X509Certificate) cert; verifyRevocationStatus(currCert, mPrevPubKey, mCRLSignFlag, true); // Make new public key if parameters are missing PublicKey cKey = currCert.getPublicKey(); if (cKey instanceof DSAPublicKey && ((DSAPublicKey)cKey).getParams() == null) { // cKey needs to inherit DSA parameters from prev key cKey = BasicChecker.makeInheritedParamsKey(cKey, mPrevPubKey); } mPrevPubKey = cKey; mCRLSignFlag = certCanSignCrl(currCert); } /** * Performs the revocation status check on the certificate using * the provided state variables, as well as the constant internal * data. * * @param currCert the Certificate * @param prevKey the previous PublicKey in the chain * @param signFlag a boolean as returned from the last call, or true * if this is the first cert in the chain * @return a boolean specifying if the cert is allowed to vouch for the * validity of a CRL for the next iteration * @exception CertPathValidatorException Exception thrown if * certificate does not verify. */ public boolean check(X509Certificate currCert, PublicKey prevKey, boolean signFlag) throws CertPathValidatorException { verifyRevocationStatus(currCert, prevKey, signFlag, true); return certCanSignCrl(currCert); } /** * Checks that a cert can be used to verify a CRL. * * @param currCert an X509Certificate to check * @return a boolean specifying if the cert is allowed to vouch for the * validity of a CRL */ static boolean certCanSignCrl(X509Certificate currCert) { // if the cert doesn't include the key usage ext, or // the key usage ext asserts cRLSigning, return true, // otherwise return false. boolean[] kbools = currCert.getKeyUsage(); if (kbools != null) { return kbools[6]; } return false; } /** * Internal method to start the verification of a cert */ private void verifyRevocationStatus(X509Certificate currCert, PublicKey prevKey, boolean signFlag, boolean allowSeparateKey) throws CertPathValidatorException { verifyRevocationStatus(currCert, prevKey, signFlag, allowSeparateKey, null, mParams.getTrustAnchors()); } /** * Internal method to start the verification of a cert * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> * whose revocation status depends on the * non-revoked status of this cert. To avoid * circular dependencies, we assume they're * revoked while checking the revocation * status of this cert. * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s */ private void verifyRevocationStatus(X509Certificate currCert, PublicKey prevKey, boolean signFlag, boolean allowSeparateKey, Set<X509Certificate> stackedCerts, Set<TrustAnchor> trustAnchors) throws CertPathValidatorException { String msg = "revocation status"; if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus()" + " ---checking " + msg + "..."); } if (mOnlyEECert && currCert.getBasicConstraints() != -1) { if (debug != null) { debug.println("Skipping revocation check, not end entity cert"); } return; } // reject circular dependencies - RFC 3280 is not explicit on how // to handle this, so we feel it is safest to reject them until // the issue is resolved in the PKIX WG. if ((stackedCerts != null) && stackedCerts.contains(currCert)) { if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus()" + " circular dependency"); } throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } // init the state for this run mPossibleCRLs = new HashSet<X509CRL>(); mApprovedCRLs = new HashSet<X509CRL>(); boolean[] reasonsMask = new boolean[9]; try { X509CRLSelector sel = new X509CRLSelector(); sel.setCertificateChecking(currCert); CertPathHelper.setDateAndTime(sel, mCurrentTime, MAX_CLOCK_SKEW); for (CertStore mStore : mStores) { for (java.security.cert.CRL crl : mStore.getCRLs(sel)) { mPossibleCRLs.add((X509CRL)crl); } } DistributionPointFetcher store = DistributionPointFetcher.getInstance(); // all CRLs returned by the DP Fetcher have also been verified mApprovedCRLs.addAll(store.getCRLs(sel, signFlag, prevKey, mSigProvider, mStores, reasonsMask, trustAnchors, mParams.getDate())); } catch (Exception e) { if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "unexpected exception: " + e.getMessage()); } throw new CertPathValidatorException(e); } if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "crls.size() = " + mPossibleCRLs.size()); } if (!mPossibleCRLs.isEmpty()) { // Now that we have a list of possible CRLs, see which ones can // be approved mApprovedCRLs.addAll(verifyPossibleCRLs(mPossibleCRLs, currCert, signFlag, prevKey, reasonsMask, trustAnchors)); } if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "approved crls.size() = " + mApprovedCRLs.size()); } // make sure that we have at least one CRL that _could_ cover // the certificate in question and all reasons are covered if (mApprovedCRLs.isEmpty() || !Arrays.equals(reasonsMask, ALL_REASONS)) { if (allowSeparateKey) { verifyWithSeparateSigningKey(currCert, prevKey, signFlag, stackedCerts); return; } else { throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } // See if the cert is in the set of approved crls. if (debug != null) { BigInteger sn = currCert.getSerialNumber(); debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "starting the final sweep..."); debug.println("CrlRevocationChecker.verifyRevocationStatus" + " cert SN: " + sn.toString()); } CRLReason reasonCode = CRLReason.UNSPECIFIED; X509CRLEntryImpl entry = null; for (X509CRL crl : mApprovedCRLs) { X509CRLEntry e = crl.getRevokedCertificate(currCert); if (e != null) { try { entry = X509CRLEntryImpl.toImpl(e); } catch (CRLException ce) { throw new CertPathValidatorException(ce); } if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus" + " CRL entry: " + entry.toString()); } /* * Abort CRL validation and throw exception if there are any * unrecognized critical CRL entry extensions (see section * 5.3 of RFC 3280). */ Set<String> unresCritExts = entry.getCriticalExtensionOIDs(); if (unresCritExts != null && !unresCritExts.isEmpty()) { /* remove any that we will process */ unresCritExts.remove (PKIXExtensions.ReasonCode_Id.toString()); unresCritExts.remove (PKIXExtensions.CertificateIssuer_Id.toString()); if (!unresCritExts.isEmpty()) { if (debug != null) { debug.println("Unrecognized " + "critical extension(s) in revoked CRL entry: " + unresCritExts); } throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } reasonCode = entry.getRevocationReason(); if (reasonCode == null) { reasonCode = CRLReason.UNSPECIFIED; } Throwable t = new CertificateRevokedException (entry.getRevocationDate(), reasonCode, crl.getIssuerX500Principal(), entry.getExtensions()); throw new CertPathValidatorException(t.getMessage(), t, null, -1, BasicReason.REVOKED); } } } /** * We have a cert whose revocation status couldn't be verified by * a CRL issued by the cert that issued the CRL. See if we can * find a valid CRL issued by a separate key that can verify the * revocation status of this certificate. * <p> * Note that this does not provide support for indirect CRLs, * only CRLs signed with a different key (but the same issuer * name) as the certificate being checked. * * @param currCert the <code>X509Certificate</code> to be checked * @param prevKey the <code>PublicKey</code> that failed * @param signFlag <code>true</code> if that key was trusted to sign CRLs * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> * whose revocation status depends on the * non-revoked status of this cert. To avoid * circular dependencies, we assume they're * revoked while checking the revocation * status of this cert. * @throws CertPathValidatorException if the cert's revocation status * cannot be verified successfully with another key */ private void verifyWithSeparateSigningKey(X509Certificate currCert, PublicKey prevKey, boolean signFlag, Set<X509Certificate> stackedCerts) throws CertPathValidatorException { String msg = "revocation status"; if (debug != null) { debug.println( "CrlRevocationChecker.verifyWithSeparateSigningKey()" + " ---checking " + msg + "..."); } // reject circular dependencies - RFC 3280 is not explicit on how // to handle this, so we feel it is safest to reject them until // the issue is resolved in the PKIX WG. if ((stackedCerts != null) && stackedCerts.contains(currCert)) { if (debug != null) { debug.println( "CrlRevocationChecker.verifyWithSeparateSigningKey()" + " circular dependency"); } throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } // If prevKey wasn't trusted, maybe we just didn't have the right // path to it. Don't rule that key out. if (!signFlag) { prevKey = null; } // Try to find another key that might be able to sign // CRLs vouching for this cert. buildToNewKey(currCert, prevKey, stackedCerts); } /** * Tries to find a CertPath that establishes a key that can be * used to verify the revocation status of a given certificate. * Ignores keys that have previously been tried. Throws a * CertPathValidatorException if no such key could be found. * * @param currCert the <code>X509Certificate</code> to be checked * @param prevKey the <code>PublicKey</code> of the certificate whose key * cannot be used to vouch for the CRL and should be ignored * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> * whose revocation status depends on the * establishment of this path. * @throws CertPathValidatorException on failure */ private void buildToNewKey(X509Certificate currCert, PublicKey prevKey, Set<X509Certificate> stackedCerts) throws CertPathValidatorException { if (debug != null) { debug.println("CrlRevocationChecker.buildToNewKey()" + " starting work"); } Set<PublicKey> badKeys = new HashSet<PublicKey>(); if (prevKey != null) { badKeys.add(prevKey); } X509CertSelector certSel = new RejectKeySelector(badKeys); certSel.setSubject(currCert.getIssuerX500Principal());Lines 1–500 of 787. Load more
/* * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.provider.certpath; import java.math.BigInteger; import java.util.Arrays; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.HashSet; import java.util.Set; import java.util.Iterator; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.*; import java.security.cert.CertPathValidatorException.BasicReason; import java.security.interfaces.DSAPublicKey; import javax.security.auth.x500.X500Principal; import sun.security.util.Debug; import sun.security.x509.AccessDescription; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.CRLDistributionPointsExtension; import sun.security.x509.DistributionPoint; import sun.security.x509.GeneralName; import sun.security.x509.GeneralNames; import sun.security.x509.PKIXExtensions; import sun.security.x509.X500Name; import sun.security.x509.X509CertImpl; import sun.security.x509.X509CRLEntryImpl; /** * CrlRevocationChecker is a <code>PKIXCertPathChecker</code> that checks * revocation status information on a PKIX certificate using CRLs obtained * from one or more <code>CertStores</code>. This is based on section 6.3 * of RFC 3280 (http://www.ietf.org/rfc/rfc3280.txt). * * @since 1.4 * @author Seth Proctor * @author Steve Hanna */ class CrlRevocationChecker extends PKIXCertPathChecker { private static final Debug debug = Debug.getInstance("certpath"); private final TrustAnchor mAnchor; private final List<CertStore> mStores; private final String mSigProvider; private final Date mCurrentTime; private PublicKey mPrevPubKey; private boolean mCRLSignFlag; private HashSet<X509CRL> mPossibleCRLs; private HashSet<X509CRL> mApprovedCRLs; private final PKIXParameters mParams; private static final boolean [] mCrlSignUsage = { false, false, false, false, false, false, true }; private static final boolean[] ALL_REASONS = {true, true, true, true, true, true, true, true, true}; private boolean mOnlyEECert = false; // Maximum clock skew in milliseconds (15 minutes) allowed when checking // validity of CRLs private static final long MAX_CLOCK_SKEW = 900000; /** * Creates a <code>CrlRevocationChecker</code>. * * @param anchor anchor selected to validate the target certificate * @param params <code>PKIXParameters</code> to be used for * finding certificates and CRLs, etc. */ CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params) throws CertPathValidatorException { this(anchor, params, null); } /** * Creates a <code>CrlRevocationChecker</code>, allowing * extra certificates to be supplied beyond those contained * in the <code>PKIXParameters</code>. * * @param anchor anchor selected to validate the target certificate * @param params <code>PKIXParameters</code> to be used for * finding certificates and CRLs, etc. * @param certs a <code>Collection</code> of certificates * that may be useful, beyond those available * through <code>params</code> (<code>null</code> * if none) */ CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, Collection<X509Certificate> certs) throws CertPathValidatorException { this(anchor, params, certs, false); } CrlRevocationChecker(TrustAnchor anchor, PKIXParameters params, Collection<X509Certificate> certs, boolean onlyEECert) throws CertPathValidatorException { mAnchor = anchor; mParams = params; mStores = new ArrayList<CertStore>(params.getCertStores()); mSigProvider = params.getSigProvider(); if (certs != null) { try { mStores.add(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certs))); } catch (Exception e) { // should never occur but not necessarily fatal, so log it, // ignore and continue if (debug != null) { debug.println("CrlRevocationChecker: " + "error creating Collection CertStore: " + e); } } } Date testDate = params.getDate(); mCurrentTime = (testDate != null ? testDate : new Date()); mOnlyEECert = onlyEECert; init(false); } /** * Initializes the internal state of the checker from parameters * specified in the constructor */ public void init(boolean forward) throws CertPathValidatorException { if (!forward) { if (mAnchor != null) { if (mAnchor.getCAPublicKey() != null) { mPrevPubKey = mAnchor.getCAPublicKey(); } else { mPrevPubKey = mAnchor.getTrustedCert().getPublicKey(); } } else { mPrevPubKey = null; } mCRLSignFlag = true; } else { throw new CertPathValidatorException("forward checking " + "not supported"); } } public boolean isForwardCheckingSupported() { return false; } public Set<String> getSupportedExtensions() { return null; } /** * Performs the revocation status check on the certificate using * its internal state. * * @param cert the Certificate * @param unresolvedCritExts a Collection of the unresolved critical * extensions * @exception CertPathValidatorException Exception thrown if * certificate does not verify */ public void check(Certificate cert, Collection<String> unresolvedCritExts) throws CertPathValidatorException { X509Certificate currCert = (X509Certificate) cert; verifyRevocationStatus(currCert, mPrevPubKey, mCRLSignFlag, true); // Make new public key if parameters are missing PublicKey cKey = currCert.getPublicKey(); if (cKey instanceof DSAPublicKey && ((DSAPublicKey)cKey).getParams() == null) { // cKey needs to inherit DSA parameters from prev key cKey = BasicChecker.makeInheritedParamsKey(cKey, mPrevPubKey); } mPrevPubKey = cKey; mCRLSignFlag = certCanSignCrl(currCert); } /** * Performs the revocation status check on the certificate using * the provided state variables, as well as the constant internal * data. * * @param currCert the Certificate * @param prevKey the previous PublicKey in the chain * @param signFlag a boolean as returned from the last call, or true * if this is the first cert in the chain * @return a boolean specifying if the cert is allowed to vouch for the * validity of a CRL for the next iteration * @exception CertPathValidatorException Exception thrown if * certificate does not verify. */ public boolean check(X509Certificate currCert, PublicKey prevKey, boolean signFlag) throws CertPathValidatorException { verifyRevocationStatus(currCert, prevKey, signFlag, true); return certCanSignCrl(currCert); } /** * Checks that a cert can be used to verify a CRL. * * @param currCert an X509Certificate to check * @return a boolean specifying if the cert is allowed to vouch for the * validity of a CRL */ static boolean certCanSignCrl(X509Certificate currCert) { // if the cert doesn't include the key usage ext, or // the key usage ext asserts cRLSigning, return true, // otherwise return false. boolean[] kbools = currCert.getKeyUsage(); if (kbools != null) { return kbools[6]; } return false; } /** * Internal method to start the verification of a cert */ private void verifyRevocationStatus(X509Certificate currCert, PublicKey prevKey, boolean signFlag, boolean allowSeparateKey) throws CertPathValidatorException { verifyRevocationStatus(currCert, prevKey, signFlag, allowSeparateKey, null, mParams.getTrustAnchors()); } /** * Internal method to start the verification of a cert * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> * whose revocation status depends on the * non-revoked status of this cert. To avoid * circular dependencies, we assume they're * revoked while checking the revocation * status of this cert. * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s */ private void verifyRevocationStatus(X509Certificate currCert, PublicKey prevKey, boolean signFlag, boolean allowSeparateKey, Set<X509Certificate> stackedCerts, Set<TrustAnchor> trustAnchors) throws CertPathValidatorException { String msg = "revocation status"; if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus()" + " ---checking " + msg + "..."); } if (mOnlyEECert && currCert.getBasicConstraints() != -1) { if (debug != null) { debug.println("Skipping revocation check, not end entity cert"); } return; } // reject circular dependencies - RFC 3280 is not explicit on how // to handle this, so we feel it is safest to reject them until // the issue is resolved in the PKIX WG. if ((stackedCerts != null) && stackedCerts.contains(currCert)) { if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus()" + " circular dependency"); } throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } // init the state for this run mPossibleCRLs = new HashSet<X509CRL>(); mApprovedCRLs = new HashSet<X509CRL>(); boolean[] reasonsMask = new boolean[9]; try { X509CRLSelector sel = new X509CRLSelector(); sel.setCertificateChecking(currCert); CertPathHelper.setDateAndTime(sel, mCurrentTime, MAX_CLOCK_SKEW); for (CertStore mStore : mStores) { for (java.security.cert.CRL crl : mStore.getCRLs(sel)) { mPossibleCRLs.add((X509CRL)crl); } } DistributionPointFetcher store = DistributionPointFetcher.getInstance(); // all CRLs returned by the DP Fetcher have also been verified mApprovedCRLs.addAll(store.getCRLs(sel, signFlag, prevKey, mSigProvider, mStores, reasonsMask, trustAnchors, mParams.getDate())); } catch (Exception e) { if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "unexpected exception: " + e.getMessage()); } throw new CertPathValidatorException(e); } if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "crls.size() = " + mPossibleCRLs.size()); } if (!mPossibleCRLs.isEmpty()) { // Now that we have a list of possible CRLs, see which ones can // be approved mApprovedCRLs.addAll(verifyPossibleCRLs(mPossibleCRLs, currCert, signFlag, prevKey, reasonsMask, trustAnchors)); } if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "approved crls.size() = " + mApprovedCRLs.size()); } // make sure that we have at least one CRL that _could_ cover // the certificate in question and all reasons are covered if (mApprovedCRLs.isEmpty() || !Arrays.equals(reasonsMask, ALL_REASONS)) { if (allowSeparateKey) { verifyWithSeparateSigningKey(currCert, prevKey, signFlag, stackedCerts); return; } else { throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } // See if the cert is in the set of approved crls. if (debug != null) { BigInteger sn = currCert.getSerialNumber(); debug.println("CrlRevocationChecker.verifyRevocationStatus() " + "starting the final sweep..."); debug.println("CrlRevocationChecker.verifyRevocationStatus" + " cert SN: " + sn.toString()); } CRLReason reasonCode = CRLReason.UNSPECIFIED; X509CRLEntryImpl entry = null; for (X509CRL crl : mApprovedCRLs) { X509CRLEntry e = crl.getRevokedCertificate(currCert); if (e != null) { try { entry = X509CRLEntryImpl.toImpl(e); } catch (CRLException ce) { throw new CertPathValidatorException(ce); } if (debug != null) { debug.println("CrlRevocationChecker.verifyRevocationStatus" + " CRL entry: " + entry.toString()); } /* * Abort CRL validation and throw exception if there are any * unrecognized critical CRL entry extensions (see section * 5.3 of RFC 3280). */ Set<String> unresCritExts = entry.getCriticalExtensionOIDs(); if (unresCritExts != null && !unresCritExts.isEmpty()) { /* remove any that we will process */ unresCritExts.remove (PKIXExtensions.ReasonCode_Id.toString()); unresCritExts.remove (PKIXExtensions.CertificateIssuer_Id.toString()); if (!unresCritExts.isEmpty()) { if (debug != null) { debug.println("Unrecognized " + "critical extension(s) in revoked CRL entry: " + unresCritExts); } throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } reasonCode = entry.getRevocationReason(); if (reasonCode == null) { reasonCode = CRLReason.UNSPECIFIED; } Throwable t = new CertificateRevokedException (entry.getRevocationDate(), reasonCode, crl.getIssuerX500Principal(), entry.getExtensions()); throw new CertPathValidatorException(t.getMessage(), t, null, -1, BasicReason.REVOKED); } } } /** * We have a cert whose revocation status couldn't be verified by * a CRL issued by the cert that issued the CRL. See if we can * find a valid CRL issued by a separate key that can verify the * revocation status of this certificate. * <p> * Note that this does not provide support for indirect CRLs, * only CRLs signed with a different key (but the same issuer * name) as the certificate being checked. * * @param currCert the <code>X509Certificate</code> to be checked * @param prevKey the <code>PublicKey</code> that failed * @param signFlag <code>true</code> if that key was trusted to sign CRLs * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> * whose revocation status depends on the * non-revoked status of this cert. To avoid * circular dependencies, we assume they're * revoked while checking the revocation * status of this cert. * @throws CertPathValidatorException if the cert's revocation status * cannot be verified successfully with another key */ private void verifyWithSeparateSigningKey(X509Certificate currCert, PublicKey prevKey, boolean signFlag, Set<X509Certificate> stackedCerts) throws CertPathValidatorException { String msg = "revocation status"; if (debug != null) { debug.println( "CrlRevocationChecker.verifyWithSeparateSigningKey()" + " ---checking " + msg + "..."); } // reject circular dependencies - RFC 3280 is not explicit on how // to handle this, so we feel it is safest to reject them until // the issue is resolved in the PKIX WG. if ((stackedCerts != null) && stackedCerts.contains(currCert)) { if (debug != null) { debug.println( "CrlRevocationChecker.verifyWithSeparateSigningKey()" + " circular dependency"); } throw new CertPathValidatorException ("Could not determine revocation status", null, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } // If prevKey wasn't trusted, maybe we just didn't have the right // path to it. Don't rule that key out. if (!signFlag) { prevKey = null; } // Try to find another key that might be able to sign // CRLs vouching for this cert. buildToNewKey(currCert, prevKey, stackedCerts); } /** * Tries to find a CertPath that establishes a key that can be * used to verify the revocation status of a given certificate. * Ignores keys that have previously been tried. Throws a * CertPathValidatorException if no such key could be found. * * @param currCert the <code>X509Certificate</code> to be checked * @param prevKey the <code>PublicKey</code> of the certificate whose key * cannot be used to vouch for the CRL and should be ignored * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s> * whose revocation status depends on the * establishment of this path. * @throws CertPathValidatorException on failure */ private void buildToNewKey(X509Certificate currCert, PublicKey prevKey, Set<X509Certificate> stackedCerts) throws CertPathValidatorException { if (debug != null) { debug.println("CrlRevocationChecker.buildToNewKey()" + " starting work"); } Set<PublicKey> badKeys = new HashSet<PublicKey>(); if (prevKey != null) { badKeys.add(prevKey); } X509CertSelector certSel = new RejectKeySelector(badKeys); certSel.setSubject(currCert.getIssuerX500Principal());