1 /*
   2  * Copyright (c) 2000, 2017, 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.InvalidAlgorithmParameterException;
  30 import java.security.cert.*;
  31 import java.util.*;
  32 
  33 import sun.security.provider.certpath.PKIX.ValidatorParams;
  34 import sun.security.x509.X509CertImpl;
  35 import sun.security.util.Debug;
  36 
  37 /**
  38  * This class implements the PKIX validation algorithm for certification
  39  * paths consisting exclusively of <code>X509Certificates</code>. It uses
  40  * the specified input parameter set (which must be a
  41  * <code>PKIXParameters</code> object).
  42  *
  43  * @since       1.4
  44  * @author      Yassir Elley
  45  */
  46 public final class PKIXCertPathValidator extends CertPathValidatorSpi {
  47 
  48     private static final Debug debug = Debug.getInstance("certpath");
  49 
  50     /**
  51      * Default constructor.
  52      */
  53     public PKIXCertPathValidator() {}
  54 
  55     @Override
  56     public CertPathChecker engineGetRevocationChecker() {
  57         return new RevocationChecker();
  58     }
  59 
  60     /**
  61      * Validates a certification path consisting exclusively of
  62      * <code>X509Certificate</code>s using the PKIX validation algorithm,
  63      * which uses the specified input parameter set.
  64      * The input parameter set must be a <code>PKIXParameters</code> object.
  65      *
  66      * @param cp the X509 certification path
  67      * @param params the input PKIX parameter set
  68      * @return the result
  69      * @throws CertPathValidatorException if cert path does not validate.
  70      * @throws InvalidAlgorithmParameterException if the specified
  71      *         parameters are inappropriate for this CertPathValidator
  72      */
  73     @Override
  74     public CertPathValidatorResult engineValidate(CertPath cp,
  75                                                   CertPathParameters params)
  76         throws CertPathValidatorException, InvalidAlgorithmParameterException
  77     {
  78         ValidatorParams valParams = PKIX.checkParams(cp, params);
  79         return validate(valParams);
  80     }
  81 
  82     private static PKIXCertPathValidatorResult validate(ValidatorParams params)
  83         throws CertPathValidatorException
  84     {
  85         if (debug != null)
  86             debug.println("PKIXCertPathValidator.engineValidate()...");
  87 
  88         // Retrieve the first certificate in the certpath
  89         // (to be used later in pre-screening)
  90         AdaptableX509CertSelector selector = null;
  91         List<X509Certificate> certList = params.certificates();
  92         if (!certList.isEmpty()) {
  93             selector = new AdaptableX509CertSelector();
  94             X509Certificate firstCert = certList.get(0);
  95             // check trusted certificate's subject
  96             selector.setSubject(firstCert.getIssuerX500Principal());
  97             /*
  98              * Facilitate certification path construction with authority
  99              * key identifier and subject key identifier.
 100              */
 101             try {
 102                 X509CertImpl firstCertImpl = X509CertImpl.toImpl(firstCert);
 103                 selector.setSkiAndSerialNumber(
 104                             firstCertImpl.getAuthorityKeyIdentifierExtension());
 105             } catch (CertificateException | IOException e) {
 106                 // ignore
 107             }
 108         }
 109 
 110         CertPathValidatorException lastException = null;
 111 
 112         // We iterate through the set of trust anchors until we find
 113         // one that works at which time we stop iterating
 114         for (TrustAnchor anchor : params.trustAnchors()) {
 115             X509Certificate trustedCert = anchor.getTrustedCert();
 116             if (trustedCert != null) {
 117                 // if this trust anchor is not worth trying,
 118                 // we move on to the next one
 119                 if (selector != null && !selector.match(trustedCert)) {
 120                     if (debug != null) {
 121                         debug.println("NO - don't try this trustedCert");
 122                     }
 123                     continue;
 124                 }
 125 
 126                 if (debug != null) {
 127                     debug.println("YES - try this trustedCert");
 128                     debug.println("anchor.getTrustedCert()."
 129                         + "getSubjectX500Principal() = "
 130                         + trustedCert.getSubjectX500Principal());
 131                 }
 132             } else {
 133                 if (debug != null) {
 134                     debug.println("PKIXCertPathValidator.engineValidate(): "
 135                         + "anchor.getTrustedCert() == null");
 136                 }
 137             }
 138 
 139             try {
 140                 return validate(anchor, params);
 141             } catch (CertPathValidatorException cpe) {
 142                 // remember this exception
 143                 lastException = cpe;
 144             }
 145         }
 146 
 147         // could not find a trust anchor that verified
 148         // (a) if we did a validation and it failed, use that exception
 149         if (lastException != null) {
 150             throw lastException;
 151         }
 152         // (b) otherwise, generate new exception
 153         throw new CertPathValidatorException
 154             ("Path does not chain with any of the trust anchors",
 155              null, null, -1, PKIXReason.NO_TRUST_ANCHOR);
 156     }
 157 
 158     private static PKIXCertPathValidatorResult validate(TrustAnchor anchor,
 159                                                         ValidatorParams params)
 160         throws CertPathValidatorException
 161     {
 162         // check if anchor is untrusted
 163         UntrustedChecker untrustedChecker = new UntrustedChecker();
 164         X509Certificate anchorCert = anchor.getTrustedCert();
 165         if (anchorCert != null) {
 166             untrustedChecker.check(anchorCert);
 167         }
 168 
 169         int certPathLen = params.certificates().size();
 170 
 171         // create PKIXCertPathCheckers
 172         List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
 173         // add standard checkers that we will be using
 174         certPathCheckers.add(untrustedChecker);
 175         certPathCheckers.add(new AlgorithmChecker(anchor, null, params.date(),
 176                 params.timestamp(), params.variant()));
 177         certPathCheckers.add(new KeyChecker(certPathLen,
 178                                             params.targetCertConstraints()));
 179         certPathCheckers.add(new ConstraintsChecker(certPathLen));
 180         PolicyNodeImpl rootNode =
 181             new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false,
 182                                Collections.singleton(PolicyChecker.ANY_POLICY),
 183                                false);
 184         PolicyChecker pc = new PolicyChecker(params.initialPolicies(),
 185                                              certPathLen,
 186                                              params.explicitPolicyRequired(),
 187                                              params.policyMappingInhibited(),
 188                                              params.anyPolicyInhibited(),
 189                                              params.policyQualifiersRejected(),
 190                                              rootNode);
 191         certPathCheckers.add(pc);
 192         // default value for date is current time
 193         BasicChecker bc;
 194         bc = new BasicChecker(anchor,
 195                 (params.timestamp() == null ? params.date() :
 196                         params.timestamp().getTimestamp()),
 197                 params.sigProvider(), false);
 198         certPathCheckers.add(bc);
 199 
 200         boolean revCheckerAdded = false;
 201         List<PKIXCertPathChecker> checkers = params.certPathCheckers();
 202         for (PKIXCertPathChecker checker : checkers) {
 203             if (checker instanceof PKIXRevocationChecker) {
 204                 if (revCheckerAdded) {
 205                     throw new CertPathValidatorException(
 206                         "Only one PKIXRevocationChecker can be specified");
 207                 }
 208                 revCheckerAdded = true;
 209                 // if it's our own, initialize it
 210                 if (checker instanceof RevocationChecker) {
 211                     ((RevocationChecker)checker).init(anchor, params);
 212                 }
 213             }
 214         }
 215         // only add a RevocationChecker if revocation is enabled and
 216         // a PKIXRevocationChecker has not already been added
 217         if (params.revocationEnabled() && !revCheckerAdded) {
 218             certPathCheckers.add(new RevocationChecker(anchor, params));
 219         }
 220         // add user-specified checkers
 221         certPathCheckers.addAll(checkers);
 222 
 223         PKIXMasterCertPathValidator.validate(params.certPath(),
 224                                              params.certificates(),
 225                                              certPathCheckers);
 226 
 227         return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(),
 228                                                bc.getPublicKey());
 229     }
 230 }