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

Print this page

        

@@ -56,11 +56,11 @@
  * @author David Brownell
  */
 final class ServerHandshaker extends Handshaker {
 
     // is the server going to require the client to authenticate?
-    private byte                doClientAuth;
+    private ClientAuthType      doClientAuth;
 
     // our authentication info
     private X509Certificate[]   certs;
     private PrivateKey          privateKey;
 

@@ -141,44 +141,45 @@
 
     /*
      * Constructor ... use the keys found in the auth context.
      */
     ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,
-            ProtocolList enabledProtocols, byte clientAuth,
+            ProtocolList enabledProtocols, ClientAuthType clientAuth,
             ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,
             boolean secureRenegotiation,
             byte[] clientVerifyData, byte[] serverVerifyData) {
 
         super(socket, context, enabledProtocols,
-                (clientAuth != SSLEngineImpl.clauth_none), false,
+                (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
                 activeProtocolVersion, isInitialHandshake, secureRenegotiation,
                 clientVerifyData, serverVerifyData);
         doClientAuth = clientAuth;
     }
 
     /*
      * Constructor ... use the keys found in the auth context.
      */
     ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,
-            ProtocolList enabledProtocols, byte clientAuth,
+            ProtocolList enabledProtocols, ClientAuthType clientAuth,
             ProtocolVersion activeProtocolVersion,
             boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
+            byte[] clientVerifyData, byte[] serverVerifyData,
+            boolean isDTLS) {
 
         super(engine, context, enabledProtocols,
-                (clientAuth != SSLEngineImpl.clauth_none), false,
+                (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
                 activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-                clientVerifyData, serverVerifyData);
+                clientVerifyData, serverVerifyData, isDTLS);
         doClientAuth = clientAuth;
     }
 
     /*
      * 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(byte clientAuth) {
+    void setClientAuth(ClientAuthType clientAuth) {
         doClientAuth = clientAuth;
     }
 
     /*
      * This routine handles all the server side handshake messages, one at

@@ -190,38 +191,34 @@
      * responses as needed using the connection in the constructor.
      */
     @Override
     void processMessage(byte type, int message_len)
             throws IOException {
-        //
-        // In SSLv3 and TLS, messages follow strictly increasing
-        // numerical order _except_ for one annoying special case.
-        //
-        if ((state >= type)
-                && (state != HandshakeMessage.ht_client_key_exchange
-                    && type != HandshakeMessage.ht_certificate_verify)) {
-            throw new SSLProtocolException(
-                    "Handshake message sequence violation, state = " + state
-                    + ", type = " + type);
-        }
 
+        // check the handshake state
+        handshakeState.check(type);
+
         switch (type) {
             case HandshakeMessage.ht_client_hello:
-                ClientHello ch = new ClientHello(input, message_len);
+                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 == SSLEngineImpl.clauth_none) {
+                if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) {
                     fatalSE(Alerts.alert_unexpected_message,
                                 "client sent unsolicited cert chain");
                     // NOTREACHED
                 }
-                this.clientCertificate(new CertificateMsg(input));
+                CertificateMsg certificateMsg = new CertificateMsg(input);
+                handshakeState.update(certificateMsg, resumingSession);
+                this.clientCertificate(certificateMsg);
                 break;
 
             case HandshakeMessage.ht_client_key_exchange:
                 SecretKey preMasterSecret;
                 switch (keyExchange) {

@@ -235,21 +232,24 @@
                      */
                     RSAClientKeyExchange pms = new RSAClientKeyExchange(
                             protocolVersion, clientRequestedVersion,
                             sslContext.getSecureRandom(), input,
                             message_len, privateKey);
+                    handshakeState.update(pms, resumingSession);
                     preMasterSecret = this.clientKeyExchange(pms);
                     break;
                 case K_KRB5:
                 case K_KRB5_EXPORT:
-                    preMasterSecret = this.clientKeyExchange(
+                    KerberosClientKeyExchange kke =
                         new KerberosClientKeyExchange(protocolVersion,
                             clientRequestedVersion,
                             sslContext.getSecureRandom(),
                             input,
                             this.getAccSE(),
-                            serviceCreds));
+                            serviceCreds);
+                    handshakeState.update(kke, resumingSession);
+                    preMasterSecret = this.clientKeyExchange(kke);
                     break;
                 case K_DHE_RSA:
                 case K_DHE_DSS:
                 case K_DH_ANON:
                     /*

@@ -256,20 +256,23 @@
                      * 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!
                      */
-                    preMasterSecret = this.clientKeyExchange(
-                            new DHClientKeyExchange(input));
+                    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:
-                    preMasterSecret = this.clientKeyExchange
-                                            (new ECDHClientKeyExchange(input));
+                    ECDHClientKeyExchange ecdhcke =
+                                    new ECDHClientKeyExchange(input);
+                    handshakeState.update(ecdhcke, resumingSession);
+                    preMasterSecret = this.clientKeyExchange(ecdhcke);
                     break;
                 default:
                     throw new SSLProtocolException
                         ("Unrecognized key exchange: " + keyExchange);
                 }

@@ -280,43 +283,32 @@
                 //
                 calculateKeys(preMasterSecret, clientRequestedVersion);
                 break;
 
             case HandshakeMessage.ht_certificate_verify:
-                this.clientCertificateVerify(new CertificateVerify(input,
-                            localSupportedSignAlgs, protocolVersion));
+                CertificateVerify cvm =
+                        new CertificateVerify(input,
+                            localSupportedSignAlgs, protocolVersion);
+                handshakeState.update(cvm, resumingSession);
+                this.clientCertificateVerify(cvm);
+
                 break;
 
             case HandshakeMessage.ht_finished:
-                // A ChangeCipherSpec record must have been received prior to
-                // reception of the Finished message (RFC 5246, 7.4.9).
-                if (!receivedChangeCipherSpec()) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Received Finished message before ChangeCipherSpec");
-                }
+                Finished cfm =
+                    new Finished(protocolVersion, input, cipherSuite);
+                handshakeState.update(cfm, resumingSession);
+                this.clientFinished(cfm);
 
-                this.clientFinished(
-                    new Finished(protocolVersion, input, cipherSuite));
                 break;
 
             default:
                 throw new SSLProtocolException(
                         "Illegal server handshake msg, " + type);
         }
 
-        //
-        // Move state machine forward if the message handling
-        // code didn't already do so
-        //
-        if (state < type) {
-            if(type == HandshakeMessage.ht_certificate_verify) {
-                state = type + 2;    // an annoying special case
-            } else {
-                state = type;
             }
-        }
-    }
 
 
     /*
      * ClientHello presents the server with a bunch of options, to which the
      * server replies with a ServerHello listing the ones which this session

@@ -342,11 +334,11 @@
         // 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 &&
-                state != HandshakeMessage.ht_hello_request) {
+                !serverHelloRequested) {
             fatalSE(Alerts.alert_handshake_failure,
                 "Client initiated renegotiation is not allowed");
         }
 
         // check the server name indication if required

@@ -436,11 +428,11 @@
                     System.out.println("Warning: No renegotiation " +
                         "indication in ClientHello, allow legacy ClientHello");
                 }
             } else if (!allowUnsafeRenegotiation) {
                 // abort the handshake
-                if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) {
+                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.

@@ -478,16 +470,57 @@
                             "Warning: continue with insecure renegotiation");
                 }
             }
         }
 
-        /*
-         * Always make sure this entire record has been digested before we
-         * start emitting output, to ensure correct digesting order.
-         */
-        input.digestNow();
+        // 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");
+            }
+        }
+
+        // cookie exchange 
+        if (isDTLS) {
+             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.

@@ -578,11 +611,11 @@
                             "is not identical to the previous one");
                     }
                 }
 
                 if (resumingSession &&
-                        (doClientAuth == SSLEngineImpl.clauth_required)) {
+                        (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) {
                     try {
                         previous.getPeerPrincipal();
                     } catch (SSLPeerUnverifiedException e) {
                         resumingSession = false;
                     }

@@ -675,11 +708,11 @@
             supportedCurves = (SupportedEllipticCurvesExtension)
                         mesg.extensions.get(ExtensionType.EXT_ELLIPTIC_CURVES);
 
             // We only need to handle the "signature_algorithm" extension
             // for full handshakes and TLS 1.2 or later.
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 SignatureAlgorithmsExtension signAlgs =
                     (SignatureAlgorithmsExtension)mesg.extensions.get(
                                     ExtensionType.EXT_SIGNATURE_ALGORITHMS);
                 if (signAlgs != null) {
                     Collection<SignatureAndHashAlgorithm> peerSignAlgs =

@@ -706,11 +739,11 @@
             session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL,
                         getLocalSupportedSignAlgs(),
                         sslContext.getSecureRandom(),
                         getHostAddressSE(), getPortSE());
 
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 if (peerSupportedSignAlgs != null) {
                     session.setPeerSupportedSignatureAlgorithms(
                             peerSupportedSignAlgs);
                 }   // else, we will set the implicit peer supported signature
                     // algorithms in chooseCipherSuite()

@@ -732,16 +765,45 @@
 
             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.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
         }
 
         m1.cipherSuite = cipherSuite;
         m1.sessionId = session.getSessionId();

@@ -769,23 +831,36 @@
                 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 (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;
         }
 
 
         /*

@@ -813,10 +888,11 @@
             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...

@@ -851,13 +927,13 @@
                         tempPublicKey, privateKey,
                         clnt_random, svr_random,
                         sslContext.getSecureRandom());
                     privateKey = tempPrivateKey;
                 } catch (GeneralSecurityException e) {
-                    throwSSLException
-                        ("Error generating RSA server key exchange", 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;
             }

@@ -871,12 +947,13 @@
                     svr_random.random_bytes,
                     sslContext.getSecureRandom(),
                     preferableSignatureAlgorithm,
                     protocolVersion);
             } catch (GeneralSecurityException e) {
-                throwSSLException("Error generating DH server key exchange", 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;

@@ -890,13 +967,13 @@
                     svr_random.random_bytes,
                     sslContext.getSecureRandom(),
                     preferableSignatureAlgorithm,
                     protocolVersion);
             } catch (GeneralSecurityException e) {
-                throwSSLException(
-                    "Error generating ECDH server key exchange", 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

@@ -908,10 +985,11 @@
         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

@@ -921,19 +999,19 @@
         //
         // Needed only if server requires client to authenticate self.
         // Illegal for anonymous flavors, so we need to check that.
         //
         // CertificateRequest is omitted for Kerberos ciphers
-        if (doClientAuth != SSLEngineImpl.clauth_none &&
+        if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
                 keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
                 keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) {
 
             CertificateRequest m4;
             X509Certificate caCerts[];
 
             Collection<SignatureAndHashAlgorithm> localSignAlgs = null;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            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();

@@ -957,10 +1035,11 @@
 
             if (debug != null && Debug.isOn("handshake")) {
                 m4.print(System.out);
             }
             m4.write(output);
+            handshakeState.update(m4, resumingSession);
         }
 
         /*
          * FIFTH, say ServerHelloDone.
          */

@@ -968,10 +1047,11 @@
 
         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

@@ -998,11 +1078,11 @@
         for (CipherSuite suite : prefered.collection()) {
             if (isNegotiable(proposed, suite) == false) {
                 continue;
             }
 
-            if (doClientAuth == SSLEngineImpl.clauth_required) {
+            if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
                 if ((suite.keyExchange == K_DH_ANON) ||
                     (suite.keyExchange == K_ECDH_ANON)) {
                     continue;
                 }
             }

@@ -1041,16 +1121,16 @@
         if (suite.isNegotiable() == false) {
             return false;
         }
 
         // must not negotiate the obsoleted weak cipher suites.
-        if (protocolVersion.v >= suite.obsoleted) {
+        if (protocolVersion.obsoletes(suite)) {
             return false;
         }
 
         // must not negotiate unsupported cipher suites.
-        if (protocolVersion.v < suite.supported) {
+        if (!protocolVersion.supports(suite)) {
             return false;
         }
 
         KeyExchange keyExchange = suite.keyExchange;
 

@@ -1060,11 +1140,11 @@
         dh = null;
         tempPrivateKey = null;
         tempPublicKey = null;
 
         Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null;
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             if (peerSupportedSignAlgs != null) {
                 supportedSignAlgs = peerSupportedSignAlgs;
             } else {
                 SignatureAndHashAlgorithm algorithm = null;
 

@@ -1149,11 +1229,11 @@
             if (setupPrivateKeyAndChain("RSA") == false) {
                 return false;
             }
 
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                         supportedSignAlgs, "RSA", privateKey);
                 if (preferableSignatureAlgorithm == null) {
                     return false;

@@ -1167,11 +1247,11 @@
             if (setupPrivateKeyAndChain("RSA") == false) {
                 return false;
             }
 
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                         supportedSignAlgs, "RSA", privateKey);
                 if (preferableSignatureAlgorithm == null) {
                     return false;

@@ -1182,11 +1262,11 @@
                 return false;
             }
             break;
         case K_DHE_DSS:
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                                 supportedSignAlgs, "DSA");
                 if (preferableSignatureAlgorithm == null) {
                     return false;

@@ -1200,11 +1280,11 @@
 
             setupEphemeralDHKeys(suite.exportable, privateKey);
             break;
         case K_ECDHE_ECDSA:
             // get preferable peer signature algorithm for server key exchange
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+            if (protocolVersion.useTLS12PlusSpec()) {
                 preferableSignatureAlgorithm =
                     SignatureAndHashAlgorithm.getPreferableAlgorithm(
                                             supportedSignAlgs, "ECDSA");
                 if (preferableSignatureAlgorithm == null) {
                     return false;

@@ -1255,11 +1335,11 @@
             throw new RuntimeException("Unrecognized cipherSuite: " + suite);
         }
         setCipherSuite(suite);
 
         // set the peer implicit supported signature algorithms
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             if (peerSupportedSignAlgs == null) {
                 setPeerSupportedSignAlgs(supportedSignAlgs);
                 // we had alreay update the session
             }
         }

@@ -1569,11 +1649,11 @@
 
         if (debug != null && Debug.isOn("handshake")) {
             mesg.print(System.out);
         }
 
-        if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
+        if (protocolVersion.useTLS12PlusSpec()) {
             SignatureAndHashAlgorithm signAlg =
                 mesg.getPreferableSignatureAlgorithm();
             if (signAlg == null) {
                 throw new SSLHandshakeException(
                         "Illegal CertificateVerify message");

@@ -1621,11 +1701,11 @@
 
         /*
          * Verify if client did send the certificate when client
          * authentication was required, otherwise server should not proceed
          */
-        if (doClientAuth == SSLEngineImpl.clauth_required) {
+        if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
            // get X500Principal of the end-entity certificate for X509-based
            // ciphersuites, or Kerberos principal for Kerberos ciphersuites
            session.getPeerPrincipal();
         }
 

@@ -1662,12 +1742,13 @@
          * 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) {
-            input.digestNow();
             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.

@@ -1693,11 +1774,12 @@
      * about that digest, it can't be used again).
      */
     private void sendChangeCipherAndFinish(boolean finishedTag)
             throws IOException {
 
-        output.flush();
+        // Reload if this message has been reserved.
+        handshakeHash.reload();
 
         Finished mesg = new Finished(protocolVersion, handshakeHash,
             Finished.SERVER, session.getMasterSecret(), cipherSuite);
 
         /*

@@ -1711,21 +1793,11 @@
          * save server verify data for secure renegotiation
          */
         if (secureRenegotiation) {
             serverVerifyData = mesg.getVerifyData();
         }
-
-        /*
-         * Update state machine so client MUST send 'finished' next
-         * The update should only take place if it is not in the fast
-         * handshake mode since the server has to wait for a finished
-         * message from the client.
-         */
-        if (finishedTag) {
-            state = HandshakeMessage.ht_finished;
         }
-    }
 
 
     /*
      * Returns a HelloRequest message to kickstart renegotiations
      */

@@ -1755,11 +1827,11 @@
          *
          * Anyone calling getPeerCertificates() on the
          * session will get an SSLPeerUnverifiedException.
          */
         if ((description == Alerts.alert_no_certificate) &&
-                (doClientAuth == SSLEngineImpl.clauth_requested)) {
+                (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) {
             return;
         }
 
         throw new SSLProtocolException("handshake alert: " + message);
     }

@@ -1796,11 +1868,11 @@
         if (peerCerts.length == 0) {
             /*
              * If the client authentication is only *REQUESTED* (e.g.
              * not *REQUIRED*, this is an acceptable condition.)
              */
-            if (doClientAuth == SSLEngineImpl.clauth_requested) {
+            if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) {
                 return;
             } else {
                 fatalSE(Alerts.alert_bad_certificate,
                     "null cert chain");
             }