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
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(): 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            if (buildParams.sigProvider() != null) {
776                (currState.cert).verify(cert.getPublicKey(),
777                                        buildParams.sigProvider());
778            } else {
779                (currState.cert).verify(cert.getPublicKey());
780            }
781        }
782    }
783
784    /**
785     * Verifies whether the input certificate completes the path.
786     * Checks the cert against each trust anchor that was specified, in order,
787     * and returns true as soon as it finds a valid anchor.
788     * Returns true if the cert matches a trust anchor specified as a
789     * certificate or if the cert verifies with a trust anchor that
790     * was specified as a trusted {pubkey, caname} pair. Returns false if none
791     * of the trust anchors are valid for this cert.
792     *
793     * @param cert the certificate to test
794     * @return a boolean value indicating whether the cert completes the path.
795     */
796    @Override
797    boolean isPathCompleted(X509Certificate cert) {
798        for (TrustAnchor anchor : trustAnchors) {
799            if (anchor.getTrustedCert() != null) {
800                if (cert.equals(anchor.getTrustedCert())) {
801                    this.trustAnchor = anchor;
802                    return true;
803                } else {
804                    continue;
805                }
806            }
807            X500Principal principal = anchor.getCA();
808            PublicKey publicKey = anchor.getCAPublicKey();
809
810            if (principal != null && publicKey != null &&
811                    principal.equals(cert.getSubjectX500Principal())) {
812                if (publicKey.equals(cert.getPublicKey())) {
813                    // the cert itself is a trust anchor
814                    this.trustAnchor = anchor;
815                    return true;
816                }
817                // else, it is a self-issued certificate of the anchor
818            }
819
820            // Check subject/issuer name chaining
821            if (principal == null ||
822                    !principal.equals(cert.getIssuerX500Principal())) {
823                continue;
824            }
825
826            // skip anchor if it contains a DSA key with no DSA params
827            if (PKIX.isDSAPublicKeyWithoutParams(publicKey)) {
828                continue;
829            }
830
831            /*
832             * Check signature
833             */
834            try {
835                if (buildParams.sigProvider() != null) {
836                    cert.verify(publicKey, buildParams.sigProvider());
837                } else {
838                    cert.verify(publicKey);
839                }
840            } catch (InvalidKeyException ike) {
841                if (debug != null) {
842                    debug.println("ForwardBuilder.isPathCompleted() invalid "
843                                  + "DSA key found");
844                }
845                continue;
846            } catch (GeneralSecurityException e){
847                if (debug != null) {
848                    debug.println("ForwardBuilder.isPathCompleted() " +
849                                  "unexpected exception");
850                    e.printStackTrace();
851                }
852                continue;
853            }
854
855            this.trustAnchor = anchor;
856            return true;
857        }
858
859        return false;
860    }
861
862    /** Adds the certificate to the certPathList
863     *
864     * @param cert the certificate to be added
865     * @param certPathList the certification path list
866     */
867    @Override
868    void addCertToPath(X509Certificate cert,
869                       LinkedList<X509Certificate> certPathList)
870    {
871        certPathList.addFirst(cert);
872    }
873
874    /** Removes final certificate from the certPathList
875     *
876     * @param certPathList the certification path list
877     */
878    @Override
879    void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
880        certPathList.removeFirst();
881    }
882}
883