866 lines · 28.6 KB · Java
RawDownload
/* * Copyright (c) 2006, 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.io.IOException;import java.io.PrintStream;import java.util.*; import java.security.spec.ECParameterSpec; import javax.net.ssl.SSLProtocolException; /** * This file contains all the classes relevant to TLS Extensions for the * ClientHello and ServerHello messages. The extension mechanism and * several extensions are defined in RFC 3546. Additional extensions are * defined in the ECC RFC 4492. * * Currently, only the two ECC extensions are fully supported. * * The classes contained in this file are: *  . HelloExtensions: a List of extensions as used in the client hello *      and server hello messages. *  . ExtensionType: an enum style class for the extension type *  . HelloExtension: abstract base class for all extensions. All subclasses *      must be immutable. * *  . UnknownExtension: used to represent all parsed extensions that we do not *      explicitly support. *  . ServerNameExtension: the server_name extension. *  . SignatureAlgorithmsExtension: the signature_algorithms extension. *  . SupportedEllipticCurvesExtension: the ECC supported curves extension. *  . SupportedEllipticPointFormatsExtension: the ECC supported point formats *      (compressed/uncompressed) extension. * * @since   1.6 * @author  Andreas Sterbenz */final class HelloExtensions {     private List<HelloExtension> extensions;    private int encodedLength;     HelloExtensions() {        extensions = Collections.emptyList();    }     HelloExtensions(HandshakeInStream s) throws IOException {        int len = s.getInt16();        extensions = new ArrayList<HelloExtension>();        encodedLength = len + 2;        while (len > 0) {            int type = s.getInt16();            int extlen = s.getInt16();            ExtensionType extType = ExtensionType.get(type);            HelloExtension extension;            if (extType == ExtensionType.EXT_SERVER_NAME) {                extension = new ServerNameExtension(s, extlen);            } else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) {                extension = new SignatureAlgorithmsExtension(s, extlen);            } else if (extType == ExtensionType.EXT_ELLIPTIC_CURVES) {                extension = new SupportedEllipticCurvesExtension(s, extlen);            } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) {                extension =                        new SupportedEllipticPointFormatsExtension(s, extlen);            } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {                extension = new RenegotiationInfoExtension(s, extlen);            } else {                extension = new UnknownExtension(s, extlen, extType);            }            extensions.add(extension);            len -= extlen + 4;        }        if (len != 0) {            throw new SSLProtocolException(                        "Error parsing extensions: extra data");        }    }     // Return the List of extensions. Must not be modified by the caller.    List<HelloExtension> list() {        return extensions;    }     void add(HelloExtension ext) {        if (extensions.isEmpty()) {            extensions = new ArrayList<HelloExtension>();        }        extensions.add(ext);        encodedLength = -1;    }     HelloExtension get(ExtensionType type) {        for (HelloExtension ext : extensions) {            if (ext.type == type) {                return ext;            }        }        return null;    }     int length() {        if (encodedLength >= 0) {            return encodedLength;        }        if (extensions.isEmpty()) {            encodedLength = 0;        } else {            encodedLength = 2;            for (HelloExtension ext : extensions) {                encodedLength += ext.length();            }        }        return encodedLength;    }     void send(HandshakeOutStream s) throws IOException {        int length = length();        if (length == 0) {            return;        }        s.putInt16(length - 2);        for (HelloExtension ext : extensions) {            ext.send(s);        }    }     void print(PrintStream s) throws IOException {        for (HelloExtension ext : extensions) {            s.println(ext.toString());        }    }} final class ExtensionType {     final int id;    final String name;     private ExtensionType(int id, String name) {        this.id = id;        this.name = name;    }     public String toString() {        return name;    }     static List<ExtensionType> knownExtensions = new ArrayList<>(9);     static ExtensionType get(int id) {        for (ExtensionType ext : knownExtensions) {            if (ext.id == id) {                return ext;            }        }        return new ExtensionType(id, "type_" + id);    }     private static ExtensionType e(int id, String name) {        ExtensionType ext = new ExtensionType(id, name);        knownExtensions.add(ext);        return ext;    }     // extensions defined in RFC 3546    final static ExtensionType EXT_SERVER_NAME =            e(0x0000, "server_name");            // IANA registry value: 0    final static ExtensionType EXT_MAX_FRAGMENT_LENGTH =            e(0x0001, "max_fragment_length");    // IANA registry value: 1    final static ExtensionType EXT_CLIENT_CERTIFICATE_URL =            e(0x0002, "client_certificate_url"); // IANA registry value: 2    final static ExtensionType EXT_TRUSTED_CA_KEYS =            e(0x0003, "trusted_ca_keys");        // IANA registry value: 3    final static ExtensionType EXT_TRUNCATED_HMAC =            e(0x0004, "truncated_hmac");         // IANA registry value: 4    final static ExtensionType EXT_STATUS_REQUEST =            e(0x0005, "status_request");         // IANA registry value: 5     // extensions defined in RFC 4681    final static ExtensionType EXT_USER_MAPPING =            e(0x0006, "user_mapping");           // IANA registry value: 6     // extensions defined in RFC 5081    final static ExtensionType EXT_CERT_TYPE =            e(0x0009, "cert_type");              // IANA registry value: 9     // extensions defined in RFC 4492 (ECC)    final static ExtensionType EXT_ELLIPTIC_CURVES =            e(0x000A, "elliptic_curves");        // IANA registry value: 10    final static ExtensionType EXT_EC_POINT_FORMATS =            e(0x000B, "ec_point_formats");       // IANA registry value: 11     // extensions defined in RFC 5054    final static ExtensionType EXT_SRP =            e(0x000C, "srp");                    // IANA registry value: 12     // extensions defined in RFC 5246    final static ExtensionType EXT_SIGNATURE_ALGORITHMS =            e(0x000D, "signature_algorithms");   // IANA registry value: 13     // extensions defined in RFC 5746    final static ExtensionType EXT_RENEGOTIATION_INFO =            e(0xff01, "renegotiation_info");     // IANA registry value: 65281} abstract class HelloExtension {     final ExtensionType type;     HelloExtension(ExtensionType type) {        this.type = type;    }     // Length of the encoded extension, including the type and length fields    abstract int length();     abstract void send(HandshakeOutStream s) throws IOException;     public abstract String toString(); } final class UnknownExtension extends HelloExtension {     private final byte[] data;     UnknownExtension(HandshakeInStream s, int len, ExtensionType type)            throws IOException {        super(type);        data = new byte[len];        // s.read() does not handle 0-length arrays.        if (len != 0) {            s.read(data);        }    }     int length() {        return 4 + data.length;    }     void send(HandshakeOutStream s) throws IOException {        s.putInt16(type.id);        s.putBytes16(data);    }     public String toString() {        return "Unsupported extension " + type + ", data: " +            Debug.toString(data);    }} /* * [RFC4366] To facilitate secure connections to servers that host multiple * 'virtual' servers at a single underlying network address, clients MAY * include an extension of type "server_name" in the (extended) client hello. * The "extension_data" field of this extension SHALL contain "ServerNameList" * where: * *     struct { *         NameType name_type; *         select (name_type) { *             case host_name: HostName; *         } name; *     } ServerName; * *     enum { *         host_name(0), (255) *     } NameType; * *     opaque HostName<1..2^16-1>; * *     struct { *         ServerName server_name_list<1..2^16-1> *     } ServerNameList; */final class ServerNameExtension extends HelloExtension {     final static int NAME_HOST_NAME = 0;     private List<ServerName> names;    private int listLength;     // ServerNameList length     ServerNameExtension(List<String> hostnames) throws IOException {        super(ExtensionType.EXT_SERVER_NAME);         listLength = 0;        names = new ArrayList<ServerName>(hostnames.size());        for (String hostname : hostnames) {            if (hostname != null && hostname.length() != 0) {                // we only support DNS hostname now.                ServerName serverName =                        new ServerName(NAME_HOST_NAME, hostname);                names.add(serverName);                listLength += serverName.length;            }        }         // As we only support DNS hostname now, the hostname list must        // not contain more than one hostname        if (names.size() > 1) {            throw new SSLProtocolException(                    "The ServerNameList MUST NOT contain more than " +                    "one name of the same name_type");        }         // We only need to add "server_name" extension in ClientHello unless        // we support SNI in server side in the future. It is possible that        // the SNI is empty in ServerHello. As we don't support SNI in        // ServerHello now, we will throw exception for empty list for now.        if (listLength == 0) {            throw new SSLProtocolException(                    "The ServerNameList cannot be empty");        }    }     ServerNameExtension(HandshakeInStream s, int len)            throws IOException {        super(ExtensionType.EXT_SERVER_NAME);         int remains = len;        if (len >= 2) {    // "server_name" extension in ClientHello            listLength = s.getInt16();     // ServerNameList length            if (listLength == 0 || listLength + 2 != len) {                throw new SSLProtocolException(                        "Invalid " + type + " extension");            }             remains -= 2;            names = new ArrayList<ServerName>();            while (remains > 0) {                ServerName name = new ServerName(s);                names.add(name);                remains -= name.length;                 // we may need to check the duplicated ServerName type            }        } else if (len == 0) {     // "server_name" extension in ServerHello            listLength = 0;            names = Collections.<ServerName>emptyList();        }         if (remains != 0) {            throw new SSLProtocolException("Invalid server_name extension");        }    }     static class ServerName {        final int length;        final int type;        final byte[] data;        final String hostname;         ServerName(int type, String hostname) throws IOException {            this.type = type;                       // NameType            this.hostname = hostname;            this.data = hostname.getBytes("UTF8");  // HostName            this.length = data.length + 3;          // NameType: 1 byte                                                    // HostName length: 2 bytes        }         ServerName(HandshakeInStream s) throws IOException {            type = s.getInt8();         // NameType            data = s.getBytes16();      // HostName (length read in getBytes16)            length = data.length + 3;   // NameType: 1 byte                                        // HostName length: 2 bytes            if (type == NAME_HOST_NAME) {                hostname = new String(data, "UTF8");            } else {                hostname = null;            }        }         public String toString() {            if (type == NAME_HOST_NAME) {                return "host_name: " + hostname;            } else {                return "unknown-" + type + ": " + Debug.toString(data);            }        }    }     int length() {        return listLength == 0 ? 4 : 6 + listLength;    }     void send(HandshakeOutStream s) throws IOException {        s.putInt16(type.id);        s.putInt16(listLength + 2);        if (listLength != 0) {            s.putInt16(listLength);             for (ServerName name : names) {                s.putInt8(name.type);           // NameType                s.putBytes16(name.data);        // HostName            }        }    }     public String toString() {        StringBuffer buffer = new StringBuffer();        for (ServerName name : names) {            buffer.append("[" + name + "]");        }         return "Extension " + type + ", server_name: " + buffer;    }} final class SupportedEllipticCurvesExtension extends HelloExtension {     // the extension value to send in the ClientHello message    static final SupportedEllipticCurvesExtension DEFAULT;     private static final boolean fips;     static {        int[] ids;        fips = SunJSSE.isFIPS();        if (fips == false) {            ids = new int[] {                // NIST curves first                // prefer NIST P-256, rest in order of increasing key length                23, 1, 3, 19, 21, 6, 7, 9, 10, 24, 11, 12, 25, 13, 14,                // non-NIST curves                15, 16, 17, 2, 18, 4, 5, 20, 8, 22,            };        } else {            ids = new int[] {                // same as above, but allow only NIST curves in FIPS mode                23, 1, 3, 19, 21, 6, 7, 9, 10, 24, 11, 12, 25, 13, 14,            };        }        DEFAULT = new SupportedEllipticCurvesExtension(ids);    }     private final int[] curveIds;     private SupportedEllipticCurvesExtension(int[] curveIds) {        super(ExtensionType.EXT_ELLIPTIC_CURVES);        this.curveIds = curveIds;    }     SupportedEllipticCurvesExtension(HandshakeInStream s, int len)            throws IOException {        super(ExtensionType.EXT_ELLIPTIC_CURVES);        int k = s.getInt16();        if (((len & 1) != 0) || (k + 2 != len)) {            throw new SSLProtocolException("Invalid " + type + " extension");        }        curveIds = new int[k >> 1];        for (int i = 0; i < curveIds.length; i++) {            curveIds[i] = s.getInt16();        }    }     boolean contains(int index) {        for (int curveId : curveIds) {            if (index == curveId) {                return true;            }        }        return false;    }     // Return a reference to the internal curveIds array.    // The caller must NOT modify the contents.    int[] curveIds() {        return curveIds;    }     int length() {        return 6 + (curveIds.length << 1);    }     void send(HandshakeOutStream s) throws IOException {        s.putInt16(type.id);

Lines 1–500 of 866.