| /* |
| * 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; |
| } |
| } |