1 /*
2  * Copyright (c) 2000, 2015, 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      */
PKIXCertPathValidator()53     public PKIXCertPathValidator() {}
54 
55     @Override
engineGetRevocationChecker()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
engineValidate(CertPath cp, CertPathParameters params)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 
validate(ValidatorParams params)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             // check the validity period
98             selector.setValidityPeriod(firstCert.getNotBefore(),
99                                        firstCert.getNotAfter());
100             /*
101              * Facilitate certification path construction with authority
102              * key identifier and subject key identifier.
103              */
104             try {
105                 X509CertImpl firstCertImpl = X509CertImpl.toImpl(firstCert);
106                 selector.setSkiAndSerialNumber(
107                             firstCertImpl.getAuthorityKeyIdentifierExtension());
108             } catch (CertificateException | IOException e) {
109                 // ignore
110             }
111         }
112 
113         CertPathValidatorException lastException = null;
114 
115         // We iterate through the set of trust anchors until we find
116         // one that works at which time we stop iterating
117         for (TrustAnchor anchor : params.trustAnchors()) {
118             X509Certificate trustedCert = anchor.getTrustedCert();
119             if (trustedCert != null) {
120                 // if this trust anchor is not worth trying,
121                 // we move on to the next one
122                 if (selector != null && !selector.match(trustedCert)) {
123                     if (debug != null) {
124                         debug.println("NO - don't try this trustedCert");
125                     }
126                     continue;
127                 }
128 
129                 if (debug != null) {
130                     debug.println("YES - try this trustedCert");
131                     debug.println("anchor.getTrustedCert()."
132                         + "getSubjectX500Principal() = "
133                         + trustedCert.getSubjectX500Principal());
134                 }
135             } else {
136                 if (debug != null) {
137                     debug.println("PKIXCertPathValidator.engineValidate(): "
138                         + "anchor.getTrustedCert() == null");
139                 }
140             }
141 
142             try {
143                 return validate(anchor, params);
144             } catch (CertPathValidatorException cpe) {
145                 // remember this exception
146                 lastException = cpe;
147             }
148         }
149 
150         // could not find a trust anchor that verified
151         // (a) if we did a validation and it failed, use that exception
152         if (lastException != null) {
153             throw lastException;
154         }
155         // (b) otherwise, generate new exception
156         throw new CertPathValidatorException
157             ("Path does not chain with any of the trust anchors",
158              null, null, -1, PKIXReason.NO_TRUST_ANCHOR);
159     }
160 
validate(TrustAnchor anchor, ValidatorParams params)161     private static PKIXCertPathValidatorResult validate(TrustAnchor anchor,
162                                                         ValidatorParams params)
163         throws CertPathValidatorException
164     {
165         // add standard checkers that we will be using
166         // Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
167         // check if anchor is untrusted
168         //UntrustedChecker untrustedChecker = new UntrustedChecker();
169         //X509Certificate anchorCert = anchor.getTrustedCert();
170         //if (anchorCert != null) {
171         //    untrustedChecker.check(anchorCert);
172         //}
173 
174         int certPathLen = params.certificates().size();
175 
176         // create PKIXCertPathCheckers
177         List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
178         // add standard checkers that we will be using
179         // Android-removed: Android doesn't use this mechanism for checking untrusted certificates.
180         // certPathCheckers.add(untrustedChecker);
181         certPathCheckers.add(new AlgorithmChecker(anchor));
182         certPathCheckers.add(new KeyChecker(certPathLen,
183                                             params.targetCertConstraints()));
184         certPathCheckers.add(new ConstraintsChecker(certPathLen));
185         PolicyNodeImpl rootNode =
186             new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false,
187                                Collections.singleton(PolicyChecker.ANY_POLICY),
188                                false);
189         PolicyChecker pc = new PolicyChecker(params.initialPolicies(),
190                                              certPathLen,
191                                              params.explicitPolicyRequired(),
192                                              params.policyMappingInhibited(),
193                                              params.anyPolicyInhibited(),
194                                              params.policyQualifiersRejected(),
195                                              rootNode);
196         certPathCheckers.add(pc);
197         // default value for date is current time
198         BasicChecker bc = new BasicChecker(anchor, params.date(),
199                                            params.sigProvider(), false);
200         certPathCheckers.add(bc);
201 
202         boolean revCheckerAdded = false;
203         List<PKIXCertPathChecker> checkers = params.certPathCheckers();
204         for (PKIXCertPathChecker checker : checkers) {
205             if (checker instanceof PKIXRevocationChecker) {
206                 if (revCheckerAdded) {
207                     throw new CertPathValidatorException(
208                         "Only one PKIXRevocationChecker can be specified");
209                 }
210                 revCheckerAdded = true;
211                 // if it's our own, initialize it
212                 if (checker instanceof RevocationChecker) {
213                     ((RevocationChecker)checker).init(anchor, params);
214                 }
215             }
216         }
217         // only add a RevocationChecker if revocation is enabled and
218         // a PKIXRevocationChecker has not already been added
219         if (params.revocationEnabled() && !revCheckerAdded) {
220             certPathCheckers.add(new RevocationChecker(anchor, params));
221         }
222         // add user-specified checkers
223         certPathCheckers.addAll(checkers);
224 
225         PKIXMasterCertPathValidator.validate(params.certPath(),
226                                              params.certificates(),
227                                              certPathCheckers);
228 
229         return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(),
230                                                bc.getPublicKey());
231     }
232 }
233