PKIXCertPathValidator.java revision 7ab89abb837c9229b3d56252c7309b498b89bdc0
1/*
2 * Copyright (c) 2000, 2014, 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) {
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        int certPathLen = params.certificates().size();
163
164        // create PKIXCertPathCheckers
165        List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
166        // add standard checkers that we will be using
167        // Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
168        // certPathCheckers.add(new UntrustedChecker());
169        certPathCheckers.add(new AlgorithmChecker(anchor));
170        certPathCheckers.add(new KeyChecker(certPathLen,
171                                            params.targetCertConstraints()));
172        certPathCheckers.add(new ConstraintsChecker(certPathLen));
173        PolicyNodeImpl rootNode =
174            new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false,
175                               Collections.singleton(PolicyChecker.ANY_POLICY),
176                               false);
177        PolicyChecker pc = new PolicyChecker(params.initialPolicies(),
178                                             certPathLen,
179                                             params.explicitPolicyRequired(),
180                                             params.policyMappingInhibited(),
181                                             params.anyPolicyInhibited(),
182                                             params.policyQualifiersRejected(),
183                                             rootNode);
184        certPathCheckers.add(pc);
185        // default value for date is current time
186        BasicChecker bc = new BasicChecker(anchor, params.date(),
187                                           params.sigProvider(), false);
188        certPathCheckers.add(bc);
189
190        boolean revCheckerAdded = false;
191        List<PKIXCertPathChecker> checkers = params.certPathCheckers();
192        for (PKIXCertPathChecker checker : checkers) {
193            if (checker instanceof PKIXRevocationChecker) {
194                if (revCheckerAdded) {
195                    throw new CertPathValidatorException(
196                        "Only one PKIXRevocationChecker can be specified");
197                }
198                revCheckerAdded = true;
199                // if it's our own, initialize it
200                if (checker instanceof RevocationChecker) {
201                    ((RevocationChecker)checker).init(anchor, params);
202                }
203            }
204        }
205        // only add a RevocationChecker if revocation is enabled and
206        // a PKIXRevocationChecker has not already been added
207        if (params.revocationEnabled() && !revCheckerAdded) {
208            certPathCheckers.add(new RevocationChecker(anchor, params));
209        }
210        // add user-specified checkers
211        certPathCheckers.addAll(checkers);
212
213        PKIXMasterCertPathValidator.validate(params.certPath(),
214                                             params.certificates(),
215                                             certPathCheckers);
216
217        return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(),
218                                               bc.getPublicKey());
219    }
220}
221