880 lines · 35.5 KB · Java
RawDownload
/* * Copyright (c) 2000, 2010, 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.io.IOException;import java.security.AccessController;import java.security.GeneralSecurityException;import java.security.InvalidAlgorithmParameterException;import java.security.Principal;import java.security.PublicKey;import java.security.cert.*;import java.security.cert.PKIXReason;import java.security.interfaces.DSAPublicKey;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.Comparator;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.LinkedList;import java.util.Set;import javax.security.auth.x500.X500Principal; import sun.security.action.GetBooleanSecurityPropertyAction;import sun.security.x509.X500Name;import sun.security.x509.PKIXExtensions;import sun.security.util.Debug; /** * This class is able to build certification paths in either the forward * or reverse directions. * * <p> If successful, it returns a certification path which has succesfully * satisfied all the constraints and requirements specified in the * PKIXBuilderParameters object and has been validated according to the PKIX * path validation algorithm defined in RFC 3280. * * <p> This implementation uses a depth-first search approach to finding * certification paths. If it comes to a point in which it cannot find * any more certificates leading to the target OR the path length is too long * it backtracks to previous paths until the target has been found or * all possible paths have been exhausted. * * <p> This implementation is not thread-safe. * * @since       1.4 * @author      Sean Mullan * @author      Yassir Elley */public final class SunCertPathBuilder extends CertPathBuilderSpi {     private static final Debug debug = Debug.getInstance("certpath");     /*     * private objects shared by methods     */    private PKIXBuilderParameters buildParams;    private CertificateFactory cf;    private boolean pathCompleted = false;    private X500Principal targetSubjectDN;    private PolicyNode policyTreeResult;    private TrustAnchor trustAnchor;    private PublicKey finalPublicKey;    private X509CertSelector targetSel;    private List<CertStore> orderedCertStores;    private boolean onlyEECert = false;     /**     * Create an instance of <code>SunCertPathBuilder</code>.     *     * @throws CertPathBuilderException if an error occurs     */    public SunCertPathBuilder() throws CertPathBuilderException {        try {            cf = CertificateFactory.getInstance("X.509");        } catch (CertificateException e) {            throw new CertPathBuilderException(e);        }        onlyEECert = AccessController.doPrivileged(            new GetBooleanSecurityPropertyAction                ("com.sun.security.onlyCheckRevocationOfEECert"));    }     /**     * Attempts to build a certification path using the Sun build     * algorithm from a trusted anchor(s) to a target subject, which must both     * be specified in the input parameter set. By default, this method will     * attempt to build in the forward direction. In order to build in the     * reverse direction, the caller needs to pass in an instance of     * SunCertPathBuilderParameters with the buildForward flag set to false.     *     * <p>The certification path that is constructed is validated     * according to the PKIX specification.     *     * @param params the parameter set for building a path. Must be an instance     *  of <code>PKIXBuilderParameters</code>.     * @return a certification path builder result.     * @exception CertPathBuilderException Exception thrown if builder is     *  unable to build a complete certification path from the trusted anchor(s)     *  to the target subject.     * @throws InvalidAlgorithmParameterException if the given parameters are     *  inappropriate for this certification path builder.     */    public CertPathBuilderResult engineBuild(CertPathParameters params)        throws CertPathBuilderException, InvalidAlgorithmParameterException {         if (debug != null) {            debug.println("SunCertPathBuilder.engineBuild(" + params + ")");        }         if (!(params instanceof PKIXBuilderParameters)) {            throw new InvalidAlgorithmParameterException("inappropriate " +                "parameter type, must be an instance of PKIXBuilderParameters");        }         boolean buildForward = true;        if (params instanceof SunCertPathBuilderParameters) {            buildForward =                ((SunCertPathBuilderParameters)params).getBuildForward();        }         buildParams = (PKIXBuilderParameters)params;         /* Check mandatory parameters */         // Make sure that none of the trust anchors include name constraints        // (not supported).        for (TrustAnchor anchor : buildParams.getTrustAnchors()) {            if (anchor.getNameConstraints() != null) {                throw new InvalidAlgorithmParameterException                    ("name constraints in trust anchor not supported");            }        }         CertSelector sel = buildParams.getTargetCertConstraints();        if (!(sel instanceof X509CertSelector)) {            throw new InvalidAlgorithmParameterException("the "                + "targetCertConstraints parameter must be an "                + "X509CertSelector");        }        targetSel = (X509CertSelector)sel;        targetSubjectDN = targetSel.getSubject();        if (targetSubjectDN == null) {            X509Certificate targetCert = targetSel.getCertificate();            if (targetCert != null) {                targetSubjectDN = targetCert.getSubjectX500Principal();            }        }        // reorder CertStores so that local CertStores are tried first        orderedCertStores =            new ArrayList<CertStore>(buildParams.getCertStores());        Collections.sort(orderedCertStores, new CertStoreComparator());        if (targetSubjectDN == null) {            targetSubjectDN = getTargetSubjectDN(orderedCertStores, targetSel);        }        if (targetSubjectDN == null) {            throw new InvalidAlgorithmParameterException                ("Could not determine unique target subject");        }         List<List<Vertex>> adjList = new ArrayList<List<Vertex>>();        CertPathBuilderResult result =            buildCertPath(buildForward, false, adjList);        if (result == null) {            if (debug != null) {                debug.println("SunCertPathBuilder.engineBuild: 2nd pass");            }            // try again            adjList.clear();            result = buildCertPath(buildForward, true, adjList);            if (result == null) {                throw new SunCertPathBuilderException("unable to find valid "                    + "certification path to requested target",                    new AdjacencyList(adjList));            }        }        return result;    }     private CertPathBuilderResult buildCertPath(boolean buildForward,        boolean searchAllCertStores, List<List<Vertex>> adjList)        throws CertPathBuilderException {         // Init shared variables and build certification path        pathCompleted = false;        trustAnchor = null;        finalPublicKey = null;        policyTreeResult = null;        LinkedList<X509Certificate> certPathList =            new LinkedList<X509Certificate>();        try {            if (buildForward) {                buildForward(adjList, certPathList, searchAllCertStores);            } else {                buildReverse(adjList, certPathList);            }        } catch (Exception e) {            if (debug != null) {                debug.println("SunCertPathBuilder.engineBuild() exception in "                    + "build");                e.printStackTrace();            }            throw new SunCertPathBuilderException("unable to find valid "                + "certification path to requested target", e,                new AdjacencyList(adjList));        }         // construct SunCertPathBuilderResult        try {            if (pathCompleted) {                if (debug != null)                    debug.println("SunCertPathBuilder.engineBuild() "                                  + "pathCompleted");                 // we must return a certpath which has the target                // as the first cert in the certpath - i.e. reverse                // the certPathList                Collections.reverse(certPathList);                 return new SunCertPathBuilderResult(                    cf.generateCertPath(certPathList), this.trustAnchor,                    policyTreeResult, finalPublicKey,                    new AdjacencyList(adjList));            }        } catch (Exception e) {            if (debug != null) {                debug.println("SunCertPathBuilder.engineBuild() exception "                              + "in wrap-up");                e.printStackTrace();            }            throw new SunCertPathBuilderException("unable to find valid "                + "certification path to requested target", e,                new AdjacencyList(adjList));        }         return null;    }     /*     * Private build reverse method.     */    private void buildReverse(List<List<Vertex>> adjacencyList,        LinkedList<X509Certificate> certPathList) throws Exception    {        if (debug != null) {            debug.println("SunCertPathBuilder.buildReverse()...");            debug.println("SunCertPathBuilder.buildReverse() InitialPolicies: "                + buildParams.getInitialPolicies());        }         ReverseState currentState = new ReverseState();        /* Initialize adjacency list */        adjacencyList.clear();        adjacencyList.add(new LinkedList<Vertex>());         /*         * Perform a search using each trust anchor, until a valid         * path is found         */        Iterator<TrustAnchor> iter = buildParams.getTrustAnchors().iterator();        while (iter.hasNext()) {            TrustAnchor anchor = iter.next();            /* check if anchor satisfies target constraints */            if (anchorIsTarget(anchor, targetSel)) {                this.trustAnchor = anchor;                this.pathCompleted = true;                this.finalPublicKey = anchor.getTrustedCert().getPublicKey();                break;            }             /* Initialize current state */            currentState.initState(buildParams.getMaxPathLength(),                       buildParams.isExplicitPolicyRequired(),                       buildParams.isPolicyMappingInhibited(),                       buildParams.isAnyPolicyInhibited(),                       buildParams.getCertPathCheckers());            currentState.updateState(anchor);            // init the crl checker            currentState.crlChecker =                new CrlRevocationChecker(null, buildParams, null, onlyEECert);            currentState.algorithmChecker = new AlgorithmChecker(anchor);            try {                depthFirstSearchReverse(null, currentState,                new ReverseBuilder(buildParams, targetSubjectDN), adjacencyList,                certPathList);            } catch (Exception e) {                // continue on error if more anchors to try                if (iter.hasNext())                    continue;                else                    throw e;            }             // break out of loop if search is successful            break;        }         if (debug != null) {            debug.println("SunCertPathBuilder.buildReverse() returned from "                + "depthFirstSearchReverse()");            debug.println("SunCertPathBuilder.buildReverse() "                + "certPathList.size: " + certPathList.size());        }    }     /*     * Private build forward method.     */    private void buildForward(List<List<Vertex>> adjacencyList,        LinkedList<X509Certificate> certPathList, boolean searchAllCertStores)        throws GeneralSecurityException, IOException    {        if (debug != null) {            debug.println("SunCertPathBuilder.buildForward()...");        }         /* Initialize current state */        ForwardState currentState = new ForwardState();        currentState.initState(buildParams.getCertPathCheckers());         /* Initialize adjacency list */        adjacencyList.clear();        adjacencyList.add(new LinkedList<Vertex>());         // init the crl checker        currentState.crlChecker            = new CrlRevocationChecker(null, buildParams, null, onlyEECert);         depthFirstSearchForward(targetSubjectDN, currentState,          new ForwardBuilder              (buildParams, targetSubjectDN, searchAllCertStores, onlyEECert),          adjacencyList, certPathList);    }     /*     * This method performs a depth first search for a certification     * path while building forward which meets the requirements set in     * the parameters object.     * It uses an adjacency list to store all certificates which were     * tried (i.e. at one time added to the path - they may not end up in     * the final path if backtracking occurs). This information can     * be used later to debug or demo the build.     *     * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"     * for an explanation of the DFS algorithm.     *     * @param dN the distinguished name being currently searched for certs     * @param currentState the current PKIX validation state     */    void depthFirstSearchForward(X500Principal dN, ForwardState currentState,        ForwardBuilder builder, List<List<Vertex>> adjList,        LinkedList<X509Certificate> certPathList)        throws GeneralSecurityException, IOException    {        //XXX This method should probably catch & handle exceptions         if (debug != null) {            debug.println("SunCertPathBuilder.depthFirstSearchForward(" + dN                + ", " + currentState.toString() + ")");        }         /*         * Find all the certificates issued to dN which         * satisfy the PKIX certification path constraints.         */        List<Vertex> vertices = addVertices           (builder.getMatchingCerts(currentState, orderedCertStores), adjList);        if (debug != null) {            debug.println("SunCertPathBuilder.depthFirstSearchForward(): "                + "certs.size=" + vertices.size());        }         /*         * For each cert in the collection, verify anything         * that hasn't been checked yet (signature, revocation, etc)         * and check for loops. Call depthFirstSearchForward()         * recursively for each good cert.         */                vertices:        for (Vertex vertex : vertices) {            /**             * Restore state to currentState each time through the loop.             * This is important because some of the user-defined             * checkers modify the state, which MUST be restored if             * the cert eventually fails to lead to the target and             * the next matching cert is tried.             */            ForwardState nextState = (ForwardState) currentState.clone();            X509Certificate cert = (X509Certificate) vertex.getCertificate();             try {                builder.verifyCert(cert, nextState, certPathList);            } catch (GeneralSecurityException gse) {                if (debug != null) {                    debug.println("SunCertPathBuilder.depthFirstSearchForward()"                        + ": validation failed: " + gse);                    gse.printStackTrace();                }                vertex.setThrowable(gse);                continue;            }             /*             * Certificate is good.             * If cert completes the path,             *    process userCheckers that don't support forward checking             *    and process policies over whole path             *    and backtrack appropriately if there is a failure             * else if cert does not complete the path,             *    add it to the path             */            if (builder.isPathCompleted(cert)) {                 BasicChecker basicChecker = null;                if (debug != null)                    debug.println("SunCertPathBuilder.depthFirstSearchForward()"                        + ": commencing final verification");                 ArrayList<X509Certificate> appendedCerts =                    new ArrayList<X509Certificate>(certPathList);                 /*                 * if the trust anchor selected is specified as a trusted                 * public key rather than a trusted cert, then verify this                 * cert (which is signed by the trusted public key), but                 * don't add it yet to the certPathList                 */                if (builder.trustAnchor.getTrustedCert() == null) {                    appendedCerts.add(0, cert);                }                 HashSet<String> initExpPolSet = new HashSet<String>(1);                initExpPolSet.add(PolicyChecker.ANY_POLICY);                 PolicyNodeImpl rootNode = new PolicyNodeImpl(null,                    PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);                 PolicyChecker policyChecker                    = new PolicyChecker(buildParams.getInitialPolicies(),                                appendedCerts.size(),                                buildParams.isExplicitPolicyRequired(),                                buildParams.isPolicyMappingInhibited(),                                buildParams.isAnyPolicyInhibited(),                                buildParams.getPolicyQualifiersRejected(),                                rootNode);                 List<PKIXCertPathChecker> userCheckers = new                    ArrayList<PKIXCertPathChecker>                        (buildParams.getCertPathCheckers());                int mustCheck = 0;                userCheckers.add(mustCheck, policyChecker);                mustCheck++;                 // add the algorithm checker                userCheckers.add(mustCheck,                        new AlgorithmChecker(builder.trustAnchor));                mustCheck++;                 if (nextState.keyParamsNeeded()) {                    PublicKey rootKey = cert.getPublicKey();                    if (builder.trustAnchor.getTrustedCert() == null) {                        rootKey = builder.trustAnchor.getCAPublicKey();                        if (debug != null)                            debug.println(                                "SunCertPathBuilder.depthFirstSearchForward " +                                "using buildParams public key: " +                                rootKey.toString());                    }                    TrustAnchor anchor = new TrustAnchor                        (cert.getSubjectX500Principal(), rootKey, null);                     // add the basic checker                    basicChecker = new BasicChecker(anchor,                                           builder.date,                                           buildParams.getSigProvider(),

Lines 1–500 of 880.