src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 56541 92cbbfc996f3
child 56543 2352538d2f6e
--- a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Fri May 11 14:55:56 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2344 +0,0 @@
-/*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package sun.security.ssl;
-
-import java.io.*;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.security.*;
-import java.security.cert.*;
-import java.security.interfaces.*;
-import java.security.spec.ECParameterSpec;
-import java.math.BigInteger;
-import java.util.function.BiFunction;
-
-import javax.crypto.SecretKey;
-import javax.net.ssl.*;
-
-import sun.security.action.GetLongAction;
-import sun.security.util.KeyUtil;
-import sun.security.util.LegacyAlgorithmConstraints;
-import sun.security.action.GetPropertyAction;
-import sun.security.ssl.HandshakeMessage.*;
-import sun.security.ssl.CipherSuite.*;
-import sun.security.ssl.SignatureAndHashAlgorithm.*;
-import static sun.security.ssl.CipherSuite.KeyExchange.*;
-
-/**
- * ServerHandshaker does the protocol handshaking from the point
- * of view of a server.  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 ServerHandshaker extends Handshaker {
-
-    // The default number of milliseconds the handshaker will wait for
-    // revocation status responses.
-    private static final long DEFAULT_STATUS_RESP_DELAY = 5000;
-
-    // is the server going to require the client to authenticate?
-    private ClientAuthType      doClientAuth;
-
-    // our authentication info
-    private X509Certificate[]   certs;
-    private PrivateKey          privateKey;
-
-    private Object              serviceCreds;
-
-    // flag to check for clientCertificateVerify message
-    private boolean             needClientVerify = false;
-
-    /*
-     * For exportable ciphersuites using non-exportable key sizes, we use
-     * ephemeral RSA keys. We could also do anonymous RSA in the same way
-     * but there are no such ciphersuites currently defined.
-     */
-    private PrivateKey          tempPrivateKey;
-    private PublicKey           tempPublicKey;
-
-    /*
-     * For anonymous and ephemeral Diffie-Hellman key exchange, we use
-     * ephemeral Diffie-Hellman keys.
-     */
-    private DHCrypt dh;
-
-    // Helper for ECDH based key exchanges
-    private ECDHCrypt ecdh;
-
-    // version request by the client in its ClientHello
-    // we remember it for the RSA premaster secret version check
-    private ProtocolVersion clientRequestedVersion;
-
-    // client supported elliptic curves
-    private SupportedGroupsExtension requestedGroups;
-
-    // the preferable signature algorithm used by ServerKeyExchange message
-    SignatureAndHashAlgorithm preferableSignatureAlgorithm;
-
-    // Flag to use smart ephemeral DH key which size matches the corresponding
-    // authentication key
-    private static final boolean useSmartEphemeralDHKeys;
-
-    // Flag to use legacy ephemeral DH key which size is 512 bits for
-    // exportable cipher suites, and 768 bits for others
-    private static final boolean useLegacyEphemeralDHKeys;
-
-    // The customized ephemeral DH key size for non-exportable cipher suites.
-    private static final int customizedDHKeySize;
-
-    // legacy algorithm constraints
-    private static final AlgorithmConstraints legacyAlgorithmConstraints =
-            new LegacyAlgorithmConstraints(
-                    LegacyAlgorithmConstraints.PROPERTY_TLS_LEGACY_ALGS,
-                    new SSLAlgorithmDecomposer());
-
-    private long statusRespTimeout;
-
-    static {
-        String property = GetPropertyAction
-                .privilegedGetProperty("jdk.tls.ephemeralDHKeySize");
-        if (property == null || property.length() == 0) {
-            useLegacyEphemeralDHKeys = false;
-            useSmartEphemeralDHKeys = false;
-            customizedDHKeySize = -1;
-        } else if ("matched".equals(property)) {
-            useLegacyEphemeralDHKeys = false;
-            useSmartEphemeralDHKeys = true;
-            customizedDHKeySize = -1;
-        } else if ("legacy".equals(property)) {
-            useLegacyEphemeralDHKeys = true;
-            useSmartEphemeralDHKeys = false;
-            customizedDHKeySize = -1;
-        } else {
-            useLegacyEphemeralDHKeys = false;
-            useSmartEphemeralDHKeys = false;
-
-            try {
-                // DH parameter generation can be extremely slow, best to
-                // use one of the supported pre-computed DH parameters
-                // (see DHCrypt class).
-                customizedDHKeySize = Integer.parseUnsignedInt(property);
-                if (customizedDHKeySize < 1024 || customizedDHKeySize > 8192 ||
-                        (customizedDHKeySize & 0x3f) != 0) {
-                    throw new IllegalArgumentException(
-                        "Unsupported customized DH key size: " +
-                        customizedDHKeySize + ". " +
-                        "The key size must be multiple of 64, " +
-                        "and can only range from 1024 to 8192 (inclusive)");
-                }
-            } catch (NumberFormatException nfe) {
-                throw new IllegalArgumentException(
-                        "Invalid system property jdk.tls.ephemeralDHKeySize");
-            }
-        }
-    }
-
-    /*
-     * Constructor ... use the keys found in the auth context.
-     */
-    ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,
-            ProtocolList enabledProtocols, ClientAuthType clientAuth,
-            ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,
-            boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
-
-        super(socket, context, enabledProtocols,
-                (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
-                activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-                clientVerifyData, serverVerifyData);
-        doClientAuth = clientAuth;
-        statusRespTimeout = AccessController.doPrivileged(
-                    new GetLongAction("jdk.tls.stapling.responseTimeout",
-                        DEFAULT_STATUS_RESP_DELAY));
-        statusRespTimeout = statusRespTimeout >= 0 ? statusRespTimeout :
-                DEFAULT_STATUS_RESP_DELAY;
-    }
-
-    /*
-     * Constructor ... use the keys found in the auth context.
-     */
-    ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,
-            ProtocolList enabledProtocols, ClientAuthType clientAuth,
-            ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData,
-            boolean isDTLS) {
-
-        super(engine, context, enabledProtocols,
-                (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
-                activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-                clientVerifyData, serverVerifyData, isDTLS);
-        doClientAuth = clientAuth;
-        statusRespTimeout = AccessController.doPrivileged(
-                    new GetLongAction("jdk.tls.stapling.responseTimeout",
-                        DEFAULT_STATUS_RESP_DELAY));
-        statusRespTimeout = statusRespTimeout >= 0 ? statusRespTimeout :
-                DEFAULT_STATUS_RESP_DELAY;
-    }
-
-    /*
-     * As long as handshaking has not started, we can change
-     * whether client authentication is required.  Otherwise,
-     * we will need to wait for the next handshake.
-     */
-    void setClientAuth(ClientAuthType clientAuth) {
-        doClientAuth = clientAuth;
-    }
-
-    /*
-     * This routine handles all the server 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 as each message is processed, and writes
-     * responses as needed using the connection in the constructor.
-     */
-    @Override
-    void processMessage(byte type, int message_len)
-            throws IOException {
-
-        // check the handshake state
-        handshakeState.check(type);
-
-        switch (type) {
-            case HandshakeMessage.ht_client_hello:
-                ClientHello ch = new ClientHello(input, message_len, isDTLS);
-                handshakeState.update(ch, resumingSession);
-
-                /*
-                 * send it off for processing.
-                 */
-                this.clientHello(ch);
-                break;
-
-            case HandshakeMessage.ht_certificate:
-                if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) {
-                    fatalSE(Alerts.alert_unexpected_message,
-                                "client sent unsolicited cert chain");
-                    // NOTREACHED
-                }
-                CertificateMsg certificateMsg = new CertificateMsg(input);
-                handshakeState.update(certificateMsg, resumingSession);
-                this.clientCertificate(certificateMsg);
-                break;
-
-            case HandshakeMessage.ht_client_key_exchange:
-                SecretKey preMasterSecret;
-                switch (keyExchange) {
-                case K_RSA:
-                case K_RSA_EXPORT:
-                    /*
-                     * The client's pre-master secret is decrypted using
-                     * either the server's normal private RSA key, or the
-                     * temporary one used for non-export or signing-only
-                     * certificates/keys.
-                     */
-                    RSAClientKeyExchange pms = new RSAClientKeyExchange(
-                            protocolVersion, clientRequestedVersion,
-                            sslContext.getSecureRandom(), input,
-                            message_len, privateKey);
-                    handshakeState.update(pms, resumingSession);
-                    preMasterSecret = this.clientKeyExchange(pms);
-                    break;
-                case K_DHE_RSA:
-                case K_DHE_DSS:
-                case K_DH_ANON:
-                    /*
-                     * The pre-master secret is derived using the normal
-                     * Diffie-Hellman calculation.   Note that the main
-                     * protocol difference in these five flavors is in how
-                     * the ServerKeyExchange message was constructed!
-                     */
-                    DHClientKeyExchange dhcke = new DHClientKeyExchange(input);
-                    handshakeState.update(dhcke, resumingSession);
-                    preMasterSecret = this.clientKeyExchange(dhcke);
-                    break;
-                case K_ECDH_RSA:
-                case K_ECDH_ECDSA:
-                case K_ECDHE_RSA:
-                case K_ECDHE_ECDSA:
-                case K_ECDH_ANON:
-                    ECDHClientKeyExchange ecdhcke =
-                                    new ECDHClientKeyExchange(input);
-                    handshakeState.update(ecdhcke, resumingSession);
-                    preMasterSecret = this.clientKeyExchange(ecdhcke);
-                    break;
-                default:
-                    ClientKeyExchangeService p =
-                            ClientKeyExchangeService.find(keyExchange.name);
-                    if (p == null) {
-                        throw new SSLProtocolException
-                                ("Unrecognized key exchange: " + keyExchange);
-                    }
-                    byte[] encodedTicket = input.getBytes16();
-                    input.getBytes16();
-                    byte[] secret = input.getBytes16();
-                    ClientKeyExchange cke = p.createServerExchange(protocolVersion,
-                            clientRequestedVersion,
-                            sslContext.getSecureRandom(),
-                            encodedTicket,
-                            secret,
-                            this.getAccSE(), serviceCreds);
-                    handshakeState.update(cke, resumingSession);
-                    preMasterSecret = this.clientKeyExchange(cke);
-                    break;
-                }
-
-                //
-                // All keys are calculated from the premaster secret
-                // and the exchanged nonces in the same way.
-                //
-                calculateKeys(preMasterSecret, clientRequestedVersion);
-                break;
-
-            case HandshakeMessage.ht_certificate_verify:
-                CertificateVerify cvm =
-                        new CertificateVerify(input,
-                            getLocalSupportedSignAlgs(), protocolVersion);
-                handshakeState.update(cvm, resumingSession);
-                this.clientCertificateVerify(cvm);
-
-                break;
-
-            case HandshakeMessage.ht_finished:
-                Finished cfm =
-                    new Finished(protocolVersion, input, cipherSuite);
-                handshakeState.update(cfm, resumingSession);
-                this.clientFinished(cfm);
-
-                break;
-
-            default:
-                throw new SSLProtocolException(
-                        "Illegal server handshake msg, " + type);
-        }
-
-    }
-
-
-    /*
-     * ClientHello presents the server with a bunch of options, to which the
-     * server replies with a ServerHello listing the ones which this session
-     * will use.  If needed, it also writes its Certificate plus in some cases
-     * a ServerKeyExchange message.  It may also write a CertificateRequest,
-     * to elicit a client certificate.
-     *
-     * All these messages are terminated by a ServerHelloDone message.  In
-     * most cases, all this can be sent in a single Record.
-     */
-    private void clientHello(ClientHello mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        // Reject client initiated renegotiation?
-        //
-        // If server side should reject client-initiated renegotiation,
-        // send an alert_handshake_failure fatal alert, not a no_renegotiation
-        // warning alert (no_renegotiation must be a warning: RFC 2246).
-        // no_renegotiation might seem more natural at first, but warnings
-        // are not appropriate because the sending party does not know how
-        // the receiving party will behave.  This state must be treated as
-        // a fatal server condition.
-        //
-        // This will not have any impact on server initiated renegotiation.
-        if (rejectClientInitiatedRenego && !isInitialHandshake &&
-                !serverHelloRequested) {
-            fatalSE(Alerts.alert_handshake_failure,
-                "Client initiated renegotiation is not allowed");
-        }
-
-        // check the server name indication if required
-        ServerNameExtension clientHelloSNIExt = (ServerNameExtension)
-                    mesg.extensions.get(ExtensionType.EXT_SERVER_NAME);
-        if (!sniMatchers.isEmpty()) {
-            // we do not reject client without SNI extension
-            if (clientHelloSNIExt != null &&
-                        !clientHelloSNIExt.isMatched(sniMatchers)) {
-                fatalSE(Alerts.alert_unrecognized_name,
-                    "Unrecognized server name indication");
-            }
-        }
-
-        // Does the message include security renegotiation indication?
-        boolean renegotiationIndicated = false;
-
-        // check the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
-        CipherSuiteList cipherSuites = mesg.getCipherSuites();
-        if (cipherSuites.contains(CipherSuite.C_SCSV)) {
-            renegotiationIndicated = true;
-            if (isInitialHandshake) {
-                secureRenegotiation = true;
-            } else {
-                // abort the handshake with a fatal handshake_failure alert
-                if (secureRenegotiation) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "The SCSV is present in a secure renegotiation");
-                } else {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "The SCSV is present in a insecure renegotiation");
-                }
-            }
-        }
-
-        // check the "renegotiation_info" extension
-        RenegotiationInfoExtension clientHelloRI = (RenegotiationInfoExtension)
-                    mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO);
-        if (clientHelloRI != null) {
-            renegotiationIndicated = true;
-            if (isInitialHandshake) {
-                // verify the length of the "renegotiated_connection" field
-                if (!clientHelloRI.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 {
-                if (!secureRenegotiation) {
-                    // unexpected RI extension for insecure renegotiation,
-                    // abort the handshake with a fatal handshake_failure alert
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "The renegotiation_info is present in a insecure " +
-                        "renegotiation");
-                }
-
-                // verify the client_verify_data value
-                if (!MessageDigest.isEqual(clientVerifyData,
-                                clientHelloRI.getRenegotiatedConnection())) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Incorrect verify data in ClientHello " +
-                        "renegotiation_info message");
-                }
-            }
-        } else if (!isInitialHandshake && secureRenegotiation) {
-           // if the connection's "secure_renegotiation" flag is set to TRUE
-           // and the "renegotiation_info" extension is not present, abort
-           // the handshake.
-            fatalSE(Alerts.alert_handshake_failure,
-                        "Inconsistent secure renegotiation indication");
-        }
-
-        // if there is no security renegotiation indication or the previous
-        // handshake is insecure.
-        if (!renegotiationIndicated || !secureRenegotiation) {
-            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");
-                }
-
-                // continue with legacy ClientHello
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Warning: No renegotiation " +
-                        "indication in ClientHello, allow legacy ClientHello");
-                }
-            } else if (!allowUnsafeRenegotiation) {
-                // abort the handshake
-                if (activeProtocolVersion.useTLS10PlusSpec()) {
-                    // respond 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 could immediately follow ClientHello
-                    // or HelloRequest. But in case of any improper messages,
-                    // we'd better check to ensure there is no remaining bytes
-                    // in the handshake input stream.
-                    if (input.available() > 0) {
-                        fatalSE(Alerts.alert_unexpected_message,
-                            "ClientHello followed by an unexpected  " +
-                            "handshake message");
-                    }
-
-                    return;
-                } 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 {   // !isInitialHandshake && allowUnsafeRenegotiation
-                // continue with unsafe renegotiation.
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println(
-                            "Warning: continue with insecure renegotiation");
-                }
-            }
-        }
-
-        // check the "max_fragment_length" extension
-        MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
-                    mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
-        if ((maxFragLenExt != null) && (maximumPacketSize != 0)) {
-            // Not yet consider the impact of IV/MAC/padding.
-            int estimatedMaxFragSize = maximumPacketSize;
-            if (isDTLS) {
-                estimatedMaxFragSize -= DTLSRecord.headerSize;
-            } else {
-                estimatedMaxFragSize -= SSLRecord.headerSize;
-            }
-
-            if (maxFragLenExt.getMaxFragLen() > estimatedMaxFragSize) {
-                // For better interoperability, abort the maximum fragment
-                // length negotiation, rather than terminate the connection
-                // with a fatal alert.
-                maxFragLenExt = null;
-
-                // fatalSE(Alerts.alert_illegal_parameter,
-                //         "Not an allowed max_fragment_length value");
-            }
-        }
-
-        // check out the "extended_master_secret" extension
-        if (useExtendedMasterSecret) {
-            ExtendedMasterSecretExtension extendedMasterSecretExtension =
-                    (ExtendedMasterSecretExtension)mesg.extensions.get(
-                            ExtensionType.EXT_EXTENDED_MASTER_SECRET);
-            if (extendedMasterSecretExtension != null) {
-                requestedToUseEMS = true;
-            } else if (mesg.protocolVersion.useTLS10PlusSpec()) {
-                if (!allowLegacyMasterSecret) {
-                    // For full handshake, if the server receives a ClientHello
-                    // without the extension, it SHOULD abort the handshake if
-                    // it does not wish to interoperate with legacy clients.
-                    //
-                    // As if extended master extension is required for full
-                    // handshake, it MUST be used in abbreviated handshake too.
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Extended Master Secret extension is required");
-                }
-            }
-        }
-
-        // check the ALPN extension
-        ALPNExtension clientHelloALPN = (ALPNExtension)
-            mesg.extensions.get(ExtensionType.EXT_ALPN);
-
-        // Use the application protocol callback when provided.
-        // Otherwise use the local list of application protocols.
-        boolean hasAPCallback =
-            ((engine != null && appProtocolSelectorSSLEngine != null) ||
-                (conn != null && appProtocolSelectorSSLSocket != null));
-
-        if (!hasAPCallback) {
-            if ((clientHelloALPN != null) && (localApl.length > 0)) {
-
-                // Intersect the requested and the locally supported,
-                // and save for later.
-                String negotiatedValue = null;
-                List<String> protocols = clientHelloALPN.getPeerAPs();
-
-                // Use server preference order
-                for (String ap : localApl) {
-                    if (protocols.contains(ap)) {
-                        negotiatedValue = ap;
-                        break;
-                    }
-                }
-
-                if (negotiatedValue == null) {
-                    fatalSE(Alerts.alert_no_application_protocol,
-                        new SSLHandshakeException(
-                            "No matching ALPN values"));
-                }
-                applicationProtocol = negotiatedValue;
-
-            } else {
-                applicationProtocol = "";
-            }
-        }  // Otherwise, applicationProtocol will be set by the callback.
-
-        session = null; // forget about the current session
-        //
-        // Here we go down either of two paths:  (a) the fast one, where
-        // the client's asked to rejoin an existing session, and the server
-        // permits this; (b) the other one, where a new session is created.
-        //
-        if (mesg.sessionId.length() != 0) {
-            // client is trying to resume a session, let's see...
-
-            SSLSessionImpl previous = ((SSLSessionContextImpl)sslContext
-                        .engineGetServerSessionContext())
-                        .get(mesg.sessionId.getId());
-            //
-            // Check if we can use the fast path, resuming a session.  We
-            // can do so iff we have a valid record for that session, and
-            // the cipher suite for that session was on the list which the
-            // client requested, and if we're not forgetting any needed
-            // authentication on the part of the client.
-            //
-            if (previous != null) {
-                resumingSession = previous.isRejoinable();
-
-                if (resumingSession) {
-                    ProtocolVersion oldVersion = previous.getProtocolVersion();
-                    // cannot resume session with different version
-                    if (oldVersion != mesg.protocolVersion) {
-                        resumingSession = false;
-                    }
-                }
-
-                if (resumingSession && useExtendedMasterSecret) {
-                    if (requestedToUseEMS &&
-                            !previous.getUseExtendedMasterSecret()) {
-                        // For abbreviated handshake request, If the original
-                        // session did not use the "extended_master_secret"
-                        // extension but the new ClientHello contains the
-                        // extension, then the server MUST NOT perform the
-                        // abbreviated handshake.  Instead, it SHOULD continue
-                        // with a full handshake.
-                        resumingSession = false;
-                    } else if (!requestedToUseEMS &&
-                            previous.getUseExtendedMasterSecret()) {
-                        // For abbreviated handshake request, if the original
-                        // session used the "extended_master_secret" extension
-                        // but the new ClientHello does not contain it, the
-                        // server MUST abort the abbreviated handshake.
-                        fatalSE(Alerts.alert_handshake_failure,
-                                "Missing Extended Master Secret extension " +
-                                "on session resumption");
-                    } else if (!requestedToUseEMS &&
-                            !previous.getUseExtendedMasterSecret()) {
-                        // For abbreviated handshake request, if neither the
-                        // original session nor the new ClientHello uses the
-                        // extension, the server SHOULD abort the handshake.
-                        if (!allowLegacyResumption) {
-                            fatalSE(Alerts.alert_handshake_failure,
-                                "Missing Extended Master Secret extension " +
-                                "on session resumption");
-                        } else {  // Otherwise, continue with a full handshake.
-                            resumingSession = false;
-                        }
-                    }
-                }
-
-                // cannot resume session with different server name indication
-                if (resumingSession) {
-                    List<SNIServerName> oldServerNames =
-                            previous.getRequestedServerNames();
-                    if (clientHelloSNIExt != null) {
-                        if (!clientHelloSNIExt.isIdentical(oldServerNames)) {
-                            resumingSession = false;
-                        }
-                    } else if (!oldServerNames.isEmpty()) {
-                        resumingSession = false;
-                    }
-
-                    if (!resumingSession &&
-                            debug != null && Debug.isOn("handshake")) {
-                        System.out.println(
-                            "The requested server name indication " +
-                            "is not identical to the previous one");
-                    }
-                }
-
-                if (resumingSession &&
-                        (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) {
-                    try {
-                        previous.getPeerPrincipal();
-                    } catch (SSLPeerUnverifiedException e) {
-                        resumingSession = false;
-                    }
-                }
-
-                // validate subject identity
-                if (resumingSession) {
-                    CipherSuite suite = previous.getSuite();
-                    ClientKeyExchangeService p =
-                        ClientKeyExchangeService.find(suite.keyExchange.name);
-                    if (p != null) {
-                        Principal localPrincipal = previous.getLocalPrincipal();
-
-                        if (p.isRelated(
-                                false, getAccSE(), localPrincipal)) {
-                            if (debug != null && Debug.isOn("session"))
-                                System.out.println("Subject can" +
-                                        " provide creds for princ");
-                        } else {
-                            resumingSession = false;
-                            if (debug != null && Debug.isOn("session"))
-                                System.out.println("Subject cannot" +
-                                        " provide creds for princ");
-                        }
-                    }
-                }
-
-                if (resumingSession) {
-                    CipherSuite suite = previous.getSuite();
-                    // verify that the ciphersuite from the cached session
-                    // is in the list of client requested ciphersuites and
-                    // we have it enabled
-                    if ((isNegotiable(suite) == false) ||
-                            (mesg.getCipherSuites().contains(suite) == false)) {
-                        resumingSession = false;
-                    } else {
-                        // everything looks ok, set the ciphersuite
-                        // this should be done last when we are sure we
-                        // will resume
-                        setCipherSuite(suite);
-                    }
-                }
-
-                if (resumingSession) {
-                    session = previous;
-                    if (debug != null &&
-                        (Debug.isOn("handshake") || Debug.isOn("session"))) {
-                        System.out.println("%% Resuming " + session);
-                    }
-                }
-            }
-        }   // else client did not try to resume
-
-        // cookie exchange
-        if (isDTLS && !resumingSession) {
-             HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
-             if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
-                    (!hcMgr.isValid(mesg))) {
-
-                //
-                // Perform cookie exchange for DTLS handshaking if no cookie
-                // or the cookie is invalid in the ClientHello message.
-                //
-                HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
-
-                if (debug != null && Debug.isOn("handshake")) {
-                    m0.print(System.out);
-                }
-
-                m0.write(output);
-                handshakeState.update(m0, resumingSession);
-                output.flush();
-
-                return;
-            }
-        }
-
-        /*
-         * FIRST, construct the ServerHello using the options and priorities
-         * from the ClientHello.  Update the (pending) cipher spec as we do
-         * so, and save the client's version to protect against rollback
-         * attacks.
-         *
-         * There are a bunch of minor tasks here, and one major one: deciding
-         * if the short or the full handshake sequence will be used.
-         */
-        ServerHello m1 = new ServerHello();
-
-        clientRequestedVersion = mesg.protocolVersion;
-
-        // select a proper protocol version.
-        ProtocolVersion selectedVersion =
-               selectProtocolVersion(clientRequestedVersion);
-        if (selectedVersion == null ||
-                selectedVersion.v == ProtocolVersion.SSL20Hello.v) {
-            fatalSE(Alerts.alert_handshake_failure,
-                "Client requested protocol " + clientRequestedVersion +
-                " not enabled or not supported");
-        }
-
-        handshakeHash.protocolDetermined(selectedVersion);
-        setVersion(selectedVersion);
-
-        m1.protocolVersion = protocolVersion;
-
-        //
-        // random ... save client and server values for later use
-        // in computing the master secret (from pre-master secret)
-        // and thence the other crypto keys.
-        //
-        // NOTE:  this use of three inputs to generating _each_ set
-        // of ciphers slows things down, but it does increase the
-        // security since each connection in the session can hold
-        // its own authenticated (and strong) keys.  One could make
-        // creation of a session a rare thing...
-        //
-        clnt_random = mesg.clnt_random;
-        svr_random = new RandomCookie(sslContext.getSecureRandom());
-        m1.svr_random = svr_random;
-
-        //
-        // If client hasn't specified a session we can resume, start a
-        // new one and choose its cipher suite and compression options.
-        // Unless new session creation is disabled for this connection!
-        //
-        if (session == null) {
-            if (!enableNewSession) {
-                throw new SSLException("Client did not resume a session");
-            }
-
-            requestedGroups = (SupportedGroupsExtension)
-                    mesg.extensions.get(ExtensionType.EXT_SUPPORTED_GROUPS);
-
-            // We only need to handle the "signature_algorithm" extension
-            // for full handshakes and TLS 1.2 or later.
-            if (protocolVersion.useTLS12PlusSpec()) {
-                SignatureAlgorithmsExtension signAlgs =
-                    (SignatureAlgorithmsExtension)mesg.extensions.get(
-                                    ExtensionType.EXT_SIGNATURE_ALGORITHMS);
-                if (signAlgs != null) {
-                    Collection<SignatureAndHashAlgorithm> peerSignAlgs =
-                                            signAlgs.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 signature and hash algorithm in common");
-                    }
-
-                    setPeerSupportedSignAlgs(supportedPeerSignAlgs);
-                } // else, need to use peer implicit supported signature algs
-            }
-
-            session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL,
-                        getLocalSupportedSignAlgs(),
-                        sslContext.getSecureRandom(),
-                        getHostAddressSE(), getPortSE(),
-                        (requestedToUseEMS &&
-                                protocolVersion.useTLS10PlusSpec()));
-
-            if (protocolVersion.useTLS12PlusSpec()) {
-                if (peerSupportedSignAlgs != null) {
-                    session.setPeerSupportedSignatureAlgorithms(
-                            peerSupportedSignAlgs);
-                }   // else, we will set the implicit peer supported signature
-                    // algorithms in chooseCipherSuite()
-            }
-
-            // set the server name indication in the session
-            List<SNIServerName> clientHelloSNI =
-                    Collections.<SNIServerName>emptyList();
-            if (clientHelloSNIExt != null) {
-                clientHelloSNI = clientHelloSNIExt.getServerNames();
-            }
-            session.setRequestedServerNames(clientHelloSNI);
-
-            // set the handshake session
-            setHandshakeSessionSE(session);
-
-            // choose cipher suite and corresponding private key
-            chooseCipherSuite(mesg);
-
-            session.setSuite(cipherSuite);
-            session.setLocalPrivateKey(privateKey);
-
-            // chooseCompression(mesg);
-
-            // set the negotiated maximum fragment in the session
-            //
-            // The protocol version and cipher suite have been negotiated
-            // in previous processes.
-            if (maxFragLenExt != null) {
-                int maxFragLen = maxFragLenExt.getMaxFragLen();
-
-                // More check of the requested "max_fragment_length" extension.
-                if (maximumPacketSize != 0) {
-                    int estimatedMaxFragSize = cipherSuite.calculatePacketSize(
-                            maxFragLen, protocolVersion, isDTLS);
-                    if (estimatedMaxFragSize > maximumPacketSize) {
-                        // For better interoperability, abort the maximum
-                        // fragment length negotiation, rather than terminate
-                        // the connection with a fatal alert.
-                        maxFragLenExt = null;
-
-                        // fatalSE(Alerts.alert_illegal_parameter,
-                        //         "Not an allowed max_fragment_length value");
-                    }
-                }
-
-                if (maxFragLenExt != null) {
-                    session.setNegotiatedMaxFragSize(maxFragLen);
-                }
-            }
-
-            session.setMaximumPacketSize(maximumPacketSize);
-        } else {
-            // set the handshake session
-            setHandshakeSessionSE(session);
-        }
-
-        if (protocolVersion.useTLS12PlusSpec()) {
-            handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
-        }
-
-        m1.cipherSuite = cipherSuite;
-        m1.sessionId = session.getSessionId();
-        m1.compression_method = session.getCompression();
-
-        if (secureRenegotiation) {
-            // For ServerHellos that are initial handshakes, then the
-            // "renegotiated_connection" field in "renegotiation_info"
-            // extension is of zero length.
-            //
-            // For ServerHellos that are renegotiating, this field contains
-            // the concatenation of client_verify_data and server_verify_data.
-            //
-            // Note that for initial handshakes, both the clientVerifyData
-            // variable and serverVerifyData variable are of zero length.
-            HelloExtension serverHelloRI = new RenegotiationInfoExtension(
-                                        clientVerifyData, serverVerifyData);
-            m1.extensions.add(serverHelloRI);
-        }
-
-        if (!sniMatchers.isEmpty() && clientHelloSNIExt != null) {
-            // When resuming a session, the server MUST NOT include a
-            // server_name extension in the server hello.
-            if (!resumingSession) {
-                ServerNameExtension serverHelloSNI = new ServerNameExtension();
-                m1.extensions.add(serverHelloSNI);
-            }
-        }
-
-        if ((maxFragLenExt != null) && !resumingSession) {
-            // When resuming a session, the server MUST NOT include a
-            // max_fragment_length extension in the server hello.
-            //
-            // Otherwise, use the same value as the requested extension.
-            m1.extensions.add(maxFragLenExt);
-        }
-
-        if (session.getUseExtendedMasterSecret()) {
-            m1.extensions.add(new ExtendedMasterSecretExtension());
-        }
-
-        StaplingParameters staplingParams = processStapling(mesg);
-        if (staplingParams != null) {
-            // We now can safely assert status_request[_v2] in our
-            // ServerHello, and know for certain that we can provide
-            // responses back to this client for this connection.
-            if (staplingParams.statusRespExt ==
-                    ExtensionType.EXT_STATUS_REQUEST) {
-                m1.extensions.add(new CertStatusReqExtension());
-            } else if (staplingParams.statusRespExt ==
-                    ExtensionType.EXT_STATUS_REQUEST_V2) {
-                m1.extensions.add(new CertStatusReqListV2Extension());
-            }
-        }
-
-        // Prepare the ALPN response
-        if (clientHelloALPN != null) {
-            List<String> peerAPs = clientHelloALPN.getPeerAPs();
-
-            // check for a callback function
-            if (hasAPCallback) {
-                if (conn != null) {
-                    applicationProtocol =
-                        appProtocolSelectorSSLSocket.apply(conn, peerAPs);
-                } else {
-                    applicationProtocol =
-                        appProtocolSelectorSSLEngine.apply(engine, peerAPs);
-                }
-            }
-
-            // check for no-match and that the selected name was also proposed
-            // by the TLS peer
-            if (applicationProtocol == null ||
-                   (!applicationProtocol.isEmpty() &&
-                        !peerAPs.contains(applicationProtocol))) {
-
-                fatalSE(Alerts.alert_no_application_protocol,
-                    new SSLHandshakeException(
-                        "No matching ALPN values"));
-
-            } else if (!applicationProtocol.isEmpty()) {
-                m1.extensions.add(new ALPNExtension(applicationProtocol));
-            }
-        } else {
-            // Nothing was negotiated, returned at end of the handshake
-            applicationProtocol = "";
-        }
-
-        if (debug != null && Debug.isOn("handshake")) {
-            m1.print(System.out);
-            System.out.println("Cipher suite:  " + session.getSuite());
-        }
-        m1.write(output);
-        handshakeState.update(m1, resumingSession);
-
-        //
-        // If we are resuming a session, we finish writing handshake
-        // messages right now and then finish.
-        //
-        if (resumingSession) {
-            calculateConnectionKeys(session.getMasterSecret());
-            sendChangeCipherAndFinish(false);
-
-            // expecting the final ChangeCipherSpec and Finished messages
-            expectingFinishFlightSE();
-
-            return;
-        }
-
-
-        /*
-         * SECOND, write the server Certificate(s) if we need to.
-         *
-         * NOTE:  while an "anonymous RSA" mode is explicitly allowed by
-         * the protocol, we can't support it since all of the SSL flavors
-         * defined in the protocol spec are explicitly stated to require
-         * using RSA certificates.
-         */
-        if (ClientKeyExchangeService.find(
-                cipherSuite.keyExchange.name) != null) {
-            // No external key exchange provider needs a cert now.
-        } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
-            if (certs == null) {
-                throw new RuntimeException("no certificates");
-            }
-
-            CertificateMsg m2 = new CertificateMsg(certs);
-
-            /*
-             * Set local certs in the SSLSession, output
-             * debug info, and then actually write to the client.
-             */
-            session.setLocalCertificates(certs);
-            if (debug != null && Debug.isOn("handshake")) {
-                m2.print(System.out);
-            }
-            m2.write(output);
-            handshakeState.update(m2, resumingSession);
-
-            // XXX has some side effects with OS TCP buffering,
-            // leave it out for now
-
-            // let client verify chain in the meantime...
-            // output.flush();
-        } else {
-            if (certs != null) {
-                throw new RuntimeException("anonymous keyexchange with certs");
-            }
-        }
-
-        /**
-         * The CertificateStatus message ... only if it is needed.
-         * This would only be needed if we've established that this handshake
-         * supports status stapling and there is at least one response to
-         * return to the client.
-         */
-        if (staplingParams != null) {
-            CertificateStatus csMsg = new CertificateStatus(
-                    staplingParams.statReqType, certs,
-                    staplingParams.responseMap);
-            if (debug != null && Debug.isOn("handshake")) {
-                csMsg.print(System.out);
-            }
-            csMsg.write(output);
-            handshakeState.update(csMsg, resumingSession);
-        }
-
-        /*
-         * THIRD, the ServerKeyExchange message ... iff it's needed.
-         *
-         * It's usually needed unless there's an encryption-capable
-         * RSA cert, or a D-H cert.  The notable exception is that
-         * exportable ciphers used with big RSA keys need to downgrade
-         * to use short RSA keys, even when the key/cert encrypts OK.
-         */
-
-        ServerKeyExchange m3;
-        switch (keyExchange) {
-        case K_RSA:
-            // no server key exchange for RSA ciphersuites
-            m3 = null;
-            break;
-        case K_RSA_EXPORT:
-            if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) {
-                try {
-                    m3 = new RSA_ServerKeyExchange(
-                        tempPublicKey, privateKey,
-                        clnt_random, svr_random,
-                        sslContext.getSecureRandom());
-                    privateKey = tempPrivateKey;
-                } catch (GeneralSecurityException e) {
-                    m3 = null; // make compiler happy
-                    throw new SSLException(
-                            "Error generating RSA server key exchange", e);
-                }
-            } else {
-                // RSA_EXPORT with short key, don't need ServerKeyExchange
-                m3 = null;
-            }
-            break;
-        case K_DHE_RSA:
-        case K_DHE_DSS:
-            try {
-                m3 = new DH_ServerKeyExchange(dh,
-                    privateKey,
-                    clnt_random.random_bytes,
-                    svr_random.random_bytes,
-                    sslContext.getSecureRandom(),
-                    preferableSignatureAlgorithm,
-                    protocolVersion);
-            } catch (GeneralSecurityException e) {
-                m3 = null; // make compiler happy
-                throw new SSLException(
-                        "Error generating DH server key exchange", e);
-            }
-            break;
-        case K_DH_ANON:
-            m3 = new DH_ServerKeyExchange(dh, protocolVersion);
-            break;
-        case K_ECDHE_RSA:
-        case K_ECDHE_ECDSA:
-        case K_ECDH_ANON:
-            try {
-                m3 = new ECDH_ServerKeyExchange(ecdh,
-                    privateKey,
-                    clnt_random.random_bytes,
-                    svr_random.random_bytes,
-                    sslContext.getSecureRandom(),
-                    preferableSignatureAlgorithm,
-                    protocolVersion);
-            } catch (GeneralSecurityException e) {
-                m3 = null; // make compiler happy
-                throw new SSLException(
-                        "Error generating ECDH server key exchange", e);
-            }
-            break;
-        case K_ECDH_RSA:
-        case K_ECDH_ECDSA:
-            // ServerKeyExchange not used for fixed ECDH
-            m3 = null;
-            break;
-        default:
-            ClientKeyExchangeService p =
-                    ClientKeyExchangeService.find(keyExchange.name);
-            if (p != null) {
-                // No external key exchange provider needs a cert now.
-                m3 = null;
-                break;
-            }
-            throw new RuntimeException("internal error: " + keyExchange);
-        }
-        if (m3 != null) {
-            if (debug != null && Debug.isOn("handshake")) {
-                m3.print(System.out);
-            }
-            m3.write(output);
-            handshakeState.update(m3, resumingSession);
-        }
-
-        //
-        // FOURTH, the CertificateRequest message.  The details of
-        // the message can be affected by the key exchange algorithm
-        // in use.  For example, certs with fixed Diffie-Hellman keys
-        // are only useful with the DH_DSS and DH_RSA key exchange
-        // algorithms.
-        //
-        // Needed only if server requires client to authenticate self.
-        // Illegal for anonymous flavors, so we need to check that.
-        //
-        // No external key exchange provider needs a cert now.
-        if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
-                keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
-                ClientKeyExchangeService.find(keyExchange.name) == null) {
-
-            CertificateRequest m4;
-            X509Certificate[] caCerts;
-
-            Collection<SignatureAndHashAlgorithm> localSignAlgs = null;
-            if (protocolVersion.useTLS12PlusSpec()) {
-                // We currently use all local upported signature and hash
-                // algorithms. However, to minimize the computation cost
-                // of requested hash algorithms, we may use a restricted
-                // set of signature algorithms in the future.
-                localSignAlgs = getLocalSupportedSignAlgs();
-                if (localSignAlgs.isEmpty()) {
-                    throw new SSLHandshakeException(
-                            "No supported signature algorithm");
-                }
-
-                Set<String> localHashAlgs =
-                    SignatureAndHashAlgorithm.getHashAlgorithmNames(
-                        localSignAlgs);
-                if (localHashAlgs.isEmpty()) {
-                    throw new SSLHandshakeException(
-                            "No supported signature algorithm");
-                }
-            }
-
-            caCerts = sslContext.getX509TrustManager().getAcceptedIssuers();
-            m4 = new CertificateRequest(caCerts, keyExchange,
-                                            localSignAlgs, protocolVersion);
-
-            if (debug != null && Debug.isOn("handshake")) {
-                m4.print(System.out);
-            }
-            m4.write(output);
-            handshakeState.update(m4, resumingSession);
-        }
-
-        /*
-         * FIFTH, say ServerHelloDone.
-         */
-        ServerHelloDone m5 = new ServerHelloDone();
-
-        if (debug != null && Debug.isOn("handshake")) {
-            m5.print(System.out);
-        }
-        m5.write(output);
-        handshakeState.update(m5, resumingSession);
-
-        /*
-         * Flush any buffered messages so the client will see them.
-         * Ideally, all the messages above go in a single network level
-         * message to the client.  Without big Certificate chains, it's
-         * going to be the common case.
-         */
-        output.flush();
-    }
-
-    /*
-     * Choose cipher suite from among those supported by client. Sets
-     * the cipherSuite and keyExchange variables.
-     */
-    private void chooseCipherSuite(ClientHello mesg) throws IOException {
-        CipherSuiteList prefered;
-        CipherSuiteList proposed;
-        if (preferLocalCipherSuites) {
-            prefered = getActiveCipherSuites();
-            proposed = mesg.getCipherSuites();
-        } else {
-            prefered = mesg.getCipherSuites();
-            proposed = getActiveCipherSuites();
-        }
-
-        List<CipherSuite> legacySuites = new ArrayList<>();
-        for (CipherSuite suite : prefered.collection()) {
-            if (isNegotiable(proposed, suite) == false) {
-                continue;
-            }
-
-            if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
-                if ((suite.keyExchange == K_DH_ANON) ||
-                    (suite.keyExchange == K_ECDH_ANON)) {
-                    continue;
-                }
-            }
-
-            if (!legacyAlgorithmConstraints.permits(null, suite.name, null)) {
-                legacySuites.add(suite);
-                continue;
-            }
-
-            if (trySetCipherSuite(suite) == false) {
-                continue;
-            }
-
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Standard ciphersuite chosen: " + suite);
-            }
-            return;
-        }
-
-        for (CipherSuite suite : legacySuites) {
-            if (trySetCipherSuite(suite)) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Legacy ciphersuite chosen: " + suite);
-                }
-                return;
-            }
-        }
-
-        fatalSE(Alerts.alert_handshake_failure, "no cipher suites in common");
-    }
-
-    /**
-     * Set the given CipherSuite, if possible. Return the result.
-     * The call succeeds if the CipherSuite is available and we have
-     * the necessary certificates to complete the handshake. We don't
-     * check if the CipherSuite is actually enabled.
-     *
-     * If successful, this method also generates ephemeral keys if
-     * required for this ciphersuite. This may take some time, so this
-     * method should only be called if you really want to use the
-     * CipherSuite.
-     *
-     * This method is called from chooseCipherSuite() in this class.
-     */
-    boolean trySetCipherSuite(CipherSuite suite) {
-        /*
-         * If we're resuming a session we know we can
-         * support this key exchange algorithm and in fact
-         * have already cached the result of it in
-         * the session state.
-         */
-        if (resumingSession) {
-            return true;
-        }
-
-        if (suite.isNegotiable() == false) {
-            return false;
-        }
-
-        // must not negotiate the obsoleted weak cipher suites.
-        if (protocolVersion.obsoletes(suite)) {
-            return false;
-        }
-
-        // must not negotiate unsupported cipher suites.
-        if (!protocolVersion.supports(suite)) {
-            return false;
-        }
-
-        KeyExchange keyExchange = suite.keyExchange;
-
-        // null out any existing references
-        privateKey = null;
-        certs = null;
-        dh = null;
-        tempPrivateKey = null;
-        tempPublicKey = null;
-
-        Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null;
-        if (protocolVersion.useTLS12PlusSpec()) {
-            if (peerSupportedSignAlgs != null) {
-                supportedSignAlgs = peerSupportedSignAlgs;
-            } else {
-                SignatureAndHashAlgorithm algorithm = null;
-
-                // we may optimize the performance
-                switch (keyExchange) {
-                    // If the negotiated key exchange algorithm is one of
-                    // (RSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA),
-                    // behave as if client had sent the value {sha1,rsa}.
-                    case K_RSA:
-                    case K_DHE_RSA:
-                    case K_DH_RSA:
-                    // case K_RSA_PSK:
-                    case K_ECDH_RSA:
-                    case K_ECDHE_RSA:
-                        algorithm = SignatureAndHashAlgorithm.valueOf(
-                                HashAlgorithm.SHA1.value,
-                                SignatureAlgorithm.RSA.value, 0);
-                        break;
-                    // If the negotiated key exchange algorithm is one of
-                    // (DHE_DSS, DH_DSS), behave as if the client had
-                    // sent the value {sha1,dsa}.
-                    case K_DHE_DSS:
-                    case K_DH_DSS:
-                        algorithm = SignatureAndHashAlgorithm.valueOf(
-                                HashAlgorithm.SHA1.value,
-                                SignatureAlgorithm.DSA.value, 0);
-                        break;
-                    // If the negotiated key exchange algorithm is one of
-                    // (ECDH_ECDSA, ECDHE_ECDSA), behave as if the client
-                    // had sent value {sha1,ecdsa}.
-                    case K_ECDH_ECDSA:
-                    case K_ECDHE_ECDSA:
-                        algorithm = SignatureAndHashAlgorithm.valueOf(
-                                HashAlgorithm.SHA1.value,
-                                SignatureAlgorithm.ECDSA.value, 0);
-                        break;
-                    default:
-                        // no peer supported signature algorithms
-                }
-
-                if (algorithm == null) {
-                    supportedSignAlgs =
-                        Collections.<SignatureAndHashAlgorithm>emptySet();
-                } else {
-                    supportedSignAlgs =
-                        new ArrayList<SignatureAndHashAlgorithm>(1);
-                    supportedSignAlgs.add(algorithm);
-
-                    supportedSignAlgs =
-                            SignatureAndHashAlgorithm.getSupportedAlgorithms(
-                                algorithmConstraints, supportedSignAlgs);
-
-                    // May be no default activated signature algorithm, but
-                    // let the following process make the final decision.
-                }
-
-                // Sets the peer supported signature algorithm to use in KM
-                // temporarily.
-                session.setPeerSupportedSignatureAlgorithms(supportedSignAlgs);
-            }
-        }
-
-        // The named group used for ECDHE and FFDHE.
-        NamedGroup namedGroup = null;
-        switch (keyExchange) {
-        case K_RSA:
-            // need RSA certs for authentication
-            if (setupPrivateKeyAndChain("RSA") == false) {
-                return false;
-            }
-            break;
-        case K_RSA_EXPORT:
-            // need RSA certs for authentication
-            if (setupPrivateKeyAndChain("RSA") == false) {
-                return false;
-            }
-
-            try {
-               if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) {
-                    if (!setupEphemeralRSAKeys(suite.exportable)) {
-                        return false;
-                    }
-               }
-            } catch (RuntimeException e) {
-                // could not determine keylength, ignore key
-                return false;
-            }
-            break;
-        case K_DHE_RSA:
-            // Is ephemeral DH cipher suite usable for the connection?
-            //
-            // [RFC 7919] If a compatible TLS server receives a Supported
-            // Groups extension from a client that includes any FFDHE group
-            // (i.e., any codepoint between 256 and 511, inclusive, even if
-            // unknown to the server), and if none of the client-proposed
-            // FFDHE groups are known and acceptable to the server, then
-            // the server MUST NOT select an FFDHE cipher suite.  In this
-            // case, the server SHOULD select an acceptable non-FFDHE cipher
-            // suite from the client's offered list.  If the extension is
-            // present with FFDHE groups, none of the client's offered
-            // groups are acceptable by the server, and none of the client's
-            // proposed non-FFDHE cipher suites are acceptable to the server,
-            // the server MUST end the connection with a fatal TLS alert
-            // of type insufficient_security(71).
-            //
-            // Note: For compatibility, if an application is customized to
-            // use legacy sizes (512 bits for exportable cipher suites and
-            // 768 bits for others), or the cipher suite is exportable, the
-            // FFDHE extension will not be used.
-            if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
-                (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
-
-                namedGroup = requestedGroups.getPreferredGroup(
-                    algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
-                if (namedGroup == null) {
-                    // no match found, cannot use this cipher suite.
-                    return false;
-                }
-            }
-
-            // need RSA certs for authentication
-            if (setupPrivateKeyAndChain("RSA") == false) {
-                return false;
-            }
-
-            // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.useTLS12PlusSpec()) {
-                preferableSignatureAlgorithm =
-                    SignatureAndHashAlgorithm.getPreferableAlgorithm(
-                                        supportedSignAlgs, "RSA", privateKey);
-                if (preferableSignatureAlgorithm == null) {
-                    if ((debug != null) && Debug.isOn("handshake")) {
-                        System.out.println(
-                                "No signature and hash algorithm for cipher " +
-                                suite);
-                    }
-                    return false;
-                }
-            }
-
-            setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey);
-            break;
-        case K_ECDHE_RSA:
-            // Is ECDHE cipher suite usable for the connection?
-            namedGroup = (requestedGroups != null) ?
-                requestedGroups.getPreferredGroup(
-                    algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
-                SupportedGroupsExtension.getPreferredECGroup(
-                    algorithmConstraints);
-            if (namedGroup == null) {
-                // no match found, cannot use this ciphersuite
-                return false;
-            }
-
-            // need RSA certs for authentication
-            if (setupPrivateKeyAndChain("RSA") == false) {
-                return false;
-            }
-
-            // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.useTLS12PlusSpec()) {
-                preferableSignatureAlgorithm =
-                    SignatureAndHashAlgorithm.getPreferableAlgorithm(
-                                        supportedSignAlgs, "RSA", privateKey);
-                if (preferableSignatureAlgorithm == null) {
-                    if ((debug != null) && Debug.isOn("handshake")) {
-                        System.out.println(
-                                "No signature and hash algorithm for cipher " +
-                                suite);
-                    }
-                    return false;
-                }
-            }
-
-            setupEphemeralECDHKeys(namedGroup);
-            break;
-        case K_DHE_DSS:
-            // Is ephemeral DH cipher suite usable for the connection?
-            //
-            // See comment in K_DHE_RSA case.
-            if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
-                (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
-
-                namedGroup = requestedGroups.getPreferredGroup(
-                    algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
-                if (namedGroup == null) {
-                    // no match found, cannot use this cipher suite.
-                    return false;
-                }
-            }
-
-            // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.useTLS12PlusSpec()) {
-                preferableSignatureAlgorithm =
-                    SignatureAndHashAlgorithm.getPreferableAlgorithm(
-                                                supportedSignAlgs, "DSA");
-                if (preferableSignatureAlgorithm == null) {
-                    if ((debug != null) && Debug.isOn("handshake")) {
-                        System.out.println(
-                                "No signature and hash algorithm for cipher " +
-                                suite);
-                    }
-                    return false;
-                }
-            }
-
-            // need DSS certs for authentication
-            if (setupPrivateKeyAndChain("DSA") == false) {
-                return false;
-            }
-
-            setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey);
-            break;
-        case K_ECDHE_ECDSA:
-            // Is ECDHE cipher suite usable for the connection?
-            namedGroup = (requestedGroups != null) ?
-                requestedGroups.getPreferredGroup(
-                    algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
-                SupportedGroupsExtension.getPreferredECGroup(
-                    algorithmConstraints);
-            if (namedGroup == null) {
-                // no match found, cannot use this ciphersuite
-                return false;
-            }
-
-            // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.useTLS12PlusSpec()) {
-                preferableSignatureAlgorithm =
-                    SignatureAndHashAlgorithm.getPreferableAlgorithm(
-                                            supportedSignAlgs, "ECDSA");
-                if (preferableSignatureAlgorithm == null) {
-                    if ((debug != null) && Debug.isOn("handshake")) {
-                        System.out.println(
-                                "No signature and hash algorithm for cipher " +
-                                suite);
-                    }
-                    return false;
-                }
-            }
-
-            // need EC cert
-            if (setupPrivateKeyAndChain("EC") == false) {
-                return false;
-            }
-
-            setupEphemeralECDHKeys(namedGroup);
-            break;
-        case K_ECDH_RSA:
-            // need EC cert
-            if (setupPrivateKeyAndChain("EC") == false) {
-                return false;
-            }
-            setupStaticECDHKeys();
-            break;
-        case K_ECDH_ECDSA:
-            // need EC cert
-            if (setupPrivateKeyAndChain("EC") == false) {
-                return false;
-            }
-            setupStaticECDHKeys();
-            break;
-        case K_DH_ANON:
-            // Is ephemeral DH cipher suite usable for the connection?
-            //
-            // See comment in K_DHE_RSA case.
-            if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) &&
-                (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) {
-                namedGroup = requestedGroups.getPreferredGroup(
-                    algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE);
-                if (namedGroup == null) {
-                    // no match found, cannot use this cipher suite.
-                    return false;
-                }
-            }
-
-            // no certs needed for anonymous
-            setupEphemeralDHKeys(namedGroup, suite.exportable, null);
-            break;
-        case K_ECDH_ANON:
-            // Is ECDHE cipher suite usable for the connection?
-            namedGroup = (requestedGroups != null) ?
-                requestedGroups.getPreferredGroup(
-                    algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) :
-                SupportedGroupsExtension.getPreferredECGroup(
-                    algorithmConstraints);
-            if (namedGroup == null) {
-                // no match found, cannot use this ciphersuite
-                return false;
-            }
-
-            // no certs needed for anonymous
-            setupEphemeralECDHKeys(namedGroup);
-            break;
-        default:
-            ClientKeyExchangeService p =
-                    ClientKeyExchangeService.find(keyExchange.name);
-            if (p == null) {
-                // internal error, unknown key exchange
-                throw new RuntimeException(
-                        "Unrecognized cipherSuite: " + suite);
-            }
-            // need service creds
-            if (serviceCreds == null) {
-                AccessControlContext acc = getAccSE();
-                serviceCreds = p.getServiceCreds(acc);
-                if (serviceCreds != null) {
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println("Using serviceCreds");
-                    }
-                }
-                if (serviceCreds == null) {
-                    return false;
-                }
-            }
-            break;
-        }
-        setCipherSuite(suite);
-
-        // set the peer implicit supported signature algorithms
-        if (protocolVersion.useTLS12PlusSpec()) {
-            if (peerSupportedSignAlgs == null) {
-                setPeerSupportedSignAlgs(supportedSignAlgs);
-                // we had alreay update the session
-            }
-        }
-        return true;
-    }
-
-    /*
-     * Get some "ephemeral" RSA keys for this context. This means
-     * generating them if it's not already been done.
-     *
-     * Note that we currently do not implement any ciphersuites that use
-     * strong ephemeral RSA. (We do not support the EXPORT1024 ciphersuites
-     * and standard RSA ciphersuites prohibit ephemeral mode for some reason)
-     * This means that export is always true and 512 bit keys are generated.
-     */
-    private boolean setupEphemeralRSAKeys(boolean export) {
-        KeyPair kp = sslContext.getEphemeralKeyManager().
-                        getRSAKeyPair(export, sslContext.getSecureRandom());
-        if (kp == null) {
-            return false;
-        } else {
-            tempPublicKey = kp.getPublic();
-            tempPrivateKey = kp.getPrivate();
-            return true;
-        }
-    }
-
-    /*
-     * Acquire some "ephemeral" Diffie-Hellman  keys for this handshake.
-     * We don't reuse these, for improved forward secrecy.
-     */
-    private void setupEphemeralDHKeys(
-            NamedGroup namedGroup, boolean export, Key key) {
-        // Are the client and server willing to negotiate FFDHE groups?
-        if ((!useLegacyEphemeralDHKeys) && (!export) && (namedGroup != null)) {
-            dh = new DHCrypt(namedGroup, sslContext.getSecureRandom());
-
-            return;
-        }   // Otherwise, the client is not compatible with FFDHE extension.
-
-        /*
-         * 768 bits ephemeral DH private keys were used to be used in
-         * ServerKeyExchange except that exportable ciphers max out at 512
-         * bits modulus values. We still adhere to this behavior in legacy
-         * mode (system property "jdk.tls.ephemeralDHKeySize" is defined
-         * as "legacy").
-         *
-         * Old JDK (JDK 7 and previous) releases don't support DH keys bigger
-         * than 1024 bits. We have to consider the compatibility requirement.
-         * 1024 bits DH key is always used for non-exportable cipher suites
-         * in default mode (system property "jdk.tls.ephemeralDHKeySize"
-         * is not defined).
-         *
-         * However, if applications want more stronger strength, setting
-         * system property "jdk.tls.ephemeralDHKeySize" to "matched"
-         * is a workaround to use ephemeral DH key which size matches the
-         * corresponding authentication key. For example, if the public key
-         * size of an authentication certificate is 2048 bits, then the
-         * ephemeral DH key size should be 2048 bits accordingly unless
-         * the cipher suite is exportable.  This key sizing scheme keeps
-         * the cryptographic strength consistent between authentication
-         * keys and key-exchange keys.
-         *
-         * Applications may also want to customize the ephemeral DH key size
-         * to a fixed length for non-exportable cipher suites. This can be
-         * approached by setting system property "jdk.tls.ephemeralDHKeySize"
-         * to a valid positive integer between 1024 and 8192 bits, inclusive.
-         *
-         * Note that the minimum acceptable key size is 1024 bits except
-         * exportable cipher suites or legacy mode.
-         *
-         * Note that per RFC 2246, the key size limit of DH is 512 bits for
-         * exportable cipher suites.  Because of the weakness, exportable
-         * cipher suites are deprecated since TLS v1.1 and they are not
-         * enabled by default in Oracle provider. The legacy behavior is
-         * reserved and 512 bits DH key is always used for exportable
-         * cipher suites.
-         */
-        int keySize = export ? 512 : 1024;           // default mode
-        if (!export) {
-            if (useLegacyEphemeralDHKeys) {          // legacy mode
-                keySize = 768;
-            } else if (useSmartEphemeralDHKeys) {    // matched mode
-                if (key != null) {
-                    int ks = KeyUtil.getKeySize(key);
-
-                    // DH parameter generation can be extremely slow, make
-                    // sure to use one of the supported pre-computed DH
-                    // parameters (see DHCrypt class).
-                    //
-                    // Old deployed applications may not be ready to support
-                    // DH key sizes bigger than 2048 bits.  Please DON'T use
-                    // value other than 1024 and 2048 at present.  May improve
-                    // the underlying providers and key size limit in the
-                    // future when the compatibility and interoperability
-                    // impact is limited.
-                    //
-                    // keySize = ks <= 1024 ? 1024 : (ks >= 2048 ? 2048 : ks);
-                    keySize = ks <= 1024 ? 1024 : 2048;
-                } // Otherwise, anonymous cipher suites, 1024-bit is used.
-            } else if (customizedDHKeySize > 0) {    // customized mode
-                keySize = customizedDHKeySize;
-            }
-        }
-
-        dh = new DHCrypt(keySize, sslContext.getSecureRandom());
-    }
-
-    /**
-     * Setup the ephemeral ECDH parameters.
-     */
-    private void setupEphemeralECDHKeys(NamedGroup namedGroup) {
-        ecdh = new ECDHCrypt(namedGroup, sslContext.getSecureRandom());
-    }
-
-    private void setupStaticECDHKeys() {
-        // don't need to check whether the curve is supported, already done
-        // in setupPrivateKeyAndChain().
-        ecdh = new ECDHCrypt(privateKey, certs[0].getPublicKey());
-    }
-
-    /**
-     * Retrieve the server key and certificate for the specified algorithm
-     * from the KeyManager and set the instance variables.
-     *
-     * @return true if successful, false if not available or invalid
-     */
-    private boolean setupPrivateKeyAndChain(String algorithm) {
-        X509ExtendedKeyManager km = sslContext.getX509KeyManager();
-        String alias;
-        if (conn != null) {
-            alias = km.chooseServerAlias(algorithm, null, conn);
-        } else {
-            alias = km.chooseEngineServerAlias(algorithm, null, engine);
-        }
-        if (alias == null) {
-            return false;
-        }
-        PrivateKey tempPrivateKey = km.getPrivateKey(alias);
-        if (tempPrivateKey == null) {
-            return false;
-        }
-        X509Certificate[] tempCerts = km.getCertificateChain(alias);
-        if ((tempCerts == null) || (tempCerts.length == 0)) {
-            return false;
-        }
-        String keyAlgorithm = algorithm.split("_")[0];
-        PublicKey publicKey = tempCerts[0].getPublicKey();
-        if ((tempPrivateKey.getAlgorithm().equals(keyAlgorithm) == false)
-                || (publicKey.getAlgorithm().equals(keyAlgorithm) == false)) {
-            return false;
-        }
-        // For ECC certs, check whether we support the EC domain parameters.
-        // If the client sent a SupportedEllipticCurves ClientHello extension,
-        // check against that too.
-        if (keyAlgorithm.equals("EC")) {
-            if (publicKey instanceof ECPublicKey == false) {
-                return false;
-            }
-            ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
-            NamedGroup namedGroup = NamedGroup.valueOf(params);
-            if ((namedGroup == null) ||
-                (!SupportedGroupsExtension.supports(namedGroup)) ||
-                ((requestedGroups != null) &&
-                        !requestedGroups.contains(namedGroup.id))) {
-                return false;
-            }
-        }
-        this.privateKey = tempPrivateKey;
-        this.certs = tempCerts;
-        return true;
-    }
-
-    /*
-     * Returns premaster secret for external key exchange services.
-     */
-    private SecretKey clientKeyExchange(ClientKeyExchange mesg)
-        throws IOException {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        // Record the principals involved in exchange
-        session.setPeerPrincipal(mesg.getPeerPrincipal());
-        session.setLocalPrincipal(mesg.getLocalPrincipal());
-
-        return mesg.clientKeyExchange();
-    }
-
-    /*
-     * Diffie Hellman key exchange is used when the server presented
-     * D-H parameters in its certificate (signed using RSA or DSS/DSA),
-     * or else the server presented no certificate but sent D-H params
-     * in a ServerKeyExchange message.  Use of D-H is specified by the
-     * cipher suite chosen.
-     *
-     * The message optionally contains the client's D-H public key (if
-     * it wasn't not sent in a client certificate).  As always with D-H,
-     * if a client and a server have each other's D-H public keys and
-     * they use common algorithm parameters, they have a shared key
-     * that's derived via the D-H calculation.  That key becomes the
-     * pre-master secret.
-     */
-    private SecretKey clientKeyExchange(DHClientKeyExchange mesg)
-            throws IOException {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        BigInteger publicKeyValue = mesg.getClientPublicKey();
-
-        // check algorithm constraints
-        dh.checkConstraints(algorithmConstraints, publicKeyValue);
-
-        return dh.getAgreedSecret(publicKeyValue, false);
-    }
-
-    private SecretKey clientKeyExchange(ECDHClientKeyExchange mesg)
-            throws IOException {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        byte[] publicPoint = mesg.getEncodedPoint();
-
-        // check algorithm constraints
-        ecdh.checkConstraints(algorithmConstraints, publicPoint);
-
-        return ecdh.getAgreedSecret(publicPoint);
-    }
-
-    /*
-     * Client wrote a message to verify the certificate it sent earlier.
-     *
-     * Note that this certificate isn't involved in key exchange.  Client
-     * authentication messages are included in the checksums used to
-     * validate the handshake (e.g. Finished messages).  Other than that,
-     * the _exact_ identity of the client is less fundamental to protocol
-     * security than its role in selecting keys via the pre-master secret.
-     */
-    private void clientCertificateVerify(CertificateVerify mesg)
-            throws IOException {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        if (protocolVersion.useTLS12PlusSpec()) {
-            SignatureAndHashAlgorithm signAlg =
-                mesg.getPreferableSignatureAlgorithm();
-            if (signAlg == null) {
-                throw new SSLHandshakeException(
-                        "Illegal CertificateVerify message");
-            }
-
-            String hashAlg =
-                SignatureAndHashAlgorithm.getHashAlgorithmName(signAlg);
-            if (hashAlg == null || hashAlg.length() == 0) {
-                throw new SSLHandshakeException(
-                        "No supported hash algorithm");
-            }
-        }
-
-        try {
-            PublicKey publicKey =
-                session.getPeerCertificates()[0].getPublicKey();
-
-            boolean valid = mesg.verify(protocolVersion, handshakeHash,
-                                        publicKey, session.getMasterSecret());
-            if (valid == false) {
-                fatalSE(Alerts.alert_bad_certificate,
-                            "certificate verify message signature error");
-            }
-        } catch (GeneralSecurityException e) {
-            fatalSE(Alerts.alert_bad_certificate,
-                "certificate verify format error", e);
-        }
-
-        // reset the flag for clientCertificateVerify message
-        needClientVerify = false;
-    }
-
-
-    /*
-     * Client writes "finished" at the end of its handshake, after cipher
-     * spec is changed.   We verify it and then send ours.
-     *
-     * When we're resuming a session, we'll have already sent our own
-     * Finished message so just the verification is needed.
-     */
-    private void clientFinished(Finished mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        /*
-         * Verify if client did send the certificate when client
-         * authentication was required, otherwise server should not proceed
-         */
-        if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
-           // get X500Principal of the end-entity certificate for X509-based
-           // ciphersuites, or Kerberos principal for Kerberos ciphersuites, etc
-           session.getPeerPrincipal();
-        }
-
-        /*
-         * Verify if client did send clientCertificateVerify message following
-         * the client Certificate, otherwise server should not proceed
-         */
-        if (needClientVerify) {
-                fatalSE(Alerts.alert_handshake_failure,
-                        "client did not send certificate verify message");
-        }
-
-        /*
-         * Verify the client's message with the "before" digest of messages,
-         * and forget about continuing to use that digest.
-         */
-        boolean verified = mesg.verify(handshakeHash, Finished.CLIENT,
-            session.getMasterSecret());
-
-        if (!verified) {
-            fatalSE(Alerts.alert_handshake_failure,
-                        "client 'finished' message doesn't verify");
-            // NOTREACHED
-        }
-
-        /*
-         * save client verify data for secure renegotiation
-         */
-        if (secureRenegotiation) {
-            clientVerifyData = mesg.getVerifyData();
-        }
-
-        /*
-         * OK, it verified.  If we're doing the full handshake, add that
-         * "Finished" message to the hash of handshake messages, then send
-         * the change_cipher_spec and Finished message.
-         */
-        if (!resumingSession) {
-            sendChangeCipherAndFinish(true);
-        } else {
-            handshakeFinished = true;
-        }
-
-        /*
-         * Update the session cache only after the handshake completed, else
-         * we're open to an attack against a partially completed handshake.
-         */
-        session.setLastAccessedTime(System.currentTimeMillis());
-        if (!resumingSession && session.isRejoinable()) {
-            ((SSLSessionContextImpl)sslContext.engineGetServerSessionContext())
-                .put(session);
-            if (debug != null && Debug.isOn("session")) {
-                System.out.println(
-                    "%% Cached server session: " + session);
-            }
-        } else if (!resumingSession &&
-                debug != null && Debug.isOn("session")) {
-            System.out.println(
-                "%% Didn't cache non-resumable server session: "
-                + session);
-        }
-    }
-
-    /*
-     * Compute finished message with the "server" digest (and then forget
-     * about that digest, it can't be used again).
-     */
-    private void sendChangeCipherAndFinish(boolean finishedTag)
-            throws IOException {
-
-        // Reload if this message has been reserved.
-        handshakeHash.reload();
-
-        Finished mesg = new Finished(protocolVersion, handshakeHash,
-            Finished.SERVER, session.getMasterSecret(), cipherSuite);
-
-        /*
-         * Send the change_cipher_spec record; then our Finished handshake
-         * message will be the last handshake message.  Flush, and now we
-         * are ready for application data!!
-         */
-        sendChangeCipherSpec(mesg, finishedTag);
-
-        /*
-         * save server verify data for secure renegotiation
-         */
-        if (secureRenegotiation) {
-            serverVerifyData = mesg.getVerifyData();
-        }
-    }
-
-
-    /*
-     * Returns a HelloRequest message to kickstart renegotiations
-     */
-    @Override
-    HandshakeMessage getKickstartMessage() {
-        return new HelloRequest();
-    }
-
-
-    /*
-     * 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);
-        }
-
-        /*
-         * It's ok to get a no_certificate alert from a client of which
-         * we *requested* authentication information.
-         * However, if we *required* it, then this is not acceptable.
-         *
-         * Anyone calling getPeerCertificates() on the
-         * session will get an SSLPeerUnverifiedException.
-         */
-        if ((description == Alerts.alert_no_certificate) &&
-                (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) {
-            return;
-        }
-
-        throw new SSLProtocolException("handshake alert: " + message);
-    }
-
-    /*
-     * RSA key exchange is normally used.  The client encrypts a "pre-master
-     * secret" with the server's public key, from the Certificate (or else
-     * ServerKeyExchange) message that was sent to it by the server.  That's
-     * decrypted using the private key before we get here.
-     */
-    private SecretKey clientKeyExchange(RSAClientKeyExchange mesg)
-            throws IOException {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        return mesg.preMaster;
-    }
-
-    /*
-     * Verify the certificate sent by the client. We'll only get one if we
-     * sent a CertificateRequest to request client authentication. If we
-     * are in TLS mode, the client may send a message with no certificates
-     * to indicate it does not have an appropriate chain. (In SSLv3 mode,
-     * it would send a no certificate alert).
-     */
-    private void clientCertificate(CertificateMsg mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        X509Certificate[] peerCerts = mesg.getCertificateChain();
-
-        if (peerCerts.length == 0) {
-            /*
-             * If the client authentication is only *REQUESTED* (e.g.
-             * not *REQUIRED*, this is an acceptable condition.)
-             */
-            if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) {
-                return;
-            } else {
-                fatalSE(Alerts.alert_bad_certificate,
-                    "null cert chain");
-            }
-        }
-
-        // ask the trust manager to verify the chain
-        X509TrustManager tm = sslContext.getX509TrustManager();
-
-        try {
-            // find out the types of client authentication used
-            PublicKey key = peerCerts[0].getPublicKey();
-            String keyAlgorithm = key.getAlgorithm();
-            String authType;
-            if (keyAlgorithm.equals("RSA")) {
-                authType = "RSA";
-            } else if (keyAlgorithm.equals("DSA")) {
-                authType = "DSA";
-            } else if (keyAlgorithm.equals("EC")) {
-                authType = "EC";
-            } else {
-                // unknown public key type
-                authType = "UNKNOWN";
-            }
-
-            if (tm instanceof X509ExtendedTrustManager) {
-                if (conn != null) {
-                    ((X509ExtendedTrustManager)tm).checkClientTrusted(
-                        peerCerts.clone(),
-                        authType,
-                        conn);
-                } else {
-                    ((X509ExtendedTrustManager)tm).checkClientTrusted(
-                        peerCerts.clone(),
-                        authType,
-                        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.
-            fatalSE(Alerts.alert_certificate_unknown, e);
-        }
-        // set the flag for clientCertificateVerify message
-        needClientVerify = true;
-
-        session.setPeerCertificates(peerCerts);
-    }
-
-    private StaplingParameters processStapling(ClientHello mesg) {
-        StaplingParameters params = null;
-        ExtensionType ext = null;
-        StatusRequestType type = null;
-        StatusRequest req = null;
-        Map<X509Certificate, byte[]> responses;
-
-        // If this feature has not been enabled, then no more processing
-        // is necessary.  Also we will only staple if we're doing a full
-        // handshake.
-        if (!sslContext.isStaplingEnabled(false) || resumingSession) {
-            return null;
-        }
-
-        // Check if the client has asserted the status_request[_v2] extension(s)
-        CertStatusReqExtension statReqExt = (CertStatusReqExtension)
-                    mesg.extensions.get(ExtensionType.EXT_STATUS_REQUEST);
-        CertStatusReqListV2Extension statReqExtV2 =
-                (CertStatusReqListV2Extension)mesg.extensions.get(
-                        ExtensionType.EXT_STATUS_REQUEST_V2);
-
-        // Determine which type of stapling we are doing and assert the
-        // proper extension in the server hello.
-        // Favor status_request_v2 over status_request and ocsp_multi
-        // over ocsp.
-        // If multiple ocsp or ocsp_multi types exist, select the first
-        // instance of a given type.  Also since we don't support ResponderId
-        // selection yet, only accept a request if the ResponderId field
-        // is empty.
-        if (statReqExtV2 != null) {             // RFC 6961 stapling
-            ext = ExtensionType.EXT_STATUS_REQUEST_V2;
-            List<CertStatusReqItemV2> reqItems =
-                    statReqExtV2.getRequestItems();
-            int ocspIdx = -1;
-            int ocspMultiIdx = -1;
-            for (int pos = 0; (pos < reqItems.size() &&
-                    (ocspIdx == -1 || ocspMultiIdx == -1)); pos++) {
-                CertStatusReqItemV2 item = reqItems.get(pos);
-                StatusRequestType curType = item.getType();
-                if (ocspIdx < 0 && curType == StatusRequestType.OCSP) {
-                    OCSPStatusRequest ocspReq =
-                            (OCSPStatusRequest)item.getRequest();
-                    if (ocspReq.getResponderIds().isEmpty()) {
-                        ocspIdx = pos;
-                    }
-                } else if (ocspMultiIdx < 0 &&
-                        curType == StatusRequestType.OCSP_MULTI) {
-                    // If the type is OCSP, then the request
-                    // is guaranteed to be OCSPStatusRequest
-                    OCSPStatusRequest ocspReq =
-                            (OCSPStatusRequest)item.getRequest();
-                    if (ocspReq.getResponderIds().isEmpty()) {
-                        ocspMultiIdx = pos;
-                    }
-                }
-            }
-            if (ocspMultiIdx >= 0) {
-                type = reqItems.get(ocspMultiIdx).getType();
-                req = reqItems.get(ocspMultiIdx).getRequest();
-            } else if (ocspIdx >= 0) {
-                type = reqItems.get(ocspIdx).getType();
-                req = reqItems.get(ocspIdx).getRequest();
-            } else {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Warning: No suitable request " +
-                            "found in the status_request_v2 extension.");
-                }
-            }
-        }
-
-        // Only attempt to process a status_request extension if:
-        // * The status_request extension is set AND
-        // * either the status_request_v2 extension is not present OR
-        // * none of the underlying OCSPStatusRequest structures is suitable
-        // for stapling.
-        // If either of the latter two bullet items is true the ext, type and
-        // req variables should all be null.  If any are null we will try
-        // processing an asserted status_request.
-        if ((statReqExt != null) &&
-               (ext == null || type == null || req == null)) {
-            ext = ExtensionType.EXT_STATUS_REQUEST;
-            type = statReqExt.getType();
-            if (type == StatusRequestType.OCSP) {
-                // If the type is OCSP, then the request is guaranteed
-                // to be OCSPStatusRequest
-                OCSPStatusRequest ocspReq =
-                        (OCSPStatusRequest)statReqExt.getRequest();
-                if (ocspReq.getResponderIds().isEmpty()) {
-                    req = ocspReq;
-                } else {
-                    if (debug != null && Debug.isOn("handshake")) {
-                        req = null;
-                        System.out.println("Warning: No suitable request " +
-                                "found in the status_request extension.");
-                    }
-                }
-            }
-        }
-
-        // If, after walking through the extensions we were unable to
-        // find a suitable StatusRequest, then stapling is disabled.
-        // The ext, type and req variables must have been set to continue.
-        if (type == null || req == null || ext == null) {
-            return null;
-        }
-
-        // Get the OCSP responses from the StatusResponseManager
-        StatusResponseManager statRespMgr =
-                sslContext.getStatusResponseManager();
-        if (statRespMgr != null) {
-            responses = statRespMgr.get(type, req, certs, statusRespTimeout,
-                    TimeUnit.MILLISECONDS);
-            if (!responses.isEmpty()) {
-                // If this RFC 6066-style stapling (SSL cert only) then the
-                // response cannot be zero length
-                if (type == StatusRequestType.OCSP) {
-                    byte[] respDER = responses.get(certs[0]);
-                    if (respDER == null || respDER.length <= 0) {
-                        return null;
-                    }
-                }
-                params = new StaplingParameters(ext, type, req, responses);
-            }
-        } else {
-            // This should not happen, but if lazy initialization of the
-            // StatusResponseManager doesn't occur we should turn off stapling.
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Warning: lazy initialization " +
-                        "of the StatusResponseManager failed.  " +
-                        "Stapling has been disabled.");
-            }
-        }
-
-        return params;
-    }
-
-    /**
-     * Inner class used to hold stapling parameters needed by the handshaker
-     * when stapling is active.
-     */
-    private class StaplingParameters {
-        private final ExtensionType statusRespExt;
-        private final StatusRequestType statReqType;
-        private final StatusRequest statReqData;
-        private final Map<X509Certificate, byte[]> responseMap;
-
-        StaplingParameters(ExtensionType ext, StatusRequestType type,
-                StatusRequest req, Map<X509Certificate, byte[]> responses) {
-            statusRespExt = ext;
-            statReqType = type;
-            statReqData = req;
-            responseMap = responses;
-        }
-    }
-}