1274 lines · 59.2 KB · Java
RawDownload
/* * Copyright (c) 2002, 2011, 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.ssl; import java.util.*; import java.security.NoSuchAlgorithmException;import java.security.InvalidKeyException;import java.security.SecureRandom; import javax.crypto.SecretKey;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec; import sun.security.ssl.CipherSuite.*;import static sun.security.ssl.CipherSuite.KeyExchange.*;import static sun.security.ssl.CipherSuite.PRF.*;import static sun.security.ssl.JsseJce.*; /** * An SSL/TLS CipherSuite. Constants for the standard key exchange, cipher, * and mac algorithms are also defined in this class. * * The CipherSuite class and the inner classes defined in this file roughly * follow the type safe enum pattern described in Effective Java. This means: * *  . instances are immutable, classes are final * *  . there is a unique instance of every value, i.e. there are never two *    instances representing the same CipherSuite, etc. This means equality *    tests can be performed using == instead of equals() (although that works *    as well). [A minor exception are *unsupported* CipherSuites read from a *    handshake message, but this is usually irrelevant] * *  . instances are obtained using the static valueOf() factory methods. * *  . properties are defined as final variables and made available as *    package private variables without method accessors * *  . if the member variable allowed is false, the given algorithm is either *    unavailable or disabled at compile time * */final class CipherSuite implements Comparable {     // minimum priority for supported CipherSuites    final static int SUPPORTED_SUITES_PRIORITY = 1;     // minimum priority for default enabled CipherSuites    final static int DEFAULT_SUITES_PRIORITY = 300;     // Flag indicating if CipherSuite availability can change dynamically.    // This is the case when we rely on a JCE cipher implementation that    // may not be available in the installed JCE providers.    // It is true because we might not have an ECC implementation.    final static boolean DYNAMIC_AVAILABILITY = true;     private final static boolean ALLOW_ECC = Debug.getBooleanProperty        ("com.sun.net.ssl.enableECC", true);     // Map Integer(id) -> CipherSuite    // contains all known CipherSuites    private final static Map<Integer,CipherSuite> idMap;     // Map String(name) -> CipherSuite    // contains only supported CipherSuites (i.e. allowed == true)    private final static Map<String,CipherSuite> nameMap;     // Protocol defined CipherSuite name, e.g. SSL_RSA_WITH_RC4_128_MD5    // we use TLS_* only for new CipherSuites, still SSL_* for old ones    final String name;     // id in 16 bit MSB format, i.e. 0x0004 for SSL_RSA_WITH_RC4_128_MD5    final int id;     // priority for the internal default preference order. the higher the    // better. Each supported CipherSuite *must* have a unique priority.    // Ciphersuites with priority >= DEFAULT_SUITES_PRIORITY are enabled    // by default    final int priority;     // key exchange, bulk cipher, mac and prf algorithms. See those    // classes below.    final KeyExchange keyExchange;    final BulkCipher cipher;    final MacAlg macAlg;    final PRF prfAlg;     // whether a CipherSuite qualifies as exportable under 512/40 bit rules.    // TLS 1.1+ (RFC 4346) must not negotiate to these suites.    final boolean exportable;     // true iff implemented and enabled at compile time    final boolean allowed;     // obsoleted since protocol version    final int obsoleted;     // supported since protocol version    final int supported;     /**     * Constructor for implemented CipherSuites.     */    private CipherSuite(String name, int id, int priority,            KeyExchange keyExchange, BulkCipher cipher,            boolean allowed, int obsoleted, int supported, PRF prfAlg) {        this.name = name;        this.id = id;        this.priority = priority;        this.keyExchange = keyExchange;        this.cipher = cipher;        this.exportable = cipher.exportable;        if (name.endsWith("_MD5")) {            macAlg = M_MD5;        } else if (name.endsWith("_SHA")) {            macAlg = M_SHA;        } else if (name.endsWith("_SHA256")) {            macAlg = M_SHA256;        } else if (name.endsWith("_SHA384")) {            macAlg = M_SHA384;        } else if (name.endsWith("_NULL")) {            macAlg = M_NULL;        } else if (name.endsWith("_SCSV")) {            macAlg = M_NULL;        } else {            throw new IllegalArgumentException                    ("Unknown MAC algorithm for ciphersuite " + name);        }         allowed &= keyExchange.allowed;        allowed &= cipher.allowed;        this.allowed = allowed;        this.obsoleted = obsoleted;        this.supported = supported;        this.prfAlg = prfAlg;    }     /**     * Constructor for unimplemented CipherSuites.     */    private CipherSuite(String name, int id) {        this.name = name;        this.id = id;        this.allowed = false;         this.priority = 0;        this.keyExchange = null;        this.cipher = null;        this.macAlg = null;        this.exportable = false;        this.obsoleted = ProtocolVersion.LIMIT_MAX_VALUE;        this.supported = ProtocolVersion.LIMIT_MIN_VALUE;        this.prfAlg = P_NONE;    }     /**     * Return whether this CipherSuite is available for use. A     * CipherSuite may be unavailable even if it is supported     * (i.e. allowed == true) if the required JCE cipher is not installed.     * In some configuration, this situation may change over time, call     * CipherSuiteList.clearAvailableCache() before this method to obtain     * the most current status.     */    boolean isAvailable() {        return allowed && keyExchange.isAvailable() && cipher.isAvailable();    }     boolean isNegotiable() {        return this != C_SCSV && isAvailable();    }     /**     * Compares CipherSuites based on their priority. Has the effect of     * sorting CipherSuites when put in a sorted collection, which is     * used by CipherSuiteList. Follows standard Comparable contract.     *     * Note that for unsupported CipherSuites parsed from a handshake     * message we violate the equals() contract.     */    public int compareTo(Object o) {        return ((CipherSuite)o).priority - priority;    }     /**     * Returns this.name.     */    public String toString() {        return name;    }     /**     * Return a CipherSuite for the given name. The returned CipherSuite     * is supported by this implementation but may not actually be     * currently useable. See isAvailable().     *     * @exception IllegalArgumentException if the CipherSuite is unknown or     * unsupported.     */    static CipherSuite valueOf(String s) {        if (s == null) {            throw new IllegalArgumentException("Name must not be null");        }         CipherSuite c = nameMap.get(s);        if ((c == null) || (c.allowed == false)) {            throw new IllegalArgumentException("Unsupported ciphersuite " + s);        }         return c;    }     /**     * Return a CipherSuite with the given ID. A temporary object is     * constructed if the ID is unknown. Use isAvailable() to verify that     * the CipherSuite can actually be used.     */    static CipherSuite valueOf(int id1, int id2) {        id1 &= 0xff;        id2 &= 0xff;        int id = (id1 << 8) | id2;        CipherSuite c = idMap.get(id);        if (c == null) {            String h1 = Integer.toString(id1, 16);            String h2 = Integer.toString(id2, 16);            c = new CipherSuite("Unknown 0x" + h1 + ":0x" + h2, id);        }        return c;    }     // for use by CipherSuiteList only    static Collection<CipherSuite> allowedCipherSuites() {        return nameMap.values();    }     /*     * Use this method when all of the values need to be specified.     * This is primarily used when defining a new ciphersuite for     * TLS 1.2+ that doesn't use the "default" PRF.     */    private static void add(String name, int id, int priority,            KeyExchange keyExchange, BulkCipher cipher,            boolean allowed, int obsoleted, int supported, PRF prf) {         CipherSuite c = new CipherSuite(name, id, priority, keyExchange,            cipher, allowed, obsoleted, supported, prf);        if (idMap.put(id, c) != null) {            throw new RuntimeException("Duplicate ciphersuite definition: "                                        + id + ", " + name);        }        if (c.allowed) {            if (nameMap.put(name, c) != null) {                throw new RuntimeException("Duplicate ciphersuite definition: "                                            + id + ", " + name);            }        }    }     /*     * Use this method when there is no lower protocol limit where this     * suite can be used, and the PRF is P_SHA256.  That is, the     * existing ciphersuites.  From RFC 5246:     *     *     All cipher suites in this document use P_SHA256.     */    private static void add(String name, int id, int priority,            KeyExchange keyExchange, BulkCipher cipher,            boolean allowed, int obsoleted) {        // If this is an obsoleted suite, then don't let the TLS 1.2        // protocol have a valid PRF value.        PRF prf = P_SHA256;        if (obsoleted < ProtocolVersion.TLS12.v) {            prf = P_NONE;        }         add(name, id, priority, keyExchange, cipher, allowed, obsoleted,            ProtocolVersion.LIMIT_MIN_VALUE, prf);    }     /*     * Use this method when there is no upper protocol limit.  That is,     * suites which have not been obsoleted.     */    private static void add(String name, int id, int priority,            KeyExchange keyExchange, BulkCipher cipher, boolean allowed) {        add(name, id, priority, keyExchange,            cipher, allowed, ProtocolVersion.LIMIT_MAX_VALUE);    }     /*     * Use this method to define an unimplemented suite.  This provides     * a number<->name mapping that can be used for debugging.     */    private static void add(String name, int id) {        CipherSuite c = new CipherSuite(name, id);        if (idMap.put(id, c) != null) {            throw new RuntimeException("Duplicate ciphersuite definition: "                                        + id + ", " + name);        }    }     /**     * An SSL/TLS key exchange algorithm.     */    static enum KeyExchange {         // key exchange algorithms        K_NULL       ("NULL",       false),        K_RSA        ("RSA",        true),        K_RSA_EXPORT ("RSA_EXPORT", true),        K_DH_RSA     ("DH_RSA",     false),        K_DH_DSS     ("DH_DSS",     false),        K_DHE_DSS    ("DHE_DSS",    true),        K_DHE_RSA    ("DHE_RSA",    true),        K_DH_ANON    ("DH_anon",    true),         K_ECDH_ECDSA ("ECDH_ECDSA",  ALLOW_ECC),        K_ECDH_RSA   ("ECDH_RSA",    ALLOW_ECC),        K_ECDHE_ECDSA("ECDHE_ECDSA", ALLOW_ECC),        K_ECDHE_RSA  ("ECDHE_RSA",   ALLOW_ECC),        K_ECDH_ANON  ("ECDH_anon",   ALLOW_ECC),         // Kerberos cipher suites        K_KRB5       ("KRB5", true),        K_KRB5_EXPORT("KRB5_EXPORT", true),         // renegotiation protection request signaling cipher suite        K_SCSV       ("SCSV",        true);         // name of the key exchange algorithm, e.g. DHE_DSS        final String name;        final boolean allowed;        private final boolean alwaysAvailable;         KeyExchange(String name, boolean allowed) {            this.name = name;            this.allowed = allowed;            this.alwaysAvailable = allowed &&                (!name.startsWith("EC")) && (!name.startsWith("KRB"));        }         boolean isAvailable() {            if (alwaysAvailable) {                return true;            }             if (name.startsWith("EC")) {                return (allowed && JsseJce.isEcAvailable());            } else if (name.startsWith("KRB")) {                return (allowed && JsseJce.isKerberosAvailable());            } else {                return allowed;            }        }         public String toString() {            return name;        }    }     /**     * An SSL/TLS bulk cipher algorithm. One instance per combination of     * cipher and key length.     *     * Also contains a factory method to obtain in initialized CipherBox     * for this algorithm.     */    final static class BulkCipher {         // Map BulkCipher -> Boolean(available)        private final static Map<BulkCipher,Boolean> availableCache =                                            new HashMap<>(8);         // descriptive name including key size, e.g. AES/128        final String description;         // JCE cipher transformation string, e.g. AES/CBC/NoPadding        final String transformation;         // algorithm name, e.g. AES        final String algorithm;         // supported and compile time enabled. Also see isAvailable()        final boolean allowed;         // number of bytes of entropy in the key        final int keySize;         // length of the actual cipher key in bytes.        // for non-exportable ciphers, this is the same as keySize        final int expandedKeySize;         // size of the IV (also block size)        final int ivSize;         // exportable under 512/40 bit rules        final boolean exportable;         BulkCipher(String transformation, int keySize,                int expandedKeySize, int ivSize, boolean allowed) {            this.transformation = transformation;            this.algorithm = transformation.split("/")[0];            this.description = this.algorithm + "/" + (keySize << 3);            this.keySize = keySize;            this.ivSize = ivSize;            this.allowed = allowed;             this.expandedKeySize = expandedKeySize;            this.exportable = true;        }         BulkCipher(String transformation, int keySize,                int ivSize, boolean allowed) {            this.transformation = transformation;            this.algorithm = transformation.split("/")[0];            this.description = this.algorithm + "/" + (keySize << 3);            this.keySize = keySize;            this.ivSize = ivSize;            this.allowed = allowed;             this.expandedKeySize = keySize;            this.exportable = false;        }         /**         * Return an initialized CipherBox for this BulkCipher.         * IV must be null for stream ciphers.         *         * @exception NoSuchAlgorithmException if anything goes wrong         */        CipherBox newCipher(ProtocolVersion version, SecretKey key,                IvParameterSpec iv, SecureRandom random,                boolean encrypt) throws NoSuchAlgorithmException {            return CipherBox.newCipherBox(version, this,                                            key, iv, random, encrypt);        }         /**         * Test if this bulk cipher is available. For use by CipherSuite.         *         * Currently all supported ciphers except AES are always available         * via the JSSE internal implementations. We also assume AES/128         * is always available since it is shipped with the SunJCE provider.         * However, AES/256 is unavailable when the default JCE policy         * jurisdiction files are installed because of key length restrictions.         */        boolean isAvailable() {            if (allowed == false) {                return false;            }            if (this == B_AES_256) {                return isAvailable(this);            }             // always available            return true;        }         // for use by CipherSuiteList.clearAvailableCache();        static synchronized void clearAvailableCache() {            if (DYNAMIC_AVAILABILITY) {                availableCache.clear();            }        }         private static synchronized boolean isAvailable(BulkCipher cipher) {            Boolean b = availableCache.get(cipher);            if (b == null) {                try {                    SecretKey key = new SecretKeySpec                        (new byte[cipher.expandedKeySize], cipher.algorithm);                    IvParameterSpec iv =                        new IvParameterSpec(new byte[cipher.ivSize]);                    cipher.newCipher(ProtocolVersion.DEFAULT,                                                key, iv, null, true);                    b = Boolean.TRUE;

Lines 1–500 of 1274.