Home | History | Annotate | Download | only in validator
      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 }
    383