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