/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* 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
*/
// the server's public key from its certificate.
// the server's ephemeral public key from the server key exchange message
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.
*/
// To switch off the SNI extension.
private final static boolean enableSNIExtension =
/*
* Constructors
*/
boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) {
}
boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] 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.
*/
throw new SSLProtocolException(
"Handshake message sequence violation, " + type);
}
switch (type) {
break;
break;
"unexpected server cert chain");
// NOTREACHED
}
break;
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.
*/
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");
}
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 {
} catch (GeneralSecurityException e) {
throwSSLException("Server key", e);
}
break;
case K_DH_ANON:
try {
this.serverKeyExchange(new DH_ServerKeyExchange(
input, protocolVersion));
} catch (GeneralSecurityException e) {
throwSSLException("Server key", e);
}
break;
case K_DHE_DSS:
case K_DHE_RSA:
try {
this.serverKeyExchange(new DH_ServerKeyExchange(
} 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
} 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;
// save for later, it's handled by serverHelloDone
throw new SSLHandshakeException(
"Client authentication requested for "+
"anonymous cipher suite.");
throw new SSLHandshakeException(
"Client certificate requested for "+
"kerberos cipher suite.");
}
}
throw new SSLHandshakeException(
"No peer supported signature algorithms");
}
if (supportedPeerSignAlgs.isEmpty()) {
throw new SSLHandshakeException(
"No supported signature and hash algorithm in common");
}
}
break;
break;
case HandshakeMessage.ht_finished:
this.serverFinished(
break;
default:
throw new SSLProtocolException(
"Illegal client handshake msg, " + type);
}
//
// Move state machine forward if the message handling
// code didn't already do so
//
}
}
/*
* 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).
*/
}
//
// Could be (e.g. at connection setup) that we already
// sent the "client hello" but the server's not seen it.
//
if (!secureRenegotiation && !allowUnsafeRenegotiation) {
// renegotiation is not allowed.
// response with a no_renegotiation warning,
// 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.
//
// 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.
"Renegotiation is not allowed");
}
} else {
if (!secureRenegotiation) {
"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.
*/
serverKeyExchangeReceived = false;
}
// check if the server selected protocol version is OK for us
if (!isNegotiable(mesgVersion)) {
throw new SSLHandshakeException(
"Server chose " + mesgVersion +
", but that protocol version is not enabled or not supported " +
"by the client.");
}
// Set protocolVersion and propagate to SSLSocket and the
// Handshake streams
// check the "renegotiation_info" extension
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
"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) {
"Unexpected renegotiation indication extension");
}
// verify the client_verify_data and server_verify_data values
byte[] verifyData =
"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
"Failed to negotiate the use of secure renegotiation");
}
secureRenegotiation = false;
"indication extension in ServerHello");
}
} else {
// For a secure renegotiation, the client must abort the
// handshake if no "renegotiation_info" extension is present.
if (secureRenegotiation) {
"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).
//
}
}
"compression type not supported, "
// NOTREACHED
}
// so far so good, let's look at the session
// we tried to resume, let's see what the server decided
// server resumed the session, let's make sure everything
// checks out
// Verify that the session ciphers are unchanged.
if (cipherSuite != sessionSuite) {
throw new SSLProtocolException
("Server returned wrong cipher suite for session");
}
// verify protocol version match
if (protocolVersion != sessionVersion) {
throw new SSLProtocolException
("Server resumed session with wrong protocol version");
}
// validate subject identity
try {
new PrivilegedExceptionAction<Subject>() {
}});
} catch (PrivilegedActionException e) {
" subject failed!");
}
}
// Eliminate dependency on KerberosPrincipal
throw new SSLProtocolException("Server resumed" +
" session with wrong subject identity");
} else {
}
} else {
" present in the current Subject; check if " +
" javax.security.auth.useSubjectAsCreds" +
" system property has been set to false");
throw new SSLProtocolException
("Server resumed session with no subject");
}
}
// looks fine; resume it, and update the state machine.
resumingSession = true;
}
} else {
// we wanted to resume, but the server refused
if (!enableNewSession) {
throw new SSLException
("New session creation is disabled");
}
}
}
}
return;
}
// check extensions
"Server sent an unsupported extension: " + type);
}
}
// Create a new session, we need to do the full handshake
}
}
/*
* Server's own key was either a signing-only key, or was too
* large for export rules ... this message holds an ephemeral
* RSA key to use for key exchange.
*/
throws IOException, GeneralSecurityException {
}
"server key exchange invalid");
// NOTREACHED
}
}
/*
* Diffie-Hellman key exchange. We save the server public key and
* our own D-H algorithm object so we can defer key calculations
* until after we've sent the client key exchange message (which
* gives client and server some useful parallelism).
*/
throws IOException {
}
}
throws IOException {
}
}
/*
* The server's "Hello Done" message is the client's sign that
* it's time to do all the hard work.
*/
}
/*
* Always make sure the input has been digested before we
* start emitting data, to ensure the hashes are correctly
* computed for the Finished and CertificateVerify messages
* which we send (here).
*/
/*
* FIRST ... if requested, send an appropriate Certificate chain
* to authenticate the client, and remember the associated private
* key to sign the CertificateVerify message.
*/
if (certRequest != null) {
switch (certRequest.types[i]) {
typeName = "RSA";
break;
typeName = "DSA";
break;
// ignore if we do not have EC crypto available
break;
// Any other values (currently not used in TLS)
default:
break;
}
}
}
if (keytypesTmpSize != 0) {
} else {
}
}
// for EC, make sure we use a supported named curve
if (publicKey instanceof ECPublicKey) {
int index =
params);
index)) {
}
}
}
}
}
//
// No appropriate cert was found ... report this to the
// server. For SSLv3, send the no_certificate alert;
// TLS uses an empty cert chain instead.
//
} else {
}
}
//
// At last ... send any client certificate chain.
//
}
}
}
/*
* SECOND ... send the client key exchange message. The
* procedure used is a function of the cipher suite selected;
* one is always needed.
*/
switch (keyExchange) {
case K_RSA:
case K_RSA_EXPORT:
throw new SSLProtocolException
("Server did not send certificate message");
}
if (!(serverKey instanceof RSAPublicKey)) {
throw new SSLProtocolException
("Server certificate does not include an RSA key");
}
/*
* For RSA key exchange, we randomly generate a new
* pre-master secret and encrypt it with the server's
* public key. Then we save that pre-master secret
* so that we can calculate the keying data later;
* it's a performance speedup not to do that until
* the client's waiting for the server response, but
* more of a speedup for the D-H case.
*
* If the RSA_EXPORT scheme is active, when the public
* key in the server certificate is less than or equal
* to 512 bits in length, use the cert's public key,
* otherwise, the ephemeral one.
*/
if (keyExchange == K_RSA) {
} else { // K_RSA_EXPORT
// extraneous ephemeralServerKey check done
// above in processMessage()
} else {
if (ephemeralServerKey == null) {
throw new SSLProtocolException("Server did not send" +
" a RSA_EXPORT Server Key Exchange message");
}
}
}
break;
case K_DH_RSA:
case K_DH_DSS:
/*
* For DH Key exchange, we only need to make sure the server
* knows our public key, so we calculate the same pre-master
* secret.
*
* For certs that had DH keys in them, we send an empty
* handshake message (no key) ... we flag this case by
* passing a null "dhPublic" value.
*
* Otherwise we send ephemeral DH keys, unsigned.
*/
// if (useDH_RSA || useDH_DSS)
m2 = new DHClientKeyExchange();
break;
case K_DHE_RSA:
case K_DHE_DSS:
case K_DH_ANON:
throw new SSLProtocolException
("Server did not send a DH Server Key Exchange message");
}
break;
case K_ECDHE_RSA:
case K_ECDHE_ECDSA:
case K_ECDH_ANON:
throw new SSLProtocolException
("Server did not send a ECDH Server Key Exchange message");
}
break;
case K_ECDH_RSA:
case K_ECDH_ECDSA:
throw new SSLProtocolException
("Server did not send certificate message");
}
if (serverKey instanceof ECPublicKey == false) {
throw new SSLProtocolException
("Server certificate does not include an EC key");
}
break;
case K_KRB5:
case K_KRB5_EXPORT:
throw new IOException("Hostname is required" +
" to use Kerberos cipher suites");
}
// Record the principals involved in exchange
m2 = kerberosMsg;
break;
default:
// somethings very wrong
throw new RuntimeException
("Unsupported key exchange: " + keyExchange);
}
}
/*
* THIRD, send a "change_cipher_spec" record followed by the
* "Finished" message. We flush the messages we've queued up, to
* get concurrency between client and server. The concurrency is
* useful as we calculate the master secret, which is needed both
* to compute the "Finished" message, and to compute the keys used
* to protect all records following the change_cipher_spec.
*/
/*
* We deferred calculating the master secret and this connection's
* keying data; we do it now. Deferring this calculation is good
* from a performance point of view, since it lets us do it during
* some time that network delays and the server's own calculations
* would otherwise cause to be "dead" in the critical path.
*/
switch (keyExchange) {
case K_RSA:
case K_RSA_EXPORT:
break;
case K_KRB5:
case K_KRB5_EXPORT:
byte[] secretBytes =
"TlsPremasterSecret");
break;
case K_DHE_RSA:
case K_DHE_DSS:
case K_DH_ANON:
break;
case K_ECDHE_RSA:
case K_ECDHE_ECDSA:
case K_ECDH_ANON:
break;
case K_ECDH_RSA:
case K_ECDH_ECDSA:
break;
default:
throw new IOException("Internal error: unknown key exchange "
+ keyExchange);
}
/*
* FOURTH, if we sent a Certificate, we need to send a signed
* CertificateVerify (unless the key in the client's certificate
* was a Diffie-Hellman key).).
*
* This uses a hash of the previous handshake messages ... either
* a nonfinal one (if the particular implementation supports it)
* or else using the third element in the arrays of hashes being
* computed.
*/
if (signingKey != null) {
try {
if (preferableSignatureAlgorithm == null) {
throw new SSLHandshakeException(
"No supported signature algorithm");
}
throw new SSLHandshakeException(
"No supported hash algorithm");
}
}
} catch (GeneralSecurityException e) {
"Error signing certificate verify", e);
// NOTREACHED, make compiler happy
}
}
} else {
}
}
/*
* OK, that's that!
*/
sendChangeCipherAndFinish(false);
}
/*
* "Finished" is the last handshake message sent. If we got this
* far, the MAC has been validated post-decryption. We validate
* the two hashes here as an additional sanity check, protecting
* the handshake against various active attacks.
*/
}
if (!verified) {
"server 'finished' message doesn't verify");
// NOTREACHED
}
/*
* save server verify data for secure renegotiation
*/
if (secureRenegotiation) {
}
/*
* OK, it verified. If we're doing the fast handshake, add that
* "Finished" message to the hash of handshake messages, then send
* our own change_cipher_spec and Finished message for the server
* to verify in turn. These are the last handshake messages.
*
* In any case, update the session cache. We're done handshaking,
* so there are no threats any more associated with partially
* completed handshakes.
*/
if (resumingSession) {
sendChangeCipherAndFinish(true);
}
if (!resumingSession) {
if (session.isRejoinable()) {
}
"%% Didn't cache non-resumable client session: "
+ session);
}
}
}
/*
* Send my change-cipher-spec and Finished message ... done as the
* last handshake act in either the short or long sequences. In
* the short one, we've already seen the server's Finished; in the
* long one, we wait for it now.
*/
throws IOException {
/*
* Send the change_cipher_spec message, then the Finished message
* which we just calculated (and protected using the keys we just
* calculated). Server responds with its Finished message, except
* in the "fast handshake" (resume session) case.
*/
/*
* save client verify data for secure renegotiation
*/
if (secureRenegotiation) {
}
/*
* Update state machine so server MUST send 'finished' next.
* (In "long" handshake case; in short case, we're responding
* to its message.)
*/
}
/*
* Returns a ClientHello message to kickstart renegotiations
*/
// session ID of the ClientHello message
// a list of cipher suites sent by the client
// set the max protocol version this client is supporting.
//
// Try to resume an existing session. This might be mandatory,
// given certain API options.
//
+ session
} else {
}
}
}
if (isNegotiable(sessionSuite) == false) {
}
}
}
}
+ " from port " + getLocalPortSE());
}
}
// Update SSL version number in underlying SSL socket and
// handshake output stream, so that the output records (at the
// record layer) have the correct version
}
/*
* Force use of the previous session ciphersuite, and
* add the SCSV if enabled.
*/
if (!enableNewSession) {
throw new SSLHandshakeException(
"Can't reuse existing SSL client session");
}
if (!secureRenegotiation &&
} // otherwise, renegotiation_info extension will be used
}
}
throw new SSLHandshakeException("No existing session to resume");
}
// exclude SCSV for secure renegotiation
}
}
}
// make sure there is a negotiable cipher suite.
boolean negotiable = false;
if (isNegotiable(suite)) {
negotiable = true;
break;
}
}
if (!negotiable) {
throw new SSLHandshakeException("No negotiable cipher suite");
}
// Not a TLS1.2+ handshake
// For SSLv2Hello, HandshakeHash.reset() will be called, so we
// cannot call HandshakeHash.protocolDetermined() here. As it does
// not follow the spec that HandshakeHash.reset() can be only be
// called before protocolDetermined.
// if (maxProtocolVersion.v < ProtocolVersion.TLS12.v) {
// handshakeHash.protocolDetermined(maxProtocolVersion);
// }
// create the ClientHello message
// add signature_algorithm extension
// we will always send the signature_algorithm extension
if (localSignAlgs.isEmpty()) {
throw new SSLHandshakeException(
"No supported signature algorithm");
}
}
// add server_name extension
if (enableSNIExtension) {
// We cannot use the hostname resolved from name services. For
// virtual hosting, multiple hostnames may be bound to the same IP
// address, so the hostname resolved from name services is not
// reliable.
// we only allow FQDN
}
}
// reset the client random cookie
/*
* need to set the renegotiation_info extension for:
* 1: secure renegotiation
* 2: initial handshake and no SCSV in the ClientHello
* 3: insecure renegotiation and no SCSV in the ClientHello
*/
if (secureRenegotiation ||
}
return clientHelloMessage;
}
/*
* Fault detected during handshake.
*/
}
}
/*
* Unless we are using an anonymous ciphersuite, the server always
* sends a certificate message (for the CipherSuites we currently
* support). The trust manager verifies the chain for us.
*/
}
"empty certificate chain");
}
// ask the trust manager to verify the chain
try {
// find out the key exchange algorithm used
// use "RSA" for non-ephemeral "RSA_EXPORT"
} else {
}
if (tm instanceof X509ExtendedTrustManager) {
conn);
} else {
engine);
}
} else {
// Unlikely to happen, because we have wrapped the old
// X509TrustManager with the new X509ExtendedTrustManager.
throw new CertificateException(
"Improper X509TrustManager implementation");
}
} catch (CertificateException e) {
// This will throw an exception, so include the original error.
}
}
}