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