• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.provider.certpath;
27 
28 import java.io.IOException;
29 import java.security.GeneralSecurityException;
30 import java.security.InvalidAlgorithmParameterException;
31 import java.security.PublicKey;
32 import java.security.cert.*;
33 import java.security.cert.CertPathValidatorException.BasicReason;
34 import java.security.cert.PKIXReason;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.HashSet;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.LinkedList;
42 import java.util.Set;
43 import javax.security.auth.x500.X500Principal;
44 
45 import sun.security.provider.certpath.PKIX.BuilderParams;
46 import static sun.security.x509.PKIXExtensions.*;
47 import sun.security.util.Debug;
48 
49 /**
50  * This class is able to build certification paths in either the forward
51  * or reverse directions.
52  *
53  * <p> If successful, it returns a certification path which has successfully
54  * satisfied all the constraints and requirements specified in the
55  * PKIXBuilderParameters object and has been validated according to the PKIX
56  * path validation algorithm defined in RFC 3280.
57  *
58  * <p> This implementation uses a depth-first search approach to finding
59  * certification paths. If it comes to a point in which it cannot find
60  * any more certificates leading to the target OR the path length is too long
61  * it backtracks to previous paths until the target has been found or
62  * all possible paths have been exhausted.
63  *
64  * <p> This implementation is not thread-safe.
65  *
66  * @since       1.4
67  * @author      Sean Mullan
68  * @author      Yassir Elley
69  */
70 public final class SunCertPathBuilder extends CertPathBuilderSpi {
71 
72     private static final Debug debug = Debug.getInstance("certpath");
73 
74     /*
75      * private objects shared by methods
76      */
77     private BuilderParams buildParams;
78     private CertificateFactory cf;
79     private boolean pathCompleted = false;
80     private PolicyNode policyTreeResult;
81     private TrustAnchor trustAnchor;
82     private PublicKey finalPublicKey;
83 
84     /**
85      * Create an instance of <code>SunCertPathBuilder</code>.
86      *
87      * @throws CertPathBuilderException if an error occurs
88      */
SunCertPathBuilder()89     public SunCertPathBuilder() throws CertPathBuilderException {
90         try {
91             cf = CertificateFactory.getInstance("X.509");
92         } catch (CertificateException e) {
93             throw new CertPathBuilderException(e);
94         }
95     }
96 
97     @Override
engineGetRevocationChecker()98     public CertPathChecker engineGetRevocationChecker() {
99         return new RevocationChecker();
100     }
101 
102     /**
103      * Attempts to build a certification path using the Sun build
104      * algorithm from a trusted anchor(s) to a target subject, which must both
105      * be specified in the input parameter set. By default, this method will
106      * attempt to build in the forward direction. In order to build in the
107      * reverse direction, the caller needs to pass in an instance of
108      * SunCertPathBuilderParameters with the buildForward flag set to false.
109      *
110      * <p>The certification path that is constructed is validated
111      * according to the PKIX specification.
112      *
113      * @param params the parameter set for building a path. Must be an instance
114      *  of <code>PKIXBuilderParameters</code>.
115      * @return a certification path builder result.
116      * @exception CertPathBuilderException Exception thrown if builder is
117      *  unable to build a complete certification path from the trusted anchor(s)
118      *  to the target subject.
119      * @throws InvalidAlgorithmParameterException if the given parameters are
120      *  inappropriate for this certification path builder.
121      */
122     @Override
engineBuild(CertPathParameters params)123     public CertPathBuilderResult engineBuild(CertPathParameters params)
124         throws CertPathBuilderException, InvalidAlgorithmParameterException {
125 
126         if (debug != null) {
127             debug.println("SunCertPathBuilder.engineBuild(" + params + ")");
128         }
129 
130         buildParams = PKIX.checkBuilderParams(params);
131         return build();
132     }
133 
build()134     private PKIXCertPathBuilderResult build() throws CertPathBuilderException {
135         List<List<Vertex>> adjList = new ArrayList<>();
136         PKIXCertPathBuilderResult result = buildCertPath(false, adjList);
137         if (result == null) {
138             if (debug != null) {
139                 debug.println("SunCertPathBuilder.engineBuild: 2nd pass");
140             }
141             // try again
142             adjList.clear();
143             result = buildCertPath(true, adjList);
144             if (result == null) {
145                 throw new SunCertPathBuilderException("unable to find valid "
146                     + "certification path to requested target",
147                     new AdjacencyList(adjList));
148             }
149         }
150         return result;
151     }
152 
buildCertPath(boolean searchAllCertStores, List<List<Vertex>> adjList)153     private PKIXCertPathBuilderResult buildCertPath(boolean searchAllCertStores,
154                                                     List<List<Vertex>> adjList)
155         throws CertPathBuilderException
156     {
157         // Init shared variables and build certification path
158         pathCompleted = false;
159         trustAnchor = null;
160         finalPublicKey = null;
161         policyTreeResult = null;
162         LinkedList<X509Certificate> certPathList = new LinkedList<>();
163         try {
164             if (buildParams.buildForward()) {
165                 buildForward(adjList, certPathList, searchAllCertStores);
166             } else {
167                 buildReverse(adjList, certPathList);
168             }
169         } catch (GeneralSecurityException | IOException e) {
170             if (debug != null) {
171                 debug.println("SunCertPathBuilder.engineBuild() exception in "
172                     + "build");
173                 e.printStackTrace();
174             }
175             throw new SunCertPathBuilderException("unable to find valid "
176                 + "certification path to requested target", e,
177                 new AdjacencyList(adjList));
178         }
179 
180         // construct SunCertPathBuilderResult
181         try {
182             if (pathCompleted) {
183                 if (debug != null)
184                     debug.println("SunCertPathBuilder.engineBuild() "
185                                   + "pathCompleted");
186 
187                 // we must return a certpath which has the target
188                 // as the first cert in the certpath - i.e. reverse
189                 // the certPathList
190                 Collections.reverse(certPathList);
191 
192                 return new SunCertPathBuilderResult(
193                     cf.generateCertPath(certPathList), trustAnchor,
194                     policyTreeResult, finalPublicKey,
195                     new AdjacencyList(adjList));
196             }
197         } catch (CertificateException e) {
198             if (debug != null) {
199                 debug.println("SunCertPathBuilder.engineBuild() exception "
200                               + "in wrap-up");
201                 e.printStackTrace();
202             }
203             throw new SunCertPathBuilderException("unable to find valid "
204                 + "certification path to requested target", e,
205                 new AdjacencyList(adjList));
206         }
207 
208         return null;
209     }
210 
211     /*
212      * Private build reverse method.
213      */
buildReverse(List<List<Vertex>> adjacencyList, LinkedList<X509Certificate> certPathList)214     private void buildReverse(List<List<Vertex>> adjacencyList,
215                               LinkedList<X509Certificate> certPathList)
216         throws GeneralSecurityException, IOException
217     {
218         if (debug != null) {
219             debug.println("SunCertPathBuilder.buildReverse()...");
220             debug.println("SunCertPathBuilder.buildReverse() InitialPolicies: "
221                 + buildParams.initialPolicies());
222         }
223 
224         ReverseState currentState = new ReverseState();
225         /* Initialize adjacency list */
226         adjacencyList.clear();
227         adjacencyList.add(new LinkedList<Vertex>());
228 
229         /*
230          * Perform a search using each trust anchor, until a valid
231          * path is found
232          */
233         Iterator<TrustAnchor> iter = buildParams.trustAnchors().iterator();
234         while (iter.hasNext()) {
235             TrustAnchor anchor = iter.next();
236 
237             /* check if anchor satisfies target constraints */
238             if (anchorIsTarget(anchor, buildParams.targetCertConstraints())) {
239                 this.trustAnchor = anchor;
240                 this.pathCompleted = true;
241                 this.finalPublicKey = anchor.getTrustedCert().getPublicKey();
242                 break;
243             }
244 
245             // skip anchor if it contains a DSA key with no DSA params
246             X509Certificate trustedCert = anchor.getTrustedCert();
247             PublicKey pubKey = trustedCert != null ? trustedCert.getPublicKey()
248                                                    : anchor.getCAPublicKey();
249 
250             if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) {
251                 continue;
252             }
253 
254             /* Initialize current state */
255             currentState.initState(buildParams);
256             currentState.updateState(anchor, buildParams);
257 
258             currentState.algorithmChecker = new AlgorithmChecker(anchor);
259             currentState.untrustedChecker = new UntrustedChecker();
260             try {
261                 depthFirstSearchReverse(null, currentState,
262                                         new ReverseBuilder(buildParams),
263                                         adjacencyList, certPathList);
264             } catch (GeneralSecurityException | IOException e) {
265                 // continue on error if more anchors to try
266                 if (iter.hasNext())
267                     continue;
268                 else
269                     throw e;
270             }
271 
272             // break out of loop if search is successful
273             if (pathCompleted) {
274                 break;
275             }
276         }
277 
278         if (debug != null) {
279             debug.println("SunCertPathBuilder.buildReverse() returned from "
280                 + "depthFirstSearchReverse()");
281             debug.println("SunCertPathBuilder.buildReverse() "
282                 + "certPathList.size: " + certPathList.size());
283         }
284     }
285 
286     /*
287      * Private build forward method.
288      */
buildForward(List<List<Vertex>> adjacencyList, LinkedList<X509Certificate> certPathList, boolean searchAllCertStores)289     private void buildForward(List<List<Vertex>> adjacencyList,
290                               LinkedList<X509Certificate> certPathList,
291                               boolean searchAllCertStores)
292         throws GeneralSecurityException, IOException
293     {
294         if (debug != null) {
295             debug.println("SunCertPathBuilder.buildForward()...");
296         }
297 
298         /* Initialize current state */
299         ForwardState currentState = new ForwardState();
300         currentState.initState(buildParams.certPathCheckers());
301 
302         /* Initialize adjacency list */
303         adjacencyList.clear();
304         adjacencyList.add(new LinkedList<Vertex>());
305 
306         currentState.untrustedChecker = new UntrustedChecker();
307 
308         depthFirstSearchForward(buildParams.targetSubject(), currentState,
309                                 new ForwardBuilder(buildParams,
310                                                    searchAllCertStores),
311                                 adjacencyList, certPathList);
312     }
313 
314     /*
315      * This method performs a depth first search for a certification
316      * path while building forward which meets the requirements set in
317      * the parameters object.
318      * It uses an adjacency list to store all certificates which were
319      * tried (i.e. at one time added to the path - they may not end up in
320      * the final path if backtracking occurs). This information can
321      * be used later to debug or demo the build.
322      *
323      * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
324      * for an explanation of the DFS algorithm.
325      *
326      * @param dN the distinguished name being currently searched for certs
327      * @param currentState the current PKIX validation state
328      */
depthFirstSearchForward(X500Principal dN, ForwardState currentState, ForwardBuilder builder, List<List<Vertex>> adjList, LinkedList<X509Certificate> cpList)329     private void depthFirstSearchForward(X500Principal dN,
330                                          ForwardState currentState,
331                                          ForwardBuilder builder,
332                                          List<List<Vertex>> adjList,
333                                          LinkedList<X509Certificate> cpList)
334         throws GeneralSecurityException, IOException
335     {
336         if (debug != null) {
337             debug.println("SunCertPathBuilder.depthFirstSearchForward(" + dN
338                           + ", " + currentState.toString() + ")");
339         }
340 
341         /*
342          * Find all the certificates issued to dN which
343          * satisfy the PKIX certification path constraints.
344          */
345         Collection<X509Certificate> certs =
346             builder.getMatchingCerts(currentState, buildParams.certStores());
347         List<Vertex> vertices = addVertices(certs, adjList);
348         if (debug != null) {
349             debug.println("SunCertPathBuilder.depthFirstSearchForward(): "
350                           + "certs.size=" + vertices.size());
351         }
352 
353         /*
354          * For each cert in the collection, verify anything
355          * that hasn't been checked yet (signature, revocation, etc)
356          * and check for loops. Call depthFirstSearchForward()
357          * recursively for each good cert.
358          */
359 
360                vertices:
361         for (Vertex vertex : vertices) {
362             /**
363              * Restore state to currentState each time through the loop.
364              * This is important because some of the user-defined
365              * checkers modify the state, which MUST be restored if
366              * the cert eventually fails to lead to the target and
367              * the next matching cert is tried.
368              */
369             ForwardState nextState = (ForwardState) currentState.clone();
370             X509Certificate cert = vertex.getCertificate();
371 
372             try {
373                 builder.verifyCert(cert, nextState, cpList);
374             } catch (GeneralSecurityException gse) {
375                 if (debug != null) {
376                     debug.println("SunCertPathBuilder.depthFirstSearchForward()"
377                                   + ": validation failed: " + gse);
378                     gse.printStackTrace();
379                 }
380                 vertex.setThrowable(gse);
381                 continue;
382             }
383 
384             /*
385              * Certificate is good.
386              * If cert completes the path,
387              *    process userCheckers that don't support forward checking
388              *    and process policies over whole path
389              *    and backtrack appropriately if there is a failure
390              * else if cert does not complete the path,
391              *    add it to the path
392              */
393             if (builder.isPathCompleted(cert)) {
394 
395                 if (debug != null)
396                     debug.println("SunCertPathBuilder.depthFirstSearchForward()"
397                                   + ": commencing final verification");
398 
399                 List<X509Certificate> appendedCerts = new ArrayList<>(cpList);
400 
401                 /*
402                  * if the trust anchor selected is specified as a trusted
403                  * public key rather than a trusted cert, then verify this
404                  * cert (which is signed by the trusted public key), but
405                  * don't add it yet to the cpList
406                  */
407                 if (builder.trustAnchor.getTrustedCert() == null) {
408                     appendedCerts.add(0, cert);
409                 }
410 
411                 Set<String> initExpPolSet =
412                     Collections.singleton(PolicyChecker.ANY_POLICY);
413 
414                 PolicyNodeImpl rootNode = new PolicyNodeImpl(null,
415                     PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false);
416 
417                 List<PKIXCertPathChecker> checkers = new ArrayList<>();
418                 PolicyChecker policyChecker
419                     = new PolicyChecker(buildParams.initialPolicies(),
420                                         appendedCerts.size(),
421                                         buildParams.explicitPolicyRequired(),
422                                         buildParams.policyMappingInhibited(),
423                                         buildParams.anyPolicyInhibited(),
424                                         buildParams.policyQualifiersRejected(),
425                                         rootNode);
426                 checkers.add(policyChecker);
427 
428                 // add the algorithm checker
429                 checkers.add(new AlgorithmChecker(builder.trustAnchor));
430 
431                 BasicChecker basicChecker = null;
432                 if (nextState.keyParamsNeeded()) {
433                     PublicKey rootKey = cert.getPublicKey();
434                     if (builder.trustAnchor.getTrustedCert() == null) {
435                         rootKey = builder.trustAnchor.getCAPublicKey();
436                         if (debug != null)
437                             debug.println(
438                                 "SunCertPathBuilder.depthFirstSearchForward " +
439                                 "using buildParams public key: " +
440                                 rootKey.toString());
441                     }
442                     TrustAnchor anchor = new TrustAnchor
443                         (cert.getSubjectX500Principal(), rootKey, null);
444 
445                     // add the basic checker
446                     basicChecker = new BasicChecker(anchor, buildParams.date(),
447                                                     buildParams.sigProvider(),
448                                                     true);
449                     checkers.add(basicChecker);
450                 }
451 
452                 buildParams.setCertPath(cf.generateCertPath(appendedCerts));
453 
454                 boolean revCheckerAdded = false;
455                 List<PKIXCertPathChecker> ckrs = buildParams.certPathCheckers();
456                 for (PKIXCertPathChecker ckr : ckrs) {
457                     if (ckr instanceof PKIXRevocationChecker) {
458                         if (revCheckerAdded) {
459                             throw new CertPathValidatorException(
460                                 "Only one PKIXRevocationChecker can be specified");
461                         }
462                         revCheckerAdded = true;
463                         // if it's our own, initialize it
464                         if (ckr instanceof RevocationChecker) {
465                             ((RevocationChecker)ckr).init(builder.trustAnchor,
466                                                           buildParams);
467                         }
468                     }
469                 }
470                 // only add a RevocationChecker if revocation is enabled and
471                 // a PKIXRevocationChecker has not already been added
472                 if (buildParams.revocationEnabled() && !revCheckerAdded) {
473                     checkers.add(new RevocationChecker(builder.trustAnchor,
474                                                        buildParams));
475                 }
476 
477                 checkers.addAll(ckrs);
478 
479                 // Why we don't need BasicChecker and RevocationChecker
480                 // if nextState.keyParamsNeeded() is false?
481 
482                 for (int i = 0; i < appendedCerts.size(); i++) {
483                     X509Certificate currCert = appendedCerts.get(i);
484                     if (debug != null)
485                         debug.println("current subject = "
486                                       + currCert.getSubjectX500Principal());
487                     Set<String> unresCritExts =
488                         currCert.getCriticalExtensionOIDs();
489                     if (unresCritExts == null) {
490                         unresCritExts = Collections.<String>emptySet();
491                     }
492 
493                     for (PKIXCertPathChecker currChecker : checkers) {
494                         if (!currChecker.isForwardCheckingSupported()) {
495                             if (i == 0) {
496                                 currChecker.init(false);
497 
498                                 // The user specified
499                                 // AlgorithmChecker may not be
500                                 // able to set the trust anchor until now.
501                                 if (currChecker instanceof AlgorithmChecker) {
502                                     ((AlgorithmChecker)currChecker).
503                                         trySetTrustAnchor(builder.trustAnchor);
504                                 }
505                             }
506 
507                             try {
508                                 currChecker.check(currCert, unresCritExts);
509                             } catch (CertPathValidatorException cpve) {
510                                 if (debug != null)
511                                     debug.println
512                                     ("SunCertPathBuilder.depthFirstSearchForward(): " +
513                                     "final verification failed: " + cpve);
514                                 // If the target cert itself is revoked, we
515                                 // cannot trust it. We can bail out here.
516                                 if (buildParams.targetCertConstraints().match(currCert)
517                                         && cpve.getReason() == BasicReason.REVOKED) {
518                                     throw cpve;
519                                 }
520                                 vertex.setThrowable(cpve);
521                                 continue vertices;
522                             }
523                         }
524                     }
525 
526                     /*
527                      * Remove extensions from user checkers that support
528                      * forward checking. After this step, we will have
529                      * removed all extensions that all user checkers
530                      * are capable of processing.
531                      */
532                     for (PKIXCertPathChecker checker :
533                          buildParams.certPathCheckers())
534                     {
535                         if (checker.isForwardCheckingSupported()) {
536                             Set<String> suppExts =
537                                 checker.getSupportedExtensions();
538                             if (suppExts != null) {
539                                 unresCritExts.removeAll(suppExts);
540                             }
541                         }
542                     }
543 
544                     if (!unresCritExts.isEmpty()) {
545                         unresCritExts.remove(BasicConstraints_Id.toString());
546                         unresCritExts.remove(NameConstraints_Id.toString());
547                         unresCritExts.remove(CertificatePolicies_Id.toString());
548                         unresCritExts.remove(PolicyMappings_Id.toString());
549                         unresCritExts.remove(PolicyConstraints_Id.toString());
550                         unresCritExts.remove(InhibitAnyPolicy_Id.toString());
551                         unresCritExts.remove(
552                             SubjectAlternativeName_Id.toString());
553                         unresCritExts.remove(KeyUsage_Id.toString());
554                         unresCritExts.remove(ExtendedKeyUsage_Id.toString());
555 
556                         if (!unresCritExts.isEmpty()) {
557                             throw new CertPathValidatorException
558                                 ("unrecognized critical extension(s)", null,
559                                  null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT);
560                         }
561                     }
562                 }
563                 if (debug != null)
564                     debug.println("SunCertPathBuilder.depthFirstSearchForward()"
565                         + ": final verification succeeded - path completed!");
566                 pathCompleted = true;
567 
568                 /*
569                  * if the user specified a trusted public key rather than
570                  * trusted certs, then add this cert (which is signed by
571                  * the trusted public key) to the cpList
572                  */
573                 if (builder.trustAnchor.getTrustedCert() == null)
574                     builder.addCertToPath(cert, cpList);
575                 // Save the trust anchor
576                 this.trustAnchor = builder.trustAnchor;
577 
578                 /*
579                  * Extract and save the final target public key
580                  */
581                 if (basicChecker != null) {
582                     finalPublicKey = basicChecker.getPublicKey();
583                 } else {
584                     Certificate finalCert;
585                     if (cpList.isEmpty()) {
586                         finalCert = builder.trustAnchor.getTrustedCert();
587                     } else {
588                         finalCert = cpList.getLast();
589                     }
590                     finalPublicKey = finalCert.getPublicKey();
591                 }
592 
593                 policyTreeResult = policyChecker.getPolicyTree();
594                 return;
595             } else {
596                 builder.addCertToPath(cert, cpList);
597             }
598 
599             /* Update the PKIX state */
600             nextState.updateState(cert);
601 
602             /*
603              * Append an entry for cert in adjacency list and
604              * set index for current vertex.
605              */
606             adjList.add(new LinkedList<Vertex>());
607             vertex.setIndex(adjList.size() - 1);
608 
609             /* recursively search for matching certs at next dN */
610             depthFirstSearchForward(cert.getIssuerX500Principal(), nextState,
611                                     builder, adjList, cpList);
612 
613             /*
614              * If path has been completed, return ASAP!
615              */
616             if (pathCompleted) {
617                 return;
618             } else {
619                 /*
620                  * If we get here, it means we have searched all possible
621                  * certs issued by the dN w/o finding any matching certs.
622                  * This means we have to backtrack to the previous cert in
623                  * the path and try some other paths.
624                  */
625                 if (debug != null)
626                     debug.println("SunCertPathBuilder.depthFirstSearchForward()"
627                                   + ": backtracking");
628                 builder.removeFinalCertFromPath(cpList);
629             }
630         }
631     }
632 
633     /*
634      * This method performs a depth first search for a certification
635      * path while building reverse which meets the requirements set in
636      * the parameters object.
637      * It uses an adjacency list to store all certificates which were
638      * tried (i.e. at one time added to the path - they may not end up in
639      * the final path if backtracking occurs). This information can
640      * be used later to debug or demo the build.
641      *
642      * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman"
643      * for an explanation of the DFS algorithm.
644      *
645      * @param dN the distinguished name being currently searched for certs
646      * @param currentState the current PKIX validation state
647      */
depthFirstSearchReverse(X500Principal dN, ReverseState currentState, ReverseBuilder builder, List<List<Vertex>> adjList, LinkedList<X509Certificate> cpList)648     private void depthFirstSearchReverse(X500Principal dN,
649                                          ReverseState currentState,
650                                          ReverseBuilder builder,
651                                          List<List<Vertex>> adjList,
652                                          LinkedList<X509Certificate> cpList)
653         throws GeneralSecurityException, IOException
654     {
655         if (debug != null)
656             debug.println("SunCertPathBuilder.depthFirstSearchReverse(" + dN
657                 + ", " + currentState.toString() + ")");
658 
659         /*
660          * Find all the certificates issued by dN which
661          * satisfy the PKIX certification path constraints.
662          */
663         Collection<X509Certificate> certs =
664             builder.getMatchingCerts(currentState, buildParams.certStores());
665         List<Vertex> vertices = addVertices(certs, adjList);
666         if (debug != null)
667             debug.println("SunCertPathBuilder.depthFirstSearchReverse(): "
668                 + "certs.size=" + vertices.size());
669 
670         /*
671          * For each cert in the collection, verify anything
672          * that hasn't been checked yet (signature, revocation, etc)
673          * and check for loops. Call depthFirstSearchReverse()
674          * recursively for each good cert.
675          */
676         for (Vertex vertex : vertices) {
677             /**
678              * Restore state to currentState each time through the loop.
679              * This is important because some of the user-defined
680              * checkers modify the state, which MUST be restored if
681              * the cert eventually fails to lead to the target and
682              * the next matching cert is tried.
683              */
684             ReverseState nextState = (ReverseState) currentState.clone();
685             X509Certificate cert = vertex.getCertificate();
686             try {
687                 builder.verifyCert(cert, nextState, cpList);
688             } catch (GeneralSecurityException gse) {
689                 if (debug != null)
690                     debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
691                         + ": validation failed: " + gse);
692                 vertex.setThrowable(gse);
693                 continue;
694             }
695 
696             /*
697              * Certificate is good, add it to the path (if it isn't a
698              * self-signed cert) and update state
699              */
700             if (!currentState.isInitial())
701                 builder.addCertToPath(cert, cpList);
702             // save trust anchor
703             this.trustAnchor = currentState.trustAnchor;
704 
705             /*
706              * Check if path is completed, return ASAP if so.
707              */
708             if (builder.isPathCompleted(cert)) {
709                 if (debug != null)
710                     debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
711                         + ": path completed!");
712                 pathCompleted = true;
713 
714                 PolicyNodeImpl rootNode = nextState.rootNode;
715 
716                 if (rootNode == null)
717                     policyTreeResult = null;
718                 else {
719                     policyTreeResult = rootNode.copyTree();
720                     ((PolicyNodeImpl)policyTreeResult).setImmutable();
721                 }
722 
723                 /*
724                  * Extract and save the final target public key
725                  */
726                 finalPublicKey = cert.getPublicKey();
727                 if (PKIX.isDSAPublicKeyWithoutParams(finalPublicKey)) {
728                     finalPublicKey =
729                         BasicChecker.makeInheritedParamsKey
730                             (finalPublicKey, currentState.pubKey);
731                 }
732 
733                 return;
734             }
735 
736             /* Update the PKIX state */
737             nextState.updateState(cert);
738 
739             /*
740              * Append an entry for cert in adjacency list and
741              * set index for current vertex.
742              */
743             adjList.add(new LinkedList<Vertex>());
744             vertex.setIndex(adjList.size() - 1);
745 
746             /* recursively search for matching certs at next dN */
747             depthFirstSearchReverse(cert.getSubjectX500Principal(), nextState,
748                                     builder, adjList, cpList);
749 
750             /*
751              * If path has been completed, return ASAP!
752              */
753             if (pathCompleted) {
754                 return;
755             } else {
756                 /*
757                  * If we get here, it means we have searched all possible
758                  * certs issued by the dN w/o finding any matching certs. This
759                  * means we have to backtrack to the previous cert in the path
760                  * and try some other paths.
761                  */
762                 if (debug != null)
763                     debug.println("SunCertPathBuilder.depthFirstSearchReverse()"
764                         + ": backtracking");
765                 if (!currentState.isInitial())
766                     builder.removeFinalCertFromPath(cpList);
767             }
768         }
769         if (debug != null)
770             debug.println("SunCertPathBuilder.depthFirstSearchReverse() all "
771                 + "certs in this adjacency list checked");
772     }
773 
774     /*
775      * Adds a collection of matching certificates to the
776      * adjacency list.
777      */
addVertices(Collection<X509Certificate> certs, List<List<Vertex>> adjList)778     private static List<Vertex> addVertices(Collection<X509Certificate> certs,
779                                             List<List<Vertex>> adjList)
780     {
781         List<Vertex> l = adjList.get(adjList.size() - 1);
782 
783         for (X509Certificate cert : certs) {
784             Vertex v = new Vertex(cert);
785             l.add(v);
786         }
787 
788         return l;
789     }
790 
791     /**
792      * Returns true if trust anchor certificate matches specified
793      * certificate constraints.
794      */
anchorIsTarget(TrustAnchor anchor, CertSelector sel)795     private static boolean anchorIsTarget(TrustAnchor anchor,
796                                           CertSelector sel)
797     {
798         X509Certificate anchorCert = anchor.getTrustedCert();
799         if (anchorCert != null) {
800             return sel.match(anchorCert);
801         }
802         return false;
803     }
804 }
805