Home | History | Annotate | Download | only in certpath
      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             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