--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,852 @@
+/*
+ * Copyright 2000-2007 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.security.provider.certpath;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.Principal;
+import java.security.PublicKey;
+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 java.security.cert.*;
+import java.security.interfaces.DSAPublicKey;
+
+import javax.security.auth.x500.X500Principal;
+
+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;
+
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * 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);
+ 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);
+
+ depthFirstSearchForward(targetSubjectDN, currentState,
+ new ForwardBuilder(buildParams, targetSubjectDN, searchAllCertStores),
+ 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++;
+
+ 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);
+ basicChecker = new BasicChecker(anchor,
+ builder.date,
+ buildParams.getSigProvider(),
+ true);
+ userCheckers.add(mustCheck, basicChecker);
+ mustCheck++;
+ if (buildParams.isRevocationEnabled()) {
+ userCheckers.add(mustCheck,
+ new CrlRevocationChecker(anchor, buildParams));
+ mustCheck++;
+ }
+ }
+
+ for (int i=0; i<appendedCerts.size(); i++) {
+ X509Certificate currCert = appendedCerts.get(i);
+ if (debug != null)
+ debug.println("current subject = "
+ + currCert.getSubjectX500Principal());
+ Set<String> unresCritExts =
+ currCert.getCriticalExtensionOIDs();
+ if (unresCritExts == null) {
+ unresCritExts = Collections.<String>emptySet();
+ }
+
+ for (int j=0; j<userCheckers.size(); j++) {
+ PKIXCertPathChecker currChecker = userCheckers.get(j);
+ if (j < mustCheck ||
+ !currChecker.isForwardCheckingSupported())
+ {
+ if (i == 0) {
+ currChecker.init(false);
+ }
+
+ try {
+ currChecker.check(currCert, unresCritExts);
+ } catch (CertPathValidatorException cpve) {
+ if (debug != null)
+ debug.println
+ ("SunCertPathBuilder.depthFirstSearchForward(): " +
+ "final verification failed: " + cpve);
+ vertex.setThrowable(cpve);
+ continue vertices;
+ }
+ }
+ }
+
+ /*
+ * Remove extensions from user checkers that support
+ * forward checking. After this step, we will have
+ * removed all extensions that all user checkers
+ * are capable of processing.
+ */
+ for (PKIXCertPathChecker checker :
+ buildParams.getCertPathCheckers())
+ {
+ if (checker.isForwardCheckingSupported()) {
+ Set<String> suppExts =
+ checker.getSupportedExtensions();
+ if (suppExts != null) {
+ unresCritExts.removeAll(suppExts);
+ }
+ }
+ }
+
+ if (!unresCritExts.isEmpty()) {
+ unresCritExts.remove
+ (PKIXExtensions.BasicConstraints_Id.toString());
+ unresCritExts.remove
+ (PKIXExtensions.NameConstraints_Id.toString());
+ unresCritExts.remove
+ (PKIXExtensions.CertificatePolicies_Id.toString());
+ unresCritExts.remove
+ (PKIXExtensions.PolicyMappings_Id.toString());
+ unresCritExts.remove
+ (PKIXExtensions.PolicyConstraints_Id.toString());
+ unresCritExts.remove
+ (PKIXExtensions.InhibitAnyPolicy_Id.toString());
+ unresCritExts.remove(PKIXExtensions.
+ SubjectAlternativeName_Id.toString());
+ unresCritExts.remove
+ (PKIXExtensions.KeyUsage_Id.toString());
+ unresCritExts.remove
+ (PKIXExtensions.ExtendedKeyUsage_Id.toString());
+
+ if (!unresCritExts.isEmpty()) {
+ throw new CertPathValidatorException("unrecognized "
+ + "critical extension(s)");
+ }
+ }
+ }
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchForward()"
+ + ": final verification succeeded - path completed!");
+ pathCompleted = true;
+
+ /*
+ * if the user specified a trusted public key rather than
+ * trusted certs, then add this cert (which is signed by
+ * the trusted public key) to the certPathList
+ */
+ if (builder.trustAnchor.getTrustedCert() == null)
+ builder.addCertToPath(cert, certPathList);
+ // Save the trust anchor
+ this.trustAnchor = builder.trustAnchor;
+
+ /*
+ * Extract and save the final target public key
+ */
+ if (basicChecker != null) {
+ finalPublicKey = basicChecker.getPublicKey();
+ } else {
+ Certificate finalCert;
+ if (certPathList.size() == 0) {
+ finalCert = builder.trustAnchor.getTrustedCert();
+ } else {
+ finalCert = certPathList.get(certPathList.size()-1);
+ }
+ finalPublicKey = finalCert.getPublicKey();
+ }
+
+ policyTreeResult = policyChecker.getPolicyTree();
+ return;
+ } else {
+ builder.addCertToPath(cert, certPathList);
+ }
+
+ /* Update the PKIX state */
+ nextState.updateState(cert);
+
+ /*
+ * Append an entry for cert in adjacency list and
+ * set index for current vertex.
+ */
+ adjList.add(new LinkedList<Vertex>());
+ vertex.setIndex(adjList.size() - 1);
+
+ /* recursively search for matching certs at next dN */
+ depthFirstSearchForward(cert.getIssuerX500Principal(), nextState, builder,
+ adjList, certPathList);
+
+ /*
+ * If path has been completed, return ASAP!
+ */
+ if (pathCompleted) {
+ return;
+ } else {
+ /*
+ * If we get here, it means we have searched all possible
+ * certs issued by the dN w/o finding any matching certs.
+ * This means we have to backtrack to the previous cert in
+ * the path and try some other paths.
+ */
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchForward()"
+ + ": backtracking");
+ builder.removeFinalCertFromPath(certPathList);
+ }
+ }
+ }
+
+ /*
+ * This method performs a depth first search for a certification
+ * path while building reverse 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 depthFirstSearchReverse(X500Principal dN, ReverseState currentState,
+ ReverseBuilder builder, List<List<Vertex>> adjList,
+ LinkedList<X509Certificate> certPathList)
+ throws GeneralSecurityException, IOException
+ {
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchReverse(" + dN
+ + ", " + currentState.toString() + ")");
+
+ /*
+ * Find all the certificates issued by dN which
+ * satisfy the PKIX certification path constraints.
+ */
+ List<Vertex> vertices = addVertices
+ (builder.getMatchingCerts(currentState, orderedCertStores), adjList);
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchReverse(): "
+ + "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 depthFirstSearchReverse()
+ * recursively for each good cert.
+ */
+ 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.
+ */
+ ReverseState nextState = (ReverseState) currentState.clone();
+ X509Certificate cert = (X509Certificate) vertex.getCertificate();
+ try {
+ builder.verifyCert(cert, nextState, certPathList);
+ } catch (GeneralSecurityException gse) {
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
+ + ": validation failed: " + gse);
+ vertex.setThrowable(gse);
+ continue;
+ }
+
+ /*
+ * Certificate is good, add it to the path (if it isn't a
+ * self-signed cert) and update state
+ */
+ if (!currentState.isInitial())
+ builder.addCertToPath(cert, certPathList);
+ // save trust anchor
+ this.trustAnchor = currentState.trustAnchor;
+
+ /*
+ * Check if path is completed, return ASAP if so.
+ */
+ if (builder.isPathCompleted(cert)) {
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
+ + ": path completed!");
+ pathCompleted = true;
+
+ PolicyNodeImpl rootNode = nextState.rootNode;
+
+ if (rootNode == null)
+ policyTreeResult = null;
+ else {
+ policyTreeResult = rootNode.copyTree();
+ ((PolicyNodeImpl)policyTreeResult).setImmutable();
+ }
+
+ /*
+ * Extract and save the final target public key
+ */
+ finalPublicKey = cert.getPublicKey();
+ if (finalPublicKey instanceof DSAPublicKey &&
+ ((DSAPublicKey)finalPublicKey).getParams() == null)
+ {
+ finalPublicKey =
+ BasicChecker.makeInheritedParamsKey
+ (finalPublicKey, currentState.pubKey);
+ }
+
+ return;
+ }
+
+ /* Update the PKIX state */
+ nextState.updateState(cert);
+
+ /*
+ * Append an entry for cert in adjacency list and
+ * set index for current vertex.
+ */
+ adjList.add(new LinkedList<Vertex>());
+ vertex.setIndex(adjList.size() - 1);
+
+ /* recursively search for matching certs at next dN */
+ depthFirstSearchReverse(cert.getSubjectX500Principal(), nextState,
+ builder, adjList, certPathList);
+
+ /*
+ * If path has been completed, return ASAP!
+ */
+ if (pathCompleted) {
+ return;
+ } else {
+ /*
+ * If we get here, it means we have searched all possible
+ * certs issued by the dN w/o finding any matching certs. This
+ * means we have to backtrack to the previous cert in the path
+ * and try some other paths.
+ */
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
+ + ": backtracking");
+ if (!currentState.isInitial())
+ builder.removeFinalCertFromPath(certPathList);
+ }
+ }
+ if (debug != null)
+ debug.println("SunCertPathBuilder.depthFirstSearchReverse() all "
+ + "certs in this adjacency list checked");
+ }
+
+ /*
+ * Adds a collection of matching certificates to the
+ * adjacency list.
+ */
+ private List<Vertex> addVertices(Collection<X509Certificate> certs,
+ List<List<Vertex>> adjList) {
+ List<Vertex> l = adjList.get(adjList.size() - 1);
+
+ for (X509Certificate cert : certs) {
+ Vertex v = new Vertex(cert);
+ l.add(v);
+ }
+
+ return l;
+ }
+
+ /**
+ * Returns true if trust anchor certificate matches specified
+ * certificate constraints.
+ */
+ private boolean anchorIsTarget(TrustAnchor anchor, X509CertSelector sel) {
+ X509Certificate anchorCert = anchor.getTrustedCert();
+ if (anchorCert != null) {
+ return sel.match(anchorCert);
+ }
+ return false;
+ }
+
+ /**
+ * Comparator that orders CertStores so that local CertStores come before
+ * remote CertStores.
+ */
+ private static class CertStoreComparator implements Comparator<CertStore> {
+ public int compare(CertStore store1, CertStore store2) {
+ if (Builder.isLocalCertStore(store1)) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ }
+
+ /**
+ * Returns the target subject DN from the first X509Certificate that
+ * is fetched that matches the specified X509CertSelector.
+ */
+ private X500Principal getTargetSubjectDN(List<CertStore> stores,
+ X509CertSelector targetSel) {
+ for (CertStore store : stores) {
+ try {
+ Collection<? extends Certificate> targetCerts =
+ (Collection<? extends Certificate>)
+ store.getCertificates(targetSel);
+ if (!targetCerts.isEmpty()) {
+ X509Certificate targetCert =
+ (X509Certificate)targetCerts.iterator().next();
+ return targetCert.getSubjectX500Principal();
+ }
+ } catch (CertStoreException e) {
+ // ignore but log it
+ if (debug != null) {
+ debug.println("SunCertPathBuilder.getTargetSubjectDN: " +
+ "non-fatal exception retrieving certs: " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+ return null;
+ }
+}