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
26package sun.security.provider.certpath;
27
28import java.io.IOException;
29import java.security.InvalidAlgorithmParameterException;
30import java.security.cert.*;
31import java.util.*;
32
33import sun.security.provider.certpath.PKIX.ValidatorParams;
34import sun.security.x509.X509CertImpl;
35import 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 */
46public 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 && Debug.isVerbose()) {
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}
231