< prev index next >

src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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

@@ -23,83 +23,16 @@
  * 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.io.IOException;
 import java.security.cert.X509Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertPathValidatorException.Reason;
-import java.security.cert.CertPathValidatorException.BasicReason;
-import javax.security.auth.x500.X500Principal;
-
-import javax.crypto.SecretKey;
-
-import javax.net.ssl.*;
-
-import sun.security.ssl.HandshakeMessage.*;
-import static sun.security.ssl.CipherSuite.KeyExchange.*;
-
-/**
- * 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 {
-
-    // constants for subject alt names of type DNS and IP
-    private static final int ALTNAME_DNS = 2;
-    private static final int ALTNAME_IP  = 7;
-
-    // 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;
 
-    private boolean staplingActive = false;
-    private X509Certificate[] deferredCerts;
-
-    /*
-     * 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 static final boolean enableSNIExtension =
-            Debug.getBooleanProperty("jsse.enableSNIExtension", true);
+import sun.security.ssl.ClientHello.ClientHelloMessage;
 
+class ClientHandshakeContext extends HandshakeContext {
     /*
      * Allow unsafe server certificate change?
      *
      * Server certificate change during SSL/TLS renegotiation may be considered
      * unsafe, as described in the Triple Handshake attacks:

@@ -111,11 +44,11 @@
      * guarantee that the server certificate change in renegotiation is legal.
      * However, endpoing identification is only enabled for HTTPS and LDAP
      * over SSL/TLS by default.  It is not enough to protect SSL/TLS
      * connections other than HTTPS and LDAP.
      *
-     * The renegotiation indication extension (See RFC 5764) is a pretty
+     * The renegotiation indication extension (See RFC 5746) is a pretty
      * strong guarantee that the endpoints on both client and server sides
      * are identical on the same connection.  However, the Triple Handshake
      * attacks can bypass this guarantee if there is a session-resumption
      * handshake between the initial full handshake and the renegotiation
      * full handshake.

@@ -139,1866 +72,36 @@
      * handshake is restricted (See isIdentityEquivalent()).
      *
      * If the system property is set to "true" explicitly, the restriction on
      * server certificate change in renegotiation is disabled.
      */
-    private static final boolean allowUnsafeServerCertChange =
-        Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false);
-
-    // To switch off the max_fragment_length extension.
-    private static final boolean enableMFLExtension =
-            Debug.getBooleanProperty("jsse.enableMFLExtension", false);
-
-    // To switch off the supported_groups extension for DHE cipher suite.
-    private static final boolean enableFFDHE =
-            Debug.getBooleanProperty("jsse.enableFFDHE", true);
-
-    // Whether an ALPN extension was sent in the ClientHello
-    private boolean alpnActive = false;
-
-    private List<SNIServerName> requestedServerNames =
-            Collections.<SNIServerName>emptyList();
-
-    // maximum fragment length
-    private int requestedMFLength = -1;     // -1: no fragment length limit
-
-    private boolean serverNamesAccepted = false;
-
-    private ClientHello initialClientHelloMsg = null;   // DTLS only
+    static final boolean allowUnsafeServerCertChange =
+            Utilities.getBooleanProperty(
+                    "jdk.tls.allowUnsafeServerCertChange", false);
 
     /*
      * the reserved server certificate chain in previous handshaking
      *
      * The server certificate chain is only reserved if the previous
      * handshake is a session-resumption abbreviated initial handshake.
      */
-    private X509Certificate[] reservedServerCerts = null;
-
-    /*
-     * Constructors
-     */
-    ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context,
-            ProtocolList enabledProtocols,
-            ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
+    X509Certificate[] reservedServerCerts = null;
 
-        super(socket, context, enabledProtocols, true, true,
-            activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-            clientVerifyData, serverVerifyData);
-    }
+    X509Certificate[] deferredCerts;
 
-    ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context,
-            ProtocolList enabledProtocols,
-            ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData,
-            boolean isDTLS) {
+    ClientHelloMessage initialClientHelloMsg = null;
 
-        super(engine, context, enabledProtocols, true, true,
-            activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-            clientVerifyData, serverVerifyData, isDTLS);
+    ClientHandshakeContext(SSLContextImpl sslContext,
+            TransportContext conContext) throws IOException {
+        super(sslContext, conContext);
     }
 
-    /*
-     * 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.
-     */
     @Override
-    void processMessage(byte type, int messageLen) throws IOException {
-        // check the handshake state
-        List<Byte> ignoredOptStates = handshakeState.check(type);
-
-        // If the state machine has skipped over certificate status
-        // and stapling was enabled, we need to check the chain immediately
-        // because it was deferred, waiting for CertificateStatus.
-        if (staplingActive && ignoredOptStates.contains(
-                HandshakeMessage.ht_certificate_status)) {
-            checkServerCerts(deferredCerts);
-            serverKey = session.getPeerCertificates()[0].getPublicKey();
-        }
-
-        switch (type) {
-        case HandshakeMessage.ht_hello_request:
-            HelloRequest helloRequest = new HelloRequest(input);
-            handshakeState.update(helloRequest, resumingSession);
-            this.serverHelloRequest(helloRequest);
-            break;
-
-        case HandshakeMessage.ht_hello_verify_request:
-            if (!isDTLS) {
-                throw new SSLProtocolException(
-                    "hello_verify_request is not a SSL/TLS handshake message");
-            }
-
-            HelloVerifyRequest helloVerifyRequest =
-                        new HelloVerifyRequest(input, messageLen);
-            handshakeState.update(helloVerifyRequest, resumingSession);
-            this.helloVerifyRequest(helloVerifyRequest);
-            break;
-
-        case HandshakeMessage.ht_server_hello:
-            ServerHello serverHello = new ServerHello(input, messageLen);
-            this.serverHello(serverHello);
-
-            // This handshake state update needs the resumingSession value
-            // set by serverHello().
-            handshakeState.update(serverHello, resumingSession);
-            break;
-
-        case HandshakeMessage.ht_certificate:
-            if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
-                    || ClientKeyExchangeService.find(keyExchange.name) != null) {
-                // No external key exchange provider needs a cert now.
-                fatalSE(Alerts.alert_unexpected_message,
-                    "unexpected server cert chain");
-                // NOTREACHED
-            }
-            CertificateMsg certificateMsg = new CertificateMsg(input);
-            handshakeState.update(certificateMsg, resumingSession);
-            this.serverCertificate(certificateMsg);
-            if (!staplingActive) {
-                // If we are not doing stapling, we can set serverKey right
-                // away.  Otherwise, we will wait until verification of the
-                // chain has completed after CertificateStatus;
-                serverKey = session.getPeerCertificates()[0].getPublicKey();
-            }
-            break;
-
-        case HandshakeMessage.ht_certificate_status:
-            CertificateStatus certStatusMsg = new CertificateStatus(input);
-            handshakeState.update(certStatusMsg, resumingSession);
-            this.certificateStatus(certStatusMsg);
-            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 {
-                    RSA_ServerKeyExchange rsaSrvKeyExchange =
-                                    new RSA_ServerKeyExchange(input);
-                    handshakeState.update(rsaSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(rsaSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("Server key", e);
-                }
-                break;
-            case K_DH_ANON:
-                try {
-                    DH_ServerKeyExchange dhSrvKeyExchange =
-                            new DH_ServerKeyExchange(input, protocolVersion);
-                    handshakeState.update(dhSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(dhSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("Server key", e);
-                }
-                break;
-            case K_DHE_DSS:
-            case K_DHE_RSA:
-                try {
-                    DH_ServerKeyExchange dhSrvKeyExchange =
-                        new DH_ServerKeyExchange(
-                            input, serverKey,
-                            clnt_random.random_bytes, svr_random.random_bytes,
-                            messageLen,
-                            getLocalSupportedSignAlgs(), protocolVersion);
-                    handshakeState.update(dhSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(dhSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("Server key", e);
-                }
-                break;
-            case K_ECDHE_ECDSA:
-            case K_ECDHE_RSA:
-            case K_ECDH_ANON:
-                try {
-                    ECDH_ServerKeyExchange ecdhSrvKeyExchange =
-                        new ECDH_ServerKeyExchange
-                            (input, serverKey, clnt_random.random_bytes,
-                            svr_random.random_bytes,
-                            getLocalSupportedSignAlgs(), protocolVersion);
-                    handshakeState.update(ecdhSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(ecdhSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("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);
-            default:
-                throw new SSLProtocolException(
-                    "unsupported or unexpected 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 (ClientKeyExchangeService.find(keyExchange.name) != null) {
-                // No external key exchange provider needs a cert now.
-                throw new SSLHandshakeException(
-                    "Client certificate requested for "+
-                    "external cipher suite: " + keyExchange);
-            }
-            certRequest = new CertificateRequest(input, protocolVersion);
-            if (debug != null && Debug.isOn("handshake")) {
-                certRequest.print(System.out);
-            }
-            handshakeState.update(certRequest, resumingSession);
-
-            if (protocolVersion.useTLS12PlusSpec()) {
-                Collection<SignatureAndHashAlgorithm> peerSignAlgs =
-                                        certRequest.getSignAlgorithms();
-                if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
-                    throw new SSLHandshakeException(
-                        "No peer supported signature algorithms");
-                }
-
-                Collection<SignatureAndHashAlgorithm> supportedPeerSignAlgs =
-                    SignatureAndHashAlgorithm.getSupportedAlgorithms(
-                            algorithmConstraints, 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:
-            ServerHelloDone serverHelloDone = new ServerHelloDone(input);
-            handshakeState.update(serverHelloDone, resumingSession);
-            this.serverHelloDone(serverHelloDone);
-
-            break;
-
-        case HandshakeMessage.ht_finished:
-            Finished serverFinished =
-                    new Finished(protocolVersion, input, cipherSuite);
-            handshakeState.update(serverFinished, resumingSession);
-            this.serverFinished(serverFinished);
-
-            break;
-
-        default:
-            throw new SSLProtocolException(
-                "Illegal client handshake msg, " + 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 (!clientHelloDelivered) {
-            if (!secureRenegotiation && !allowUnsafeRenegotiation) {
-                // renegotiation is not allowed.
-                if (activeProtocolVersion.useTLS10PlusSpec()) {
-                    // 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();
-            }
-        }
-    }
-
-    private void helloVerifyRequest(
-            HelloVerifyRequest mesg) throws IOException {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        //
-        // Note that HelloVerifyRequest.server_version is used solely to
-        // indicate packet formatting, and not as part of version negotiation.
-        // Need not to check version values match for HelloVerifyRequest
-        // message.
-        //
-        initialClientHelloMsg.cookie = mesg.cookie.clone();
-
-        if (debug != null && Debug.isOn("handshake")) {
-            initialClientHelloMsg.print(System.out);
-        }
-
-        // deliver the ClientHello message with cookie
-        initialClientHelloMsg.write(output);
-        handshakeState.update(initialClientHelloMsg, resumingSession);
-    }
-
-    /*
-     * 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 {
-        // Dispose the reserved ClientHello message (if exists).
-        initialClientHelloMsg = null;
-
-        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 (!MessageDigest.isEqual(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.useTLS12PlusSpec()) {
-            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
-                ClientKeyExchangeService p =
-                        ClientKeyExchangeService.find(
-                                sessionSuite.keyExchange.name);
-                if (p != null) {
-                    Principal localPrincipal = session.getLocalPrincipal();
-
-                    if (p.isRelated(true, getAccSE(), localPrincipal)) {
-                        if (debug != null && Debug.isOn("session"))
-                            System.out.println("Subject identity is same");
-                    } else {
-                        throw new SSLProtocolException(
-                                "Server resumed session with " +
-                                "wrong subject identity or no subject");
-                    }
-                }
-
-                // looks fine; resume it.
-                resumingSession = true;
-                calculateConnectionKeys(session.getMasterSecret());
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% Server resumed " + session);
-                }
-            } else {
-                // we wanted to resume, but the server refused
-                //
-                // Invalidate the session for initial handshake in case
-                // of reusing next time.
-                if (isInitialHandshake) {
-                    session.invalidate();
-                }
-                session = null;
-                if (!enableNewSession) {
-                    throw new SSLException("New session creation is disabled");
-                }
-            }
-        }
-
-        // check the "max_fragment_length" extension
-        MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
-                mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
-        if (maxFragLenExt != null) {
-            if ((requestedMFLength == -1) ||
-                    maxFragLenExt.getMaxFragLen() != requestedMFLength) {
-                // If the client did not request this extension, or the
-                // response value is different from the length it requested,
-                // abort the handshake with a fatal illegal_parameter alert.
-                fatalSE(Alerts.alert_illegal_parameter,
-                        "Failed to negotiate the max_fragment_length");
-            }
-        } else if (!resumingSession) {
-            // no "max_fragment_length" extension
-            requestedMFLength = -1;
-        }   // Otherwise, using the value negotiated during the original
-            // session initiation
-
-        // check the "extended_master_secret" extension
-        ExtendedMasterSecretExtension extendedMasterSecretExt =
-                (ExtendedMasterSecretExtension)mesg.extensions.get(
-                        ExtensionType.EXT_EXTENDED_MASTER_SECRET);
-        if (extendedMasterSecretExt != null) {
-            // Is it the expected server extension?
-            if (!useExtendedMasterSecret ||
-                    !mesgVersion.useTLS10PlusSpec() || !requestedToUseEMS) {
-                fatalSE(Alerts.alert_unsupported_extension,
-                        "Server sent the extended_master_secret " +
-                        "extension improperly");
-            }
-
-            // For abbreviated handshake, if the original session did not use
-            // the "extended_master_secret" extension but the new ServerHello
-            // contains the extension, the client MUST abort the handshake.
-            if (resumingSession && (session != null) &&
-                    !session.getUseExtendedMasterSecret()) {
-                fatalSE(Alerts.alert_unsupported_extension,
-                        "Server sent an unexpected extended_master_secret " +
-                        "extension on session resumption");
-            }
-        } else {
-            if (useExtendedMasterSecret && !allowLegacyMasterSecret) {
-                // For full handshake, if a client receives a ServerHello
-                // without the extension, it SHOULD abort the handshake if
-                // it does not wish to interoperate with legacy servers.
-                fatalSE(Alerts.alert_handshake_failure,
-                    "Extended Master Secret extension is required");
-            }
-
-            if (resumingSession && (session != null)) {
-                if (session.getUseExtendedMasterSecret()) {
-                    // For abbreviated handshake, if the original session used
-                    // the "extended_master_secret" extension but the new
-                    // ServerHello does not contain the extension, the client
-                    // MUST abort the handshake.
-                    fatalSE(Alerts.alert_handshake_failure,
-                            "Missing Extended Master Secret extension " +
-                            "on session resumption");
-                } else if (useExtendedMasterSecret && !allowLegacyResumption) {
-                    // Unlikely, abbreviated handshake should be discarded.
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Extended Master Secret extension is required");
-                }
-            }
-        }
-
-        // check the ALPN extension
-        ALPNExtension serverHelloALPN =
-            (ALPNExtension) mesg.extensions.get(ExtensionType.EXT_ALPN);
-
-        if (serverHelloALPN != null) {
-            // Check whether an ALPN extension was sent in ClientHello message
-            if (!alpnActive) {
-                fatalSE(Alerts.alert_unsupported_extension,
-                    "Server sent " + ExtensionType.EXT_ALPN +
-                    " extension when not requested by client");
-            }
-
-            List<String> protocols = serverHelloALPN.getPeerAPs();
-            // Only one application protocol name should be present
-            String p;
-            if ((protocols.size() == 1) &&
-                    !((p = protocols.get(0)).isEmpty())) {
-                int i;
-                for (i = 0; i < localApl.length; i++) {
-                    if (localApl[i].equals(p)) {
-                        break;
-                    }
-                }
-                if (i == localApl.length) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Server has selected an application protocol name " +
-                        "which was not offered by the client: " + p);
-                }
-                applicationProtocol = p;
-            } else {
-                fatalSE(Alerts.alert_handshake_failure,
-                    "Incorrect data in ServerHello " + ExtensionType.EXT_ALPN +
-                    " message");
-            }
-        } else {
-            applicationProtocol = "";
-        }
-
-        if (resumingSession && session != null) {
-            setHandshakeSessionSE(session);
-            // Reserve the handshake state if this is a session-resumption
-            // abbreviated initial handshake.
-            if (isInitialHandshake) {
-                session.setAsSessionResumption(true);
-            }
-
+    void kickstart() throws IOException {
+        if (kickstartMessageDelivered) {
             return;
         }
 
-        // check extensions
-        for (HelloExtension ext : mesg.extensions.list()) {
-            ExtensionType type = ext.type;
-            if (type == ExtensionType.EXT_SERVER_NAME) {
-                serverNamesAccepted = true;
-            } else if (type == ExtensionType.EXT_STATUS_REQUEST ||
-                    type == ExtensionType.EXT_STATUS_REQUEST_V2) {
-                // Only enable the stapling feature if the client asserted
-                // these extensions.
-                if (sslContext.isStaplingEnabled(true)) {
-                    staplingActive = true;
-                } else {
-                    fatalSE(Alerts.alert_unexpected_message, "Server set " +
-                            type + " extension when not requested by client");
-                }
-            } else if ((type != ExtensionType.EXT_SUPPORTED_GROUPS)
-                    && (type != ExtensionType.EXT_EC_POINT_FORMATS)
-                    && (type != ExtensionType.EXT_SERVER_NAME)
-                    && (type != ExtensionType.EXT_ALPN)
-                    && (type != ExtensionType.EXT_RENEGOTIATION_INFO)
-                    && (type != ExtensionType.EXT_STATUS_REQUEST)
-                    && (type != ExtensionType.EXT_STATUS_REQUEST_V2)
-                    && (type != ExtensionType.EXT_EXTENDED_MASTER_SECRET)) {
-                // Note: Better to check client requested extensions rather
-                // than all supported extensions.
-                fatalSE(Alerts.alert_unsupported_extension,
-                    "Server sent an unsupported extension: " + type);
-            }
-        }
-
-        // Create a new session, we need to do the full handshake
-        session = new SSLSessionImpl(protocolVersion, cipherSuite,
-                            getLocalSupportedSignAlgs(),
-                            mesg.sessionId, getHostSE(), getPortSE(),
-                            (extendedMasterSecretExt != null));
-        session.setRequestedServerNames(requestedServerNames);
-        session.setNegotiatedMaxFragSize(requestedMFLength);
-        session.setMaximumPacketSize(maximumPacketSize);
-        setHandshakeSessionSE(session);
-        if (debug != null && Debug.isOn("handshake")) {
-            System.out.println("** " + cipherSuite);
-        }
-    }
-
-    /*
-     * 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.
-     */
-    private void serverKeyExchange(RSA_ServerKeyExchange mesg)
-            throws IOException, GeneralSecurityException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        if (!mesg.verify(serverKey, clnt_random, svr_random)) {
-            fatalSE(Alerts.alert_handshake_failure,
-                "server key exchange invalid");
-            // NOTREACHED
-        }
-        ephemeralServerKey = mesg.getPublicKey();
-
-        // check constraints of RSA PublicKey
-        if (!algorithmConstraints.permits(
-            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), ephemeralServerKey)) {
-
-            throw new SSLHandshakeException("RSA ServerKeyExchange " +
-                    "does not comply to algorithm constraints");
-        }
-    }
-
-    /*
-     * 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).
-     *
-     * Note per section 3 of RFC 7919, if the server is not compatible with
-     * FFDHE specification, the client MAY decide to continue the connection
-     * if the selected DHE group is acceptable under local policy, or it MAY
-     * decide to terminate the connection with a fatal insufficient_security
-     * (71) alert.  The algorithm constraints mechanism is JDK local policy
-     * used for additional DHE parameters checking.  So this implementation
-     * does not check the server compatibility and just pass to the local
-     * algorithm constraints checking.  The client will continue the
-     * connection if the server selected DHE group is acceptable by the
-     * specified algorithm constraints.
-     */
-    private void serverKeyExchange(DH_ServerKeyExchange mesg)
-            throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        dh = new DHCrypt(mesg.getModulus(), mesg.getBase(),
-                                            sslContext.getSecureRandom());
-        serverDH = mesg.getServerPublicKey();
-
-        // check algorithm constraints
-        dh.checkConstraints(algorithmConstraints, serverDH);
-    }
-
-    private void serverKeyExchange(ECDH_ServerKeyExchange mesg)
-            throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        ECPublicKey key = mesg.getPublicKey();
-        ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom());
-        ephemeralServerKey = key;
-
-        // check constraints of EC PublicKey
-        if (!algorithmConstraints.permits(
-            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), ephemeralServerKey)) {
-
-            throw new SSLHandshakeException("ECDH ServerKeyExchange " +
-                    "does not comply to algorithm constraints");
-        }
-    }
-
-    /*
-     * The server's "Hello Done" message is the client's sign that
-     * it's time to do all the hard work.
-     */
-    private void serverHelloDone(ServerHelloDone mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        /*
-         * FIRST ... if requested, send an appropriate Certificate chain
-         * to authenticate the client, and remember the associated private
-         * key to sign the CertificateVerify message.
-         */
-        PrivateKey signingKey = null;
-
-        if (certRequest != null) {
-            X509ExtendedKeyManager km = sslContext.getX509KeyManager();
-
-            ArrayList<String> keytypesTmp = new ArrayList<>(4);
-
-            for (int i = 0; i < certRequest.types.length; i++) {
-                String typeName;
-
-                switch (certRequest.types[i]) {
-                    case CertificateRequest.cct_rsa_sign:
-                        typeName = "RSA";
-                        break;
-
-                    case CertificateRequest.cct_dss_sign:
-                        typeName = "DSA";
-                            break;
-
-                    case CertificateRequest.cct_ecdsa_sign:
-                        // ignore if we do not have EC crypto available
-                        typeName = JsseJce.isEcAvailable() ? "EC" : null;
-                        break;
-
-                    // Fixed DH/ECDH client authentication not supported
-                    //
-                    // case CertificateRequest.cct_rsa_fixed_dh:
-                    // case CertificateRequest.cct_dss_fixed_dh:
-                    // case CertificateRequest.cct_rsa_fixed_ecdh:
-                    // case CertificateRequest.cct_ecdsa_fixed_ecdh:
-                    //
-                    // Any other values (currently not used in TLS)
-                    //
-                    // case CertificateRequest.cct_rsa_ephemeral_dh:
-                    // case CertificateRequest.cct_dss_ephemeral_dh:
-                    default:
-                        typeName = null;
-                        break;
-                }
-
-                if ((typeName != null) && (!keytypesTmp.contains(typeName))) {
-                    keytypesTmp.add(typeName);
-                }
-            }
-
-            String alias = null;
-            int keytypesTmpSize = keytypesTmp.size();
-            if (keytypesTmpSize != 0) {
-                String[] keytypes =
-                        keytypesTmp.toArray(new String[keytypesTmpSize]);
-
-                if (conn != null) {
-                    alias = km.chooseClientAlias(keytypes,
-                        certRequest.getAuthorities(), conn);
-                } else {
-                    alias = km.chooseEngineClientAlias(keytypes,
-                        certRequest.getAuthorities(), engine);
-                }
-            }
-
-            CertificateMsg m1 = null;
-            if (alias != null) {
-                X509Certificate[] certs = km.getCertificateChain(alias);
-                if ((certs != null) && (certs.length != 0)) {
-                    PublicKey publicKey = certs[0].getPublicKey();
-                    if (publicKey != null) {
-                        m1 = new CertificateMsg(certs);
-                        signingKey = km.getPrivateKey(alias);
-                        session.setLocalPrivateKey(signingKey);
-                        session.setLocalCertificates(certs);
-                    }
-                }
-            }
-            if (m1 == null) {
-                //
-                // No appropriate cert was found ... report this to the
-                // server.  For SSLv3, send the no_certificate alert;
-                // TLS uses an empty cert chain instead.
-                //
-                if (protocolVersion.useTLS10PlusSpec()) {
-                    m1 = new CertificateMsg(new X509Certificate [0]);
-                } else {
-                    warningSE(Alerts.alert_no_certificate);
-                }
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println(
-                        "Warning: no suitable certificate found - " +
-                        "continuing without client authentication");
-                }
-            }
-
-            //
-            // At last ... send any client certificate chain.
-            //
-            if (m1 != null) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    m1.print(System.out);
-                }
-                m1.write(output);
-                handshakeState.update(m1, resumingSession);
-            }
-        }
-
-        /*
-         * SECOND ... send the client key exchange message.  The
-         * procedure used is a function of the cipher suite selected;
-         * one is always needed.
-         */
-        HandshakeMessage m2;
-
-        switch (keyExchange) {
-
-        case K_RSA:
-        case K_RSA_EXPORT:
-            if (serverKey == null) {
-                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.
-             */
-            PublicKey key;
-            if (keyExchange == K_RSA) {
-                key = serverKey;
-            } else {    // K_RSA_EXPORT
-                if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
-                    // extraneous ephemeralServerKey check done
-                    // above in processMessage()
-                    key = serverKey;
-                } else {
-                    if (ephemeralServerKey == null) {
-                        throw new SSLProtocolException("Server did not send" +
-                            " a RSA_EXPORT Server Key Exchange message");
-                    }
-                    key = ephemeralServerKey;
-                }
-            }
-
-            m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion,
-                                sslContext.getSecureRandom(), key);
-            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:
-            if (dh == null) {
-                throw new SSLProtocolException
-                    ("Server did not send a DH Server Key Exchange message");
-            }
-            m2 = new DHClientKeyExchange(dh.getPublicKey());
-            break;
-        case K_ECDHE_RSA:
-        case K_ECDHE_ECDSA:
-        case K_ECDH_ANON:
-            if (ecdh == null) {
-                throw new SSLProtocolException
-                    ("Server did not send a ECDH Server Key Exchange message");
-            }
-            m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
-            break;
-        case K_ECDH_RSA:
-        case K_ECDH_ECDSA:
-            if (serverKey == null) {
-                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");
-            }
-            ECParameterSpec params = ((ECPublicKey)serverKey).getParams();
-            ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
-            m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
-            break;
-        default:
-            ClientKeyExchangeService p =
-                    ClientKeyExchangeService.find(keyExchange.name);
-            if (p == null) {
-                // somethings very wrong
-                throw new RuntimeException
-                        ("Unsupported key exchange: " + keyExchange);
-            }
-            String sniHostname = null;
-            for (SNIServerName serverName : requestedServerNames) {
-                if (serverName instanceof SNIHostName) {
-                    sniHostname = ((SNIHostName) serverName).getAsciiName();
-                    break;
-                }
-            }
-
-            ClientKeyExchange exMsg = null;
-            if (sniHostname != null) {
-                // use first requested SNI hostname
-                try {
-                    exMsg = p.createClientExchange(
-                            sniHostname, getAccSE(), protocolVersion,
-                            sslContext.getSecureRandom());
-                } catch(IOException e) {
-                    if (serverNamesAccepted) {
-                        // server accepted requested SNI hostname,
-                        // so it must be used
-                        throw e;
-                    }
-                    // fallback to using hostname
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println(
-                            "Warning, cannot use Server Name Indication: "
-                                + e.getMessage());
-                    }
-                }
-            }
-
-            if (exMsg == null) {
-                String hostname = getHostSE();
-                if (hostname == null) {
-                    throw new IOException("Hostname is required" +
-                        " to use " + keyExchange + " key exchange");
-                }
-                exMsg = p.createClientExchange(
-                        hostname, getAccSE(), protocolVersion,
-                        sslContext.getSecureRandom());
-            }
-
-            // Record the principals involved in exchange
-            session.setPeerPrincipal(exMsg.getPeerPrincipal());
-            session.setLocalPrincipal(exMsg.getLocalPrincipal());
-            m2 = exMsg;
-            break;
-        }
-        if (debug != null && Debug.isOn("handshake")) {
-            m2.print(System.out);
-        }
-        m2.write(output);
-        handshakeState.update(m2, resumingSession);
-
-        /*
-         * 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.
-         */
-        output.flush();
-
-        /*
-         * 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.
-         */
-        SecretKey preMasterSecret;
-        switch (keyExchange) {
-        case K_RSA:
-        case K_RSA_EXPORT:
-            preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
-            break;
-        case K_DHE_RSA:
-        case K_DHE_DSS:
-        case K_DH_ANON:
-            preMasterSecret = dh.getAgreedSecret(serverDH, true);
-            break;
-        case K_ECDHE_RSA:
-        case K_ECDHE_ECDSA:
-        case K_ECDH_ANON:
-            preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey);
-            break;
-        case K_ECDH_RSA:
-        case K_ECDH_ECDSA:
-            preMasterSecret = ecdh.getAgreedSecret(serverKey);
-            break;
-        default:
-            if (ClientKeyExchangeService.find(keyExchange.name) != null) {
-                preMasterSecret =
-                        ((ClientKeyExchange) m2).clientKeyExchange();
-            } else {
-                throw new IOException("Internal error: unknown key exchange "
-                        + keyExchange);
-            }
-        }
-
-        calculateKeys(preMasterSecret, null);
-
-        /*
-         * 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) {
-            CertificateVerify m3;
-            try {
-                SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
-                if (protocolVersion.useTLS12PlusSpec()) {
-                    preferableSignatureAlgorithm =
-                        SignatureAndHashAlgorithm.getPreferableAlgorithm(
-                            getPeerSupportedSignAlgs(),
-                            signingKey.getAlgorithm(), signingKey);
-
-                    if (preferableSignatureAlgorithm == null) {
-                        throw new SSLHandshakeException(
-                            "No supported signature algorithm");
-                    }
-
-                    String hashAlg =
-                        SignatureAndHashAlgorithm.getHashAlgorithmName(
-                                preferableSignatureAlgorithm);
-                    if (hashAlg == null || hashAlg.length() == 0) {
-                        throw new SSLHandshakeException(
-                                "No supported hash algorithm");
-                    }
-                }
-
-                m3 = new CertificateVerify(protocolVersion, handshakeHash,
-                    signingKey, session.getMasterSecret(),
-                    sslContext.getSecureRandom(),
-                    preferableSignatureAlgorithm);
-            } catch (GeneralSecurityException e) {
-                fatalSE(Alerts.alert_handshake_failure,
-                    "Error signing certificate verify", e);
-                // NOTREACHED, make compiler happy
-                m3 = null;
-            }
-            if (debug != null && Debug.isOn("handshake")) {
-                m3.print(System.out);
-            }
-            m3.write(output);
-            handshakeState.update(m3, resumingSession);
-            output.flush();
-        }
-
-        /*
-         * OK, that's that!
-         */
-        sendChangeCipherAndFinish(false);
-
-        // expecting the final ChangeCipherSpec and Finished messages
-        expectingFinishFlightSE();
-    }
-
-
-    /*
-     * "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.
-     */
-    private void serverFinished(Finished mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        boolean verified = mesg.verify(handshakeHash, Finished.SERVER,
-            session.getMasterSecret());
-
-        if (!verified) {
-            fatalSE(Alerts.alert_illegal_parameter,
-                       "server 'finished' message doesn't verify");
-            // NOTREACHED
-        }
-
-        /*
-         * save server verify data for secure renegotiation
-         */
-        if (secureRenegotiation) {
-            serverVerifyData = mesg.getVerifyData();
-        }
-
-        /*
-         * Reset the handshake state if this is not an initial handshake.
-         */
-        if (!isInitialHandshake) {
-            session.setAsSessionResumption(false);
-        }
-
-        /*
-         * 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);
-        } else {
-            handshakeFinished = true;
-        }
-        session.setLastAccessedTime(System.currentTimeMillis());
-
-        if (!resumingSession) {
-            if (session.isRejoinable()) {
-                ((SSLSessionContextImpl) sslContext
-                        .engineGetClientSessionContext())
-                        .put(session);
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% Cached client session: " + session);
-                }
-            } else if (debug != null && Debug.isOn("session")) {
-                System.out.println(
-                    "%% 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.
-     */
-    private void sendChangeCipherAndFinish(boolean finishedTag)
-            throws IOException {
-
-        // Reload if this message has been reserved.
-        handshakeHash.reload();
-
-        Finished mesg = new Finished(protocolVersion, handshakeHash,
-            Finished.CLIENT, session.getMasterSecret(), cipherSuite);
-
-        /*
-         * 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.
-         */
-        sendChangeCipherSpec(mesg, finishedTag);
-
-        /*
-         * save client verify data for secure renegotiation
-         */
-        if (secureRenegotiation) {
-            clientVerifyData = mesg.getVerifyData();
-        }
-    }
-
-
-    /*
-     * Returns a ClientHello message to kickstart renegotiations
-     */
-    @Override
-    HandshakeMessage getKickstartMessage() throws SSLException {
-        // session ID of the ClientHello message
-        SessionId sessionId = SSLSessionImpl.nullSession.getSessionId();
-
-        // a list of cipher suites sent by the client
-        CipherSuiteList cipherSuites = getActiveCipherSuites();
-
-        // set the max protocol version this client is supporting.
-        maxProtocolVersion = protocolVersion;
-
-        //
-        // Try to resume an existing session.  This might be mandatory,
-        // given certain API options.
-        //
-        session = ((SSLSessionContextImpl)sslContext
-                        .engineGetClientSessionContext())
-                        .get(getHostSE(), getPortSE());
-        if (debug != null && Debug.isOn("session")) {
-            if (session != null) {
-                System.out.println("%% Client cached "
-                    + session
-                    + (session.isRejoinable() ? "" : " (not rejoinable)"));
-            } else {
-                System.out.println("%% No cached client session");
-            }
-        }
-        if (session != null) {
-            // If unsafe server certificate change is not allowed, reserve
-            // current server certificates if the previous handshake is a
-            // session-resumption abbreviated initial handshake.
-            if (!allowUnsafeServerCertChange && session.isSessionResumption()) {
-                try {
-                    // If existing, peer certificate chain cannot be null.
-                    reservedServerCerts =
-                        (X509Certificate[])session.getPeerCertificates();
-                } catch (SSLPeerUnverifiedException puve) {
-                    // Maybe not certificate-based, ignore the exception.
-                }
-            }
-
-            if (!session.isRejoinable()) {
-                session = null;
-            }
-        }
-
-        if (session != null) {
-            CipherSuite sessionSuite = session.getSuite();
-            ProtocolVersion sessionVersion = session.getProtocolVersion();
-            if (isNegotiable(sessionSuite) == false) {
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% can't resume, unavailable cipher");
-                }
-                session = null;
-            }
-
-            if ((session != null) && !isNegotiable(sessionVersion)) {
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% can't resume, protocol disabled");
-                }
-                session = null;
-            }
-
-            if ((session != null) && useExtendedMasterSecret) {
-                boolean isTLS10Plus = sessionVersion.useTLS10PlusSpec();
-                if (isTLS10Plus && !session.getUseExtendedMasterSecret()) {
-                    if (!allowLegacyResumption) {
-                        // perform full handshake instead
-                        //
-                        // The client SHOULD NOT offer an abbreviated handshake
-                        // to resume a session that does not use an extended
-                        // master secret.  Instead, it SHOULD offer a full
-                        // handshake.
-                        session = null;
-                    }
-                }
-
-                if ((session != null) && !allowUnsafeServerCertChange) {
-                    // It is fine to move on with abbreviate handshake if
-                    // endpoint identification is enabled.
-                    String identityAlg = getEndpointIdentificationAlgorithmSE();
-                    if ((identityAlg == null || identityAlg.length() == 0)) {
-                        if (isTLS10Plus) {
-                            if (!session.getUseExtendedMasterSecret()) {
-                                // perform full handshake instead
-                                session = null;
-                            }   // Otherwise, use extended master secret.
-                        } else {
-                            // The extended master secret extension does not
-                            // apply to SSL 3.0.  Perform a full handshake
-                            // instead.
-                            //
-                            // Note that the useExtendedMasterSecret is
-                            // extended to protect SSL 3.0 connections,
-                            // by discarding abbreviate handshake.
-                            session = null;
-                        }
-                    }
-                }
-            }
-
-            if (session != null) {
-                if (debug != null) {
-                    if (Debug.isOn("handshake") || Debug.isOn("session")) {
-                        System.out.println("%% Try resuming " + session
-                            + " from port " + getLocalPortSE());
-                    }
-                }
-
-                sessionId = session.getSessionId();
-                maxProtocolVersion = sessionVersion;
-
-                // 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
-                setVersion(sessionVersion);
-            }
-
-            /*
-             * Force use of the previous session ciphersuite, and
-             * add the SCSV if enabled.
-             */
-            if (!enableNewSession) {
-                if (session == null) {
-                    throw new SSLHandshakeException(
-                        "Can't reuse existing SSL client session");
-                }
-
-                Collection<CipherSuite> cipherList = new ArrayList<>(2);
-                cipherList.add(sessionSuite);
-                if (!secureRenegotiation &&
-                        cipherSuites.contains(CipherSuite.C_SCSV)) {
-                    cipherList.add(CipherSuite.C_SCSV);
-                }   // otherwise, renegotiation_info extension will be used
-
-                cipherSuites = new CipherSuiteList(cipherList);
-            }
-        }
-
-        if (session == null && !enableNewSession) {
-            throw new SSLHandshakeException("No existing session to resume");
-        }
-
-        // exclude SCSV for secure renegotiation
-        if (secureRenegotiation && cipherSuites.contains(CipherSuite.C_SCSV)) {
-            Collection<CipherSuite> cipherList =
-                        new ArrayList<>(cipherSuites.size() - 1);
-            for (CipherSuite suite : cipherSuites.collection()) {
-                if (suite != CipherSuite.C_SCSV) {
-                    cipherList.add(suite);
-                }
-            }
-
-            cipherSuites = new CipherSuiteList(cipherList);
-        }
-
-        // make sure there is a negotiable cipher suite.
-        boolean negotiable = false;
-        for (CipherSuite suite : cipherSuites.collection()) {
-            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
-        ClientHello clientHelloMessage = new ClientHello(
-                sslContext.getSecureRandom(), maxProtocolVersion,
-                sessionId, cipherSuites, isDTLS);
-
-        // Add named groups extension for ECDHE and FFDHE if necessary.
-        SupportedGroupsExtension sge =
-                SupportedGroupsExtension.createExtension(
-                        algorithmConstraints,
-                        cipherSuites, enableFFDHE);
-        if (sge != null) {
-            clientHelloMessage.extensions.add(sge);
-            // Add elliptic point format extensions
-            if (cipherSuites.contains(NamedGroupType.NAMED_GROUP_ECDHE)) {
-                clientHelloMessage.extensions.add(
-                    EllipticPointFormatsExtension.DEFAULT);
-            }
-        }
-
-        // add signature_algorithm extension
-        if (maxProtocolVersion.useTLS12PlusSpec()) {
-            // we will always send the signature_algorithm extension
-            Collection<SignatureAndHashAlgorithm> localSignAlgs =
-                                                getLocalSupportedSignAlgs();
-            if (localSignAlgs.isEmpty()) {
-                throw new SSLHandshakeException(
-                            "No supported signature algorithm");
-            }
-
-            clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs);
-        }
-
-        // add Extended Master Secret extension
-        if (useExtendedMasterSecret && maxProtocolVersion.useTLS10PlusSpec()) {
-            if ((session == null) || session.getUseExtendedMasterSecret()) {
-                clientHelloMessage.addExtendedMasterSecretExtension();
-                requestedToUseEMS = true;
-            }
-        }
-
-        // add server_name extension
-        if (enableSNIExtension) {
-            if (session != null) {
-                requestedServerNames = session.getRequestedServerNames();
-            } else {
-                requestedServerNames = serverNames;
-            }
-
-            if (!requestedServerNames.isEmpty()) {
-                clientHelloMessage.addSNIExtension(requestedServerNames);
-            }
-        }
-
-        // add max_fragment_length extension
-        if (enableMFLExtension) {
-            if (session != null) {
-                // The same extension should be sent for resumption.
-                requestedMFLength = session.getNegotiatedMaxFragSize();
-            } else if (maximumPacketSize != 0) {
-                // Maybe we can calculate the fragment size more accurate
-                // by condering the enabled cipher suites in the future.
-                requestedMFLength = maximumPacketSize;
-                if (isDTLS) {
-                    requestedMFLength -= DTLSRecord.maxPlaintextPlusSize;
-                } else {
-                    requestedMFLength -= SSLRecord.maxPlaintextPlusSize;
-                }
-            } else {
-                // Need no max_fragment_length extension.
-                requestedMFLength = -1;
-            }
-
-            if ((requestedMFLength > 0) &&
-                MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) {
-
-                requestedMFLength =
-                        MaxFragmentLengthExtension.getValidMaxFragLen(
-                                                        requestedMFLength);
-                clientHelloMessage.addMFLExtension(requestedMFLength);
-            } else {
-                requestedMFLength = -1;
-            }
-        }
-
-        // Add status_request and status_request_v2 extensions
-        if (sslContext.isStaplingEnabled(true)) {
-            clientHelloMessage.addCertStatusReqListV2Extension();
-            clientHelloMessage.addCertStatusRequestExtension();
-        }
-
-        // Add ALPN extension
-        if (localApl != null && localApl.length > 0) {
-            clientHelloMessage.addALPNExtension(localApl);
-            alpnActive = true;
-        }
-
-        // reset the client random cookie
-        clnt_random = clientHelloMessage.clnt_random;
-
-        /*
-         * 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 ||
-                !cipherSuites.contains(CipherSuite.C_SCSV)) {
-            clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData);
-        }
-
-        if (isDTLS) {
-            // Cookie exchange need to reserve the initial ClientHello message.
-            initialClientHelloMsg = clientHelloMessage;
-        }
-
-        return clientHelloMessage;
-    }
-
-    /*
-     * Fault detected during handshake.
-     */
-    @Override
-    void handshakeAlert(byte description) throws SSLProtocolException {
-        String message = Alerts.alertDescription(description);
-
-        if (debug != null && Debug.isOn("handshake")) {
-            System.out.println("SSL - handshake alert: " + message);
-        }
-        throw new SSLProtocolException("handshake alert:  " + message);
-    }
-
-    /*
-     * 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.
-     */
-    private void serverCertificate(CertificateMsg mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        X509Certificate[] peerCerts = mesg.getCertificateChain();
-        if (peerCerts.length == 0) {
-            fatalSE(Alerts.alert_bad_certificate, "empty certificate chain");
-        }
-
-        // Allow server certificate change in client side during renegotiation
-        // after a session-resumption abbreviated initial handshake?
-        //
-        // DO NOT need to check allowUnsafeServerCertChange here.  We only
-        // reserve server certificates when allowUnsafeServerCertChange is
-        // flase.
-        //
-        // Allow server certificate change if it is negotiated to use the
-        // extended master secret.
-        if ((reservedServerCerts != null) &&
-                !session.getUseExtendedMasterSecret()) {
-            // It is not necessary to check the certificate update if endpoint
-            // identification is enabled.
-            String identityAlg = getEndpointIdentificationAlgorithmSE();
-            if ((identityAlg == null || identityAlg.length() == 0) &&
-                !isIdentityEquivalent(peerCerts[0], reservedServerCerts[0])) {
-
-                fatalSE(Alerts.alert_bad_certificate,
-                        "server certificate change is restricted " +
-                        "during renegotiation");
-            }
-        }
-
-        // ask the trust manager to verify the chain
-        if (staplingActive) {
-            // Defer the certificate check until after we've received the
-            // CertificateStatus message.  If that message doesn't come in
-            // immediately following this message we will execute the check
-            // directly from processMessage before any other SSL/TLS processing.
-            deferredCerts = peerCerts;
-        } else {
-            // We're not doing stapling, so perform the check right now
-            checkServerCerts(peerCerts);
-        }
-    }
-
-    /**
-     * If certificate status stapling has been enabled, the server will send
-     * one or more status messages to the client.
-     *
-     * @param mesg a {@code CertificateStatus} object built from the data
-     *      sent by the server.
-     *
-     * @throws IOException if any parsing errors occur.
-     */
-    private void certificateStatus(CertificateStatus mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        // Perform the certificate check using the deferred certificates
-        // and responses that we have obtained.
-        session.setStatusResponses(mesg.getResponses());
-        checkServerCerts(deferredCerts);
-    }
-
-    /*
-     * Whether the certificates can represent the same identity?
-     *
-     * The certificates can be used to represent the same identity:
-     *     1. If the subject alternative names of IP address are present in
-     *        both certificates, they should be identical; otherwise,
-     *     2. if the subject alternative names of DNS name are present in
-     *        both certificates, they should be identical; otherwise,
-     *     3. if the subject fields are present in both certificates, the
-     *        certificate subjects and issuers should be identical.
-     */
-    private static boolean isIdentityEquivalent(X509Certificate thisCert,
-            X509Certificate prevCert) {
-        if (thisCert.equals(prevCert)) {
-            return true;
-        }
-
-        // check subject alternative names
-        Collection<List<?>> thisSubjectAltNames = null;
-        try {
-            thisSubjectAltNames = thisCert.getSubjectAlternativeNames();
-        } catch (CertificateParsingException cpe) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println(
-                        "Attempt to obtain subjectAltNames extension failed!");
-            }
-        }
-
-        Collection<List<?>> prevSubjectAltNames = null;
-        try {
-            prevSubjectAltNames = prevCert.getSubjectAlternativeNames();
-        } catch (CertificateParsingException cpe) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println(
-                        "Attempt to obtain subjectAltNames extension failed!");
-            }
-        }
-
-        if ((thisSubjectAltNames != null) && (prevSubjectAltNames != null)) {
-            // check the iPAddress field in subjectAltName extension
-            Collection<String> thisSubAltIPAddrs =
-                        getSubjectAltNames(thisSubjectAltNames, ALTNAME_IP);
-            Collection<String> prevSubAltIPAddrs =
-                        getSubjectAltNames(prevSubjectAltNames, ALTNAME_IP);
-            if ((thisSubAltIPAddrs != null) && (prevSubAltIPAddrs != null) &&
-                (isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs))) {
-
-                return true;
-            }
-
-            // check the dNSName field in subjectAltName extension
-            Collection<String> thisSubAltDnsNames =
-                        getSubjectAltNames(thisSubjectAltNames, ALTNAME_DNS);
-            Collection<String> prevSubAltDnsNames =
-                        getSubjectAltNames(prevSubjectAltNames, ALTNAME_DNS);
-            if ((thisSubAltDnsNames != null) && (prevSubAltDnsNames != null) &&
-                (isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames))) {
-
-                return true;
-            }
-        }
-
-        // check the certificate subject and issuer
-        X500Principal thisSubject = thisCert.getSubjectX500Principal();
-        X500Principal prevSubject = prevCert.getSubjectX500Principal();
-        X500Principal thisIssuer = thisCert.getIssuerX500Principal();
-        X500Principal prevIssuer = prevCert.getIssuerX500Principal();
-        if (!thisSubject.getName().isEmpty() &&
-                !prevSubject.getName().isEmpty() &&
-                thisSubject.equals(prevSubject) &&
-                thisIssuer.equals(prevIssuer)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /*
-     * Returns the subject alternative name of the specified type in the
-     * subjectAltNames extension of a certificate.
-     *
-     * Note that only those subjectAltName types that use String data
-     * should be passed into this function.
-     */
-    private static Collection<String> getSubjectAltNames(
-            Collection<List<?>> subjectAltNames, int type) {
-
-        HashSet<String> subAltDnsNames = null;
-        for (List<?> subjectAltName : subjectAltNames) {
-            int subjectAltNameType = (Integer)subjectAltName.get(0);
-            if (subjectAltNameType == type) {
-                String subAltDnsName = (String)subjectAltName.get(1);
-                if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) {
-                    if (subAltDnsNames == null) {
-                        subAltDnsNames =
-                                new HashSet<>(subjectAltNames.size());
-                    }
-                    subAltDnsNames.add(subAltDnsName);
-                }
-            }
-        }
-
-        return subAltDnsNames;
-    }
-
-    private static boolean isEquivalent(Collection<String> thisSubAltNames,
-            Collection<String> prevSubAltNames) {
-
-        for (String thisSubAltName : thisSubAltNames) {
-            for (String prevSubAltName : prevSubAltNames) {
-                // Only allow the exactly match.  Check no wildcard character.
-                if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Perform client-side checking of server certificates.
-     *
-     * @param certs an array of {@code X509Certificate} objects presented
-     *      by the server in the ServerCertificate message.
-     *
-     * @throws IOException if a failure occurs during validation or
-     *      the trust manager associated with the {@code SSLContext} is not
-     *      an {@code X509ExtendedTrustManager}.
-     */
-    private void checkServerCerts(X509Certificate[] certs)
-            throws IOException {
-        X509TrustManager tm = sslContext.getX509TrustManager();
-
-        // find out the key exchange algorithm used
-        // use "RSA" for non-ephemeral "RSA_EXPORT"
-        String keyExchangeString;
-        if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) {
-            keyExchangeString = K_RSA.name;
-        } else {
-            keyExchangeString = keyExchange.name;
-        }
-
-        try {
-            if (tm instanceof X509ExtendedTrustManager) {
-                if (conn != null) {
-                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
-                        certs.clone(),
-                        keyExchangeString,
-                        conn);
-                } else {
-                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
-                        certs.clone(),
-                        keyExchangeString,
-                        engine);
-                }
-            } else {
-                // Unlikely to happen, because we have wrapped the old
-                // X509TrustManager with the new X509ExtendedTrustManager.
-                throw new CertificateException(
-                        "Improper X509TrustManager implementation");
-            }
-
-            // Once the server certificate chain has been validated, set
-            // the certificate chain in the TLS session.
-            session.setPeerCertificates(certs);
-        } catch (CertificateException ce) {
-            fatalSE(getCertificateAlert(ce), ce);
-        }
-    }
-
-    /**
-     * When a failure happens during certificate checking from an
-     * {@link X509TrustManager}, determine what TLS alert description to use.
-     *
-     * @param cexc The exception thrown by the {@link X509TrustManager}
-     *
-     * @return A byte value corresponding to a TLS alert description number.
-     */
-    private byte getCertificateAlert(CertificateException cexc) {
-        // The specific reason for the failure will determine how to
-        // set the alert description value
-        byte alertDesc = Alerts.alert_certificate_unknown;
-
-        Throwable baseCause = cexc.getCause();
-        if (baseCause instanceof CertPathValidatorException) {
-            CertPathValidatorException cpve =
-                    (CertPathValidatorException)baseCause;
-            Reason reason = cpve.getReason();
-            if (reason == BasicReason.REVOKED) {
-                alertDesc = staplingActive ?
-                        Alerts.alert_bad_certificate_status_response :
-                        Alerts.alert_certificate_revoked;
-            } else if (reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
-                alertDesc = staplingActive ?
-                        Alerts.alert_bad_certificate_status_response :
-                        Alerts.alert_certificate_unknown;
-            }
-        }
-
-        return alertDesc;
+        SSLHandshake.kickstart(this);
+        kickstartMessageDelivered = true;
     }
 }
-
< prev index next >