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