0N/A/*
4756N/A * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.provider.certpath;
0N/A
0N/Aimport java.io.IOException;
1652N/Aimport java.security.AccessController;
0N/Aimport java.security.GeneralSecurityException;
0N/Aimport java.security.InvalidAlgorithmParameterException;
0N/Aimport java.security.Principal;
0N/Aimport java.security.PublicKey;
585N/Aimport java.security.cert.*;
585N/Aimport java.security.cert.PKIXReason;
585N/Aimport java.security.interfaces.DSAPublicKey;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.Collection;
0N/Aimport java.util.Collections;
0N/Aimport java.util.Comparator;
0N/Aimport java.util.HashSet;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.List;
0N/Aimport java.util.LinkedList;
0N/Aimport java.util.Set;
0N/Aimport javax.security.auth.x500.X500Principal;
0N/A
1652N/Aimport sun.security.action.GetBooleanSecurityPropertyAction;
0N/Aimport sun.security.x509.X500Name;
0N/Aimport sun.security.x509.PKIXExtensions;
0N/Aimport sun.security.util.Debug;
0N/A
0N/A/**
0N/A * This class is able to build certification paths in either the forward
0N/A * or reverse directions.
0N/A *
0N/A * <p> If successful, it returns a certification path which has succesfully
0N/A * satisfied all the constraints and requirements specified in the
0N/A * PKIXBuilderParameters object and has been validated according to the PKIX
0N/A * path validation algorithm defined in RFC 3280.
0N/A *
0N/A * <p> This implementation uses a depth-first search approach to finding
0N/A * certification paths. If it comes to a point in which it cannot find
0N/A * any more certificates leading to the target OR the path length is too long
0N/A * it backtracks to previous paths until the target has been found or
0N/A * all possible paths have been exhausted.
0N/A *
0N/A * <p> This implementation is not thread-safe.
0N/A *
0N/A * @since 1.4
0N/A * @author Sean Mullan
0N/A * @author Yassir Elley
0N/A */
0N/Apublic final class SunCertPathBuilder extends CertPathBuilderSpi {
0N/A
0N/A private static final Debug debug = Debug.getInstance("certpath");
0N/A
0N/A /*
0N/A * private objects shared by methods
0N/A */
0N/A private PKIXBuilderParameters buildParams;
0N/A private CertificateFactory cf;
0N/A private boolean pathCompleted = false;
0N/A private X500Principal targetSubjectDN;
0N/A private PolicyNode policyTreeResult;
0N/A private TrustAnchor trustAnchor;
0N/A private PublicKey finalPublicKey;
0N/A private X509CertSelector targetSel;
0N/A private List<CertStore> orderedCertStores;
1652N/A private boolean onlyEECert = false;
0N/A
0N/A /**
0N/A * Create an instance of <code>SunCertPathBuilder</code>.
0N/A *
0N/A * @throws CertPathBuilderException if an error occurs
0N/A */
0N/A public SunCertPathBuilder() throws CertPathBuilderException {
0N/A try {
0N/A cf = CertificateFactory.getInstance("X.509");
0N/A } catch (CertificateException e) {
0N/A throw new CertPathBuilderException(e);
0N/A }
1652N/A onlyEECert = AccessController.doPrivileged(
1652N/A new GetBooleanSecurityPropertyAction
1652N/A ("com.sun.security.onlyCheckRevocationOfEECert"));
0N/A }
0N/A
0N/A /**
0N/A * Attempts to build a certification path using the Sun build
0N/A * algorithm from a trusted anchor(s) to a target subject, which must both
0N/A * be specified in the input parameter set. By default, this method will
0N/A * attempt to build in the forward direction. In order to build in the
0N/A * reverse direction, the caller needs to pass in an instance of
0N/A * SunCertPathBuilderParameters with the buildForward flag set to false.
0N/A *
0N/A * <p>The certification path that is constructed is validated
0N/A * according to the PKIX specification.
0N/A *
0N/A * @param params the parameter set for building a path. Must be an instance
0N/A * of <code>PKIXBuilderParameters</code>.
0N/A * @return a certification path builder result.
0N/A * @exception CertPathBuilderException Exception thrown if builder is
0N/A * unable to build a complete certification path from the trusted anchor(s)
0N/A * to the target subject.
0N/A * @throws InvalidAlgorithmParameterException if the given parameters are
0N/A * inappropriate for this certification path builder.
0N/A */
0N/A public CertPathBuilderResult engineBuild(CertPathParameters params)
0N/A throws CertPathBuilderException, InvalidAlgorithmParameterException {
0N/A
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.engineBuild(" + params + ")");
0N/A }
0N/A
0N/A if (!(params instanceof PKIXBuilderParameters)) {
0N/A throw new InvalidAlgorithmParameterException("inappropriate " +
0N/A "parameter type, must be an instance of PKIXBuilderParameters");
0N/A }
0N/A
0N/A boolean buildForward = true;
0N/A if (params instanceof SunCertPathBuilderParameters) {
0N/A buildForward =
0N/A ((SunCertPathBuilderParameters)params).getBuildForward();
0N/A }
0N/A
0N/A buildParams = (PKIXBuilderParameters)params;
0N/A
0N/A /* Check mandatory parameters */
0N/A
0N/A // Make sure that none of the trust anchors include name constraints
0N/A // (not supported).
0N/A for (TrustAnchor anchor : buildParams.getTrustAnchors()) {
0N/A if (anchor.getNameConstraints() != null) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("name constraints in trust anchor not supported");
0N/A }
0N/A }
0N/A
0N/A CertSelector sel = buildParams.getTargetCertConstraints();
0N/A if (!(sel instanceof X509CertSelector)) {
0N/A throw new InvalidAlgorithmParameterException("the "
0N/A + "targetCertConstraints parameter must be an "
0N/A + "X509CertSelector");
0N/A }
0N/A targetSel = (X509CertSelector)sel;
0N/A targetSubjectDN = targetSel.getSubject();
0N/A if (targetSubjectDN == null) {
0N/A X509Certificate targetCert = targetSel.getCertificate();
0N/A if (targetCert != null) {
0N/A targetSubjectDN = targetCert.getSubjectX500Principal();
0N/A }
0N/A }
0N/A // reorder CertStores so that local CertStores are tried first
0N/A orderedCertStores =
0N/A new ArrayList<CertStore>(buildParams.getCertStores());
0N/A Collections.sort(orderedCertStores, new CertStoreComparator());
0N/A if (targetSubjectDN == null) {
0N/A targetSubjectDN = getTargetSubjectDN(orderedCertStores, targetSel);
0N/A }
0N/A if (targetSubjectDN == null) {
0N/A throw new InvalidAlgorithmParameterException
0N/A ("Could not determine unique target subject");
0N/A }
0N/A
0N/A List<List<Vertex>> adjList = new ArrayList<List<Vertex>>();
0N/A CertPathBuilderResult result =
0N/A buildCertPath(buildForward, false, adjList);
0N/A if (result == null) {
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.engineBuild: 2nd pass");
0N/A }
0N/A // try again
0N/A adjList.clear();
0N/A result = buildCertPath(buildForward, true, adjList);
0N/A if (result == null) {
0N/A throw new SunCertPathBuilderException("unable to find valid "
0N/A + "certification path to requested target",
0N/A new AdjacencyList(adjList));
0N/A }
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A private CertPathBuilderResult buildCertPath(boolean buildForward,
0N/A boolean searchAllCertStores, List<List<Vertex>> adjList)
0N/A throws CertPathBuilderException {
0N/A
0N/A // Init shared variables and build certification path
0N/A pathCompleted = false;
0N/A trustAnchor = null;
0N/A finalPublicKey = null;
0N/A policyTreeResult = null;
0N/A LinkedList<X509Certificate> certPathList =
0N/A new LinkedList<X509Certificate>();
0N/A try {
0N/A if (buildForward) {
0N/A buildForward(adjList, certPathList, searchAllCertStores);
0N/A } else {
0N/A buildReverse(adjList, certPathList);
0N/A }
0N/A } catch (Exception e) {
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.engineBuild() exception in "
0N/A + "build");
0N/A e.printStackTrace();
0N/A }
0N/A throw new SunCertPathBuilderException("unable to find valid "
0N/A + "certification path to requested target", e,
0N/A new AdjacencyList(adjList));
0N/A }
0N/A
0N/A // construct SunCertPathBuilderResult
0N/A try {
0N/A if (pathCompleted) {
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.engineBuild() "
0N/A + "pathCompleted");
0N/A
0N/A // we must return a certpath which has the target
0N/A // as the first cert in the certpath - i.e. reverse
0N/A // the certPathList
0N/A Collections.reverse(certPathList);
0N/A
0N/A return new SunCertPathBuilderResult(
0N/A cf.generateCertPath(certPathList), this.trustAnchor,
0N/A policyTreeResult, finalPublicKey,
0N/A new AdjacencyList(adjList));
0N/A }
0N/A } catch (Exception e) {
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.engineBuild() exception "
0N/A + "in wrap-up");
0N/A e.printStackTrace();
0N/A }
0N/A throw new SunCertPathBuilderException("unable to find valid "
0N/A + "certification path to requested target", e,
0N/A new AdjacencyList(adjList));
0N/A }
0N/A
0N/A return null;
0N/A }
0N/A
0N/A /*
0N/A * Private build reverse method.
0N/A */
0N/A private void buildReverse(List<List<Vertex>> adjacencyList,
0N/A LinkedList<X509Certificate> certPathList) throws Exception
0N/A {
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.buildReverse()...");
0N/A debug.println("SunCertPathBuilder.buildReverse() InitialPolicies: "
0N/A + buildParams.getInitialPolicies());
0N/A }
0N/A
0N/A ReverseState currentState = new ReverseState();
0N/A /* Initialize adjacency list */
0N/A adjacencyList.clear();
0N/A adjacencyList.add(new LinkedList<Vertex>());
0N/A
0N/A /*
0N/A * Perform a search using each trust anchor, until a valid
0N/A * path is found
0N/A */
0N/A Iterator<TrustAnchor> iter = buildParams.getTrustAnchors().iterator();
0N/A while (iter.hasNext()) {
0N/A TrustAnchor anchor = iter.next();
4756N/A
0N/A /* check if anchor satisfies target constraints */
0N/A if (anchorIsTarget(anchor, targetSel)) {
0N/A this.trustAnchor = anchor;
0N/A this.pathCompleted = true;
0N/A this.finalPublicKey = anchor.getTrustedCert().getPublicKey();
0N/A break;
0N/A }
0N/A
0N/A /* Initialize current state */
0N/A currentState.initState(buildParams.getMaxPathLength(),
0N/A buildParams.isExplicitPolicyRequired(),
0N/A buildParams.isPolicyMappingInhibited(),
0N/A buildParams.isAnyPolicyInhibited(),
0N/A buildParams.getCertPathCheckers());
0N/A currentState.updateState(anchor);
0N/A // init the crl checker
0N/A currentState.crlChecker =
1652N/A new CrlRevocationChecker(null, buildParams, null, onlyEECert);
2999N/A currentState.algorithmChecker = new AlgorithmChecker(anchor);
4756N/A currentState.untrustedChecker = new UntrustedChecker();
0N/A try {
0N/A depthFirstSearchReverse(null, currentState,
0N/A new ReverseBuilder(buildParams, targetSubjectDN), adjacencyList,
0N/A certPathList);
0N/A } catch (Exception e) {
0N/A // continue on error if more anchors to try
0N/A if (iter.hasNext())
0N/A continue;
0N/A else
0N/A throw e;
0N/A }
0N/A
0N/A // break out of loop if search is successful
0N/A break;
0N/A }
0N/A
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.buildReverse() returned from "
0N/A + "depthFirstSearchReverse()");
0N/A debug.println("SunCertPathBuilder.buildReverse() "
0N/A + "certPathList.size: " + certPathList.size());
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Private build forward method.
0N/A */
0N/A private void buildForward(List<List<Vertex>> adjacencyList,
0N/A LinkedList<X509Certificate> certPathList, boolean searchAllCertStores)
0N/A throws GeneralSecurityException, IOException
0N/A {
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.buildForward()...");
0N/A }
0N/A
0N/A /* Initialize current state */
0N/A ForwardState currentState = new ForwardState();
0N/A currentState.initState(buildParams.getCertPathCheckers());
0N/A
0N/A /* Initialize adjacency list */
0N/A adjacencyList.clear();
0N/A adjacencyList.add(new LinkedList<Vertex>());
0N/A
0N/A // init the crl checker
1652N/A currentState.crlChecker
1652N/A = new CrlRevocationChecker(null, buildParams, null, onlyEECert);
4756N/A currentState.untrustedChecker = new UntrustedChecker();
0N/A
0N/A depthFirstSearchForward(targetSubjectDN, currentState,
1652N/A new ForwardBuilder
1652N/A (buildParams, targetSubjectDN, searchAllCertStores, onlyEECert),
0N/A adjacencyList, certPathList);
0N/A }
0N/A
0N/A /*
0N/A * This method performs a depth first search for a certification
0N/A * path while building forward which meets the requirements set in
0N/A * the parameters object.
0N/A * It uses an adjacency list to store all certificates which were
0N/A * tried (i.e. at one time added to the path - they may not end up in
0N/A * the final path if backtracking occurs). This information can
0N/A * be used later to debug or demo the build.
0N/A *
0N/A * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
0N/A * for an explanation of the DFS algorithm.
0N/A *
0N/A * @param dN the distinguished name being currently searched for certs
0N/A * @param currentState the current PKIX validation state
0N/A */
0N/A void depthFirstSearchForward(X500Principal dN, ForwardState currentState,
0N/A ForwardBuilder builder, List<List<Vertex>> adjList,
0N/A LinkedList<X509Certificate> certPathList)
0N/A throws GeneralSecurityException, IOException
0N/A {
0N/A //XXX This method should probably catch & handle exceptions
0N/A
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.depthFirstSearchForward(" + dN
0N/A + ", " + currentState.toString() + ")");
0N/A }
0N/A
0N/A /*
0N/A * Find all the certificates issued to dN which
0N/A * satisfy the PKIX certification path constraints.
0N/A */
0N/A List<Vertex> vertices = addVertices
0N/A (builder.getMatchingCerts(currentState, orderedCertStores), adjList);
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.depthFirstSearchForward(): "
0N/A + "certs.size=" + vertices.size());
0N/A }
0N/A
0N/A /*
0N/A * For each cert in the collection, verify anything
0N/A * that hasn't been checked yet (signature, revocation, etc)
0N/A * and check for loops. Call depthFirstSearchForward()
0N/A * recursively for each good cert.
0N/A */
0N/A
0N/A vertices:
0N/A for (Vertex vertex : vertices) {
0N/A /**
0N/A * Restore state to currentState each time through the loop.
0N/A * This is important because some of the user-defined
0N/A * checkers modify the state, which MUST be restored if
0N/A * the cert eventually fails to lead to the target and
0N/A * the next matching cert is tried.
0N/A */
0N/A ForwardState nextState = (ForwardState) currentState.clone();
0N/A X509Certificate cert = (X509Certificate) vertex.getCertificate();
0N/A
0N/A try {
0N/A builder.verifyCert(cert, nextState, certPathList);
0N/A } catch (GeneralSecurityException gse) {
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.depthFirstSearchForward()"
0N/A + ": validation failed: " + gse);
0N/A gse.printStackTrace();
0N/A }
0N/A vertex.setThrowable(gse);
0N/A continue;
0N/A }
0N/A
0N/A /*
0N/A * Certificate is good.
0N/A * If cert completes the path,
0N/A * process userCheckers that don't support forward checking
0N/A * and process policies over whole path
0N/A * and backtrack appropriately if there is a failure
0N/A * else if cert does not complete the path,
0N/A * add it to the path
0N/A */
0N/A if (builder.isPathCompleted(cert)) {
0N/A
0N/A BasicChecker basicChecker = null;
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchForward()"
0N/A + ": commencing final verification");
0N/A
0N/A ArrayList<X509Certificate> appendedCerts =
0N/A new ArrayList<X509Certificate>(certPathList);
0N/A
0N/A /*
0N/A * if the trust anchor selected is specified as a trusted
0N/A * public key rather than a trusted cert, then verify this
0N/A * cert (which is signed by the trusted public key), but
0N/A * don't add it yet to the certPathList
0N/A */
0N/A if (builder.trustAnchor.getTrustedCert() == null) {
0N/A appendedCerts.add(0, cert);
0N/A }
0N/A
0N/A HashSet<String> initExpPolSet = new HashSet<String>(1);
0N/A initExpPolSet.add(PolicyChecker.ANY_POLICY);
0N/A
0N/A PolicyNodeImpl rootNode = new PolicyNodeImpl(null,
0N/A PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);
0N/A
0N/A PolicyChecker policyChecker
0N/A = new PolicyChecker(buildParams.getInitialPolicies(),
0N/A appendedCerts.size(),
0N/A buildParams.isExplicitPolicyRequired(),
0N/A buildParams.isPolicyMappingInhibited(),
0N/A buildParams.isAnyPolicyInhibited(),
0N/A buildParams.getPolicyQualifiersRejected(),
0N/A rootNode);
0N/A
0N/A List<PKIXCertPathChecker> userCheckers = new
0N/A ArrayList<PKIXCertPathChecker>
0N/A (buildParams.getCertPathCheckers());
0N/A int mustCheck = 0;
0N/A userCheckers.add(mustCheck, policyChecker);
0N/A mustCheck++;
0N/A
2999N/A // add the algorithm checker
2999N/A userCheckers.add(mustCheck,
2999N/A new AlgorithmChecker(builder.trustAnchor));
2999N/A mustCheck++;
2999N/A
0N/A if (nextState.keyParamsNeeded()) {
0N/A PublicKey rootKey = cert.getPublicKey();
0N/A if (builder.trustAnchor.getTrustedCert() == null) {
0N/A rootKey = builder.trustAnchor.getCAPublicKey();
0N/A if (debug != null)
2999N/A debug.println(
2999N/A "SunCertPathBuilder.depthFirstSearchForward " +
2999N/A "using buildParams public key: " +
2999N/A rootKey.toString());
0N/A }
0N/A TrustAnchor anchor = new TrustAnchor
0N/A (cert.getSubjectX500Principal(), rootKey, null);
2999N/A
2999N/A // add the basic checker
0N/A basicChecker = new BasicChecker(anchor,
0N/A builder.date,
0N/A buildParams.getSigProvider(),
0N/A true);
0N/A userCheckers.add(mustCheck, basicChecker);
0N/A mustCheck++;
2999N/A
2999N/A // add the crl revocation checker
0N/A if (buildParams.isRevocationEnabled()) {
1652N/A userCheckers.add(mustCheck, new CrlRevocationChecker
1652N/A (anchor, buildParams, null, onlyEECert));
0N/A mustCheck++;
0N/A }
0N/A }
2999N/A // Why we don't need BasicChecker and CrlRevocationChecker
2999N/A // if nextState.keyParamsNeeded() is false?
0N/A
0N/A for (int i=0; i<appendedCerts.size(); i++) {
0N/A X509Certificate currCert = appendedCerts.get(i);
0N/A if (debug != null)
0N/A debug.println("current subject = "
0N/A + currCert.getSubjectX500Principal());
0N/A Set<String> unresCritExts =
0N/A currCert.getCriticalExtensionOIDs();
0N/A if (unresCritExts == null) {
0N/A unresCritExts = Collections.<String>emptySet();
0N/A }
0N/A
0N/A for (int j=0; j<userCheckers.size(); j++) {
0N/A PKIXCertPathChecker currChecker = userCheckers.get(j);
0N/A if (j < mustCheck ||
2999N/A !currChecker.isForwardCheckingSupported()) {
0N/A if (i == 0) {
0N/A currChecker.init(false);
2999N/A
2999N/A // The user specified
2999N/A // AlgorithmChecker may not be
2999N/A // able to set the trust anchor until now.
2999N/A if (j >= mustCheck &&
2999N/A currChecker instanceof AlgorithmChecker) {
2999N/A ((AlgorithmChecker)currChecker).
2999N/A trySetTrustAnchor(builder.trustAnchor);
2999N/A }
0N/A }
0N/A
0N/A try {
0N/A currChecker.check(currCert, unresCritExts);
0N/A } catch (CertPathValidatorException cpve) {
0N/A if (debug != null)
0N/A debug.println
0N/A ("SunCertPathBuilder.depthFirstSearchForward(): " +
0N/A "final verification failed: " + cpve);
0N/A vertex.setThrowable(cpve);
0N/A continue vertices;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Remove extensions from user checkers that support
0N/A * forward checking. After this step, we will have
0N/A * removed all extensions that all user checkers
0N/A * are capable of processing.
0N/A */
0N/A for (PKIXCertPathChecker checker :
0N/A buildParams.getCertPathCheckers())
0N/A {
0N/A if (checker.isForwardCheckingSupported()) {
0N/A Set<String> suppExts =
0N/A checker.getSupportedExtensions();
0N/A if (suppExts != null) {
0N/A unresCritExts.removeAll(suppExts);
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (!unresCritExts.isEmpty()) {
0N/A unresCritExts.remove
0N/A (PKIXExtensions.BasicConstraints_Id.toString());
0N/A unresCritExts.remove
0N/A (PKIXExtensions.NameConstraints_Id.toString());
0N/A unresCritExts.remove
0N/A (PKIXExtensions.CertificatePolicies_Id.toString());
0N/A unresCritExts.remove
0N/A (PKIXExtensions.PolicyMappings_Id.toString());
0N/A unresCritExts.remove
0N/A (PKIXExtensions.PolicyConstraints_Id.toString());
0N/A unresCritExts.remove
0N/A (PKIXExtensions.InhibitAnyPolicy_Id.toString());
0N/A unresCritExts.remove(PKIXExtensions.
0N/A SubjectAlternativeName_Id.toString());
0N/A unresCritExts.remove
0N/A (PKIXExtensions.KeyUsage_Id.toString());
0N/A unresCritExts.remove
0N/A (PKIXExtensions.ExtendedKeyUsage_Id.toString());
0N/A
0N/A if (!unresCritExts.isEmpty()) {
585N/A throw new CertPathValidatorException
585N/A ("unrecognized critical extension(s)", null,
585N/A null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT);
0N/A }
0N/A }
0N/A }
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchForward()"
0N/A + ": final verification succeeded - path completed!");
0N/A pathCompleted = true;
0N/A
0N/A /*
0N/A * if the user specified a trusted public key rather than
0N/A * trusted certs, then add this cert (which is signed by
0N/A * the trusted public key) to the certPathList
0N/A */
0N/A if (builder.trustAnchor.getTrustedCert() == null)
0N/A builder.addCertToPath(cert, certPathList);
0N/A // Save the trust anchor
0N/A this.trustAnchor = builder.trustAnchor;
0N/A
0N/A /*
0N/A * Extract and save the final target public key
0N/A */
0N/A if (basicChecker != null) {
0N/A finalPublicKey = basicChecker.getPublicKey();
0N/A } else {
0N/A Certificate finalCert;
0N/A if (certPathList.size() == 0) {
0N/A finalCert = builder.trustAnchor.getTrustedCert();
0N/A } else {
0N/A finalCert = certPathList.get(certPathList.size()-1);
0N/A }
0N/A finalPublicKey = finalCert.getPublicKey();
0N/A }
0N/A
0N/A policyTreeResult = policyChecker.getPolicyTree();
0N/A return;
0N/A } else {
0N/A builder.addCertToPath(cert, certPathList);
0N/A }
0N/A
0N/A /* Update the PKIX state */
0N/A nextState.updateState(cert);
0N/A
0N/A /*
0N/A * Append an entry for cert in adjacency list and
0N/A * set index for current vertex.
0N/A */
0N/A adjList.add(new LinkedList<Vertex>());
0N/A vertex.setIndex(adjList.size() - 1);
0N/A
0N/A /* recursively search for matching certs at next dN */
4756N/A depthFirstSearchForward(cert.getIssuerX500Principal(),
4756N/A nextState, builder, adjList, certPathList);
0N/A
0N/A /*
0N/A * If path has been completed, return ASAP!
0N/A */
0N/A if (pathCompleted) {
0N/A return;
0N/A } else {
0N/A /*
0N/A * If we get here, it means we have searched all possible
0N/A * certs issued by the dN w/o finding any matching certs.
0N/A * This means we have to backtrack to the previous cert in
0N/A * the path and try some other paths.
0N/A */
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchForward()"
0N/A + ": backtracking");
0N/A builder.removeFinalCertFromPath(certPathList);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * This method performs a depth first search for a certification
0N/A * path while building reverse which meets the requirements set in
0N/A * the parameters object.
0N/A * It uses an adjacency list to store all certificates which were
0N/A * tried (i.e. at one time added to the path - they may not end up in
0N/A * the final path if backtracking occurs). This information can
0N/A * be used later to debug or demo the build.
0N/A *
0N/A * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
0N/A * for an explanation of the DFS algorithm.
0N/A *
0N/A * @param dN the distinguished name being currently searched for certs
0N/A * @param currentState the current PKIX validation state
0N/A */
0N/A void depthFirstSearchReverse(X500Principal dN, ReverseState currentState,
0N/A ReverseBuilder builder, List<List<Vertex>> adjList,
0N/A LinkedList<X509Certificate> certPathList)
0N/A throws GeneralSecurityException, IOException
0N/A {
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchReverse(" + dN
0N/A + ", " + currentState.toString() + ")");
0N/A
0N/A /*
0N/A * Find all the certificates issued by dN which
0N/A * satisfy the PKIX certification path constraints.
0N/A */
0N/A List<Vertex> vertices = addVertices
0N/A (builder.getMatchingCerts(currentState, orderedCertStores), adjList);
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchReverse(): "
0N/A + "certs.size=" + vertices.size());
0N/A
0N/A /*
0N/A * For each cert in the collection, verify anything
0N/A * that hasn't been checked yet (signature, revocation, etc)
0N/A * and check for loops. Call depthFirstSearchReverse()
0N/A * recursively for each good cert.
0N/A */
0N/A for (Vertex vertex : vertices) {
0N/A /**
0N/A * Restore state to currentState each time through the loop.
0N/A * This is important because some of the user-defined
0N/A * checkers modify the state, which MUST be restored if
0N/A * the cert eventually fails to lead to the target and
0N/A * the next matching cert is tried.
0N/A */
0N/A ReverseState nextState = (ReverseState) currentState.clone();
0N/A X509Certificate cert = (X509Certificate) vertex.getCertificate();
0N/A try {
0N/A builder.verifyCert(cert, nextState, certPathList);
0N/A } catch (GeneralSecurityException gse) {
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
0N/A + ": validation failed: " + gse);
0N/A vertex.setThrowable(gse);
0N/A continue;
0N/A }
0N/A
0N/A /*
0N/A * Certificate is good, add it to the path (if it isn't a
0N/A * self-signed cert) and update state
0N/A */
0N/A if (!currentState.isInitial())
0N/A builder.addCertToPath(cert, certPathList);
0N/A // save trust anchor
0N/A this.trustAnchor = currentState.trustAnchor;
0N/A
0N/A /*
0N/A * Check if path is completed, return ASAP if so.
0N/A */
0N/A if (builder.isPathCompleted(cert)) {
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
0N/A + ": path completed!");
0N/A pathCompleted = true;
0N/A
0N/A PolicyNodeImpl rootNode = nextState.rootNode;
0N/A
0N/A if (rootNode == null)
0N/A policyTreeResult = null;
0N/A else {
0N/A policyTreeResult = rootNode.copyTree();
0N/A ((PolicyNodeImpl)policyTreeResult).setImmutable();
0N/A }
0N/A
0N/A /*
0N/A * Extract and save the final target public key
0N/A */
0N/A finalPublicKey = cert.getPublicKey();
0N/A if (finalPublicKey instanceof DSAPublicKey &&
0N/A ((DSAPublicKey)finalPublicKey).getParams() == null)
0N/A {
0N/A finalPublicKey =
0N/A BasicChecker.makeInheritedParamsKey
0N/A (finalPublicKey, currentState.pubKey);
0N/A }
0N/A
0N/A return;
0N/A }
0N/A
0N/A /* Update the PKIX state */
0N/A nextState.updateState(cert);
0N/A
0N/A /*
0N/A * Append an entry for cert in adjacency list and
0N/A * set index for current vertex.
0N/A */
0N/A adjList.add(new LinkedList<Vertex>());
0N/A vertex.setIndex(adjList.size() - 1);
0N/A
0N/A /* recursively search for matching certs at next dN */
0N/A depthFirstSearchReverse(cert.getSubjectX500Principal(), nextState,
0N/A builder, adjList, certPathList);
0N/A
0N/A /*
0N/A * If path has been completed, return ASAP!
0N/A */
0N/A if (pathCompleted) {
0N/A return;
0N/A } else {
0N/A /*
0N/A * If we get here, it means we have searched all possible
0N/A * certs issued by the dN w/o finding any matching certs. This
0N/A * means we have to backtrack to the previous cert in the path
0N/A * and try some other paths.
0N/A */
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
0N/A + ": backtracking");
0N/A if (!currentState.isInitial())
0N/A builder.removeFinalCertFromPath(certPathList);
0N/A }
0N/A }
0N/A if (debug != null)
0N/A debug.println("SunCertPathBuilder.depthFirstSearchReverse() all "
0N/A + "certs in this adjacency list checked");
0N/A }
0N/A
0N/A /*
0N/A * Adds a collection of matching certificates to the
0N/A * adjacency list.
0N/A */
0N/A private List<Vertex> addVertices(Collection<X509Certificate> certs,
0N/A List<List<Vertex>> adjList) {
0N/A List<Vertex> l = adjList.get(adjList.size() - 1);
0N/A
0N/A for (X509Certificate cert : certs) {
0N/A Vertex v = new Vertex(cert);
0N/A l.add(v);
0N/A }
0N/A
0N/A return l;
0N/A }
0N/A
0N/A /**
0N/A * Returns true if trust anchor certificate matches specified
0N/A * certificate constraints.
0N/A */
0N/A private boolean anchorIsTarget(TrustAnchor anchor, X509CertSelector sel) {
0N/A X509Certificate anchorCert = anchor.getTrustedCert();
0N/A if (anchorCert != null) {
0N/A return sel.match(anchorCert);
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Comparator that orders CertStores so that local CertStores come before
0N/A * remote CertStores.
0N/A */
0N/A private static class CertStoreComparator implements Comparator<CertStore> {
0N/A public int compare(CertStore store1, CertStore store2) {
0N/A if (Builder.isLocalCertStore(store1)) {
0N/A return -1;
0N/A } else {
0N/A return 1;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the target subject DN from the first X509Certificate that
0N/A * is fetched that matches the specified X509CertSelector.
0N/A */
0N/A private X500Principal getTargetSubjectDN(List<CertStore> stores,
0N/A X509CertSelector targetSel) {
0N/A for (CertStore store : stores) {
0N/A try {
0N/A Collection<? extends Certificate> targetCerts =
0N/A (Collection<? extends Certificate>)
0N/A store.getCertificates(targetSel);
0N/A if (!targetCerts.isEmpty()) {
0N/A X509Certificate targetCert =
0N/A (X509Certificate)targetCerts.iterator().next();
0N/A return targetCert.getSubjectX500Principal();
0N/A }
0N/A } catch (CertStoreException e) {
0N/A // ignore but log it
0N/A if (debug != null) {
0N/A debug.println("SunCertPathBuilder.getTargetSubjectDN: " +
0N/A "non-fatal exception retrieving certs: " + e);
0N/A e.printStackTrace();
0N/A }
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A}