1341 lines · 53.3 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.util.*; import java.security.interfaces.ECPublicKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.ECParameterSpec; import java.security.cert.X509Certificate;import java.security.cert.CertificateException; import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.*; import javax.security.auth.Subject; import sun.security.ssl.HandshakeMessage.*;import sun.security.ssl.CipherSuite.*;import static sun.security.ssl.CipherSuite.KeyExchange.*; import sun.net.util.IPAddressUtil; /** * ClientHandshaker does the protocol handshaking from the point * of view of a client.  It is driven asychronously by handshake messages * as delivered by the parent Handshaker class, and also uses * common functionality (e.g. key generation) that is provided there. * * @author David Brownell */final class ClientHandshaker extends Handshaker {     // the server's public key from its certificate.    private PublicKey serverKey;     // the server's ephemeral public key from the server key exchange message    // for ECDHE/ECDH_anon and RSA_EXPORT.    private PublicKey ephemeralServerKey;     // server's ephemeral public value for DHE/DH_anon key exchanges    private BigInteger          serverDH;     private DHCrypt             dh;     private ECDHCrypt ecdh;     private CertificateRequest  certRequest;     private boolean serverKeyExchangeReceived;     /*     * The RSA PreMasterSecret needs to know the version of     * ClientHello that was used on this handshake.  This represents     * the "max version" this client is supporting.  In the     * case of an initial handshake, it's the max version enabled,     * but in the case of a resumption attempt, it's the version     * of the session we're trying to resume.     */    private ProtocolVersion maxProtocolVersion;     // To switch off the SNI extension.    private final static boolean enableSNIExtension =            Debug.getBooleanProperty("jsse.enableSNIExtension", true);     /*     * Constructors     */    ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context,            ProtocolList enabledProtocols,            ProtocolVersion activeProtocolVersion,            boolean isInitialHandshake, boolean secureRenegotiation,            byte[] clientVerifyData, byte[] serverVerifyData) {         super(socket, context, enabledProtocols, true, true,            activeProtocolVersion, isInitialHandshake, secureRenegotiation,            clientVerifyData, serverVerifyData);    }     ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context,            ProtocolList enabledProtocols,            ProtocolVersion activeProtocolVersion,            boolean isInitialHandshake, boolean secureRenegotiation,            byte[] clientVerifyData, byte[] serverVerifyData) {         super(engine, context, enabledProtocols, true, true,            activeProtocolVersion, isInitialHandshake, secureRenegotiation,            clientVerifyData, serverVerifyData);    }     /*     * This routine handles all the client side handshake messages, one at     * a time.  Given the message type (and in some cases the pending cipher     * spec) it parses the type-specific message.  Then it calls a function     * that handles that specific message.     *     * It updates the state machine (need to verify it) as each message     * is processed, and writes responses as needed using the connection     * in the constructor.     */    void processMessage(byte type, int messageLen) throws IOException {        if (state > type                && (type != HandshakeMessage.ht_hello_request                    && state != HandshakeMessage.ht_client_hello)) {            throw new SSLProtocolException(                    "Handshake message sequence violation, " + type);        }         switch (type) {        case HandshakeMessage.ht_hello_request:            this.serverHelloRequest(new HelloRequest(input));            break;         case HandshakeMessage.ht_server_hello:            this.serverHello(new ServerHello(input, messageLen));            break;         case HandshakeMessage.ht_certificate:            if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON                    || keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {                fatalSE(Alerts.alert_unexpected_message,                    "unexpected server cert chain");                // NOTREACHED            }            this.serverCertificate(new CertificateMsg(input));            serverKey =                session.getPeerCertificates()[0].getPublicKey();            break;         case HandshakeMessage.ht_server_key_exchange:            serverKeyExchangeReceived = true;            switch (keyExchange) {            case K_RSA_EXPORT:                /**                 * The server key exchange message is sent by the server only                 * when the server certificate message does not contain the                 * proper amount of data to allow the client to exchange a                 * premaster secret, such as when RSA_EXPORT is used and the                 * public key in the server certificate is longer than 512 bits.                 */                if (serverKey == null) {                    throw new SSLProtocolException                        ("Server did not send certificate message");                }                 if (!(serverKey instanceof RSAPublicKey)) {                    throw new SSLProtocolException("Protocol violation:" +                        " the certificate type must be appropriate for the" +                        " selected cipher suite's key exchange algorithm");                }                 if (JsseJce.getRSAKeyLength(serverKey) <= 512) {                    throw new SSLProtocolException("Protocol violation:" +                        " server sent a server key exchange message for" +                        " key exchange " + keyExchange +                        " when the public key in the server certificate" +                        " is less than or equal to 512 bits in length");                }                 try {                    this.serverKeyExchange(new RSA_ServerKeyExchange(input));                } catch (GeneralSecurityException e) {                    throwSSLException("Server key", e);                }                break;            case K_DH_ANON:                this.serverKeyExchange(new DH_ServerKeyExchange(                                                input, protocolVersion));                break;            case K_DHE_DSS:            case K_DHE_RSA:                try {                    this.serverKeyExchange(new DH_ServerKeyExchange(                        input, serverKey,                        clnt_random.random_bytes, svr_random.random_bytes,                        messageLen,                        localSupportedSignAlgs, protocolVersion));                } catch (GeneralSecurityException e) {                    throwSSLException("Server key", e);                }                break;            case K_ECDHE_ECDSA:            case K_ECDHE_RSA:            case K_ECDH_ANON:                try {                    this.serverKeyExchange(new ECDH_ServerKeyExchange                        (input, serverKey, clnt_random.random_bytes,                        svr_random.random_bytes,                        localSupportedSignAlgs, protocolVersion));                } catch (GeneralSecurityException e) {                    throwSSLException("Server key", e);                }                break;            case K_RSA:            case K_DH_RSA:            case K_DH_DSS:            case K_ECDH_ECDSA:            case K_ECDH_RSA:                throw new SSLProtocolException(                    "Protocol violation: server sent a server key exchange"                    + "message for key exchange " + keyExchange);            case K_KRB5:            case K_KRB5_EXPORT:                throw new SSLProtocolException(                    "unexpected receipt of server key exchange algorithm");            default:                throw new SSLProtocolException(                    "unsupported key exchange algorithm = "                    + keyExchange);            }            break;         case HandshakeMessage.ht_certificate_request:            // save for later, it's handled by serverHelloDone            if ((keyExchange == K_DH_ANON) || (keyExchange == K_ECDH_ANON)) {                throw new SSLHandshakeException(                    "Client authentication requested for "+                    "anonymous cipher suite.");            } else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {                throw new SSLHandshakeException(                    "Client certificate requested for "+                    "kerberos cipher suite.");            }            certRequest = new CertificateRequest(input, protocolVersion);            if (debug != null && Debug.isOn("handshake")) {                certRequest.print(System.out);            }             if (protocolVersion.v >= ProtocolVersion.TLS12.v) {                Collection<SignatureAndHashAlgorithm> peerSignAlgs =                                        certRequest.getSignAlgorithms();                if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {                    throw new SSLHandshakeException(                        "No peer supported signature algorithms");                }                 Collection<SignatureAndHashAlgorithm> supportedPeerSignAlgs =                    SignatureAndHashAlgorithm.getSupportedAlgorithms(                                                            peerSignAlgs);                if (supportedPeerSignAlgs.isEmpty()) {                    throw new SSLHandshakeException(                        "No supported signature and hash algorithm in common");                }                 setPeerSupportedSignAlgs(supportedPeerSignAlgs);                session.setPeerSupportedSignatureAlgorithms(                                                supportedPeerSignAlgs);            }             break;         case HandshakeMessage.ht_server_hello_done:            this.serverHelloDone(new ServerHelloDone(input));            break;         case HandshakeMessage.ht_finished:            this.serverFinished(                new Finished(protocolVersion, input, cipherSuite));            break;         default:            throw new SSLProtocolException(                "Illegal client handshake msg, " + type);        }         //        // Move state machine forward if the message handling        // code didn't already do so        //        if (state < type) {            state = type;        }    }     /*     * Used by the server to kickstart negotiations -- this requests a     * "client hello" to renegotiate current cipher specs (e.g. maybe lots     * of data has been encrypted with the same keys, or the server needs     * the client to present a certificate).     */    private void serverHelloRequest(HelloRequest mesg) throws IOException {        if (debug != null && Debug.isOn("handshake")) {            mesg.print(System.out);        }         //        // Could be (e.g. at connection setup) that we already        // sent the "client hello" but the server's not seen it.        //        if (state < HandshakeMessage.ht_client_hello) {            if (!secureRenegotiation && !allowUnsafeRenegotiation) {                // renegotiation is not allowed.                if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {                    // response with a no_renegotiation warning,                    warningSE(Alerts.alert_no_renegotiation);                     // invalidate the handshake so that the caller can                    // dispose this object.                    invalidated = true;                     // If there is still unread block in the handshake                    // input stream, it would be truncated with the disposal                    // and the next handshake message will become incomplete.                    //                    // However, according to SSL/TLS specifications, no more                    // handshake message should immediately follow ClientHello                    // or HelloRequest. So just let it be.                } else {                    // For SSLv3, send the handshake_failure fatal error.                    // Note that SSLv3 does not define a no_renegotiation                    // alert like TLSv1. However we cannot ignore the message                    // simply, otherwise the other side was waiting for a                    // response that would never come.                    fatalSE(Alerts.alert_handshake_failure,                        "Renegotiation is not allowed");                }            } else {                if (!secureRenegotiation) {                    if (debug != null && Debug.isOn("handshake")) {                        System.out.println(                            "Warning: continue with insecure renegotiation");                    }                }                kickstart();            }        }    }      /*     * Server chooses session parameters given options created by the     * client -- basically, cipher options, session id, and someday a     * set of compression options.     *     * There are two branches of the state machine, decided by the     * details of this message.  One is the "fast" handshake, where we     * can resume the pre-existing session we asked resume.  The other     * is a more expensive "full" handshake, with key exchange and     * probably authentication getting done.     */    private void serverHello(ServerHello mesg) throws IOException {        serverKeyExchangeReceived = false;        if (debug != null && Debug.isOn("handshake")) {            mesg.print(System.out);        }         // check if the server selected protocol version is OK for us        ProtocolVersion mesgVersion = mesg.protocolVersion;        if (!isNegotiable(mesgVersion)) {            throw new SSLHandshakeException(                "Server chose " + mesgVersion +                ", but that protocol version is not enabled or not supported " +                "by the client.");        }         handshakeHash.protocolDetermined(mesgVersion);         // Set protocolVersion and propagate to SSLSocket and the        // Handshake streams        setVersion(mesgVersion);         // check the "renegotiation_info" extension        RenegotiationInfoExtension serverHelloRI = (RenegotiationInfoExtension)                    mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO);        if (serverHelloRI != null) {            if (isInitialHandshake) {                // verify the length of the "renegotiated_connection" field                if (!serverHelloRI.isEmpty()) {                    // abort the handshake with a fatal handshake_failure alert                    fatalSE(Alerts.alert_handshake_failure,                        "The renegotiation_info field is not empty");                }                 secureRenegotiation = true;            } else {                // For a legacy renegotiation, the client MUST verify that                // it does not contain the "renegotiation_info" extension.                if (!secureRenegotiation) {                    fatalSE(Alerts.alert_handshake_failure,                        "Unexpected renegotiation indication extension");                }                 // verify the client_verify_data and server_verify_data values                byte[] verifyData =                    new byte[clientVerifyData.length + serverVerifyData.length];                System.arraycopy(clientVerifyData, 0, verifyData,                        0, clientVerifyData.length);                System.arraycopy(serverVerifyData, 0, verifyData,                        clientVerifyData.length, serverVerifyData.length);                if (!Arrays.equals(verifyData,                                serverHelloRI.getRenegotiatedConnection())) {                    fatalSE(Alerts.alert_handshake_failure,                        "Incorrect verify data in ServerHello " +                        "renegotiation_info message");                }            }        } else {            // no renegotiation indication extension            if (isInitialHandshake) {                if (!allowLegacyHelloMessages) {                    // abort the handshake with a fatal handshake_failure alert                    fatalSE(Alerts.alert_handshake_failure,                        "Failed to negotiate the use of secure renegotiation");                }                 secureRenegotiation = false;                if (debug != null && Debug.isOn("handshake")) {                    System.out.println("Warning: No renegotiation " +                                    "indication extension in ServerHello");                }            } else {                // For a secure renegotiation, the client must abort the                // handshake if no "renegotiation_info" extension is present.                if (secureRenegotiation) {                    fatalSE(Alerts.alert_handshake_failure,                        "No renegotiation indication extension");                }                 // we have already allowed unsafe renegotation before request                // the renegotiation.            }        }         //        // Save server nonce, we always use it to compute connection        // keys and it's also used to create the master secret if we're        // creating a new session (i.e. in the full handshake).        //        svr_random = mesg.svr_random;         if (isNegotiable(mesg.cipherSuite) == false) {            fatalSE(Alerts.alert_illegal_parameter,                "Server selected improper ciphersuite " + mesg.cipherSuite);        }         setCipherSuite(mesg.cipherSuite);        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {            handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());        }         if (mesg.compression_method != 0) {            fatalSE(Alerts.alert_illegal_parameter,                "compression type not supported, "                + mesg.compression_method);            // NOTREACHED        }         // so far so good, let's look at the session        if (session != null) {            // we tried to resume, let's see what the server decided            if (session.getSessionId().equals(mesg.sessionId)) {                // server resumed the session, let's make sure everything                // checks out                 // Verify that the session ciphers are unchanged.                CipherSuite sessionSuite = session.getSuite();                if (cipherSuite != sessionSuite) {                    throw new SSLProtocolException                        ("Server returned wrong cipher suite for session");                }                 // verify protocol version match                ProtocolVersion sessionVersion = session.getProtocolVersion();                if (protocolVersion != sessionVersion) {                    throw new SSLProtocolException                        ("Server resumed session with wrong protocol version");                }                 // validate subject identity                if (sessionSuite.keyExchange == K_KRB5 ||                    sessionSuite.keyExchange == K_KRB5_EXPORT) {

Lines 1–500 of 1341.