View Javadoc
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.InvalidKeyException;
31  import java.security.PublicKey;
32  import java.security.cert.CertificateException;
33  import java.security.cert.CertPathValidatorException;
34  import java.security.cert.PKIXReason;
35  import java.security.cert.CertStore;
36  import java.security.cert.CertStoreException;
37  import java.security.cert.PKIXBuilderParameters;
38  import java.security.cert.PKIXCertPathChecker;
39  import java.security.cert.TrustAnchor;
40  import java.security.cert.X509Certificate;
41  import java.security.cert.X509CertSelector;
42  import java.util.*;
43  import javax.security.auth.x500.X500Principal;
44  
45  import sun.security.provider.certpath.PKIX.BuilderParams;
46  import sun.security.util.Debug;
47  import sun.security.x509.AccessDescription;
48  import sun.security.x509.AuthorityInfoAccessExtension;
49  import static sun.security.x509.PKIXExtensions.*;
50  import sun.security.x509.X500Name;
51  import sun.security.x509.AuthorityKeyIdentifierExtension;
52  
53  /**
54   * This class represents a forward builder, which is able to retrieve
55   * matching certificates from CertStores and verify a particular certificate
56   * against a ForwardState.
57   *
58   * @since       1.4
59   * @author      Yassir Elley
60   * @author      Sean Mullan
61   */
62  class ForwardBuilder extends Builder {
63  
64      private static final Debug debug = Debug.getInstance("certpath");
65      private final Set<X509Certificate> trustedCerts;
66      private final Set<X500Principal> trustedSubjectDNs;
67      private final Set<TrustAnchor> trustAnchors;
68      private X509CertSelector eeSelector;
69      private AdaptableX509CertSelector caSelector;
70      private X509CertSelector caTargetSelector;
71      TrustAnchor trustAnchor;
72      private Comparator<X509Certificate> comparator;
73      private boolean searchAllCertStores = true;
74  
75      /**
76       * Initialize the builder with the input parameters.
77       *
78       * @param params the parameter set used to build a certification path
79       */
80      ForwardBuilder(BuilderParams buildParams, boolean searchAllCertStores) {
81          super(buildParams);
82  
83          // populate sets of trusted certificates and subject DNs
84          trustAnchors = buildParams.trustAnchors();
85          trustedCerts = new HashSet<X509Certificate>(trustAnchors.size());
86          trustedSubjectDNs = new HashSet<X500Principal>(trustAnchors.size());
87          for (TrustAnchor anchor : trustAnchors) {
88              X509Certificate trustedCert = anchor.getTrustedCert();
89              if (trustedCert != null) {
90                  trustedCerts.add(trustedCert);
91                  trustedSubjectDNs.add(trustedCert.getSubjectX500Principal());
92              } else {
93                  trustedSubjectDNs.add(anchor.getCA());
94              }
95          }
96          comparator = new PKIXCertComparator(trustedSubjectDNs);
97          this.searchAllCertStores = searchAllCertStores;
98      }
99  
100     /**
101      * Retrieves all certs from the specified CertStores that satisfy the
102      * requirements specified in the parameters and the current
103      * PKIX state (name constraints, policy constraints, etc).
104      *
105      * @param currentState the current state.
106      *        Must be an instance of <code>ForwardState</code>
107      * @param certStores list of CertStores
108      */
109     @Override
110     Collection<X509Certificate> getMatchingCerts(State currentState,
111                                                  List<CertStore> certStores)
112         throws CertStoreException, CertificateException, IOException
113     {
114         if (debug != null) {
115             debug.println("ForwardBuilder.getMatchingCerts()...");
116         }
117 
118         ForwardState currState = (ForwardState) currentState;
119 
120         /*
121          * We store certs in a Set because we don't want duplicates.
122          * As each cert is added, it is sorted based on the PKIXCertComparator
123          * algorithm.
124          */
125         Set<X509Certificate> certs = new TreeSet<>(comparator);
126 
127         /*
128          * Only look for EE certs if search has just started.
129          */
130         if (currState.isInitial()) {
131             getMatchingEECerts(currState, certStores, certs);
132         }
133         getMatchingCACerts(currState, certStores, certs);
134 
135         return certs;
136     }
137 
138     /*
139      * Retrieves all end-entity certificates which satisfy constraints
140      * and requirements specified in the parameters and PKIX state.
141      */
142     private void getMatchingEECerts(ForwardState currentState,
143                                     List<CertStore> certStores,
144                                     Collection<X509Certificate> eeCerts)
145         throws IOException
146     {
147         if (debug != null) {
148             debug.println("ForwardBuilder.getMatchingEECerts()...");
149         }
150         /*
151          * Compose a certificate matching rule to filter out
152          * certs which don't satisfy constraints
153          *
154          * First, retrieve clone of current target cert constraints,
155          * and then add more selection criteria based on current validation
156          * state. Since selector never changes, cache local copy & reuse.
157          */
158         if (eeSelector == null) {
159             eeSelector = (X509CertSelector) targetCertConstraints.clone();
160 
161             /*
162              * Match on certificate validity date
163              */
164             eeSelector.setCertificateValid(buildParams.date());
165 
166             /*
167              * Policy processing optimizations
168              */
169             if (buildParams.explicitPolicyRequired()) {
170                 eeSelector.setPolicy(getMatchingPolicies());
171             }
172             /*
173              * Require EE certs
174              */
175             eeSelector.setBasicConstraints(-2);
176         }
177 
178         /* Retrieve matching EE certs from CertStores */
179         addMatchingCerts(eeSelector, certStores, eeCerts, searchAllCertStores);
180     }
181 
182     /**
183      * Retrieves all CA certificates which satisfy constraints
184      * and requirements specified in the parameters and PKIX state.
185      */
186     private void getMatchingCACerts(ForwardState currentState,
187                                     List<CertStore> certStores,
188                                     Collection<X509Certificate> caCerts)
189         throws IOException
190     {
191         if (debug != null) {
192             debug.println("ForwardBuilder.getMatchingCACerts()...");
193         }
194         int initialSize = caCerts.size();
195 
196         /*
197          * Compose a CertSelector to filter out
198          * certs which do not satisfy requirements.
199          */
200         X509CertSelector sel = null;
201 
202         if (currentState.isInitial()) {
203             if (targetCertConstraints.getBasicConstraints() == -2) {
204                 // no need to continue: this means we never can match a CA cert
205                 return;
206             }
207 
208             /* This means a CA is the target, so match on same stuff as
209              * getMatchingEECerts
210              */
211             if (debug != null) {
212                 debug.println("ForwardBuilder.getMatchingCACerts(): ca is target");
213             }
214 
215             if (caTargetSelector == null) {
216                 caTargetSelector =
217                     (X509CertSelector) targetCertConstraints.clone();
218 
219                 /*
220                  * Since we don't check the validity period of trusted
221                  * certificates, please don't set the certificate valid
222                  * criterion unless the trusted certificate matching is
223                  * completed.
224                  */
225 
226                 /*
227                  * Policy processing optimizations
228                  */
229                 if (buildParams.explicitPolicyRequired())
230                     caTargetSelector.setPolicy(getMatchingPolicies());
231             }
232 
233             sel = caTargetSelector;
234         } else {
235 
236             if (caSelector == null) {
237                 caSelector = new AdaptableX509CertSelector();
238 
239                 /*
240                  * Since we don't check the validity period of trusted
241                  * certificates, please don't set the certificate valid
242                  * criterion unless the trusted certificate matching is
243                  * completed.
244                  */
245 
246                 /*
247                  * Policy processing optimizations
248                  */
249                 if (buildParams.explicitPolicyRequired())
250                     caSelector.setPolicy(getMatchingPolicies());
251             }
252 
253             /*
254              * Match on subject (issuer of previous cert)
255              */
256             caSelector.setSubject(currentState.issuerDN);
257 
258             /*
259              * Match on subjectNamesTraversed (both DNs and AltNames)
260              * (checks that current cert's name constraints permit it
261              * to certify all the DNs and AltNames that have been traversed)
262              */
263             CertPathHelper.setPathToNames
264                 (caSelector, currentState.subjectNamesTraversed);
265 
266             /*
267              * Facilitate certification path construction with authority
268              * key identifier and subject key identifier.
269              */
270             AuthorityKeyIdentifierExtension akidext =
271                     currentState.cert.getAuthorityKeyIdentifierExtension();
272             caSelector.parseAuthorityKeyIdentifierExtension(akidext);
273 
274             /*
275              * check the validity period
276              */
277             caSelector.setValidityPeriod(currentState.cert.getNotBefore(),
278                                          currentState.cert.getNotAfter());
279 
280             sel = caSelector;
281         }
282 
283         /*
284          * For compatibility, conservatively, we don't check the path
285          * length constraint of trusted anchors.  Please don't set the
286          * basic constraints criterion unless the trusted certificate
287          * matching is completed.
288          */
289         sel.setBasicConstraints(-1);
290 
291         for (X509Certificate trustedCert : trustedCerts) {
292             if (sel.match(trustedCert)) {
293                 if (debug != null) {
294                     debug.println("ForwardBuilder.getMatchingCACerts: "
295                         + "found matching trust anchor");
296                 }
297                 if (caCerts.add(trustedCert) && !searchAllCertStores) {
298                     return;
299                 }
300             }
301         }
302 
303         /*
304          * The trusted certificate matching is completed. We need to match
305          * on certificate validity date.
306          */
307         sel.setCertificateValid(buildParams.date());
308 
309         /*
310          * Require CA certs with a pathLenConstraint that allows
311          * at least as many CA certs that have already been traversed
312          */
313         sel.setBasicConstraints(currentState.traversedCACerts);
314 
315         /*
316          * If we have already traversed as many CA certs as the maxPathLength
317          * will allow us to, then we don't bother looking through these
318          * certificate pairs. If maxPathLength has a value of -1, this
319          * means it is unconstrained, so we always look through the
320          * certificate pairs.
321          */
322         if (currentState.isInitial() ||
323            (buildParams.maxPathLength() == -1) ||
324            (buildParams.maxPathLength() > currentState.traversedCACerts))
325         {
326             if (addMatchingCerts(sel, certStores,
327                                  caCerts, searchAllCertStores)
328                 && !searchAllCertStores) {
329                 return;
330             }
331         }
332 
333         if (!currentState.isInitial() && Builder.USE_AIA) {
334             // check for AuthorityInformationAccess extension
335             AuthorityInfoAccessExtension aiaExt =
336                 currentState.cert.getAuthorityInfoAccessExtension();
337             if (aiaExt != null) {
338                 getCerts(aiaExt, caCerts);
339             }
340         }
341 
342         if (debug != null) {
343             int numCerts = caCerts.size() - initialSize;
344             debug.println("ForwardBuilder.getMatchingCACerts: found " +
345                 numCerts + " CA certs");
346         }
347     }
348 
349     /**
350      * Download Certificates from the given AIA and add them to the
351      * specified Collection.
352      */
353     // cs.getCertificates(caSelector) returns a collection of X509Certificate's
354     // because of the selector, so the cast is safe
355     @SuppressWarnings("unchecked")
356     private boolean getCerts(AuthorityInfoAccessExtension aiaExt,
357                              Collection<X509Certificate> certs)
358     {
359         if (Builder.USE_AIA == false) {
360             return false;
361         }
362         List<AccessDescription> adList = aiaExt.getAccessDescriptions();
363         if (adList == null || adList.isEmpty()) {
364             return false;
365         }
366 
367         boolean add = false;
368         for (AccessDescription ad : adList) {
369             CertStore cs = URICertStore.getInstance(ad);
370             if (cs != null) {
371                 try {
372                     if (certs.addAll((Collection<X509Certificate>)
373                         cs.getCertificates(caSelector))) {
374                         add = true;
375                         if (!searchAllCertStores) {
376                             return true;
377                         }
378                     }
379                 } catch (CertStoreException cse) {
380                     if (debug != null) {
381                         debug.println("exception getting certs from CertStore:");
382                         cse.printStackTrace();
383                     }
384                 }
385             }
386         }
387         return add;
388     }
389 
390     /**
391      * This inner class compares 2 PKIX certificates according to which
392      * should be tried first when building a path from the target.
393      * The preference order is as follows:
394      *
395      * Given trusted certificate(s):
396      *    Subject:ou=D,ou=C,o=B,c=A
397      *
398      * Preference order for current cert:
399      *
400      * 1) Issuer matches a trusted subject
401      *    Issuer: ou=D,ou=C,o=B,c=A
402      *
403      * 2) Issuer is a descendant of a trusted subject (in order of
404      *    number of links to the trusted subject)
405      *    a) Issuer: ou=E,ou=D,ou=C,o=B,c=A        [links=1]
406      *    b) Issuer: ou=F,ou=E,ou=D,ou=C,ou=B,c=A  [links=2]
407      *
408      * 3) Issuer is an ancestor of a trusted subject (in order of number of
409      *    links to the trusted subject)
410      *    a) Issuer: ou=C,o=B,c=A [links=1]
411      *    b) Issuer: o=B,c=A      [links=2]
412      *
413      * 4) Issuer is in the same namespace as a trusted subject (in order of
414      *    number of links to the trusted subject)
415      *    a) Issuer: ou=G,ou=C,o=B,c=A  [links=2]
416      *    b) Issuer: ou=H,o=B,c=A       [links=3]
417      *
418      * 5) Issuer is an ancestor of certificate subject (in order of number
419      *    of links to the certificate subject)
420      *    a) Issuer:  ou=K,o=J,c=A
421      *       Subject: ou=L,ou=K,o=J,c=A
422      *    b) Issuer:  o=J,c=A
423      *       Subject: ou=L,ou=K,0=J,c=A
424      *
425      * 6) Any other certificates
426      */
427     static class PKIXCertComparator implements Comparator<X509Certificate> {
428 
429         final static String METHOD_NME = "PKIXCertComparator.compare()";
430 
431         private final Set<X500Principal> trustedSubjectDNs;
432 
433         PKIXCertComparator(Set<X500Principal> trustedSubjectDNs) {
434             this.trustedSubjectDNs = trustedSubjectDNs;
435         }
436 
437         /**
438          * @param oCert1 First X509Certificate to be compared
439          * @param oCert2 Second X509Certificate to be compared
440          * @return -1 if oCert1 is preferable to oCert2, or
441          *            if oCert1 and oCert2 are equally preferable (in this
442          *            case it doesn't matter which is preferable, but we don't
443          *            return 0 because the comparator would behave strangely
444          *            when used in a SortedSet).
445          *          1 if oCert2 is preferable to oCert1
446          *          0 if oCert1.equals(oCert2). We only return 0 if the
447          *          certs are equal so that this comparator behaves
448          *          correctly when used in a SortedSet.
449          * @throws ClassCastException if either argument is not of type
450          * X509Certificate
451          */
452         @Override
453         public int compare(X509Certificate oCert1, X509Certificate oCert2) {
454 
455             // if certs are the same, return 0
456             if (oCert1.equals(oCert2)) return 0;
457 
458             X500Principal cIssuer1 = oCert1.getIssuerX500Principal();
459             X500Principal cIssuer2 = oCert2.getIssuerX500Principal();
460             X500Name cIssuer1Name = X500Name.asX500Name(cIssuer1);
461             X500Name cIssuer2Name = X500Name.asX500Name(cIssuer2);
462 
463             if (debug != null) {
464                 debug.println(METHOD_NME + " o1 Issuer:  " + cIssuer1);
465                 debug.println(METHOD_NME + " o2 Issuer:  " + cIssuer2);
466             }
467 
468             /* If one cert's issuer matches a trusted subject, then it is
469              * preferable.
470              */
471             if (debug != null) {
472                 debug.println(METHOD_NME + " MATCH TRUSTED SUBJECT TEST...");
473             }
474 
475             boolean m1 = trustedSubjectDNs.contains(cIssuer1);
476             boolean m2 = trustedSubjectDNs.contains(cIssuer2);
477             if (debug != null) {
478                 debug.println(METHOD_NME + " m1: " + m1);
479                 debug.println(METHOD_NME + " m2: " + m2);
480             }
481             if (m1 && m2) {
482                 return -1;
483             } else if (m1) {
484                 return -1;
485             } else if (m2) {
486                 return 1;
487             }
488 
489             /* If one cert's issuer is a naming descendant of a trusted subject,
490              * then it is preferable, in order of increasing naming distance.
491              */
492             if (debug != null) {
493                 debug.println(METHOD_NME + " NAMING DESCENDANT TEST...");
494             }
495             for (X500Principal tSubject : trustedSubjectDNs) {
496                 X500Name tSubjectName = X500Name.asX500Name(tSubject);
497                 int distanceTto1 =
498                     Builder.distance(tSubjectName, cIssuer1Name, -1);
499                 int distanceTto2 =
500                     Builder.distance(tSubjectName, cIssuer2Name, -1);
501                 if (debug != null) {
502                     debug.println(METHOD_NME +" distanceTto1: " + distanceTto1);
503                     debug.println(METHOD_NME +" distanceTto2: " + distanceTto2);
504                 }
505                 if (distanceTto1 > 0 || distanceTto2 > 0) {
506                     if (distanceTto1 == distanceTto2) {
507                         return -1;
508                     } else if (distanceTto1 > 0 && distanceTto2 <= 0) {
509                         return -1;
510                     } else if (distanceTto1 <= 0 && distanceTto2 > 0) {
511                         return 1;
512                     } else if (distanceTto1 < distanceTto2) {
513                         return -1;
514                     } else {    // distanceTto1 > distanceTto2
515                         return 1;
516                     }
517                 }
518             }
519 
520             /* If one cert's issuer is a naming ancestor of a trusted subject,
521              * then it is preferable, in order of increasing naming distance.
522              */
523             if (debug != null) {
524                 debug.println(METHOD_NME + " NAMING ANCESTOR TEST...");
525             }
526             for (X500Principal tSubject : trustedSubjectDNs) {
527                 X500Name tSubjectName = X500Name.asX500Name(tSubject);
528 
529                 int distanceTto1 = Builder.distance
530                     (tSubjectName, cIssuer1Name, Integer.MAX_VALUE);
531                 int distanceTto2 = Builder.distance
532                     (tSubjectName, cIssuer2Name, Integer.MAX_VALUE);
533                 if (debug != null) {
534                     debug.println(METHOD_NME +" distanceTto1: " + distanceTto1);
535                     debug.println(METHOD_NME +" distanceTto2: " + distanceTto2);
536                 }
537                 if (distanceTto1 < 0 || distanceTto2 < 0) {
538                     if (distanceTto1 == distanceTto2) {
539                         return -1;
540                     } else if (distanceTto1 < 0 && distanceTto2 >= 0) {
541                         return -1;
542                     } else if (distanceTto1 >= 0 && distanceTto2 < 0) {
543                         return 1;
544                     } else if (distanceTto1 > distanceTto2) {
545                         return -1;
546                     } else {
547                         return 1;
548                     }
549                 }
550             }
551 
552             /* If one cert's issuer is in the same namespace as a trusted
553              * subject, then it is preferable, in order of increasing naming
554              * distance.
555              */
556             if (debug != null) {
557                 debug.println(METHOD_NME +" SAME NAMESPACE AS TRUSTED TEST...");
558             }
559             for (X500Principal tSubject : trustedSubjectDNs) {
560                 X500Name tSubjectName = X500Name.asX500Name(tSubject);
561                 X500Name tAo1 = tSubjectName.commonAncestor(cIssuer1Name);
562                 X500Name tAo2 = tSubjectName.commonAncestor(cIssuer2Name);
563                 if (debug != null) {
564                     debug.println(METHOD_NME +" tAo1: " + String.valueOf(tAo1));
565                     debug.println(METHOD_NME +" tAo2: " + String.valueOf(tAo2));
566                 }
567                 if (tAo1 != null || tAo2 != null) {
568                     if (tAo1 != null && tAo2 != null) {
569                         int hopsTto1 = Builder.hops
570                             (tSubjectName, cIssuer1Name, Integer.MAX_VALUE);
571                         int hopsTto2 = Builder.hops
572                             (tSubjectName, cIssuer2Name, Integer.MAX_VALUE);
573                         if (debug != null) {
574                             debug.println(METHOD_NME +" hopsTto1: " + hopsTto1);
575                             debug.println(METHOD_NME +" hopsTto2: " + hopsTto2);
576                         }
577                         if (hopsTto1 == hopsTto2) {
578                         } else if (hopsTto1 > hopsTto2) {
579                             return 1;
580                         } else {  // hopsTto1 < hopsTto2
581                             return -1;
582                         }
583                     } else if (tAo1 == null) {
584                         return 1;
585                     } else {
586                         return -1;
587                     }
588                 }
589             }
590 
591 
592             /* If one cert's issuer is an ancestor of that cert's subject,
593              * then it is preferable, in order of increasing naming distance.
594              */
595             if (debug != null) {
596                 debug.println(METHOD_NME+" CERT ISSUER/SUBJECT COMPARISON TEST...");
597             }
598             X500Principal cSubject1 = oCert1.getSubjectX500Principal();
599             X500Principal cSubject2 = oCert2.getSubjectX500Principal();
600             X500Name cSubject1Name = X500Name.asX500Name(cSubject1);
601             X500Name cSubject2Name = X500Name.asX500Name(cSubject2);
602 
603             if (debug != null) {
604                 debug.println(METHOD_NME + " o1 Subject: " + cSubject1);
605                 debug.println(METHOD_NME + " o2 Subject: " + cSubject2);
606             }
607             int distanceStoI1 = Builder.distance
608                 (cSubject1Name, cIssuer1Name, Integer.MAX_VALUE);
609             int distanceStoI2 = Builder.distance
610                 (cSubject2Name, cIssuer2Name, Integer.MAX_VALUE);
611             if (debug != null) {
612                 debug.println(METHOD_NME + " distanceStoI1: " + distanceStoI1);
613                 debug.println(METHOD_NME + " distanceStoI2: " + distanceStoI2);
614             }
615             if (distanceStoI2 > distanceStoI1) {
616                 return -1;
617             } else if (distanceStoI2 < distanceStoI1) {
618                 return 1;
619             }
620 
621             /* Otherwise, certs are equally preferable.
622              */
623             if (debug != null) {
624                 debug.println(METHOD_NME + " no tests matched; RETURN 0");
625             }
626             return -1;
627         }
628     }
629 
630     /**
631      * Verifies a matching certificate.
632      *
633      * This method executes the validation steps in the PKIX path
634      * validation algorithm <draft-ietf-pkix-new-part1-08.txt> which were
635      * not satisfied by the selection criteria used by getCertificates()
636      * to find the certs and only the steps that can be executed in a
637      * forward direction (target to trust anchor). Those steps that can
638      * only be executed in a reverse direction are deferred until the
639      * complete path has been built.
640      *
641      * Trust anchor certs are not validated, but are used to verify the
642      * signature and revocation status of the previous cert.
643      *
644      * If the last certificate is being verified (the one whose subject
645      * matches the target subject, then steps in 6.1.4 of the PKIX
646      * Certification Path Validation algorithm are NOT executed,
647      * regardless of whether or not the last cert is an end-entity
648      * cert or not. This allows callers to certify CA certs as
649      * well as EE certs.
650      *
651      * @param cert the certificate to be verified
652      * @param currentState the current state against which the cert is verified
653      * @param certPathList the certPathList generated thus far
654      */
655     @Override
656     void verifyCert(X509Certificate cert, State currentState,
657                     List<X509Certificate> certPathList)
658         throws GeneralSecurityException
659     {
660         if (debug != null) {
661             debug.println("ForwardBuilder.verifyCert(SN: "
662                 + Debug.toHexString(cert.getSerialNumber())
663                 + "\n  Issuer: " + cert.getIssuerX500Principal() + ")"
664                 + "\n  Subject: " + cert.getSubjectX500Principal() + ")");
665         }
666 
667         ForwardState currState = (ForwardState)currentState;
668 
669         // Don't bother to verify untrusted certificate more.
670         currState.untrustedChecker.check(cert, Collections.<String>emptySet());
671 
672         /*
673          * check for looping - abort a loop if we encounter the same
674          * certificate twice
675          */
676         if (certPathList != null) {
677             for (X509Certificate cpListCert : certPathList) {
678                 if (cert.equals(cpListCert)) {
679                     if (debug != null) {
680                         debug.println("loop detected!!");
681                     }
682                     throw new CertPathValidatorException("loop detected");
683                 }
684             }
685         }
686 
687         /* check if trusted cert */
688         boolean isTrustedCert = trustedCerts.contains(cert);
689 
690         /* we don't perform any validation of the trusted cert */
691         if (!isTrustedCert) {
692             /*
693              * Check CRITICAL private extensions for user checkers that
694              * support forward checking (forwardCheckers) and remove
695              * ones we know how to check.
696              */
697             Set<String> unresCritExts = cert.getCriticalExtensionOIDs();
698             if (unresCritExts == null) {
699                 unresCritExts = Collections.<String>emptySet();
700             }
701             for (PKIXCertPathChecker checker : currState.forwardCheckers) {
702                 checker.check(cert, unresCritExts);
703             }
704 
705             /*
706              * Remove extensions from user checkers that don't support
707              * forward checking. After this step, we will have removed
708              * all extensions that all user checkers are capable of
709              * processing.
710              */
711             for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) {
712                 if (!checker.isForwardCheckingSupported()) {
713                     Set<String> supportedExts = checker.getSupportedExtensions();
714                     if (supportedExts != null) {
715                         unresCritExts.removeAll(supportedExts);
716                     }
717                 }
718             }
719 
720             /*
721              * Look at the remaining extensions and remove any ones we know how
722              * to check. If there are any left, throw an exception!
723              */
724             if (!unresCritExts.isEmpty()) {
725                 unresCritExts.remove(BasicConstraints_Id.toString());
726                 unresCritExts.remove(NameConstraints_Id.toString());
727                 unresCritExts.remove(CertificatePolicies_Id.toString());
728                 unresCritExts.remove(PolicyMappings_Id.toString());
729                 unresCritExts.remove(PolicyConstraints_Id.toString());
730                 unresCritExts.remove(InhibitAnyPolicy_Id.toString());
731                 unresCritExts.remove(SubjectAlternativeName_Id.toString());
732                 unresCritExts.remove(KeyUsage_Id.toString());
733                 unresCritExts.remove(ExtendedKeyUsage_Id.toString());
734 
735                 if (!unresCritExts.isEmpty())
736                     throw new CertPathValidatorException
737                         ("Unrecognized critical extension(s)", null, null, -1,
738                          PKIXReason.UNRECOGNIZED_CRIT_EXT);
739             }
740         }
741 
742         /*
743          * if this is the target certificate (init=true), then we are
744          * not able to do any more verification, so just return
745          */
746         if (currState.isInitial()) {
747             return;
748         }
749 
750         /* we don't perform any validation of the trusted cert */
751         if (!isTrustedCert) {
752             /* Make sure this is a CA cert */
753             if (cert.getBasicConstraints() == -1) {
754                 throw new CertificateException("cert is NOT a CA cert");
755             }
756 
757             /*
758              * Check keyUsage extension
759              */
760             KeyChecker.verifyCAKeyUsage(cert);
761         }
762 
763         /*
764          * the following checks are performed even when the cert
765          * is a trusted cert, since we are only extracting the
766          * subjectDN, and publicKey from the cert
767          * in order to verify a previous cert
768          */
769 
770         /*
771          * Check signature only if no key requiring key parameters has been
772          * encountered.
773          */
774         if (!currState.keyParamsNeeded()) {
775             (currState.cert).verify(cert.getPublicKey(),
776                                     buildParams.sigProvider());
777         }
778     }
779 
780     /**
781      * Verifies whether the input certificate completes the path.
782      * Checks the cert against each trust anchor that was specified, in order,
783      * and returns true as soon as it finds a valid anchor.
784      * Returns true if the cert matches a trust anchor specified as a
785      * certificate or if the cert verifies with a trust anchor that
786      * was specified as a trusted {pubkey, caname} pair. Returns false if none
787      * of the trust anchors are valid for this cert.
788      *
789      * @param cert the certificate to test
790      * @return a boolean value indicating whether the cert completes the path.
791      */
792     @Override
793     boolean isPathCompleted(X509Certificate cert) {
794         for (TrustAnchor anchor : trustAnchors) {
795             if (anchor.getTrustedCert() != null) {
796                 if (cert.equals(anchor.getTrustedCert())) {
797                     this.trustAnchor = anchor;
798                     return true;
799                 } else {
800                     continue;
801                 }
802             }
803             X500Principal principal = anchor.getCA();
804             PublicKey publicKey = anchor.getCAPublicKey();
805 
806             if (principal != null && publicKey != null &&
807                     principal.equals(cert.getSubjectX500Principal())) {
808                 if (publicKey.equals(cert.getPublicKey())) {
809                     // the cert itself is a trust anchor
810                     this.trustAnchor = anchor;
811                     return true;
812                 }
813                 // else, it is a self-issued certificate of the anchor
814             }
815 
816             // Check subject/issuer name chaining
817             if (principal == null ||
818                     !principal.equals(cert.getIssuerX500Principal())) {
819                 continue;
820             }
821 
822             // skip anchor if it contains a DSA key with no DSA params
823             if (PKIX.isDSAPublicKeyWithoutParams(publicKey)) {
824                 continue;
825             }
826 
827             /*
828              * Check signature
829              */
830             try {
831                 cert.verify(publicKey, buildParams.sigProvider());
832             } catch (InvalidKeyException ike) {
833                 if (debug != null) {
834                     debug.println("ForwardBuilder.isPathCompleted() invalid "
835                                   + "DSA key found");
836                 }
837                 continue;
838             } catch (GeneralSecurityException e){
839                 if (debug != null) {
840                     debug.println("ForwardBuilder.isPathCompleted() " +
841                                   "unexpected exception");
842                     e.printStackTrace();
843                 }
844                 continue;
845             }
846 
847             this.trustAnchor = anchor;
848             return true;
849         }
850 
851         return false;
852     }
853 
854     /** Adds the certificate to the certPathList
855      *
856      * @param cert the certificate to be added
857      * @param certPathList the certification path list
858      */
859     @Override
860     void addCertToPath(X509Certificate cert,
861                        LinkedList<X509Certificate> certPathList)
862     {
863         certPathList.addFirst(cert);
864     }
865 
866     /** Removes final certificate from the certPathList
867      *
868      * @param certPathList the certification path list
869      */
870     @Override
871     void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
872         certPathList.removeFirst();
873     }
874 }