/*
 * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.security.validator;

import java.util.*;

import java.security.cert.*;

import sun.security.x509.NetscapeCertTypeExtension;

Class to check if an end entity cert is suitable for use in some context.

This class is used internally by the validator. Currently, seven variants are supported defined as VAR_XXX constants in the Validator class:

  • Generic. No additional requirements, all certificates are ok.
  • TLS server. Requires that a String parameter is passed to validate that specifies the name of the TLS key exchange algorithm in use. See the JSSE X509TrustManager spec for details.
  • TLS client.
  • Code signing.
  • JCE code signing. Some early JCE code signing certs issued to providers had incorrect extensions. In this mode the checks are relaxed compared to standard code signing checks in order to allow these certificates to pass.
  • Plugin code signing. WebStart and Plugin require their own variant which is equivalent to VAR_CODE_SIGNING with additional checks for compatibility/special cases. See also PKIXValidator.
  • TSA Server (see RFC 3161, section 2.3).
Author:Andreas Sterbenz
/** * Class to check if an end entity cert is suitable for use in some * context.<p> * * This class is used internally by the validator. Currently, seven variants * are supported defined as VAR_XXX constants in the Validator class: * <ul> * <li>Generic. No additional requirements, all certificates are ok. * * <li>TLS server. Requires that a String parameter is passed to * validate that specifies the name of the TLS key exchange algorithm * in use. See the JSSE X509TrustManager spec for details. * * <li>TLS client. * * <li>Code signing. * * <li>JCE code signing. Some early JCE code signing certs issued to * providers had incorrect extensions. In this mode the checks * are relaxed compared to standard code signing checks in order to * allow these certificates to pass. * * <li>Plugin code signing. WebStart and Plugin require their own variant * which is equivalent to VAR_CODE_SIGNING with additional checks for * compatibility/special cases. See also PKIXValidator. * * <li>TSA Server (see RFC 3161, section 2.3). * * </ul> * * @author Andreas Sterbenz */
class EndEntityChecker { // extended key usage OIDs for TLS server, TLS client, code signing // and any usage private static final String OID_EXTENDED_KEY_USAGE = SimpleValidator.OID_EXTENDED_KEY_USAGE; private static final String OID_EKU_TLS_SERVER = "1.3.6.1.5.5.7.3.1"; private static final String OID_EKU_TLS_CLIENT = "1.3.6.1.5.5.7.3.2"; private static final String OID_EKU_CODE_SIGNING = "1.3.6.1.5.5.7.3.3"; private static final String OID_EKU_TIME_STAMPING = "1.3.6.1.5.5.7.3.8"; private static final String OID_EKU_ANY_USAGE = "2.5.29.37.0"; // the Netscape Server-Gated-Cryptography EKU extension OID private static final String OID_EKU_NS_SGC = "2.16.840.1.113730.4.1"; // the Microsoft Server-Gated-Cryptography EKU extension OID private static final String OID_EKU_MS_SGC = "1.3.6.1.4.1.311.10.3.3"; // the recognized extension OIDs private static final String OID_SUBJECT_ALT_NAME = "2.5.29.17"; private static final String NSCT_SSL_CLIENT = NetscapeCertTypeExtension.SSL_CLIENT; private static final String NSCT_SSL_SERVER = NetscapeCertTypeExtension.SSL_SERVER; private static final String NSCT_CODE_SIGNING = NetscapeCertTypeExtension.OBJECT_SIGNING; // bit numbers in the key usage extension private static final int KU_SIGNATURE = 0; private static final int KU_KEY_ENCIPHERMENT = 2; private static final int KU_KEY_AGREEMENT = 4; // TLS key exchange algorithms requiring digitalSignature key usage private static final Collection<String> KU_SERVER_SIGNATURE = Arrays.asList("DHE_DSS", "DHE_RSA", "ECDHE_ECDSA", "ECDHE_RSA", "RSA_EXPORT", "UNKNOWN"); // TLS key exchange algorithms requiring keyEncipherment key usage private static final Collection<String> KU_SERVER_ENCRYPTION = Arrays.asList("RSA"); // TLS key exchange algorithms requiring keyAgreement key usage private static final Collection<String> KU_SERVER_KEY_AGREEMENT = Arrays.asList("DH_DSS", "DH_RSA", "ECDH_ECDSA", "ECDH_RSA"); // variant of this end entity cert checker private final String variant; // type of the validator this checker belongs to private final String type; private EndEntityChecker(String type, String variant) { this.type = type; this.variant = variant; } static EndEntityChecker getInstance(String type, String variant) { return new EndEntityChecker(type, variant); } void check(X509Certificate cert, Object parameter, boolean checkUnresolvedCritExts) throws CertificateException { if (variant.equals(Validator.VAR_GENERIC)) { return; // no checks } Set<String> exts = getCriticalExtensions(cert); if (variant.equals(Validator.VAR_TLS_SERVER)) { checkTLSServer(cert, (String)parameter, exts); } else if (variant.equals(Validator.VAR_TLS_CLIENT)) { checkTLSClient(cert, exts); } else if (variant.equals(Validator.VAR_CODE_SIGNING)) { checkCodeSigning(cert, exts); } else if (variant.equals(Validator.VAR_JCE_SIGNING)) { checkCodeSigning(cert, exts); } else if (variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING)) { checkCodeSigning(cert, exts); } else if (variant.equals(Validator.VAR_TSA_SERVER)) { checkTSAServer(cert, exts); } else { throw new CertificateException("Unknown variant: " + variant); } // if neither VAR_GENERIC variant nor unknown variant if (checkUnresolvedCritExts) { checkRemainingExtensions(exts); } }
Utility method returning the Set of critical extensions for certificate cert (never null).
/** * Utility method returning the Set of critical extensions for * certificate cert (never null). */
private Set<String> getCriticalExtensions(X509Certificate cert) { Set<String> exts = cert.getCriticalExtensionOIDs(); if (exts == null) { exts = Collections.emptySet(); } return exts; }
Utility method checking if there are any unresolved critical extensions.
Throws:
  • CertificateException – if so.
/** * Utility method checking if there are any unresolved critical extensions. * @throws CertificateException if so. */
private void checkRemainingExtensions(Set<String> exts) throws CertificateException { // basic constraints irrelevant in EE certs exts.remove(SimpleValidator.OID_BASIC_CONSTRAINTS); // If the subject field contains an empty sequence, the subjectAltName // extension MUST be marked critical. // We do not check the validity of the critical extension, just mark // it recognizable here. exts.remove(OID_SUBJECT_ALT_NAME); if (!exts.isEmpty()) { throw new CertificateException("Certificate contains unsupported " + "critical extensions: " + exts); } }
Utility method checking if the extended key usage extension in certificate cert allows use for expectedEKU.
/** * Utility method checking if the extended key usage extension in * certificate cert allows use for expectedEKU. */
private boolean checkEKU(X509Certificate cert, Set<String> exts, String expectedEKU) throws CertificateException { List<String> eku = cert.getExtendedKeyUsage(); if (eku == null) { return true; } return eku.contains(expectedEKU) || eku.contains(OID_EKU_ANY_USAGE); }
Utility method checking if bit 'bit' is set in this certificates key usage extension.
Throws:
  • CertificateException – if not
/** * Utility method checking if bit 'bit' is set in this certificates * key usage extension. * @throws CertificateException if not */
private boolean checkKeyUsage(X509Certificate cert, int bit) throws CertificateException { boolean[] keyUsage = cert.getKeyUsage(); if (keyUsage == null) { return true; } return (keyUsage.length > bit) && keyUsage[bit]; }
Check whether this certificate can be used for TLS client authentication.
Throws:
  • CertificateException – if not.
/** * Check whether this certificate can be used for TLS client * authentication. * @throws CertificateException if not. */
private void checkTLSClient(X509Certificate cert, Set<String> exts) throws CertificateException { if (checkKeyUsage(cert, KU_SIGNATURE) == false) { throw new ValidatorException ("KeyUsage does not allow digital signatures", ValidatorException.T_EE_EXTENSIONS, cert); } if (checkEKU(cert, exts, OID_EKU_TLS_CLIENT) == false) { throw new ValidatorException("Extended key usage does not " + "permit use for TLS client authentication", ValidatorException.T_EE_EXTENSIONS, cert); } if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_CLIENT)) { throw new ValidatorException ("Netscape cert type does not permit use for SSL client", ValidatorException.T_EE_EXTENSIONS, cert); } // remove extensions we checked exts.remove(SimpleValidator.OID_KEY_USAGE); exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); }
Check whether this certificate can be used for TLS server authentication using the specified authentication type parameter. See X509TrustManager specification for details.
Throws:
  • CertificateException – if not.
/** * Check whether this certificate can be used for TLS server authentication * using the specified authentication type parameter. See X509TrustManager * specification for details. * @throws CertificateException if not. */
private void checkTLSServer(X509Certificate cert, String parameter, Set<String> exts) throws CertificateException { if (KU_SERVER_ENCRYPTION.contains(parameter)) { if (checkKeyUsage(cert, KU_KEY_ENCIPHERMENT) == false) { throw new ValidatorException ("KeyUsage does not allow key encipherment", ValidatorException.T_EE_EXTENSIONS, cert); } } else if (KU_SERVER_SIGNATURE.contains(parameter)) { if (checkKeyUsage(cert, KU_SIGNATURE) == false) { throw new ValidatorException ("KeyUsage does not allow digital signatures", ValidatorException.T_EE_EXTENSIONS, cert); } } else if (KU_SERVER_KEY_AGREEMENT.contains(parameter)) { if (checkKeyUsage(cert, KU_KEY_AGREEMENT) == false) { throw new ValidatorException ("KeyUsage does not allow key agreement", ValidatorException.T_EE_EXTENSIONS, cert); } } else { throw new CertificateException("Unknown authType: " + parameter); } if (checkEKU(cert, exts, OID_EKU_TLS_SERVER) == false) { // check for equivalent but now obsolete Server-Gated-Cryptography // (aka Step-Up, 128 bit) EKU OIDs if ((checkEKU(cert, exts, OID_EKU_MS_SGC) == false) && (checkEKU(cert, exts, OID_EKU_NS_SGC) == false)) { throw new ValidatorException ("Extended key usage does not permit use for TLS " + "server authentication", ValidatorException.T_EE_EXTENSIONS, cert); } } if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_SERVER)) { throw new ValidatorException ("Netscape cert type does not permit use for SSL server", ValidatorException.T_EE_EXTENSIONS, cert); } // remove extensions we checked exts.remove(SimpleValidator.OID_KEY_USAGE); exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); }
Check whether this certificate can be used for code signing.
Throws:
  • CertificateException – if not.
/** * Check whether this certificate can be used for code signing. * @throws CertificateException if not. */
private void checkCodeSigning(X509Certificate cert, Set<String> exts) throws CertificateException { if (checkKeyUsage(cert, KU_SIGNATURE) == false) { throw new ValidatorException ("KeyUsage does not allow digital signatures", ValidatorException.T_EE_EXTENSIONS, cert); } if (checkEKU(cert, exts, OID_EKU_CODE_SIGNING) == false) { throw new ValidatorException ("Extended key usage does not permit use for code signing", ValidatorException.T_EE_EXTENSIONS, cert); } // do not check Netscape cert type for JCE code signing checks // (some certs were issued with incorrect extensions) if (variant.equals(Validator.VAR_JCE_SIGNING) == false) { if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING)) { throw new ValidatorException ("Netscape cert type does not permit use for code signing", ValidatorException.T_EE_EXTENSIONS, cert); } exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); } // remove extensions we checked exts.remove(SimpleValidator.OID_KEY_USAGE); exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); }
Check whether this certificate can be used by a time stamping authority server (see RFC 3161, section 2.3).
Throws:
  • CertificateException – if not.
/** * Check whether this certificate can be used by a time stamping authority * server (see RFC 3161, section 2.3). * @throws CertificateException if not. */
private void checkTSAServer(X509Certificate cert, Set<String> exts) throws CertificateException { if (checkKeyUsage(cert, KU_SIGNATURE) == false) { throw new ValidatorException ("KeyUsage does not allow digital signatures", ValidatorException.T_EE_EXTENSIONS, cert); } if (cert.getExtendedKeyUsage() == null) { throw new ValidatorException ("Certificate does not contain an extended key usage " + "extension required for a TSA server", ValidatorException.T_EE_EXTENSIONS, cert); } if (checkEKU(cert, exts, OID_EKU_TIME_STAMPING) == false) { throw new ValidatorException ("Extended key usage does not permit use for TSA server", ValidatorException.T_EE_EXTENSIONS, cert); } // remove extensions we checked exts.remove(SimpleValidator.OID_KEY_USAGE); exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); } }