787 lines · 33.0 KB · Java
RawDownload
/* * 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.