/* * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.provider.certpath; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.cert.*; import java.util.*; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import jdk.internal.event.X509ValidationEvent; import jdk.internal.event.EventHelper; import sun.security.provider.certpath.PKIX.ValidatorParams; import sun.security.validator.Validator; import sun.security.x509.X509CertImpl; import sun.security.util.Debug; /** * This class implements the PKIX validation algorithm for certification * paths consisting exclusively of X509Certificates. It uses * the specified input parameter set (which must be a * PKIXParameters object). * * @since 1.4 * @author Yassir Elley */ public final class PKIXCertPathValidator extends CertPathValidatorSpi { private static final Debug debug = Debug.getInstance("certpath"); private static final AtomicLong validationEventNumber = new AtomicLong(); /** * Default constructor. */ public PKIXCertPathValidator() {} @Override public CertPathChecker engineGetRevocationChecker() { return new RevocationChecker(); } /** * Validates a certification path consisting exclusively of * X509Certificates using the PKIX validation algorithm, * which uses the specified input parameter set. * The input parameter set must be a PKIXParameters object. * * @param cp the X509 certification path * @param params the input PKIX parameter set * @return the result * @throws CertPathValidatorException if cert path does not validate. * @throws InvalidAlgorithmParameterException if the specified * parameters are inappropriate for this CertPathValidator */ @Override public CertPathValidatorResult engineValidate(CertPath cp, CertPathParameters params) throws CertPathValidatorException, InvalidAlgorithmParameterException { ValidatorParams valParams = PKIX.checkParams(cp, params); return validate(valParams); } private static PKIXCertPathValidatorResult validate(ValidatorParams params) throws CertPathValidatorException { if (debug != null) debug.println("PKIXCertPathValidator.engineValidate()..."); // Retrieve the first certificate in the certpath // (to be used later in pre-screening) AdaptableX509CertSelector selector = null; List certList = params.certificates(); if (!certList.isEmpty()) { selector = new AdaptableX509CertSelector(); X509Certificate firstCert = certList.get(0); // check trusted certificate's subject selector.setSubject(firstCert.getIssuerX500Principal()); /* * Facilitate certification path construction with authority * key identifier and subject key identifier. */ try { X509CertImpl firstCertImpl = X509CertImpl.toImpl(firstCert); selector.setSkiAndSerialNumber( firstCertImpl.getAuthorityKeyIdentifierExtension()); } catch (CertificateException | IOException e) { // ignore } } CertPathValidatorException lastException = null; // We iterate through the set of trust anchors until we find // one that works at which time we stop iterating for (TrustAnchor anchor : params.trustAnchors()) { X509Certificate trustedCert = anchor.getTrustedCert(); if (trustedCert != null) { // if this trust anchor is not worth trying, // we move on to the next one if (selector != null && !selector.match(trustedCert)) { if (debug != null && Debug.isVerbose()) { debug.println("NO - don't try this trustedCert"); } continue; } if (debug != null) { debug.println("YES - try this trustedCert"); debug.println("anchor.getTrustedCert()." + "getSubjectX500Principal() = " + trustedCert.getSubjectX500Principal()); } } else { if (debug != null) { debug.println("PKIXCertPathValidator.engineValidate(): " + "anchor.getTrustedCert() == null"); } } try { return validate(anchor, params); } catch (CertPathValidatorException cpe) { // remember this exception lastException = cpe; } } // could not find a trust anchor that verified // (a) if we did a validation and it failed, use that exception if (lastException != null) { throw lastException; } // (b) otherwise, generate new exception throw new CertPathValidatorException ("Path does not chain with any of the trust anchors", null, null, -1, PKIXReason.NO_TRUST_ANCHOR); } private static PKIXCertPathValidatorResult validate(TrustAnchor anchor, ValidatorParams params) throws CertPathValidatorException { // check if anchor is untrusted UntrustedChecker untrustedChecker = new UntrustedChecker(); X509Certificate anchorCert = anchor.getTrustedCert(); if (anchorCert != null) { untrustedChecker.check(anchorCert); } int certPathLen = params.certificates().size(); // create PKIXCertPathCheckers List certPathCheckers = new ArrayList<>(); // add standard checkers that we will be using certPathCheckers.add(untrustedChecker); certPathCheckers.add(new AlgorithmChecker(anchor, null, params.date(), params.timestamp(), params.variant())); certPathCheckers.add(new KeyChecker(certPathLen, params.targetCertConstraints())); certPathCheckers.add(new ConstraintsChecker(certPathLen)); PolicyNodeImpl rootNode = new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false, Collections.singleton(PolicyChecker.ANY_POLICY), false); PolicyChecker pc = new PolicyChecker(params.initialPolicies(), certPathLen, params.explicitPolicyRequired(), params.policyMappingInhibited(), params.anyPolicyInhibited(), params.policyQualifiersRejected(), rootNode); certPathCheckers.add(pc); // the time that the certificate validity period should be // checked against Date timeToCheck = null; // use timestamp if checking signed code that is timestamped, otherwise // use date parameter from PKIXParameters if ((params.variant() == Validator.VAR_CODE_SIGNING || params.variant() == Validator.VAR_PLUGIN_CODE_SIGNING) && params.timestamp() != null) { timeToCheck = params.timestamp().getTimestamp(); } else { timeToCheck = params.date(); } BasicChecker bc = new BasicChecker(anchor, timeToCheck, params.sigProvider(), false); certPathCheckers.add(bc); boolean revCheckerAdded = false; List checkers = params.certPathCheckers(); for (PKIXCertPathChecker checker : checkers) { if (checker instanceof PKIXRevocationChecker) { if (revCheckerAdded) { throw new CertPathValidatorException( "Only one PKIXRevocationChecker can be specified"); } revCheckerAdded = true; // if it's our own, initialize it if (checker instanceof RevocationChecker) { ((RevocationChecker)checker).init(anchor, params); } } } // only add a RevocationChecker if revocation is enabled and // a PKIXRevocationChecker has not already been added if (params.revocationEnabled() && !revCheckerAdded) { certPathCheckers.add(new RevocationChecker(anchor, params)); } // add user-specified checkers certPathCheckers.addAll(checkers); PKIXMasterCertPathValidator.validate(params.certPath(), params.certificates(), certPathCheckers); X509ValidationEvent xve = new X509ValidationEvent(); if(xve.shouldCommit() || EventHelper.isLoggingSecurity()) { int[] hashCodes = params.certificates().stream() .mapToInt(x -> x.hashCode()) .toArray(); int anchorHashId = anchor.getTrustedCert().hashCode(); if (xve.shouldCommit()) { xve.hashCode = anchorHashId; int certificatePos = 1; //anchor cert xve.certificatePosition = certificatePos; xve.validationId = validationEventNumber.incrementAndGet(); xve.commit(); // now, iterate through remaining for (int hashCode : hashCodes) { xve.hashCode = hashCode; xve.certificatePosition = ++certificatePos; xve.commit(); } } if (EventHelper.isLoggingSecurity()) { EventHelper.logX509ValidationEvent(anchorHashId, hashCodes); } } return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(), bc.getPublicKey()); } }