1 /* 2 * Copyright (c) 2002, 2008, 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 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 // the recognized extension OIDs 91 private final static String OID_SUBJECT_ALT_NAME = "2.5.29.17"; 92 93 private final static String NSCT_SSL_CLIENT = 94 NetscapeCertTypeExtension.SSL_CLIENT; 95 96 private final static String NSCT_SSL_SERVER = 97 NetscapeCertTypeExtension.SSL_SERVER; 98 99 private final static String NSCT_CODE_SIGNING = 100 NetscapeCertTypeExtension.OBJECT_SIGNING; 101 102 // bit numbers in the key usage extension 103 private final static int KU_SIGNATURE = 0; 104 private final static int KU_KEY_ENCIPHERMENT = 2; 105 private final static int KU_KEY_AGREEMENT = 4; 106 107 // TLS key exchange algorithms requiring digitalSignature key usage 108 private final static 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 final static Collection<String> KU_SERVER_ENCRYPTION = 114 Arrays.asList("RSA"); 115 116 // TLS key exchange algorithms requiring keyAgreement key usage 117 private final static 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 126 private EndEntityChecker(String type, String variant) { 127 this.type = type; 128 this.variant = variant; 129 } 130 131 static EndEntityChecker getInstance(String type, String variant) { 132 return new EndEntityChecker(type, variant); 133 } 134 135 void check(X509Certificate cert, Object parameter) 136 throws CertificateException { 137 if (variant.equals(Validator.VAR_GENERIC)) { 138 // no checks 139 return; 140 } else if (variant.equals(Validator.VAR_TLS_SERVER)) { 141 checkTLSServer(cert, (String)parameter); 142 } else if (variant.equals(Validator.VAR_TLS_CLIENT)) { 143 checkTLSClient(cert); 144 } else if (variant.equals(Validator.VAR_CODE_SIGNING)) { 145 checkCodeSigning(cert); 146 } else if (variant.equals(Validator.VAR_JCE_SIGNING)) { 147 checkCodeSigning(cert); 148 } else if (variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING)) { 149 checkCodeSigning(cert); 150 } else if (variant.equals(Validator.VAR_TSA_SERVER)) { 151 checkTSAServer(cert); 152 } else { 153 throw new CertificateException("Unknown variant: " + variant); 154 } 155 } 156 157 /** 158 * Utility method returning the Set of critical extensions for 159 * certificate cert (never null). 160 */ 161 private Set<String> getCriticalExtensions(X509Certificate cert) { 162 Set<String> exts = cert.getCriticalExtensionOIDs(); 163 if (exts == null) { 164 exts = Collections.emptySet(); 165 } 166 return exts; 167 } 168 169 /** 170 * Utility method checking if there are any unresolved critical extensions. 171 * @throws CertificateException if so. 172 */ 173 private void checkRemainingExtensions(Set<String> exts) 174 throws CertificateException { 175 // basic constraints irrelevant in EE certs 176 exts.remove(SimpleValidator.OID_BASIC_CONSTRAINTS); 177 178 // If the subject field contains an empty sequence, the subjectAltName 179 // extension MUST be marked critical. 180 // We do not check the validity of the critical extension, just mark 181 // it recognizable here. 182 exts.remove(OID_SUBJECT_ALT_NAME); 183 184 if (!exts.isEmpty()) { 185 throw new CertificateException("Certificate contains unsupported " 186 + "critical extensions: " + exts); 187 } 188 } 189 190 /** 191 * Utility method checking if the extended key usage extension in 192 * certificate cert allows use for expectedEKU. 193 */ 194 private boolean checkEKU(X509Certificate cert, Set<String> exts, 195 String expectedEKU) throws CertificateException { 196 List<String> eku = cert.getExtendedKeyUsage(); 197 if (eku == null) { 198 return true; 199 } 200 return eku.contains(expectedEKU) || eku.contains(OID_EKU_ANY_USAGE); 201 } 202 203 /** 204 * Utility method checking if bit 'bit' is set in this certificates 205 * key usage extension. 206 * @throws CertificateException if not 207 */ 208 private boolean checkKeyUsage(X509Certificate cert, int bit) 209 throws CertificateException { 210 boolean[] keyUsage = cert.getKeyUsage(); 211 if (keyUsage == null) { 212 return true; 213 } 214 return (keyUsage.length > bit) && keyUsage[bit]; 215 } 216 217 /** 218 * Check whether this certificate can be used for TLS client 219 * authentication. 220 * @throws CertificateException if not. 221 */ 222 private void checkTLSClient(X509Certificate cert) 223 throws CertificateException { 224 Set<String> exts = getCriticalExtensions(cert); 225 226 if (checkKeyUsage(cert, KU_SIGNATURE) == false) { 227 throw new ValidatorException 228 ("KeyUsage does not allow digital signatures", 229 ValidatorException.T_EE_EXTENSIONS, cert); 230 } 231 232 if (checkEKU(cert, exts, OID_EKU_TLS_CLIENT) == false) { 233 throw new ValidatorException("Extended key usage does not " 234 + "permit use for TLS client authentication", 235 ValidatorException.T_EE_EXTENSIONS, cert); 236 } 237 238 if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_CLIENT)) { 239 throw new ValidatorException 240 ("Netscape cert type does not permit use for SSL client", 241 ValidatorException.T_EE_EXTENSIONS, cert); 242 } 243 244 // remove extensions we checked 245 exts.remove(SimpleValidator.OID_KEY_USAGE); 246 exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); 247 exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); 248 249 checkRemainingExtensions(exts); 250 } 251 252 /** 253 * Check whether this certificate can be used for TLS server authentication 254 * using the specified authentication type parameter. See X509TrustManager 255 * specification for details. 256 * @throws CertificateException if not. 257 */ 258 private void checkTLSServer(X509Certificate cert, String parameter) 259 throws CertificateException { 260 Set<String> exts = getCriticalExtensions(cert); 261 262 if (KU_SERVER_ENCRYPTION.contains(parameter)) { 263 if (checkKeyUsage(cert, KU_KEY_ENCIPHERMENT) == false) { 264 throw new ValidatorException 265 ("KeyUsage does not allow key encipherment", 266 ValidatorException.T_EE_EXTENSIONS, cert); 267 } 268 } else if (KU_SERVER_SIGNATURE.contains(parameter)) { 269 if (checkKeyUsage(cert, KU_SIGNATURE) == false) { 270 throw new ValidatorException 271 ("KeyUsage does not allow digital signatures", 272 ValidatorException.T_EE_EXTENSIONS, cert); 273 } 274 } else if (KU_SERVER_KEY_AGREEMENT.contains(parameter)) { 275 if (checkKeyUsage(cert, KU_KEY_AGREEMENT) == false) { 276 throw new ValidatorException 277 ("KeyUsage does not allow key agreement", 278 ValidatorException.T_EE_EXTENSIONS, cert); 279 } 280 } else { 281 throw new CertificateException("Unknown authType: " + parameter); 282 } 283 284 if (checkEKU(cert, exts, OID_EKU_TLS_SERVER) == false) { 285 // check for equivalent but now obsolete Server-Gated-Cryptography 286 // (aka Step-Up, 128 bit) EKU OIDs 287 if ((checkEKU(cert, exts, OID_EKU_MS_SGC) == false) && 288 (checkEKU(cert, exts, OID_EKU_NS_SGC) == false)) { 289 throw new ValidatorException 290 ("Extended key usage does not permit use for TLS " 291 + "server authentication", 292 ValidatorException.T_EE_EXTENSIONS, cert); 293 } 294 } 295 296 if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_SERVER)) { 297 throw new ValidatorException 298 ("Netscape cert type does not permit use for SSL server", 299 ValidatorException.T_EE_EXTENSIONS, cert); 300 } 301 302 // remove extensions we checked 303 exts.remove(SimpleValidator.OID_KEY_USAGE); 304 exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); 305 exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); 306 307 checkRemainingExtensions(exts); 308 } 309 310 /** 311 * Check whether this certificate can be used for code signing. 312 * @throws CertificateException if not. 313 */ 314 private void checkCodeSigning(X509Certificate cert) 315 throws CertificateException { 316 Set<String> exts = getCriticalExtensions(cert); 317 318 if (checkKeyUsage(cert, KU_SIGNATURE) == false) { 319 throw new ValidatorException 320 ("KeyUsage does not allow digital signatures", 321 ValidatorException.T_EE_EXTENSIONS, cert); 322 } 323 324 if (checkEKU(cert, exts, OID_EKU_CODE_SIGNING) == false) { 325 throw new ValidatorException 326 ("Extended key usage does not permit use for code signing", 327 ValidatorException.T_EE_EXTENSIONS, cert); 328 } 329 330 // do not check Netscape cert type for JCE code signing checks 331 // (some certs were issued with incorrect extensions) 332 if (variant.equals(Validator.VAR_JCE_SIGNING) == false) { 333 if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING)) { 334 throw new ValidatorException 335 ("Netscape cert type does not permit use for code signing", 336 ValidatorException.T_EE_EXTENSIONS, cert); 337 } 338 exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); 339 } 340 341 // remove extensions we checked 342 exts.remove(SimpleValidator.OID_KEY_USAGE); 343 exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); 344 345 checkRemainingExtensions(exts); 346 } 347 348 /** 349 * Check whether this certificate can be used by a time stamping authority 350 * server (see RFC 3161, section 2.3). 351 * @throws CertificateException if not. 352 */ 353 private void checkTSAServer(X509Certificate cert) 354 throws CertificateException { 355 Set<String> exts = getCriticalExtensions(cert); 356 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 checkRemainingExtensions(exts); 381 } 382 }