2006 lines · 67.7 KB · Java
RawDownload
/* * Copyright (c) 1996, 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.*;import java.math.BigInteger;import java.security.*;import java.security.interfaces.*;import java.security.spec.*;import java.security.cert.*;import java.security.cert.Certificate;import java.util.*;import java.util.concurrent.ConcurrentHashMap; import java.lang.reflect.*; import javax.security.auth.x500.X500Principal; import javax.crypto.KeyGenerator;import javax.crypto.SecretKey; import javax.net.ssl.*; import sun.security.internal.spec.TlsPrfParameterSpec;import sun.security.ssl.CipherSuite.*;import static sun.security.ssl.CipherSuite.PRF.*; /** * Many data structures are involved in the handshake messages.  These * classes are used as structures, with public data members.  They are * not visible outside the SSL package. * * Handshake messages all have a common header format, and they are all * encoded in a "handshake data" SSL record substream.  The base class * here (HandshakeMessage) provides a common framework and records the * SSL record type of the particular handshake message. * * This file contains subclasses for all the basic handshake messages. * All handshake messages know how to encode and decode themselves on * SSL streams; this facilitates using the same code on SSL client and * server sides, although they don't send and receive the same messages. * * Messages also know how to print themselves, which is quite handy * for debugging.  They always identify their type, and can optionally * dump all of their content. * * @author David Brownell */public abstract class HandshakeMessage {     HandshakeMessage() { }     // enum HandshakeType:    static final byte   ht_hello_request = 0;    static final byte   ht_client_hello = 1;    static final byte   ht_server_hello = 2;     static final byte   ht_certificate = 11;    static final byte   ht_server_key_exchange = 12;    static final byte   ht_certificate_request = 13;    static final byte   ht_server_hello_done = 14;    static final byte   ht_certificate_verify = 15;    static final byte   ht_client_key_exchange = 16;     static final byte   ht_finished = 20;     /* Class and subclass dynamic debugging support */    public static final Debug debug = Debug.getInstance("ssl");     /**     * Utility method to convert a BigInteger to a byte array in unsigned     * format as needed in the handshake messages. BigInteger uses     * 2's complement format, i.e. it prepends an extra zero if the MSB     * is set. We remove that.     */    static byte[] toByteArray(BigInteger bi) {        byte[] b = bi.toByteArray();        if ((b.length > 1) && (b[0] == 0)) {            int n = b.length - 1;            byte[] newarray = new byte[n];            System.arraycopy(b, 1, newarray, 0, n);            b = newarray;        }        return b;    }     /*     * SSL 3.0 MAC padding constants.     * Also used by CertificateVerify and Finished during the handshake.     */    static final byte[] MD5_pad1 = genPad(0x36, 48);    static final byte[] MD5_pad2 = genPad(0x5c, 48);     static final byte[] SHA_pad1 = genPad(0x36, 40);    static final byte[] SHA_pad2 = genPad(0x5c, 40);     private static byte[] genPad(int b, int count) {        byte[] padding = new byte[count];        Arrays.fill(padding, (byte)b);        return padding;    }     /*     * Write a handshake message on the (handshake) output stream.     * This is just a four byte header followed by the data.     *     * NOTE that huge messages -- notably, ones with huge cert     * chains -- are handled correctly.     */    final void write(HandshakeOutStream s) throws IOException {        int len = messageLength();        if (len > (1 << 24)) {            throw new SSLException("Handshake message too big"                + ", type = " + messageType() + ", len = " + len);        }        s.write(messageType());        s.putInt24(len);        send(s);    }     /*     * Subclasses implement these methods so those kinds of     * messages can be emitted.  Base class delegates to subclass.     */    abstract int  messageType();    abstract int  messageLength();    abstract void send(HandshakeOutStream s) throws IOException;     /*     * Write a descriptive message on the output stream; for debugging.     */    abstract void print(PrintStream p) throws IOException; //// NOTE:  the rest of these classes are nested within this one, and are// imported by other classes in this package.  There are a few other// handshake message classes, not neatly nested here because of current// licensing requirement for native (RSA) methods.  They belong here,// but those native methods complicate things a lot!//  /* * HelloRequest ... SERVER --> CLIENT * * Server can ask the client to initiate a new handshake, e.g. to change * session parameters after a connection has been (re)established. */static final class HelloRequest extends HandshakeMessage {    int messageType() { return ht_hello_request; }     HelloRequest() { }     HelloRequest(HandshakeInStream in) throws IOException    {        // nothing in this message    }     int messageLength() { return 0; }     void send(HandshakeOutStream out) throws IOException    {        // nothing in this messaage    }     void print(PrintStream out) throws IOException    {        out.println("*** HelloRequest (empty)");    } }  /* * ClientHello ... CLIENT --> SERVER * * Client initiates handshake by telling server what it wants, and what it * can support (prioritized by what's first in the ciphe suite list). * * By RFC2246:7.4.1.2 it's explicitly anticipated that this message * will have more data added at the end ... e.g. what CAs the client trusts. * Until we know how to parse it, we will just read what we know * about, and let our caller handle the jumps over unknown data. */static final class ClientHello extends HandshakeMessage {     ProtocolVersion     protocolVersion;    RandomCookie        clnt_random;    SessionId           sessionId;    private CipherSuiteList    cipherSuites;    byte[]              compression_methods;     HelloExtensions extensions = new HelloExtensions();     private final static byte[]  NULL_COMPRESSION = new byte[] {0};     ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,            SessionId sessionId, CipherSuiteList cipherSuites) {         this.protocolVersion = protocolVersion;        this.sessionId = sessionId;        this.cipherSuites = cipherSuites;         if (cipherSuites.containsEC()) {            extensions.add(SupportedEllipticCurvesExtension.DEFAULT);            extensions.add(SupportedEllipticPointFormatsExtension.DEFAULT);        }         clnt_random = new RandomCookie(generator);        compression_methods = NULL_COMPRESSION;    }     ClientHello(HandshakeInStream s, int messageLength) throws IOException {        protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8());        clnt_random = new RandomCookie(s);        sessionId = new SessionId(s.getBytes8());        cipherSuites = new CipherSuiteList(s);        compression_methods = s.getBytes8();        if (messageLength() != messageLength) {            extensions = new HelloExtensions(s);        }    }     CipherSuiteList getCipherSuites() {        return cipherSuites;    }     // add renegotiation_info extension    void addRenegotiationInfoExtension(byte[] clientVerifyData) {        HelloExtension renegotiationInfo = new RenegotiationInfoExtension(                    clientVerifyData, new byte[0]);        extensions.add(renegotiationInfo);    }     // add server_name extension    void addServerNameIndicationExtension(String hostname) {        // We would have checked that the hostname ia a FQDN.        ArrayList<String> hostnames = new ArrayList<>(1);        hostnames.add(hostname);         try {            extensions.add(new ServerNameExtension(hostnames));        } catch (IOException ioe) {            // ignore the exception and return        }    }     // add signature_algorithm extension    void addSignatureAlgorithmsExtension(            Collection<SignatureAndHashAlgorithm> algorithms) {        HelloExtension signatureAlgorithm =                new SignatureAlgorithmsExtension(algorithms);        extensions.add(signatureAlgorithm);    }     @Override    int messageType() { return ht_client_hello; }     @Override    int messageLength() {        /*         * Add fixed size parts of each field...         * version + random + session + cipher + compress         */        return (2 + 32 + 1 + 2 + 1            + sessionId.length()                /* ... + variable parts */            + (cipherSuites.size() * 2)            + compression_methods.length)            + extensions.length();    }     @Override    void send(HandshakeOutStream s) throws IOException {        s.putInt8(protocolVersion.major);        s.putInt8(protocolVersion.minor);        clnt_random.send(s);        s.putBytes8(sessionId.getId());        cipherSuites.send(s);        s.putBytes8(compression_methods);        extensions.send(s);    }     @Override    void print(PrintStream s) throws IOException {        s.println("*** ClientHello, " + protocolVersion);         if (debug != null && Debug.isOn("verbose")) {            s.print("RandomCookie:  ");            clnt_random.print(s);             s.print("Session ID:  ");            s.println(sessionId);             s.println("Cipher Suites: " + cipherSuites);             Debug.println(s, "Compression Methods", compression_methods);            extensions.print(s);            s.println("***");        }    }} /* * ServerHello ... SERVER --> CLIENT * * Server chooses protocol options from among those it supports and the * client supports.  Then it sends the basic session descriptive parameters * back to the client. */static finalclass ServerHello extends HandshakeMessage{    int messageType() { return ht_server_hello; }     ProtocolVersion     protocolVersion;    RandomCookie        svr_random;    SessionId           sessionId;    CipherSuite         cipherSuite;    byte                compression_method;    HelloExtensions extensions = new HelloExtensions();     ServerHello() {        // empty    }     ServerHello(HandshakeInStream input, int messageLength)            throws IOException {        protocolVersion = ProtocolVersion.valueOf(input.getInt8(),                                                  input.getInt8());        svr_random = new RandomCookie(input);        sessionId = new SessionId(input.getBytes8());        cipherSuite = CipherSuite.valueOf(input.getInt8(), input.getInt8());        compression_method = (byte)input.getInt8();        if (messageLength() != messageLength) {            extensions = new HelloExtensions(input);        }    }     int messageLength()    {        // almost fixed size, except session ID and extensions:        //      major + minor = 2        //      random = 32        //      session ID len field = 1        //      cipher suite + compression = 3        //      extensions: if present, 2 + length of extensions        return 38 + sessionId.length() + extensions.length();    }     void send(HandshakeOutStream s) throws IOException    {        s.putInt8(protocolVersion.major);        s.putInt8(protocolVersion.minor);        svr_random.send(s);        s.putBytes8(sessionId.getId());        s.putInt8(cipherSuite.id >> 8);        s.putInt8(cipherSuite.id & 0xff);        s.putInt8(compression_method);        extensions.send(s);    }     void print(PrintStream s) throws IOException    {        s.println("*** ServerHello, " + protocolVersion);         if (debug != null && Debug.isOn("verbose")) {            s.print("RandomCookie:  ");            svr_random.print(s);             int i;             s.print("Session ID:  ");            s.println(sessionId);             s.println("Cipher Suite: " + cipherSuite);            s.println("Compression Method: " + compression_method);            extensions.print(s);            s.println("***");        }    }}  /* * CertificateMsg ... send by both CLIENT and SERVER * * Each end of a connection may need to pass its certificate chain to * the other end.  Such chains are intended to validate an identity with * reference to some certifying authority.  Examples include companies * like Verisign, or financial institutions.  There's some control over * the certifying authorities which are sent. * * NOTE: that these messages might be huge, taking many handshake records. * Up to 2^48 bytes of certificate may be sent, in records of at most 2^14 * bytes each ... up to 2^32 records sent on the output stream. */static finalclass CertificateMsg extends HandshakeMessage{    int messageType() { return ht_certificate; }     private X509Certificate[] chain;     private List<byte[]> encodedChain;     private int messageLength;     CertificateMsg(X509Certificate[] certs) {        chain = certs;    }     CertificateMsg(HandshakeInStream input) throws IOException {        int chainLen = input.getInt24();        List<Certificate> v = new ArrayList<>(4);         CertificateFactory cf = null;        while (chainLen > 0) {            byte[] cert = input.getBytes24();            chainLen -= (3 + cert.length);            try {                if (cf == null) {                    cf = CertificateFactory.getInstance("X.509");                }                v.add(cf.generateCertificate(new ByteArrayInputStream(cert)));            } catch (CertificateException e) {                throw (SSLProtocolException)new SSLProtocolException(                    e.getMessage()).initCause(e);            }        }         chain = v.toArray(new X509Certificate[v.size()]);    }     int messageLength() {        if (encodedChain == null) {            messageLength = 3;            encodedChain = new ArrayList<byte[]>(chain.length);            try {                for (X509Certificate cert : chain) {                    byte[] b = cert.getEncoded();                    encodedChain.add(b);                    messageLength += b.length + 3;                }            } catch (CertificateEncodingException e) {                encodedChain = null;                throw new RuntimeException("Could not encode certificates", e);            }        }        return messageLength;    }     void send(HandshakeOutStream s) throws IOException {        s.putInt24(messageLength() - 3);        for (byte[] b : encodedChain) {            s.putBytes24(b);        }    }     void print(PrintStream s) throws IOException {        s.println("*** Certificate chain");         if (debug != null && Debug.isOn("verbose")) {            for (int i = 0; i < chain.length; i++)                s.println("chain [" + i + "] = " + chain[i]);            s.println("***");        }    }     X509Certificate[] getCertificateChain() {        return chain.clone();    }} /* * ServerKeyExchange ... SERVER --> CLIENT * * The cipher suite selected, when combined with the certificate exchanged,

Lines 1–500 of 2006.