/* * 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. Load more
/* * 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) {