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