382 lines · 14.5 KB · Java
RawDownload
/* * Copyright (c) 2002, 2008, 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.<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 final static String OID_EXTENDED_KEY_USAGE =                                SimpleValidator.OID_EXTENDED_KEY_USAGE;     private final static String OID_EKU_TLS_SERVER = "1.3.6.1.5.5.7.3.1";     private final static String OID_EKU_TLS_CLIENT = "1.3.6.1.5.5.7.3.2";     private final static String OID_EKU_CODE_SIGNING = "1.3.6.1.5.5.7.3.3";     private final static String OID_EKU_TIME_STAMPING = "1.3.6.1.5.5.7.3.8";     private final static String OID_EKU_ANY_USAGE = "2.5.29.37.0";     // the Netscape Server-Gated-Cryptography EKU extension OID    private final static String OID_EKU_NS_SGC = "2.16.840.1.113730.4.1";     // the Microsoft Server-Gated-Cryptography EKU extension OID    private final static String OID_EKU_MS_SGC = "1.3.6.1.4.1.311.10.3.3";     // the recognized extension OIDs    private final static String OID_SUBJECT_ALT_NAME = "2.5.29.17";     private final static String NSCT_SSL_CLIENT =                                NetscapeCertTypeExtension.SSL_CLIENT;     private final static String NSCT_SSL_SERVER =                                NetscapeCertTypeExtension.SSL_SERVER;     private final static String NSCT_CODE_SIGNING =                                NetscapeCertTypeExtension.OBJECT_SIGNING;     // bit numbers in the key usage extension    private final static int KU_SIGNATURE = 0;    private final static int KU_KEY_ENCIPHERMENT = 2;    private final static int KU_KEY_AGREEMENT = 4;     // TLS key exchange algorithms requiring digitalSignature key usage    private final static 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 final static Collection<String> KU_SERVER_ENCRYPTION =        Arrays.asList("RSA");     // TLS key exchange algorithms requiring keyAgreement key usage    private final static 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)            throws CertificateException {        if (variant.equals(Validator.VAR_GENERIC)) {            // no checks            return;        } else if (variant.equals(Validator.VAR_TLS_SERVER)) {            checkTLSServer(cert, (String)parameter);        } else if (variant.equals(Validator.VAR_TLS_CLIENT)) {            checkTLSClient(cert);        } else if (variant.equals(Validator.VAR_CODE_SIGNING)) {            checkCodeSigning(cert);        } else if (variant.equals(Validator.VAR_JCE_SIGNING)) {            checkCodeSigning(cert);        } else if (variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING)) {            checkCodeSigning(cert);        } else if (variant.equals(Validator.VAR_TSA_SERVER)) {            checkTSAServer(cert);        } else {            throw new CertificateException("Unknown variant: " + variant);        }    }     /**     * 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.     */    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.     */    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     */    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.     */    private void checkTLSClient(X509Certificate cert)            throws CertificateException {        Set<String> exts = getCriticalExtensions(cert);         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);         checkRemainingExtensions(exts);    }     /**     * 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)            throws CertificateException {        Set<String> exts = getCriticalExtensions(cert);         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);         checkRemainingExtensions(exts);    }     /**     * Check whether this certificate can be used for code signing.     * @throws CertificateException if not.     */    private void checkCodeSigning(X509Certificate cert)            throws CertificateException {        Set<String> exts = getCriticalExtensions(cert);         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);         checkRemainingExtensions(exts);    }     /**     * 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)            throws CertificateException {        Set<String> exts = getCriticalExtensions(cert);         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);         checkRemainingExtensions(exts);    }}