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