blob: f0fb1787fa35909076475c06477919de0f685f82 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.validator;
27
28import java.util.*;
29
30import java.security.cert.*;
31
32import 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 */
66class EndEntityChecker {
67
68 // extended key usage OIDs for TLS server, TLS client, code signing
69 // and any usage
70
71 private final static String OID_EXTENDED_KEY_USAGE =
72 SimpleValidator.OID_EXTENDED_KEY_USAGE;
73
74 private final static String OID_EKU_TLS_SERVER = "1.3.6.1.5.5.7.3.1";
75
76 private final static String OID_EKU_TLS_CLIENT = "1.3.6.1.5.5.7.3.2";
77
78 private final static String OID_EKU_CODE_SIGNING = "1.3.6.1.5.5.7.3.3";
79
80 private final static String OID_EKU_TIME_STAMPING = "1.3.6.1.5.5.7.3.8";
81
82 private final static String OID_EKU_ANY_USAGE = "2.5.29.37.0";
83
84 // the Netscape Server-Gated-Cryptography EKU extension OID
85 private final static String OID_EKU_NS_SGC = "2.16.840.1.113730.4.1";
86
87 // the Microsoft Server-Gated-Cryptography EKU extension OID
88 private final static String OID_EKU_MS_SGC = "1.3.6.1.4.1.311.10.3.3";
89
90 private final static String NSCT_SSL_CLIENT =
91 NetscapeCertTypeExtension.SSL_CLIENT;
92
93 private final static String NSCT_SSL_SERVER =
94 NetscapeCertTypeExtension.SSL_SERVER;
95
96 private final static String NSCT_CODE_SIGNING =
97 NetscapeCertTypeExtension.OBJECT_SIGNING;
98
99 // bit numbers in the key usage extension
100 private final static int KU_SIGNATURE = 0;
101 private final static int KU_KEY_ENCIPHERMENT = 2;
102 private final static int KU_KEY_AGREEMENT = 4;
103
104 // TLS key exchange algorithms requiring digitalSignature key usage
105 private final static Collection<String> KU_SERVER_SIGNATURE =
106 Arrays.asList("DHE_DSS", "DHE_RSA", "ECDHE_ECDSA", "ECDHE_RSA",
107 "RSA_EXPORT", "UNKNOWN");
108
109 // TLS key exchange algorithms requiring keyEncipherment key usage
110 private final static Collection<String> KU_SERVER_ENCRYPTION =
111 Arrays.asList("RSA");
112
113 // TLS key exchange algorithms requiring keyAgreement key usage
114 private final static Collection<String> KU_SERVER_KEY_AGREEMENT =
115 Arrays.asList("DH_DSS", "DH_RSA", "ECDH_ECDSA", "ECDH_RSA");
116
117 // variant of this end entity cert checker
118 private final String variant;
119
120 // type of the validator this checker belongs to
121 private final String type;
122
123 private EndEntityChecker(String type, String variant) {
124 this.type = type;
125 this.variant = variant;
126 }
127
128 static EndEntityChecker getInstance(String type, String variant) {
129 return new EndEntityChecker(type, variant);
130 }
131
132 void check(X509Certificate cert, Object parameter)
133 throws CertificateException {
134 if (variant.equals(Validator.VAR_GENERIC)) {
135 // no checks
136 return;
137 } else if (variant.equals(Validator.VAR_TLS_SERVER)) {
138 checkTLSServer(cert, (String)parameter);
139 } else if (variant.equals(Validator.VAR_TLS_CLIENT)) {
140 checkTLSClient(cert);
141 } else if (variant.equals(Validator.VAR_CODE_SIGNING)) {
142 checkCodeSigning(cert);
143 } else if (variant.equals(Validator.VAR_JCE_SIGNING)) {
144 checkCodeSigning(cert);
145 } else if (variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING)) {
146 checkCodeSigning(cert);
147 } else if (variant.equals(Validator.VAR_TSA_SERVER)) {
148 checkTSAServer(cert);
149 } else {
150 throw new CertificateException("Unknown variant: " + variant);
151 }
152 }
153
154 /**
155 * Utility method returning the Set of critical extensions for
156 * certificate cert (never null).
157 */
158 private Set<String> getCriticalExtensions(X509Certificate cert) {
159 Set<String> exts = cert.getCriticalExtensionOIDs();
160 if (exts == null) {
161 exts = Collections.emptySet();
162 }
163 return exts;
164 }
165
166 /**
167 * Utility method checking if there are any unresolved critical extensions.
168 * @throws CertificateException if so.
169 */
170 private void checkRemainingExtensions(Set<String> exts)
171 throws CertificateException {
172 // basic constraints irrelevant in EE certs
173 exts.remove(SimpleValidator.OID_BASIC_CONSTRAINTS);
174 if (!exts.isEmpty()) {
175 throw new CertificateException("Certificate contains unsupported "
176 + "critical extensions: " + exts);
177 }
178 }
179
180 /**
181 * Utility method checking if the extended key usage extension in
182 * certificate cert allows use for expectedEKU.
183 */
184 private boolean checkEKU(X509Certificate cert, Set<String> exts,
185 String expectedEKU) throws CertificateException {
186 List<String> eku = cert.getExtendedKeyUsage();
187 if (eku == null) {
188 return true;
189 }
190 return eku.contains(expectedEKU) || eku.contains(OID_EKU_ANY_USAGE);
191 }
192
193 /**
194 * Utility method checking if bit 'bit' is set in this certificates
195 * key usage extension.
196 * @throws CertificateException if not
197 */
198 private boolean checkKeyUsage(X509Certificate cert, int bit)
199 throws CertificateException {
200 boolean[] keyUsage = cert.getKeyUsage();
201 if (keyUsage == null) {
202 return true;
203 }
204 return (keyUsage.length > bit) && keyUsage[bit];
205 }
206
207 /**
208 * Check whether this certificate can be used for TLS client
209 * authentication.
210 * @throws CertificateException if not.
211 */
212 private void checkTLSClient(X509Certificate cert)
213 throws CertificateException {
214 Set<String> exts = getCriticalExtensions(cert);
215
216 if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
217 throw new ValidatorException
218 ("KeyUsage does not allow digital signatures",
219 ValidatorException.T_EE_EXTENSIONS, cert);
220 }
221
222 if (checkEKU(cert, exts, OID_EKU_TLS_CLIENT) == false) {
223 throw new ValidatorException("Extended key usage does not "
224 + "permit use for TLS client authentication",
225 ValidatorException.T_EE_EXTENSIONS, cert);
226 }
227
228 if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_CLIENT)) {
229 throw new ValidatorException
230 ("Netscape cert type does not permit use for SSL client",
231 ValidatorException.T_EE_EXTENSIONS, cert);
232 }
233
234 // remove extensions we checked
235 exts.remove(SimpleValidator.OID_KEY_USAGE);
236 exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
237 exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE);
238
239 checkRemainingExtensions(exts);
240 }
241
242 /**
243 * Check whether this certificate can be used for TLS server authentication
244 * using the specified authentication type parameter. See X509TrustManager
245 * specification for details.
246 * @throws CertificateException if not.
247 */
248 private void checkTLSServer(X509Certificate cert, String parameter)
249 throws CertificateException {
250 Set<String> exts = getCriticalExtensions(cert);
251
252 if (KU_SERVER_ENCRYPTION.contains(parameter)) {
253 if (checkKeyUsage(cert, KU_KEY_ENCIPHERMENT) == false) {
254 throw new ValidatorException
255 ("KeyUsage does not allow key encipherment",
256 ValidatorException.T_EE_EXTENSIONS, cert);
257 }
258 } else if (KU_SERVER_SIGNATURE.contains(parameter)) {
259 if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
260 throw new ValidatorException
261 ("KeyUsage does not allow digital signatures",
262 ValidatorException.T_EE_EXTENSIONS, cert);
263 }
264 } else if (KU_SERVER_KEY_AGREEMENT.contains(parameter)) {
265 if (checkKeyUsage(cert, KU_KEY_AGREEMENT) == false) {
266 throw new ValidatorException
267 ("KeyUsage does not allow key agreement",
268 ValidatorException.T_EE_EXTENSIONS, cert);
269 }
270 } else {
271 throw new CertificateException("Unknown authType: " + parameter);
272 }
273
274 if (checkEKU(cert, exts, OID_EKU_TLS_SERVER) == false) {
275 // check for equivalent but now obsolete Server-Gated-Cryptography
276 // (aka Step-Up, 128 bit) EKU OIDs
277 if ((checkEKU(cert, exts, OID_EKU_MS_SGC) == false) &&
278 (checkEKU(cert, exts, OID_EKU_NS_SGC) == false)) {
279 throw new ValidatorException
280 ("Extended key usage does not permit use for TLS "
281 + "server authentication",
282 ValidatorException.T_EE_EXTENSIONS, cert);
283 }
284 }
285
286 if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_SERVER)) {
287 throw new ValidatorException
288 ("Netscape cert type does not permit use for SSL server",
289 ValidatorException.T_EE_EXTENSIONS, cert);
290 }
291
292 // remove extensions we checked
293 exts.remove(SimpleValidator.OID_KEY_USAGE);
294 exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
295 exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE);
296
297 checkRemainingExtensions(exts);
298 }
299
300 /**
301 * Check whether this certificate can be used for code signing.
302 * @throws CertificateException if not.
303 */
304 private void checkCodeSigning(X509Certificate cert)
305 throws CertificateException {
306 Set<String> exts = getCriticalExtensions(cert);
307
308 if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
309 throw new ValidatorException
310 ("KeyUsage does not allow digital signatures",
311 ValidatorException.T_EE_EXTENSIONS, cert);
312 }
313
314 if (checkEKU(cert, exts, OID_EKU_CODE_SIGNING) == false) {
315 throw new ValidatorException
316 ("Extended key usage does not permit use for code signing",
317 ValidatorException.T_EE_EXTENSIONS, cert);
318 }
319
320 // do not check Netscape cert type for JCE code signing checks
321 // (some certs were issued with incorrect extensions)
322 if (variant.equals(Validator.VAR_JCE_SIGNING) == false) {
323 if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING)) {
324 throw new ValidatorException
325 ("Netscape cert type does not permit use for code signing",
326 ValidatorException.T_EE_EXTENSIONS, cert);
327 }
328 exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE);
329 }
330
331 // remove extensions we checked
332 exts.remove(SimpleValidator.OID_KEY_USAGE);
333 exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
334
335 checkRemainingExtensions(exts);
336 }
337
338 /**
339 * Check whether this certificate can be used by a time stamping authority
340 * server (see RFC 3161, section 2.3).
341 * @throws CertificateException if not.
342 */
343 private void checkTSAServer(X509Certificate cert)
344 throws CertificateException {
345 Set<String> exts = getCriticalExtensions(cert);
346
347 if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
348 throw new ValidatorException
349 ("KeyUsage does not allow digital signatures",
350 ValidatorException.T_EE_EXTENSIONS, cert);
351 }
352
353 if (cert.getExtendedKeyUsage() == null) {
354 throw new ValidatorException
355 ("Certificate does not contain an extended key usage " +
356 "extension required for a TSA server",
357 ValidatorException.T_EE_EXTENSIONS, cert);
358 }
359
360 if (checkEKU(cert, exts, OID_EKU_TIME_STAMPING) == false) {
361 throw new ValidatorException
362 ("Extended key usage does not permit use for TSA server",
363 ValidatorException.T_EE_EXTENSIONS, cert);
364 }
365
366 // remove extensions we checked
367 exts.remove(SimpleValidator.OID_KEY_USAGE);
368 exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE);
369
370 checkRemainingExtensions(exts);
371 }
372}