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