1 /*
2  * Copyright (c) 2002, 2019, 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.validator;
27 
28 import java.util.*;
29 
30 import java.security.cert.*;
31 
32 import sun.security.x509.NetscapeCertTypeExtension;
33 
34 /**
35  * Class to check if an end entity cert is suitable for use in some
36  * context.<p>
37  *
38  * This class is used internally by the validator. Currently, seven variants
39  * are supported defined as VAR_XXX constants in the Validator class:
40  * <ul>
41  * <li>Generic. No additional requirements, all certificates are ok.
42  *
43  * <li>TLS server. Requires that a String parameter is passed to
44  * validate that specifies the name of the TLS key exchange algorithm
45  * in use. See the JSSE X509TrustManager spec for details.
46  *
47  * <li>TLS client.
48  *
49  * <li>Code signing.
50  *
51  * <li>JCE code signing. Some early JCE code signing certs issued to
52  * providers had incorrect extensions. In this mode the checks
53  * are relaxed compared to standard code signing checks in order to
54  * allow these certificates to pass.
55  *
56  * <li>Plugin code signing. WebStart and Plugin require their own variant
57  * which is equivalent to VAR_CODE_SIGNING with additional checks for
58  * compatibility/special cases. See also PKIXValidator.
59  *
60  * <li>TSA Server (see RFC 3161, section 2.3).
61  *
62  * </ul>
63  *
64  * @author Andreas Sterbenz
65  */
66 class EndEntityChecker {
67 
68     // extended key usage OIDs for TLS server, TLS client, code signing
69     // and any usage
70 
71     private static final String OID_EXTENDED_KEY_USAGE =
72                                 SimpleValidator.OID_EXTENDED_KEY_USAGE;
73 
74     private static final String OID_EKU_TLS_SERVER = "1.3.6.1.5.5.7.3.1";
75 
76     private static final String OID_EKU_TLS_CLIENT = "1.3.6.1.5.5.7.3.2";
77 
78     private static final String OID_EKU_CODE_SIGNING = "1.3.6.1.5.5.7.3.3";
79 
80     private static final String OID_EKU_TIME_STAMPING = "1.3.6.1.5.5.7.3.8";
81 
82     private static final String OID_EKU_ANY_USAGE = "2.5.29.37.0";
83 
84     // the Netscape Server-Gated-Cryptography EKU extension OID
85     private static final String OID_EKU_NS_SGC = "2.16.840.1.113730.4.1";
86 
87     // the Microsoft Server-Gated-Cryptography EKU extension OID
88     private static final String OID_EKU_MS_SGC = "1.3.6.1.4.1.311.10.3.3";
89 
90     // the recognized extension OIDs
91     private static final String OID_SUBJECT_ALT_NAME = "2.5.29.17";
92 
93     private static final String NSCT_SSL_CLIENT =
94                                 NetscapeCertTypeExtension.SSL_CLIENT;
95 
96     private static final String NSCT_SSL_SERVER =
97                                 NetscapeCertTypeExtension.SSL_SERVER;
98 
99     private static final String NSCT_CODE_SIGNING =
100                                 NetscapeCertTypeExtension.OBJECT_SIGNING;
101 
102     // bit numbers in the key usage extension
103     private static final int KU_SIGNATURE = 0;
104     private static final int KU_KEY_ENCIPHERMENT = 2;
105     private static final int KU_KEY_AGREEMENT = 4;
106 
107     // TLS key exchange algorithms requiring digitalSignature key usage
108     private static final Collection<String> KU_SERVER_SIGNATURE =
109         Arrays.asList("DHE_DSS", "DHE_RSA", "ECDHE_ECDSA", "ECDHE_RSA",
110             "RSA_EXPORT", "UNKNOWN");
111 
112     // TLS key exchange algorithms requiring keyEncipherment key usage
113     private static final Collection<String> KU_SERVER_ENCRYPTION =
114         Arrays.asList("RSA");
115 
116     // TLS key exchange algorithms requiring keyAgreement key usage
117     private static final Collection<String> KU_SERVER_KEY_AGREEMENT =
118         Arrays.asList("DH_DSS", "DH_RSA", "ECDH_ECDSA", "ECDH_RSA");
119 
120     // variant of this end entity cert checker
121     private final String variant;
122 
123     // type of the validator this checker belongs to
124     private final String type;
125 
EndEntityChecker(String type, String variant)126     private EndEntityChecker(String type, String variant) {
127         this.type = type;
128         this.variant = variant;
129     }
130 
getInstance(String type, String variant)131     static EndEntityChecker getInstance(String type, String variant) {
132         return new EndEntityChecker(type, variant);
133     }
134 
check(X509Certificate[] chain, Object parameter, boolean checkUnresolvedCritExts)135     void check(X509Certificate[] chain, Object parameter,
136             boolean checkUnresolvedCritExts) throws CertificateException {
137 
138         if (variant.equals(Validator.VAR_GENERIC)) {
139             return; // no checks
140         }
141 
142         Set<String> exts = getCriticalExtensions(chain[0]);
143         if (variant.equals(Validator.VAR_TLS_SERVER)) {
144             checkTLSServer(chain[0], (String)parameter, exts);
145         } else if (variant.equals(Validator.VAR_TLS_CLIENT)) {
146             checkTLSClient(chain[0], exts);
147         } else if (variant.equals(Validator.VAR_CODE_SIGNING)) {
148             checkCodeSigning(chain[0], exts);
149         } else if (variant.equals(Validator.VAR_JCE_SIGNING)) {
150             checkCodeSigning(chain[0], exts);
151         } else if (variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING)) {
152             checkCodeSigning(chain[0], exts);
153         } else if (variant.equals(Validator.VAR_TSA_SERVER)) {
154             checkTSAServer(chain[0], exts);
155         } else {
156             throw new CertificateException("Unknown variant: " + variant);
157         }
158 
159         // if neither VAR_GENERIC variant nor unknown variant
160         if (checkUnresolvedCritExts) {
161             checkRemainingExtensions(exts);
162         }
163 
164         // check if certificate should be distrusted according to policies
165         // set in the jdk.security.caDistrustPolicies security property
166         for (CADistrustPolicy policy : CADistrustPolicy.POLICIES) {
167             policy.checkDistrust(variant, chain);
168         }
169     }
170 
171     /**
172      * Utility method returning the Set of critical extensions for
173      * certificate cert (never null).
174      */
getCriticalExtensions(X509Certificate cert)175     private Set<String> getCriticalExtensions(X509Certificate cert) {
176         Set<String> exts = cert.getCriticalExtensionOIDs();
177         if (exts == null) {
178             exts = Collections.emptySet();
179         }
180         return exts;
181     }
182 
183     /**
184      * Utility method checking if there are any unresolved critical extensions.
185      * @throws CertificateException if so.
186      */
checkRemainingExtensions(Set<String> exts)187     private void checkRemainingExtensions(Set<String> exts)
188             throws CertificateException {
189         // basic constraints irrelevant in EE certs
190         exts.remove(SimpleValidator.OID_BASIC_CONSTRAINTS);
191 
192         // If the subject field contains an empty sequence, the subjectAltName
193         // extension MUST be marked critical.
194         // We do not check the validity of the critical extension, just mark
195         // it recognizable here.
196         exts.remove(OID_SUBJECT_ALT_NAME);
197 
198         if (!exts.isEmpty()) {
199             throw new CertificateException("Certificate contains unsupported "
200                 + "critical extensions: " + exts);
201         }
202     }
203 
204     /**
205      * Utility method checking if the extended key usage extension in
206      * certificate cert allows use for expectedEKU.
207      */
checkEKU(X509Certificate cert, Set<String> exts, String expectedEKU)208     private boolean checkEKU(X509Certificate cert, Set<String> exts,
209             String expectedEKU) throws CertificateException {
210         List<String> eku = cert.getExtendedKeyUsage();
211         if (eku == null) {
212             return true;
213         }
214         return eku.contains(expectedEKU) || eku.contains(OID_EKU_ANY_USAGE);
215     }
216 
217     /**
218      * Utility method checking if bit 'bit' is set in this certificates
219      * key usage extension.
220      * @throws CertificateException if not
221      */
checkKeyUsage(X509Certificate cert, int bit)222     private boolean checkKeyUsage(X509Certificate cert, int bit)
223             throws CertificateException {
224         boolean[] keyUsage = cert.getKeyUsage();
225         if (keyUsage == null) {
226             return true;
227         }
228         return (keyUsage.length > bit) && keyUsage[bit];
229     }
230 
231     /**
232      * Check whether this certificate can be used for TLS client
233      * authentication.
234      * @throws CertificateException if not.
235      */
checkTLSClient(X509Certificate cert, Set<String> exts)236     private void checkTLSClient(X509Certificate cert, Set<String> exts)
237             throws CertificateException {
238         if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
239             throw new ValidatorException
240                 ("KeyUsage does not allow digital signatures",
241                 ValidatorException.T_EE_EXTENSIONS, cert);
242         }
243 
244         if (checkEKU(cert, exts, OID_EKU_TLS_CLIENT) == false) {
245             throw new ValidatorException("Extended key usage does not "
246                 + "permit use for TLS client authentication",
247                 ValidatorException.T_EE_EXTENSIONS, cert);
248         }
249 
250         if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_CLIENT)) {
251             throw new ValidatorException
252                 ("Netscape cert type does not permit use for SSL client",
253                 ValidatorException.T_EE_EXTENSIONS, cert);
254         }
255 
256         // remove extensions we checked
257         exts.remove(SimpleValidator.OID_KEY_USAGE);
258         exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
259         exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE);
260     }
261 
262     /**
263      * Check whether this certificate can be used for TLS server authentication
264      * using the specified authentication type parameter. See X509TrustManager
265      * specification for details.
266      * @throws CertificateException if not.
267      */
checkTLSServer(X509Certificate cert, String parameter, Set<String> exts)268     private void checkTLSServer(X509Certificate cert, String parameter,
269             Set<String> exts) throws CertificateException {
270         if (KU_SERVER_ENCRYPTION.contains(parameter)) {
271             if (checkKeyUsage(cert, KU_KEY_ENCIPHERMENT) == false) {
272                 throw new ValidatorException
273                         ("KeyUsage does not allow key encipherment",
274                         ValidatorException.T_EE_EXTENSIONS, cert);
275             }
276         } else if (KU_SERVER_SIGNATURE.contains(parameter)) {
277             if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
278                 throw new ValidatorException
279                         ("KeyUsage does not allow digital signatures",
280                         ValidatorException.T_EE_EXTENSIONS, cert);
281             }
282         } else if (KU_SERVER_KEY_AGREEMENT.contains(parameter)) {
283             if (checkKeyUsage(cert, KU_KEY_AGREEMENT) == false) {
284                 throw new ValidatorException
285                         ("KeyUsage does not allow key agreement",
286                         ValidatorException.T_EE_EXTENSIONS, cert);
287             }
288         } else {
289             throw new CertificateException("Unknown authType: " + parameter);
290         }
291 
292         if (checkEKU(cert, exts, OID_EKU_TLS_SERVER) == false) {
293             // check for equivalent but now obsolete Server-Gated-Cryptography
294             // (aka Step-Up, 128 bit) EKU OIDs
295             if ((checkEKU(cert, exts, OID_EKU_MS_SGC) == false) &&
296                 (checkEKU(cert, exts, OID_EKU_NS_SGC) == false)) {
297                 throw new ValidatorException
298                     ("Extended key usage does not permit use for TLS "
299                     + "server authentication",
300                     ValidatorException.T_EE_EXTENSIONS, cert);
301             }
302         }
303 
304         if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_SERVER)) {
305             throw new ValidatorException
306                 ("Netscape cert type does not permit use for SSL server",
307                 ValidatorException.T_EE_EXTENSIONS, cert);
308         }
309 
310         // remove extensions we checked
311         exts.remove(SimpleValidator.OID_KEY_USAGE);
312         exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
313         exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE);
314     }
315 
316     /**
317      * Check whether this certificate can be used for code signing.
318      * @throws CertificateException if not.
319      */
checkCodeSigning(X509Certificate cert, Set<String> exts)320     private void checkCodeSigning(X509Certificate cert, Set<String> exts)
321             throws CertificateException {
322         if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
323             throw new ValidatorException
324                 ("KeyUsage does not allow digital signatures",
325                 ValidatorException.T_EE_EXTENSIONS, cert);
326         }
327 
328         if (checkEKU(cert, exts, OID_EKU_CODE_SIGNING) == false) {
329             throw new ValidatorException
330                 ("Extended key usage does not permit use for code signing",
331                 ValidatorException.T_EE_EXTENSIONS, cert);
332         }
333 
334         // do not check Netscape cert type for JCE code signing checks
335         // (some certs were issued with incorrect extensions)
336         if (variant.equals(Validator.VAR_JCE_SIGNING) == false) {
337             if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING)) {
338                 throw new ValidatorException
339                     ("Netscape cert type does not permit use for code signing",
340                     ValidatorException.T_EE_EXTENSIONS, cert);
341             }
342             exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE);
343         }
344 
345         // remove extensions we checked
346         exts.remove(SimpleValidator.OID_KEY_USAGE);
347         exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
348     }
349 
350     /**
351      * Check whether this certificate can be used by a time stamping authority
352      * server (see RFC 3161, section 2.3).
353      * @throws CertificateException if not.
354      */
checkTSAServer(X509Certificate cert, Set<String> exts)355     private void checkTSAServer(X509Certificate cert, Set<String> exts)
356             throws CertificateException {
357         if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
358             throw new ValidatorException
359                 ("KeyUsage does not allow digital signatures",
360                 ValidatorException.T_EE_EXTENSIONS, cert);
361         }
362 
363         if (cert.getExtendedKeyUsage() == null) {
364             throw new ValidatorException
365                 ("Certificate does not contain an extended key usage " +
366                 "extension required for a TSA server",
367                 ValidatorException.T_EE_EXTENSIONS, cert);
368         }
369 
370         if (checkEKU(cert, exts, OID_EKU_TIME_STAMPING) == false) {
371             throw new ValidatorException
372                 ("Extended key usage does not permit use for TSA server",
373                 ValidatorException.T_EE_EXTENSIONS, cert);
374         }
375 
376         // remove extensions we checked
377         exts.remove(SimpleValidator.OID_KEY_USAGE);
378         exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
379     }
380 }
381