8196584: TLS 1.3 Implementation
authorxuelei
Mon, 25 Jun 2018 13:41:39 -0700
changeset 50768 68fa3d4026ea
parent 50767 356eaea05bf0
child 50769 1bf8f9840705
8196584: TLS 1.3 Implementation Reviewed-by: ascarpino, coffeys, dfuchs, jjiang, jnimeh, mullan, rhalade, ssahoo, valeriep, weijun, wetmore, xuelei Contributed-by: Adam Petcher <adam.petcher@oracle.com>, Amanda Jiang <amanda.jiang@oracle.com>, Anthony Scarpino <anthony.scarpino@oracle.com>, Bradford Wetmore <bradford.wetmore@oracle.com>, Jamil Nimeh <jamil.j.nimeh@oracle.com>, John Jiang <sha.jiang@oracle.com>, Rajan Halade <rajan.halade@oracle.com>, Sibabrata Sahoo <sibabrata.sahoo@oracle.com>, Valerie Peng <valerie.peng@oracle.com>, Weijun Wang <weijun.wang@oracle.com>, Xuelei Fan <xuelei.fan@oracle.com>
src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java
src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java
src/java.base/share/classes/module-info.java
src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java
src/java.base/share/classes/sun/security/ssl/ALPNExtension.java
src/java.base/share/classes/sun/security/ssl/Alert.java
src/java.base/share/classes/sun/security/ssl/Alerts.java
src/java.base/share/classes/sun/security/ssl/AlpnExtension.java
src/java.base/share/classes/sun/security/ssl/AppInputStream.java
src/java.base/share/classes/sun/security/ssl/AppOutputStream.java
src/java.base/share/classes/sun/security/ssl/Authenticator.java
src/java.base/share/classes/sun/security/ssl/BaseSSLSocketImpl.java
src/java.base/share/classes/sun/security/ssl/ByteBufferInputStream.java
src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java
src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java
src/java.base/share/classes/sun/security/ssl/CertStatusReqExtension.java
src/java.base/share/classes/sun/security/ssl/CertStatusReqItemV2.java
src/java.base/share/classes/sun/security/ssl/CertStatusReqListV2Extension.java
src/java.base/share/classes/sun/security/ssl/CertificateMessage.java
src/java.base/share/classes/sun/security/ssl/CertificateRequest.java
src/java.base/share/classes/sun/security/ssl/CertificateStatus.java
src/java.base/share/classes/sun/security/ssl/CertificateVerify.java
src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java
src/java.base/share/classes/sun/security/ssl/CipherBox.java
src/java.base/share/classes/sun/security/ssl/CipherSuite.java
src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java
src/java.base/share/classes/sun/security/ssl/CipherType.java
src/java.base/share/classes/sun/security/ssl/Ciphertext.java
src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java
src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java
src/java.base/share/classes/sun/security/ssl/ClientHello.java
src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java
src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java
src/java.base/share/classes/sun/security/ssl/ConnectionContext.java
src/java.base/share/classes/sun/security/ssl/ContentType.java
src/java.base/share/classes/sun/security/ssl/CookieExtension.java
src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java
src/java.base/share/classes/sun/security/ssl/DHCrypt.java
src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java
src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java
src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java
src/java.base/share/classes/sun/security/ssl/DTLSRecord.java
src/java.base/share/classes/sun/security/ssl/Debug.java
src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java
src/java.base/share/classes/sun/security/ssl/ECDHCrypt.java
src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java
src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java
src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java
src/java.base/share/classes/sun/security/ssl/EllipticPointFormatsExtension.java
src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java
src/java.base/share/classes/sun/security/ssl/EphemeralKeyManager.java
src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java
src/java.base/share/classes/sun/security/ssl/ExtensionType.java
src/java.base/share/classes/sun/security/ssl/Finished.java
src/java.base/share/classes/sun/security/ssl/HKDF.java
src/java.base/share/classes/sun/security/ssl/HandshakeAbsence.java
src/java.base/share/classes/sun/security/ssl/HandshakeConsumer.java
src/java.base/share/classes/sun/security/ssl/HandshakeContext.java
src/java.base/share/classes/sun/security/ssl/HandshakeHash.java
src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java
src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java
src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java
src/java.base/share/classes/sun/security/ssl/HandshakeProducer.java
src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java
src/java.base/share/classes/sun/security/ssl/Handshaker.java
src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java
src/java.base/share/classes/sun/security/ssl/HelloExtension.java
src/java.base/share/classes/sun/security/ssl/HelloExtensions.java
src/java.base/share/classes/sun/security/ssl/HelloRequest.java
src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java
src/java.base/share/classes/sun/security/ssl/InputRecord.java
src/java.base/share/classes/sun/security/ssl/JsseJce.java
src/java.base/share/classes/sun/security/ssl/KeyManagerFactoryImpl.java
src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java
src/java.base/share/classes/sun/security/ssl/KeyUpdate.java
src/java.base/share/classes/sun/security/ssl/MAC.java
src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java
src/java.base/share/classes/sun/security/ssl/MaxFragmentLengthExtension.java
src/java.base/share/classes/sun/security/ssl/NamedGroup.java
src/java.base/share/classes/sun/security/ssl/NamedGroupType.java
src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java
src/java.base/share/classes/sun/security/ssl/OCSPStatusRequest.java
src/java.base/share/classes/sun/security/ssl/OutputRecord.java
src/java.base/share/classes/sun/security/ssl/Plaintext.java
src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java
src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java
src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java
src/java.base/share/classes/sun/security/ssl/ProtocolList.java
src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java
src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java
src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java
src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java
src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java
src/java.base/share/classes/sun/security/ssl/RSASignature.java
src/java.base/share/classes/sun/security/ssl/RandomCookie.java
src/java.base/share/classes/sun/security/ssl/Record.java
src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java
src/java.base/share/classes/sun/security/ssl/RenegotiationInfoExtension.java
src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java
src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java
src/java.base/share/classes/sun/security/ssl/SSLAuthentication.java
src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java
src/java.base/share/classes/sun/security/ssl/SSLCipher.java
src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java
src/java.base/share/classes/sun/security/ssl/SSLConsumer.java
src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java
src/java.base/share/classes/sun/security/ssl/SSLCredentials.java
src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java
src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java
src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java
src/java.base/share/classes/sun/security/ssl/SSLExtension.java
src/java.base/share/classes/sun/security/ssl/SSLExtensions.java
src/java.base/share/classes/sun/security/ssl/SSLHandshake.java
src/java.base/share/classes/sun/security/ssl/SSLHandshakeBinding.java
src/java.base/share/classes/sun/security/ssl/SSLKeyAgreement.java
src/java.base/share/classes/sun/security/ssl/SSLKeyAgreementGenerator.java
src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java
src/java.base/share/classes/sun/security/ssl/SSLKeyDerivationGenerator.java
src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java
src/java.base/share/classes/sun/security/ssl/SSLLogger.java
src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java
src/java.base/share/classes/sun/security/ssl/SSLPossession.java
src/java.base/share/classes/sun/security/ssl/SSLPossessionGenerator.java
src/java.base/share/classes/sun/security/ssl/SSLProducer.java
src/java.base/share/classes/sun/security/ssl/SSLRecord.java
src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java
src/java.base/share/classes/sun/security/ssl/SSLServerSocketFactoryImpl.java
src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java
src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java
src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
src/java.base/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java
src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java
src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java
src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java
src/java.base/share/classes/sun/security/ssl/SSLStringizer.java
src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java
src/java.base/share/classes/sun/security/ssl/SSLTransport.java
src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java
src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
src/java.base/share/classes/sun/security/ssl/ServerHello.java
src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java
src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java
src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java
src/java.base/share/classes/sun/security/ssl/SessionId.java
src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java
src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java
src/java.base/share/classes/sun/security/ssl/SignatureScheme.java
src/java.base/share/classes/sun/security/ssl/StatusRequest.java
src/java.base/share/classes/sun/security/ssl/StatusRequestType.java
src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java
src/java.base/share/classes/sun/security/ssl/SunJSSE.java
src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java
src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java
src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java
src/java.base/share/classes/sun/security/ssl/TransportContext.java
src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java
src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java
src/java.base/share/classes/sun/security/ssl/UnknownExtension.java
src/java.base/share/classes/sun/security/ssl/UnknownStatusRequest.java
src/java.base/share/classes/sun/security/ssl/Utilities.java
src/java.base/share/classes/sun/security/ssl/X509Authentication.java
src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java
src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java
src/java.base/share/classes/sun/security/util/HostnameChecker.java
src/java.base/share/conf/security/java.security
src/java.naming/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java
src/java.security.jgss/share/classes/module-info.java
src/java.security.jgss/share/classes/sun/security/jgss/GSSCaller.java
src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java
src/java.security.jgss/share/classes/sun/security/krb5/EncryptedData.java
src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java
src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java
test/jdk/ProblemList.txt
test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java
test/jdk/java/net/httpclient/MockServer.java
test/jdk/javax/net/ssl/DTLS/InvalidRecords.java
test/jdk/javax/net/ssl/SSLEngine/CheckStatus.java
test/jdk/javax/net/ssl/SSLEngine/ConnectionTest.java
test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java
test/jdk/javax/net/ssl/SSLEngine/IllegalHandshakeMessage.java
test/jdk/javax/net/ssl/SSLEngine/IllegalRecordVersion.java
test/jdk/javax/net/ssl/SSLEngine/LargeBufs.java
test/jdk/javax/net/ssl/SSLEngine/NoAuthClientAuth.java
test/jdk/javax/net/ssl/SSLSession/RenegotiateTLS13.java
test/jdk/javax/net/ssl/SSLSession/TestEnabledProtocols.java
test/jdk/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java
test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java
test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java
test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java
test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java
test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java
test/jdk/javax/net/ssl/TLSCommon/CipherSuite.java
test/jdk/javax/net/ssl/TLSCommon/Protocol.java
test/jdk/javax/net/ssl/TLSCommon/SSLEngineTestCase.java
test/jdk/javax/net/ssl/TLSv12/DisabledShortDSAKeys.java
test/jdk/javax/net/ssl/TLSv12/DisabledShortRSAKeys.java
test/jdk/javax/net/ssl/ciphersuites/ECCurvesconstraints.java
test/jdk/javax/net/ssl/etc/README
test/jdk/javax/net/ssl/etc/keystore
test/jdk/javax/net/ssl/etc/truststore
test/jdk/javax/net/ssl/sanity/ciphersuites/CipherSuitesInOrder.java
test/jdk/javax/net/ssl/sanity/interop/CipherTest.java
test/jdk/javax/net/ssl/sanity/interop/ClientJSSEServerJSSE.java
test/jdk/javax/net/ssl/sanity/interop/JSSEClient.java
test/jdk/javax/net/ssl/sanity/interop/JSSEServer.java
test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java
test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java
test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ReadTimeout.java
test/jdk/sun/security/ec/TestEC.java
test/jdk/sun/security/krb5/auto/SSL.java
test/jdk/sun/security/krb5/auto/SSLwithPerms.java
test/jdk/sun/security/krb5/auto/UnboundSSL.java
test/jdk/sun/security/krb5/auto/UnboundSSLMultipleKeys.java
test/jdk/sun/security/krb5/auto/UnboundSSLPrincipalProperty.java
test/jdk/sun/security/krb5/auto/UnboundSSLUtils.java
test/jdk/sun/security/krb5/auto/unbound.ssl.jaas.conf
test/jdk/sun/security/krb5/auto/unbound.ssl.policy
test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java
test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.sh
test/jdk/sun/security/pkcs11/sslecc/CipherTest.java
test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java
test/jdk/sun/security/pkcs11/sslecc/JSSEClient.java
test/jdk/sun/security/ssl/AppOutputStream/NoExceptionOnClose.java
test/jdk/sun/security/ssl/CipherSuite/SSL_NULL.java
test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java
test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.sh
test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java
test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSDefaultProtocols.java
test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSServerDefaultProtocols.java
test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDefaultProtocols.java
test/jdk/sun/security/ssl/SSLContextImpl/CustomizedServerDefaultProtocols.java
test/jdk/sun/security/ssl/SSLContextImpl/DefaultDTLSEnabledProtocols.java
test/jdk/sun/security/ssl/SSLContextImpl/DefaultEnabledProtocols.java
test/jdk/sun/security/ssl/SSLContextImpl/NoOldVersionContext.java
test/jdk/sun/security/ssl/SSLContextImpl/TrustTrustedCert.java
test/jdk/sun/security/ssl/SSLEngineImpl/CloseEngineException.java
test/jdk/sun/security/ssl/SSLEngineImpl/CloseInboundException.java
test/jdk/sun/security/ssl/SSLEngineImpl/EngineEnforceUseClientMode.java
test/jdk/sun/security/ssl/SSLEngineImpl/RehandshakeFinished.java
test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java
test/jdk/sun/security/ssl/SSLEngineImpl/TLS13BeginHandshake.java
test/jdk/sun/security/ssl/SSLSocketImpl/AsyncSSLSocketClose.java
test/jdk/sun/security/ssl/SSLSocketImpl/ClientTimeout.java
test/jdk/sun/security/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java
test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java
test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java
test/jdk/sun/security/ssl/SSLSocketImpl/RejectClientRenego.java
test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java
test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java
test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java
test/jdk/sun/security/ssl/Stapling/TEST.properties
test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java
test/jdk/sun/security/ssl/StatusStapling/RunStatReqSelect.java
test/jdk/sun/security/ssl/StatusStapling/TEST.properties
test/jdk/sun/security/ssl/StatusStapling/TestRun.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/BogusStatusRequest.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqExtensionTests.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqItemV2Tests.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqListV2ExtensionTests.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/OCSPStatusRequestTests.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusReqSelection.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusResponseManagerTests.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestCase.java
test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestUtils.java
test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java
test/jdk/sun/security/ssl/X509TrustManagerImpl/CertRequestOverflow.java
test/jdk/sun/security/ssl/X509TrustManagerImpl/SelfIssuedCert.java
test/jdk/sun/security/ssl/internal/TEST.properties
test/jdk/sun/security/ssl/internal/TestRun.java
test/jdk/sun/security/ssl/internal/java.base/sun/security/ssl/TestHkdf.java
test/jdk/sun/security/tools/keytool/PrintSSL.java
--- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java	Mon Jun 25 13:41:39 2018 -0700
@@ -95,7 +95,7 @@
             premasterMajor = premaster[0] & 0xff;
             premasterMinor = premaster[1] & 0xff;
         } else {
-            // DH, KRB5, others
+            // DH, others
             premasterMajor = -1;
             premasterMinor = -1;
         }
--- a/src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java	Mon Jun 25 13:41:39 2018 -0700
@@ -113,27 +113,19 @@
      * In com.sun.net.ssl.HostnameVerifier the method is defined
      * as verify(String urlHostname, String certHostname).
      * This means we need to extract the hostname from the X.509 certificate
-     * or from the Kerberos principal name, in this wrapper.
+     * in this wrapper.
      */
     public boolean verify(String hostname, javax.net.ssl.SSLSession session) {
         try {
-            String serverName;
-            // Use ciphersuite to determine whether Kerberos is active.
-            if (session.getCipherSuite().startsWith("TLS_KRB5")) {
-                serverName =
-                    HostnameChecker.getServerName(getPeerPrincipal(session));
-
-            } else { // X.509
-                Certificate[] serverChain = session.getPeerCertificates();
-                if ((serverChain == null) || (serverChain.length == 0)) {
-                    return false;
-                }
-                if (serverChain[0] instanceof X509Certificate == false) {
-                    return false;
-                }
-                X509Certificate serverCert = (X509Certificate)serverChain[0];
-                serverName = getServername(serverCert);
+            Certificate[] serverChain = session.getPeerCertificates();
+            if ((serverChain == null) || (serverChain.length == 0)) {
+                return false;
             }
+            if (serverChain[0] instanceof X509Certificate == false) {
+                return false;
+            }
+            X509Certificate serverCert = (X509Certificate)serverChain[0];
+            String serverName = getServername(serverCert);
             if (serverName == null) {
                 return false;
             }
@@ -144,23 +136,6 @@
     }
 
     /*
-     * Get the peer principal from the session
-     */
-    private Principal getPeerPrincipal(javax.net.ssl.SSLSession session)
-        throws javax.net.ssl.SSLPeerUnverifiedException
-    {
-        Principal principal;
-        try {
-            principal = session.getPeerPrincipal();
-        } catch (AbstractMethodError e) {
-            // if the provider does not support it, return null, since
-            // we need it only for Kerberos.
-            principal = null;
-        }
-        return principal;
-    }
-
-    /*
      * Extract the name of the SSL server from the certificate.
      *
      * Note this code is essentially a subset of the hostname extraction
--- a/src/java.base/share/classes/module-info.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/module-info.java	Mon Jun 25 13:41:39 2018 -0700
@@ -360,7 +360,6 @@
     // JDK-internal service types
 
     uses jdk.internal.logger.DefaultLoggerFinder;
-    uses sun.security.ssl.ClientKeyExchangeService;
     uses sun.text.spi.JavaTimeDateTimePatternProvider;
     uses sun.util.spi.CalendarProvider;
     uses sun.util.locale.provider.LocaleDataMetaInfo;
--- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java	Mon Jun 25 13:41:39 2018 -0700
@@ -608,26 +608,17 @@
             HostnameChecker checker = HostnameChecker.getInstance(
                                                 HostnameChecker.TYPE_TLS);
 
-            // Use ciphersuite to determine whether Kerberos is present.
-            if (cipher.startsWith("TLS_KRB5")) {
-                if (!HostnameChecker.match(host, getPeerPrincipal())) {
-                    throw new SSLPeerUnverifiedException("Hostname checker" +
-                                " failed for Kerberos");
-                }
-            } else { // X.509
+            // get the subject's certificate
+            peerCerts = session.getPeerCertificates();
 
-                // get the subject's certificate
-                peerCerts = session.getPeerCertificates();
-
-                X509Certificate peerCert;
-                if (peerCerts[0] instanceof
-                        java.security.cert.X509Certificate) {
-                    peerCert = (java.security.cert.X509Certificate)peerCerts[0];
-                } else {
-                    throw new SSLPeerUnverifiedException("");
-                }
-                checker.match(host, peerCert);
+            X509Certificate peerCert;
+            if (peerCerts[0] instanceof
+                    java.security.cert.X509Certificate) {
+                peerCert = (java.security.cert.X509Certificate)peerCerts[0];
+            } else {
+                throw new SSLPeerUnverifiedException("");
             }
+            checker.match(host, peerCert);
 
             // if it doesn't throw an exception, we passed. Return.
             return;
--- a/src/java.base/share/classes/sun/security/ssl/ALPNExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-import java.nio.charset.*;
-import java.util.*;
-
-import javax.net.ssl.*;
-
-/*
- * [RFC 7301]
- * This TLS extension facilitates the negotiation of application-layer protocols
- * within the TLS handshake. Clients MAY include an extension of type
- * "application_layer_protocol_negotiation" in the (extended) ClientHello
- * message. The "extension_data" field of this extension SHALL contain a
- * "ProtocolNameList" value:
- *
- *     enum {
- *         application_layer_protocol_negotiation(16), (65535)
- *     } ExtensionType;
- *
- *     opaque ProtocolName<1..2^8-1>;
- *
- *     struct {
- *         ProtocolName protocol_name_list<2..2^16-1>
- *     } ProtocolNameList;
- */
-final class ALPNExtension extends HelloExtension {
-
-    final static int ALPN_HEADER_LENGTH = 1;
-    final static int MAX_APPLICATION_PROTOCOL_LENGTH = 255;
-    final static int MAX_APPLICATION_PROTOCOL_LIST_LENGTH = 65535;
-    private int listLength = 0;     // ProtocolNameList length
-    private List<String> protocolNames = null;
-
-    // constructor for ServerHello
-    ALPNExtension(String protocolName) throws SSLException {
-        this(new String[]{ protocolName });
-    }
-
-    // constructor for ClientHello
-    ALPNExtension(String[] protocolNames) throws SSLException {
-        super(ExtensionType.EXT_ALPN);
-        if (protocolNames.length == 0) { // never null, never empty
-            throw new IllegalArgumentException(
-                "The list of application protocols cannot be empty");
-        }
-        this.protocolNames = Arrays.asList(protocolNames);
-        for (String p : protocolNames) {
-            int length = p.getBytes(StandardCharsets.UTF_8).length;
-            if (length == 0) {
-                throw new SSLProtocolException(
-                    "Application protocol name is empty");
-            }
-            if (length <= MAX_APPLICATION_PROTOCOL_LENGTH) {
-                listLength += length + ALPN_HEADER_LENGTH;
-            } else {
-                throw new SSLProtocolException(
-                    "Application protocol name is too long: " + p);
-            }
-            if (listLength > MAX_APPLICATION_PROTOCOL_LIST_LENGTH) {
-                throw new SSLProtocolException(
-                    "Application protocol name list is too long");
-            }
-        }
-    }
-
-    // constructor for ServerHello for parsing ALPN extension
-    ALPNExtension(HandshakeInStream s, int len) throws IOException {
-        super(ExtensionType.EXT_ALPN);
-
-        if (len >= 2) {
-            listLength = s.getInt16(); // list length
-            if (listLength < 2 || listLength + 2 != len) {
-                throw new SSLProtocolException(
-                    "Invalid " + type + " extension: incorrect list length " +
-                    "(length=" + listLength + ")");
-            }
-        } else {
-            throw new SSLProtocolException(
-                "Invalid " + type + " extension: insufficient data " +
-                "(length=" + len + ")");
-        }
-
-        int remaining = listLength;
-        this.protocolNames = new ArrayList<>();
-        while (remaining > 0) {
-            // opaque ProtocolName<1..2^8-1>; // RFC 7301
-            byte[] bytes = s.getBytes8();
-            if (bytes.length == 0) {
-                throw new SSLProtocolException("Invalid " + type +
-                    " extension: empty application protocol name");
-            }
-            String p =
-                new String(bytes, StandardCharsets.UTF_8); // app protocol
-            protocolNames.add(p);
-            remaining -= bytes.length + ALPN_HEADER_LENGTH;
-        }
-
-        if (remaining != 0) {
-            throw new SSLProtocolException(
-                "Invalid " + type + " extension: extra data " +
-                "(length=" + remaining + ")");
-        }
-    }
-
-    List<String> getPeerAPs() {
-        return protocolNames;
-    }
-
-    /*
-     * Return the length in bytes, including extension type and length fields.
-     */
-    @Override
-    int length() {
-        return 6 + listLength;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(listLength + 2); // length of extension_data
-        s.putInt16(listLength);     // length of ProtocolNameList
-
-        for (String p : protocolNames) {
-            s.putBytes8(p.getBytes(StandardCharsets.UTF_8));
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        if (protocolNames == null || protocolNames.isEmpty()) {
-            sb.append("<empty>");
-        } else {
-            for (String protocolName : protocolNames) {
-                sb.append("[" + protocolName + "]");
-            }
-        }
-
-        return "Extension " + type +
-            ", protocol names: " + sb;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/Alert.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+
+/**
+ * SSL/(D)TLS Alter description
+ */
+enum Alert {
+    // Please refer to TLS Alert Registry for the latest (D)TLS Alert values:
+    //     https://www.iana.org/assignments/tls-parameters/
+    CLOSE_NOTIFY            ((byte)0,   "close_notify", false),
+    UNEXPECTED_MESSAGE      ((byte)10,  "unexpected_message", false),
+    BAD_RECORD_MAC          ((byte)20,  "bad_record_mac", false),
+    DECRYPTION_FAILED       ((byte)21,  "decryption_failed", false),
+    RECORD_OVERFLOW         ((byte)22,  "record_overflow", false),
+    DECOMPRESSION_FAILURE   ((byte)30,  "decompression_failure", false),
+    HANDSHAKE_FAILURE       ((byte)40,  "handshake_failure", true),
+    NO_CERTIFICATE          ((byte)41,  "no_certificate", true),
+    BAD_CERTIFICATE         ((byte)42,  "bad_certificate", true),
+    UNSUPPORTED_CERTIFCATE  ((byte)43,  "unsupported_certificate", true),
+    CERTIFICATE_REVOKED     ((byte)44,  "certificate_revoked", true),
+    CERTIFICATE_EXPIRED     ((byte)45,  "certificate_expired", true),
+    CERTIFICATE_UNKNOWN     ((byte)46,  "certificate_unknown", true),
+    ILLEGAL_PARAMETER       ((byte)47,  "illegal_parameter", true),
+    UNKNOWN_CA              ((byte)48,  "unknown_ca", true),
+    ACCESS_DENIED           ((byte)49,  "access_denied", true),
+    DECODE_ERROR            ((byte)50,  "decode_error", true),
+    DECRYPT_ERROR           ((byte)51,  "decrypt_error", true),
+    EXPORT_RESTRICTION      ((byte)60,  "export_restriction", true),
+    PROTOCOL_VERSION        ((byte)70,  "protocol_version", true),
+    INSUFFICIENT_SECURITY   ((byte)71,  "insufficient_security", true),
+    INTERNAL_ERROR          ((byte)80,  "internal_error", false),
+    INAPPROPRIATE_FALLBACK  ((byte)86,  "inappropriate_fallback", false),
+    USER_CANCELED           ((byte)90,  "user_canceled", false),
+    NO_RENEGOTIATION        ((byte)100, "no_renegotiation", true),
+    MISSING_EXTENSION       ((byte)109, "missing_extension", true),
+    UNSUPPORTED_EXTENSION   ((byte)110, "unsupported_extension", true),
+    CERT_UNOBTAINABLE       ((byte)111, "certificate_unobtainable", true),
+    UNRECOGNIZED_NAME       ((byte)112, "unrecognized_name", true),
+    BAD_CERT_STATUS_RESPONSE((byte)113,
+                                    "bad_certificate_status_response", true),
+    BAD_CERT_HASH_VALUE     ((byte)114, "bad_certificate_hash_value", true),
+    UNKNOWN_PSK_IDENTITY    ((byte)115, "unknown_psk_identity", true),
+    CERTIFICATE_REQUIRED    ((byte)116, "certificate_required", true),
+    NO_APPLICATION_PROTOCOL ((byte)120, "no_application_protocol", true);
+
+    // ordinal value of the Alert
+    final byte id;
+
+    // description of the Alert
+    final String description;
+
+    // Does tha alert happen during handshake only?
+    final boolean handshakeOnly;
+
+    // Alert message consumer
+    static final SSLConsumer alertConsumer = new AlertConsumer();
+
+    private Alert(byte id, String description, boolean handshakeOnly) {
+        this.id = id;
+        this.description = description;
+        this.handshakeOnly = handshakeOnly;
+    }
+
+    static Alert valueOf(byte id) {
+        for (Alert al : Alert.values()) {
+            if (al.id == id) {
+                return al;
+            }
+        }
+
+        return null;
+    }
+
+    static String nameOf(byte id) {
+        for (Alert al : Alert.values()) {
+            if (al.id == id) {
+                return al.description;
+            }
+        }
+
+        return "UNKNOWN ALERT (" + (id & 0x0FF) + ")";
+    }
+
+    SSLException createSSLException(String reason) {
+        return createSSLException(reason, null);
+    }
+
+    SSLException createSSLException(String reason, Throwable cause) {
+        if (reason == null) {
+            reason = (cause != null) ? cause.getMessage() : "";
+        }
+
+        SSLException ssle = handshakeOnly ?
+                new SSLHandshakeException(reason) : new SSLException(reason);
+        if (cause != null) {
+            ssle.initCause(cause);
+        }
+
+        return ssle;
+    }
+
+    /**
+     * SSL/(D)TLS Alert level.
+     */
+    enum Level {
+        WARNING ((byte)1, "warning"),
+        FATAL   ((byte)2, "fatal");
+
+        // ordinal value of the Alert level
+        final byte level;
+
+        // description of the Alert level
+        final String description;
+
+        private Level(byte level, String description) {
+            this.level = level;
+            this.description = description;
+        }
+
+        static Level valueOf(byte level) {
+            for (Level lv : Level.values()) {
+                if (lv.level == level) {
+                    return lv;
+                }
+            }
+
+            return null;
+        }
+
+        static String nameOf(byte level) {
+            for (Level lv : Level.values()) {
+                if (lv.level == level) {
+                    return lv.description;
+                }
+            }
+
+            return "UNKNOWN ALERT LEVEL (" + (level & 0x0FF) + ")";
+        }
+    }
+
+    /**
+     * The Alert message.
+     */
+    private static final class AlertMessage {
+        private final byte level;       // level
+        private final byte id;          // description
+
+        AlertMessage(TransportContext context,
+                ByteBuffer m) throws IOException {
+            //  struct {
+            //      AlertLevel level;
+            //      AlertDescription description;
+            //  } Alert;
+            if (m.remaining() != 2) {
+                context.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid Alert message: no sufficient data");
+            }
+
+            this.level = m.get();   // level
+            this.id = m.get();      // description
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"Alert\": '{'\n" +
+                    "  \"level\"      : \"{0}\",\n" +
+                    "  \"description\": \"{1}\"\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            Object[] messageFields = {
+                Level.nameOf(level),
+                Alert.nameOf(id)
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * Consumer of alert messages
+     */
+    private static final class AlertConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private AlertConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer m) throws IOException {
+            TransportContext tc = (TransportContext)context;
+
+            AlertMessage am = new AlertMessage(tc, m);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("Received alert message", am);
+            }
+
+            Level level = Level.valueOf(am.level);
+            Alert alert = Alert.valueOf(am.id);
+            if (alert == Alert.CLOSE_NOTIFY) {
+                if (tc.handshakeContext != null) {
+                    tc.fatal(Alert.UNEXPECTED_MESSAGE,
+                            "Received close_notify during handshake");
+                }
+
+                tc.isInputCloseNotified = true;
+                tc.closeInbound();
+            } else if ((level == Level.WARNING) && (alert != null)) {
+                // Terminate the connection if an alert with a level of warning
+                // is received during handshaking, except the no_certificate
+                // warning.
+                if (alert.handshakeOnly && (tc.handshakeContext != null)) {
+                    // It's OK to get a no_certificate alert from a client of
+                    // which we requested client authentication.  However,
+                    // if we required it, then this is not acceptable.
+                     if (tc.sslConfig.isClientMode ||
+                            alert != Alert.NO_CERTIFICATE ||
+                            (tc.sslConfig.clientAuthType !=
+                                    ClientAuthType.CLIENT_AUTH_REQUESTED)) {
+                        tc.fatal(Alert.HANDSHAKE_FAILURE,
+                            "received handshake warning: " + alert.description);
+                    }  // Otherwise, ignore the warning
+                }   // Otherwise, ignore the warning.
+            } else {    // fatal or unknown
+                String diagnostic;
+                if (alert == null) {
+                    alert = Alert.UNEXPECTED_MESSAGE;
+                    diagnostic = "Unknown alert description (" + am.id + ")";
+                } else {
+                    diagnostic = "Received fatal alert: " + alert.description;
+                }
+
+                tc.fatal(alert, diagnostic, true, null);
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/Alerts.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 2003, 2015, 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 javax.net.ssl.*;
-
-/*
- * A simple class to congregate alerts, their definitions, and common
- * support methods.
- */
-
-final class Alerts {
-
-    /*
-     * Alerts are always a fixed two byte format (level/description).
-     */
-
-    // warnings and fatal errors are package private facilities/constants
-
-    // Alert levels (enum AlertLevel)
-    static final byte           alert_warning = 1;
-    static final byte           alert_fatal = 2;
-
-    /*
-     * Alert descriptions (enum AlertDescription)
-     *
-     * We may not use them all in our processing, but if someone
-     * sends us one, we can at least convert it to a string for the
-     * user.
-     */
-    static final byte           alert_close_notify = 0;
-    static final byte           alert_unexpected_message = 10;
-    static final byte           alert_bad_record_mac = 20;
-    static final byte           alert_decryption_failed = 21;
-    static final byte           alert_record_overflow = 22;
-    static final byte           alert_decompression_failure = 30;
-    static final byte           alert_handshake_failure = 40;
-    static final byte           alert_no_certificate = 41;
-    static final byte           alert_bad_certificate = 42;
-    static final byte           alert_unsupported_certificate = 43;
-    static final byte           alert_certificate_revoked = 44;
-    static final byte           alert_certificate_expired = 45;
-    static final byte           alert_certificate_unknown = 46;
-    static final byte           alert_illegal_parameter = 47;
-    static final byte           alert_unknown_ca = 48;
-    static final byte           alert_access_denied = 49;
-    static final byte           alert_decode_error = 50;
-    static final byte           alert_decrypt_error = 51;
-    static final byte           alert_export_restriction = 60;
-    static final byte           alert_protocol_version = 70;
-    static final byte           alert_insufficient_security = 71;
-    static final byte           alert_internal_error = 80;
-    static final byte           alert_user_canceled = 90;
-    static final byte           alert_no_renegotiation = 100;
-
-    // from RFC 3546 (TLS Extensions)
-    static final byte           alert_unsupported_extension = 110;
-    static final byte           alert_certificate_unobtainable = 111;
-    static final byte           alert_unrecognized_name = 112;
-    static final byte           alert_bad_certificate_status_response = 113;
-    static final byte           alert_bad_certificate_hash_value = 114;
-
-    // from RFC 7301 (TLS ALPN Extension)
-    static final byte           alert_no_application_protocol = 120;
-
-    static String alertDescription(byte code) {
-        switch (code) {
-
-        case alert_close_notify:
-            return "close_notify";
-        case alert_unexpected_message:
-            return "unexpected_message";
-        case alert_bad_record_mac:
-            return "bad_record_mac";
-        case alert_decryption_failed:
-            return "decryption_failed";
-        case alert_record_overflow:
-            return "record_overflow";
-        case alert_decompression_failure:
-            return "decompression_failure";
-        case alert_handshake_failure:
-            return "handshake_failure";
-        case alert_no_certificate:
-            return "no_certificate";
-        case alert_bad_certificate:
-            return "bad_certificate";
-        case alert_unsupported_certificate:
-            return "unsupported_certificate";
-        case alert_certificate_revoked:
-            return "certificate_revoked";
-        case alert_certificate_expired:
-            return "certificate_expired";
-        case alert_certificate_unknown:
-            return "certificate_unknown";
-        case alert_illegal_parameter:
-            return "illegal_parameter";
-        case alert_unknown_ca:
-            return "unknown_ca";
-        case alert_access_denied:
-            return "access_denied";
-        case alert_decode_error:
-            return "decode_error";
-        case alert_decrypt_error:
-            return "decrypt_error";
-        case alert_export_restriction:
-            return "export_restriction";
-        case alert_protocol_version:
-            return "protocol_version";
-        case alert_insufficient_security:
-            return "insufficient_security";
-        case alert_internal_error:
-            return "internal_error";
-        case alert_user_canceled:
-            return "user_canceled";
-        case alert_no_renegotiation:
-            return "no_renegotiation";
-        case alert_unsupported_extension:
-            return "unsupported_extension";
-        case alert_certificate_unobtainable:
-            return "certificate_unobtainable";
-        case alert_unrecognized_name:
-            return "unrecognized_name";
-        case alert_bad_certificate_status_response:
-            return "bad_certificate_status_response";
-        case alert_bad_certificate_hash_value:
-            return "bad_certificate_hash_value";
-        case alert_no_application_protocol:
-            return "no_application_protocol";
-
-        default:
-            return "<UNKNOWN ALERT: " + (code & 0x0ff) + ">";
-        }
-    }
-
-    static SSLException getSSLException(byte description, String reason) {
-        return getSSLException(description, null, reason);
-    }
-
-    /*
-     * Try to be a little more specific in our choice of
-     * exceptions to throw.
-     */
-    static SSLException getSSLException(byte description, Throwable cause,
-            String reason) {
-
-        SSLException e;
-        // the SSLException classes do not have a no-args constructor
-        // make up a message if there is none
-        if (reason == null) {
-            if (cause != null) {
-                reason = cause.toString();
-            } else {
-                reason = "";
-            }
-        }
-        switch (description) {
-        case alert_handshake_failure:
-        case alert_no_certificate:
-        case alert_bad_certificate:
-        case alert_unsupported_certificate:
-        case alert_certificate_revoked:
-        case alert_certificate_expired:
-        case alert_certificate_unknown:
-        case alert_unknown_ca:
-        case alert_access_denied:
-        case alert_decrypt_error:
-        case alert_export_restriction:
-        case alert_insufficient_security:
-        case alert_unsupported_extension:
-        case alert_certificate_unobtainable:
-        case alert_unrecognized_name:
-        case alert_bad_certificate_status_response:
-        case alert_bad_certificate_hash_value:
-        case alert_no_application_protocol:
-            e = new SSLHandshakeException(reason);
-            break;
-
-        case alert_close_notify:
-        case alert_unexpected_message:
-        case alert_bad_record_mac:
-        case alert_decryption_failed:
-        case alert_record_overflow:
-        case alert_decompression_failure:
-        case alert_illegal_parameter:
-        case alert_decode_error:
-        case alert_protocol_version:
-        case alert_internal_error:
-        case alert_user_canceled:
-        case alert_no_renegotiation:
-        default:
-            e = new SSLException(reason);
-            break;
-        }
-
-        if (cause != null) {
-            e.initCause(cause);
-        }
-        return e;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLSocket;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "application_layer_protocol_negotiation" extensions [RFC 7301].
+ */
+final class AlpnExtension {
+    static final HandshakeProducer chNetworkProducer = new CHAlpnProducer();
+    static final ExtensionConsumer chOnLoadConsumer = new CHAlpnConsumer();
+    static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();
+
+    static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();
+    static final ExtensionConsumer shOnLoadConsumer = new SHAlpnConsumer();
+    static final HandshakeAbsence shOnLoadAbsence = new SHAlpnAbsence();
+
+    // Note: we reuse ServerHello operations for EncryptedExtensions for now.
+    // Please be careful about any code or specification changes in the future.
+    static final HandshakeProducer eeNetworkProducer = new SHAlpnProducer();
+    static final ExtensionConsumer eeOnLoadConsumer = new SHAlpnConsumer();
+    static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();
+
+    static final SSLStringizer alpnStringizer = new AlpnStringizer();
+
+    /**
+     * The "application_layer_protocol_negotiation" extension.
+     *
+     * See RFC 7301 for the specification of this extension.
+     */
+    static final class AlpnSpec implements SSLExtensionSpec {
+        final List<String> applicationProtocols;
+
+        private AlpnSpec(String[] applicationProtocols) {
+            this.applicationProtocols = Collections.unmodifiableList(
+                    Arrays.asList(applicationProtocols));
+        }
+
+        private AlpnSpec(ByteBuffer buffer) throws IOException {
+            // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
+            if (buffer.remaining() < 2) {
+                throw new SSLProtocolException(
+                    "Invalid application_layer_protocol_negotiation: " +
+                    "insufficient data (length=" + buffer.remaining() + ")");
+            }
+
+            int listLen = Record.getInt16(buffer);
+            if (listLen < 2 || listLen != buffer.remaining()) {
+                throw new SSLProtocolException(
+                    "Invalid application_layer_protocol_negotiation: " +
+                    "incorrect list length (length=" + listLen + ")");
+            }
+
+            List<String> protocolNames = new LinkedList<>();
+            while (buffer.hasRemaining()) {
+                // opaque ProtocolName<1..2^8-1>, RFC 7301.
+                byte[] bytes = Record.getBytes8(buffer);
+                if (bytes.length == 0) {
+                    throw new SSLProtocolException(
+                        "Invalid application_layer_protocol_negotiation " +
+                        "extension: empty application protocol name");
+                }
+
+                String appProtocol = new String(bytes, StandardCharsets.UTF_8);
+                protocolNames.add(appProtocol);
+            }
+
+            this.applicationProtocols =
+                    Collections.unmodifiableList(protocolNames);
+        }
+
+        @Override
+        public String toString() {
+            return applicationProtocols.toString();
+        }
+    }
+
+    private static final class AlpnStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new AlpnSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of the extension in a ClientHello
+     * handshake message.
+     */
+    private static final class CHAlpnProducer implements HandshakeProducer {
+        static final int MAX_AP_LENGTH = 255;
+        static final int MAX_AP_LIST_LENGTH = 65535;
+
+        // Prevent instantiation of this class.
+        private CHAlpnProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.info(
+                            "Ignore client unavailable extension: " +
+                            SSLExtension.CH_ALPN.name);
+                }
+
+                chc.applicationProtocol = "";
+                chc.conContext.applicationProtocol = "";
+                return null;
+            }
+
+            String[] laps = chc.sslConfig.applicationProtocols;
+            if ((laps == null) || (laps.length == 0)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.info(
+                            "No available application protocols");
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            int listLength = 0;     // ProtocolNameList length
+            for (String ap : laps) {
+                int length = ap.getBytes(StandardCharsets.UTF_8).length;
+                if (length == 0) {
+                    // log the configuration problem
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.severe(
+                                "Application protocol name cannot be empty");
+                    }
+                    chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                            "Application protocol name cannot be empty");
+                }
+
+                if (length <= MAX_AP_LENGTH) {
+                    // opaque ProtocolName<1..2^8-1>, RFC 7301.
+                    listLength += (length + 1);
+                } else {
+                    // log the configuration problem
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.severe(
+                                "Application protocol name (" + ap +
+                                ") exceeds the size limit (" +
+                                MAX_AP_LENGTH + " bytes)");
+                    }
+                    chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                                "Application protocol name (" + ap +
+                                ") exceeds the size limit (" +
+                                MAX_AP_LENGTH + " bytes)");
+                }
+
+                if (listLength > MAX_AP_LIST_LENGTH) {
+                    // log the configuration problem
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.severe(
+                                "The configured application protocols (" +
+                                Arrays.toString(laps) +
+                                ") exceed the size limit (" +
+                                MAX_AP_LIST_LENGTH + " bytes)");
+                    }
+                    chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                                "The configured application protocols (" +
+                                Arrays.toString(laps) +
+                                ") exceed the size limit (" +
+                                MAX_AP_LIST_LENGTH + " bytes)");
+                }
+            }
+
+            // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
+            byte[] extData = new byte[listLength + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, listLength);
+            for (String ap : laps) {
+                Record.putBytes8(m, ap.getBytes(StandardCharsets.UTF_8));
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(SSLExtension.CH_ALPN,
+                    new AlpnSpec(chc.sslConfig.applicationProtocols));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of the extension in a ClientHello
+     * handshake message.
+     */
+    private static final class CHAlpnConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHAlpnConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
+                shc.applicationProtocol = "";
+                shc.conContext.applicationProtocol = "";
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.info(
+                            "Ignore server unavailable extension: " +
+                            SSLExtension.CH_ALPN.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Is the extension enabled?
+            boolean noAPSelector;
+            if (shc.conContext.transport instanceof SSLEngine) {
+                noAPSelector = (shc.sslConfig.engineAPSelector == null);
+            } else {
+                noAPSelector = (shc.sslConfig.socketAPSelector == null);
+            }
+
+            boolean noAlpnProtocols =
+                    shc.sslConfig.applicationProtocols == null ||
+                    shc.sslConfig.applicationProtocols.length == 0;
+            if (noAPSelector && noAlpnProtocols) {
+                shc.applicationProtocol = "";
+                shc.conContext.applicationProtocol = "";
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore server unenabled extension: " +
+                            SSLExtension.CH_ALPN.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            AlpnSpec spec;
+            try {
+                spec = new AlpnSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            if (noAPSelector) {     // noAlpnProtocols is false
+                List<String> protocolNames = spec.applicationProtocols;
+                boolean matched = false;
+                // Use server application protocol preference order.
+                for (String ap : shc.sslConfig.applicationProtocols) {
+                    if (protocolNames.contains(ap)) {
+                        shc.applicationProtocol = ap;
+                        shc.conContext.applicationProtocol = ap;
+                        matched = true;
+                        break;
+                    }
+                }
+
+                if (!matched) {
+                    shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
+                            "No matching application layer protocol values");
+                }
+            }   // Otherwise, applicationProtocol will be set by the
+                // application selector callback later.
+
+            shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);
+
+            // No impact on session resumption.
+            //
+            // [RFC 7301] Unlike many other TLS extensions, this extension
+            // does not establish properties of the session, only of the
+            // connection.  When session resumption or session tickets are
+            // used, the previous contents of this extension are irrelevant,
+            // and only the values in the new handshake messages are
+            // considered.
+        }
+    }
+
+    /**
+     * The absence processing if the extension is not present in
+     * a ClientHello handshake message.
+     */
+    private static final class CHAlpnAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Please don't use the previous negotiated application protocol.
+            shc.applicationProtocol = "";
+            shc.conContext.applicationProtocol = "";
+        }
+    }
+
+    /**
+     * Network data producer of the extension in the ServerHello
+     * handshake message.
+     */
+    private static final class SHAlpnProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHAlpnProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to ALPN request only
+            AlpnSpec requestedAlps =
+                    (AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);
+            if (requestedAlps == null) {
+                // Ignore, this extension was not requested and accepted.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable extension: " +
+                            SSLExtension.SH_ALPN.name);
+                }
+
+                shc.applicationProtocol = "";
+                shc.conContext.applicationProtocol = "";
+                return null;
+            }
+
+            List<String> alps = requestedAlps.applicationProtocols;
+            if (shc.conContext.transport instanceof SSLEngine) {
+                if (shc.sslConfig.engineAPSelector != null) {
+                    SSLEngine engine = (SSLEngine)shc.conContext.transport;
+                    shc.applicationProtocol =
+                        shc.sslConfig.engineAPSelector.apply(engine, alps);
+                    if ((shc.applicationProtocol == null) ||
+                            (!shc.applicationProtocol.isEmpty() &&
+                            !alps.contains(shc.applicationProtocol))) {
+                        shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
+                            "No matching application layer protocol values");
+                    }
+                }
+            } else {
+                if (shc.sslConfig.socketAPSelector != null) {
+                    SSLSocket socket = (SSLSocket)shc.conContext.transport;
+                    shc.applicationProtocol =
+                        shc.sslConfig.socketAPSelector.apply(socket, alps);
+                    if ((shc.applicationProtocol == null) ||
+                            (!shc.applicationProtocol.isEmpty() &&
+                            !alps.contains(shc.applicationProtocol))) {
+                        shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
+                            "No matching application layer protocol values");
+                    }
+                }
+            }
+
+            if ((shc.applicationProtocol == null) ||
+                    (shc.applicationProtocol.isEmpty())) {
+                // Ignore, no negotiated application layer protocol.
+                shc.applicationProtocol = "";
+                shc.conContext.applicationProtocol = "";
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                        "Ignore, no negotiated application layer protocol");
+                }
+
+                return null;
+            }
+
+            // opaque ProtocolName<1..2^8-1>, RFC 7301.
+            int listLen = shc.applicationProtocol.length() + 1;
+                                                        // 1: length byte
+            // ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
+            byte[] extData = new byte[listLen + 2];     // 2: list length
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, listLen);
+            Record.putBytes8(m,
+                    shc.applicationProtocol.getBytes(StandardCharsets.UTF_8));
+
+            // Update the context.
+            shc.conContext.applicationProtocol = shc.applicationProtocol;
+
+            // Clean or register the extension
+            //
+            // No further use of the request and respond extension any more.
+            shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of the extension in the ServerHello
+     * handshake message.
+     */
+    private static final class SHAlpnConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHAlpnConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to ALPN request only
+            AlpnSpec requestedAlps =
+                    (AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);
+            if (requestedAlps == null ||
+                    requestedAlps.applicationProtocols == null ||
+                    requestedAlps.applicationProtocols.isEmpty()) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected " + SSLExtension.CH_ALPN.name + " extension");
+            }
+
+            // Parse the extension.
+            AlpnSpec spec;
+            try {
+                spec = new AlpnSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Only one application protocol is allowed.
+            if (spec.applicationProtocols.size() != 1) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
+                    "Only one application protocol name " +
+                    "is allowed in ServerHello message");
+            }
+
+            // The respond application protocol must be one of the requested.
+            if (!requestedAlps.applicationProtocols.containsAll(
+                    spec.applicationProtocols)) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
+                    "Only client specified application protocol " +
+                    "is allowed in ServerHello message");
+            }
+
+            // Update the context.
+            chc.applicationProtocol = spec.applicationProtocols.get(0);
+            chc.conContext.applicationProtocol = chc.applicationProtocol;
+
+            // Clean or register the extension
+            //
+            // No further use of the request and respond extension any more.
+            chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
+        }
+    }
+
+    /**
+     * The absence processing if the extension is not present in
+     * the ServerHello handshake message.
+     */
+    private static final class SHAlpnAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Please don't use the previous negotiated application protocol.
+            chc.applicationProtocol = "";
+            chc.conContext.applicationProtocol = "";
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/AppInputStream.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +0,0 @@
-/*
- * Copyright (c) 1996, 2015, 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.InputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import javax.net.ssl.SSLProtocolException;
-
-/**
- * InputStream for application data as returned by SSLSocket.getInputStream().
- *
- * @author David Brownell
- */
-final class AppInputStream extends InputStream {
-    // the buffer size for each read of network data
-    private static final int READ_BUFFER_SIZE = 4096;
-
-    // static dummy array we use to implement skip()
-    private static final byte[] SKIP_ARRAY = new byte[256];
-
-    // the related socket of the input stream
-    private final SSLSocketImpl socket;
-
-    // the temporary buffer used to read network
-    private ByteBuffer buffer;
-
-    // Is application data available in the stream?
-    private boolean appDataIsAvailable;
-
-    // One element array used to implement the single byte read() method
-    private final byte[] oneByte = new byte[1];
-
-    AppInputStream(SSLSocketImpl conn) {
-        this.buffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
-        this.socket = conn;
-        this.appDataIsAvailable = false;
-    }
-
-    /**
-     * Return the minimum number of bytes that can be read without blocking.
-     *
-     * Currently not synchronized.
-     */
-    @Override
-    public int available() throws IOException {
-        if ((!appDataIsAvailable) || socket.checkEOF()) {
-            return 0;
-        }
-
-        return buffer.remaining();
-    }
-
-    /**
-     * Read a single byte, returning -1 on non-fault EOF status.
-     */
-    @Override
-    public synchronized int read() throws IOException {
-        int n = read(oneByte, 0, 1);
-        if (n <= 0) { // EOF
-            return -1;
-        }
-        return oneByte[0] & 0xFF;
-    }
-
-    /**
-     * Reads up to {@code len} bytes of data from the input stream into an
-     * array of bytes. An attempt is made to read as many as {@code len} bytes,
-     * but a smaller number may be read. The number of bytes actually read
-     * is returned as an integer.
-     *
-     * If the layer above needs more data, it asks for more, so we
-     * are responsible only for blocking to fill at most one buffer,
-     * and returning "-1" on non-fault EOF status.
-     */
-    @Override
-    public synchronized int read(byte[] b, int off, int len)
-            throws IOException {
-        if (b == null) {
-            throw new NullPointerException();
-        } else if (off < 0 || len < 0 || len > b.length - off) {
-            throw new IndexOutOfBoundsException();
-        } else if (len == 0) {
-            return 0;
-        }
-
-        if (socket.checkEOF()) {
-            return -1;
-        }
-
-        // Read the available bytes at first.
-        int remains = available();
-        if (remains > 0) {
-            int howmany = Math.min(remains, len);
-            buffer.get(b, off, howmany);
-
-            return howmany;
-        }
-
-        appDataIsAvailable = false;
-        int volume = 0;
-
-        try {
-            /*
-             * Read data if needed ... notice that the connection guarantees
-             * that handshake, alert, and change cipher spec data streams are
-             * handled as they arrive, so we never see them here.
-             */
-            while(volume == 0) {
-                // Clear the buffer for a new record reading.
-                buffer.clear();
-
-                //
-                // grow the buffer if needed
-                //
-
-                // Read the header of a record into the buffer, and return
-                // the packet size.
-                int packetLen = socket.bytesInCompletePacket();
-                if (packetLen < 0) {    // EOF
-                    return -1;
-                }
-
-                // Is this packet bigger than SSL/TLS normally allows?
-                if (packetLen > SSLRecord.maxLargeRecordSize) {
-                    throw new SSLProtocolException(
-                        "Illegal packet size: " + packetLen);
-                }
-
-                if (packetLen > buffer.remaining()) {
-                    buffer = ByteBuffer.allocate(packetLen);
-                }
-
-                volume = socket.readRecord(buffer);
-                if (volume < 0) {    // EOF
-                    return -1;
-                } else if (volume > 0) {
-                    appDataIsAvailable = true;
-                    break;
-                }
-            }
-
-            int howmany = Math.min(len, volume);
-            buffer.get(b, off, howmany);
-            return howmany;
-        } catch (Exception e) {
-            // shutdown and rethrow (wrapped) exception as appropriate
-            socket.handleException(e);
-
-            // dummy for compiler
-            return -1;
-        }
-    }
-
-
-    /**
-     * Skip n bytes. This implementation is somewhat less efficient
-     * than possible, but not badly so (redundant copy). We reuse
-     * the read() code to keep things simpler. Note that SKIP_ARRAY
-     * is static and may garbled by concurrent use, but we are not interested
-     * in the data anyway.
-     */
-    @Override
-    public synchronized long skip(long n) throws IOException {
-        long skipped = 0;
-        while (n > 0) {
-            int len = (int)Math.min(n, SKIP_ARRAY.length);
-            int r = read(SKIP_ARRAY, 0, len);
-            if (r <= 0) {
-                break;
-            }
-            n -= r;
-            skipped += r;
-        }
-        return skipped;
-    }
-
-    /*
-     * Socket close is already synchronized, no need to block here.
-     */
-    @Override
-    public void close() throws IOException {
-        socket.close();
-    }
-
-    // inherit default mark/reset behavior (throw Exceptions) from InputStream
-}
--- a/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 1996, 2012, 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.OutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/*
- * OutputStream for application data as returned by SSLSocket.getOutputStream().
- *
- * @author  David Brownell
- */
-class AppOutputStream extends OutputStream {
-
-    private SSLSocketImpl socket;
-
-    // One element array used to implement the write(byte) method
-    private final byte[] oneByte = new byte[1];
-
-    AppOutputStream(SSLSocketImpl conn) {
-        this.socket = conn;
-    }
-
-    /**
-     * Write the data out, NOW.
-     */
-    @Override
-    public synchronized void write(byte[] b, int off, int len)
-            throws IOException {
-        if (b == null) {
-            throw new NullPointerException();
-        } else if (off < 0 || len < 0 || len > b.length - off) {
-            throw new IndexOutOfBoundsException();
-        } else if (len == 0) {
-            return;
-        }
-
-        // check if the Socket is invalid (error or closed)
-        socket.checkWrite();
-
-        // Delegate the writing to the underlying socket.
-        try {
-            socket.writeRecord(b, off, len);
-            socket.checkWrite();
-        } catch (Exception e) {
-            // shutdown and rethrow (wrapped) exception as appropriate
-            socket.handleException(e);
-        }
-    }
-
-    /**
-     * Write one byte now.
-     */
-    @Override
-    public synchronized void write(int i) throws IOException {
-        oneByte[0] = (byte)i;
-        write(oneByte, 0, 1);
-    }
-
-    /*
-     * Socket close is already synchronized, no need to block here.
-     */
-    @Override
-    public void close() throws IOException {
-        socket.close();
-    }
-
-    // inherit no-op flush()
-}
--- a/src/java.base/share/classes/sun/security/ssl/Authenticator.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/Authenticator.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,92 +25,78 @@
 
 package sun.security.ssl;
 
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import sun.security.ssl.CipherSuite.MacAlg;
 
 /**
  * This class represents an SSL/TLS/DTLS message authentication token,
  * which encapsulates a sequence number and ensures that attempts to
  * delete or reorder messages can be detected.
- *
- * Each connection state contains a sequence number, which is maintained
- * separately for read and write states.
- *
- * For SSL/TLS protocols, the sequence number MUST be set to zero
- * whenever a connection state is made the active state.
- *
- * DTLS uses an explicit sequence number, rather than an implicit one.
- * Sequence numbers are maintained separately for each epoch, with
- * each sequence number initially being 0 for each epoch.  The sequence
- * number used to compute the DTLS MAC is the 64-bit value formed by
- * concatenating the epoch and the sequence number.
- *
- * Sequence numbers do not wrap.  If an implementation would need to wrap
- * a sequence number, it must renegotiate instead.  A sequence number is
- * incremented after each record: specifically, the first record transmitted
- * under a particular connection state MUST use sequence number 0.
  */
-class Authenticator {
-
+abstract class Authenticator {
     // byte array containing the additional authentication information for
     // each record
-    private final byte[] block;
-
-    // the block size of SSL v3.0:
-    // sequence number + record type + + record length
-    private static final int BLOCK_SIZE_SSL = 8 + 1 + 2;
-
-    // the block size of TLS v1.0 and later:
-    // sequence number + record type + protocol version + record length
-    private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2;
-
-    // the block size of DTLS v1.0 and later:
-    // epoch + sequence number + record type + protocol version + record length
-    private static final int BLOCK_SIZE_DTLS = 2 + 6 + 1 + 2 + 2;
-
-    private final boolean isDTLS;
+    protected final byte[] block;   // at least 8 bytes for sequence number
 
-    /**
-     * Default construct, no message authentication token is initialized.
-     *
-     * Note that this construct can only be called for null MAC
-     */
-    protected Authenticator(boolean isDTLS) {
-        if (isDTLS) {
-            // For DTLS protocols, plaintexts use explicit epoch and
-            // sequence number in each record.  The first 8 byte of
-            // the block is initialized for null MAC so that the
-            // epoch and sequence number can be acquired to generate
-            // plaintext records.
-            block = new byte[8];
-        } else {
-            block = new byte[0];
-        }
-
-        this.isDTLS = isDTLS;
+    private Authenticator(byte[] block) {
+        this.block = block;
     }
 
     /**
      * Constructs the message authentication token for the specified
      * SSL/TLS protocol.
      */
-    Authenticator(ProtocolVersion protocolVersion) {
-        if (protocolVersion.isDTLSProtocol()) {
-            block = new byte[BLOCK_SIZE_DTLS];
-            block[9] = protocolVersion.major;
-            block[10] = protocolVersion.minor;
+    static Authenticator valueOf(ProtocolVersion protocolVersion) {
+        if (protocolVersion.isDTLS) {
+            if (protocolVersion.useTLS13PlusSpec()) {
+                return new DTLS13Authenticator(protocolVersion);
+            } else {
+                return new DTLS10Authenticator(protocolVersion);
+            }
+        } else {
+            if (protocolVersion.useTLS13PlusSpec()) {
+                return new TLS13Authenticator(protocolVersion);
+            } else if (protocolVersion.useTLS10PlusSpec()) {
+                return new TLS10Authenticator(protocolVersion);
+            } else {
+                return new SSL30Authenticator();
+            }
+        }
+    }
 
-            this.isDTLS = true;
-        } else if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
-            block = new byte[BLOCK_SIZE_TLS];
-            block[9] = protocolVersion.major;
-            block[10] = protocolVersion.minor;
+    @SuppressWarnings({"unchecked"})
+    static <T extends Authenticator & MAC> T
+         valueOf(ProtocolVersion protocolVersion, MacAlg macAlg,
+                 SecretKey key) throws NoSuchAlgorithmException,
+                        InvalidKeyException {
+        if (protocolVersion.isDTLS) {
+            if (protocolVersion.useTLS13PlusSpec()) {
+                throw new RuntimeException("No MacAlg used in DTLS 1.3");
+            } else {
+                return (T)(new DTLS10Mac(protocolVersion, macAlg, key));
+            }
+        } else {
+            if (protocolVersion.useTLS13PlusSpec()) {
+                throw new RuntimeException("No MacAlg used in TLS 1.3");
+            } else if (protocolVersion.useTLS10PlusSpec()) {
+                return (T)(new TLS10Mac(protocolVersion, macAlg, key));
+            } else {
+                return (T)(new SSL30Mac(protocolVersion, macAlg, key));
+            }
+        }
+    }
 
-            this.isDTLS = false;
-        } else {
-            block = new byte[BLOCK_SIZE_SSL];
+    static Authenticator nullTlsMac() {
+        return new SSLNullMac();
+    }
 
-            this.isDTLS = false;
-        }
+    static Authenticator nullDtlsMac() {
+        return new DTLSNullMac();
     }
 
     /**
@@ -122,25 +108,7 @@
      *
      * @return true if the sequence number is close to wrap
      */
-    final boolean seqNumOverflow() {
-        /*
-         * Conservatively, we don't allow more records to be generated
-         * when there are only 2^8 sequence numbers left.
-         */
-        if (isDTLS) {
-            return (block.length != 0 &&
-                // no epoch bytes, block[0] and block[1]
-                block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
-                block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
-                block[6] == (byte)0xFF);
-        } else {
-            return (block.length != 0 &&
-                block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
-                block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
-                block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
-                block[6] == (byte)0xFF);
-        }
-    }
+    abstract boolean seqNumOverflow();
 
     /**
      * Checks whether the sequence number close to renew.
@@ -152,21 +120,7 @@
      *
      * @return true if the sequence number is huge enough to renew
      */
-    final boolean seqNumIsHuge() {
-        /*
-         * Conservatively, we should ask for renegotiation when there are
-         * only 2^32 sequence numbers left.
-         */
-        if (isDTLS) {
-            return (block.length != 0 &&
-                // no epoch bytes, block[0] and block[1]
-                block[2] == (byte)0xFF && block[3] == (byte)0xFF);
-        } else {
-            return (block.length != 0 &&
-                block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
-                block[2] == (byte)0xFF && block[3] == (byte)0xFF);
-        }
-    }
+    abstract boolean seqNumIsHuge();
 
     /**
      * Gets the current sequence number, including the epoch number for
@@ -181,14 +135,9 @@
     /**
      * Sets the epoch number (only apply to DTLS protocols).
      */
-    final void setEpochNumber(int epoch) {
-        if (!isDTLS) {
-            throw new RuntimeException(
+    void setEpochNumber(int epoch) {
+        throw new UnsupportedOperationException(
                 "Epoch numbers apply to DTLS protocols only");
-        }
-
-        block[0] = (byte)((epoch >> 8) & 0xFF);
-        block[1] = (byte)(epoch & 0xFF);
     }
 
     /**
@@ -208,7 +157,7 @@
     /**
      * Acquires the current message authentication information with the
      * specified record type and fragment length, and then increases the
-     * sequence number.
+     * sequence number if using implicit sequence number.
      *
      * @param  type the record type
      * @param  length the fragment of the record
@@ -216,32 +165,465 @@
      *
      * @return the byte array of the current message authentication information
      */
-    final byte[] acquireAuthenticationBytes(
+    byte[] acquireAuthenticationBytes(
             byte type, int length, byte[] sequence) {
+        throw new UnsupportedOperationException("Used by AEAD algorithms only");
+    }
+
+    private static class SSLAuthenticator extends Authenticator {
+        private SSLAuthenticator(byte[] block) {
+            super(block);
+        }
+
+        @Override
+        boolean seqNumOverflow() {
+            /*
+             * Conservatively, we don't allow more records to be generated
+             * when there are only 2^8 sequence numbers left.
+             */
+            return (block.length != 0 &&
+                block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
+                block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
+                block[6] == (byte)0xFF);
+        }
+
+        @Override
+        boolean seqNumIsHuge() {
+            return (block.length != 0 &&
+                block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF);
+        }
+    }
+
+    // For null MAC only.
+    private static class SSLNullAuthenticator extends SSLAuthenticator {
+        private SSLNullAuthenticator() {
+            super(new byte[8]);
+        }
+    }
+
+    // For SSL 3.0
+    private static class SSL30Authenticator extends SSLAuthenticator {
+        // Block size of SSL v3.0:
+        //     sequence number + record type + + record length
+        private static final int BLOCK_SIZE = 11;   // 8 + 1 + 2
 
-        byte[] copy = block.clone();
-        if (sequence != null) {
-            if (sequence.length != 8) {
-                throw new RuntimeException(
-                        "Insufficient explicit sequence number bytes");
+        private SSL30Authenticator() {
+            super(new byte[BLOCK_SIZE]);
+        }
+
+        @Override
+        byte[] acquireAuthenticationBytes(
+                byte type, int length, byte[] sequence) {
+            byte[] ad = block.clone();
+
+            // Increase the implicit sequence number in the block array.
+            increaseSequenceNumber();
+
+            ad[8] = type;
+            ad[9] = (byte)(length >> 8);
+            ad[10] = (byte)(length);
+
+            return ad;
+        }
+    }
+
+    // For TLS 1.0 - 1.2
+    private static class TLS10Authenticator extends SSLAuthenticator {
+        // Block size of TLS v1.0/1.1/1.2.
+        //     sequence number + record type + protocol version + record length
+        private static final int BLOCK_SIZE = 13;   // 8 + 1 + 2 + 2
+
+        private TLS10Authenticator(ProtocolVersion protocolVersion) {
+            super(new byte[BLOCK_SIZE]);
+            block[9] = protocolVersion.major;
+            block[10] = protocolVersion.minor;
+        }
+
+        @Override
+        byte[] acquireAuthenticationBytes(
+                byte type, int length, byte[] sequence) {
+            byte[] ad = block.clone();
+            if (sequence != null) {
+                if (sequence.length != 8) {
+                    throw new RuntimeException(
+                            "Insufficient explicit sequence number bytes");
+                }
+
+                System.arraycopy(sequence, 0, ad, 0, sequence.length);
+            } else {    // Otherwise, use the implicit sequence number.
+                // Increase the implicit sequence number in the block array.
+                increaseSequenceNumber();
             }
 
-            System.arraycopy(sequence, 0, copy, 0, sequence.length);
-        }   // Otherwise, use the implicit sequence number.
+            ad[8] = type;
+            ad[11] = (byte)(length >> 8);
+            ad[12] = (byte)(length);
+
+            return ad;
+        }
+    }
+
+    // For TLS 1.3
+    private static final class TLS13Authenticator extends SSLAuthenticator {
+        // Block size of TLS v1.3:
+        //     record type + protocol version + record length + sequence number
+        private static final int BLOCK_SIZE = 13;   // 1 + 2 + 2 + 8
+
+        private TLS13Authenticator(ProtocolVersion protocolVersion) {
+            super(new byte[BLOCK_SIZE]);
+            block[9] = ProtocolVersion.TLS12.major;
+            block[10] = ProtocolVersion.TLS12.minor;
+        }
+
+        @Override
+        byte[] acquireAuthenticationBytes(
+                byte type, int length, byte[] sequence) {
+            byte[] ad = Arrays.copyOfRange(block, 8, 13);
+
+            // Increase the implicit sequence number in the block array.
+            increaseSequenceNumber();
+
+            ad[0] = type;
+            ad[3] = (byte)(length >> 8);
+            ad[4] = (byte)(length & 0xFF);
+
+            return ad;
+        }
+    }
+
+    private static class DTLSAuthenticator extends Authenticator {
+        private DTLSAuthenticator(byte[] block) {
+            super(block);
+        }
 
-        if (block.length != 0) {
-            copy[8] = type;
+        @Override
+        boolean seqNumOverflow() {
+            /*
+             * Conservatively, we don't allow more records to be generated
+             * when there are only 2^8 sequence numbers left.
+             */
+            return (block.length != 0 &&
+                // no epoch bytes, block[0] and block[1]
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
+                block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
+                block[6] == (byte)0xFF);
+        }
+
+        @Override
+        boolean seqNumIsHuge() {
+            return (block.length != 0 &&
+                // no epoch bytes, block[0] and block[1]
+                block[2] == (byte)0xFF && block[3] == (byte)0xFF);
+        }
+
+        @Override
+        void setEpochNumber(int epoch) {
+            block[0] = (byte)((epoch >> 8) & 0xFF);
+            block[1] = (byte)(epoch & 0xFF);
+        }
+    }
 
-            copy[copy.length - 2] = (byte)(length >> 8);
-            copy[copy.length - 1] = (byte)(length);
+    // For null MAC only.
+    private static class DTLSNullAuthenticator extends DTLSAuthenticator {
+        private DTLSNullAuthenticator() {
+            // For DTLS protocols, plaintexts use explicit epoch and
+            // sequence number in each record.  The first 8 byte of
+            // the block is initialized for null MAC so that the
+            // epoch and sequence number can be acquired to generate
+            // plaintext records.
+            super(new byte[8]);
+        }
+    }
+
+    // DTLS 1.0/1.2
+    private static class DTLS10Authenticator extends DTLSAuthenticator {
+        // Block size of DTLS v1.0 and later:
+        //     epoch + sequence number +
+        //     record type + protocol version + record length
+        private static final int BLOCK_SIZE = 13;  // 2 + 6 + 1 + 2 + 2;
 
-            if (sequence == null || sequence.length != 0) {
+        private DTLS10Authenticator(ProtocolVersion protocolVersion) {
+            super(new byte[BLOCK_SIZE]);
+            block[9] = protocolVersion.major;
+            block[10] = protocolVersion.minor;
+        }
+
+        @Override
+        byte[] acquireAuthenticationBytes(
+                byte type, int length, byte[] sequence) {
+            byte[] ad = block.clone();
+            if (sequence != null) {
+                if (sequence.length != 8) {
+                    throw new RuntimeException(
+                            "Insufficient explicit sequence number bytes");
+                }
+
+                System.arraycopy(sequence, 0, ad, 0, sequence.length);
+            } else {    // Otherwise, use the implicit sequence number.
                 // Increase the implicit sequence number in the block array.
                 increaseSequenceNumber();
             }
+
+            ad[8] = type;
+            ad[11] = (byte)(length >> 8);
+            ad[12] = (byte)(length);
+
+            return ad;
+        }
+    }
+
+    // DTLS 1.3
+    private static final class DTLS13Authenticator extends DTLSAuthenticator {
+        // Block size of DTLS v1.0 and later:
+        //     epoch + sequence number +
+        //     record type + protocol version + record length
+        private static final int BLOCK_SIZE = 13;  // 2 + 6 + 1 + 2 + 2;
+
+        private DTLS13Authenticator(ProtocolVersion protocolVersion) {
+            super(new byte[BLOCK_SIZE]);
+            block[9] = ProtocolVersion.TLS12.major;
+            block[10] = ProtocolVersion.TLS12.minor;
+        }
+
+        @Override
+        byte[] acquireAuthenticationBytes(
+                byte type, int length, byte[] sequence) {
+            byte[] ad = Arrays.copyOfRange(block, 8, 13);
+
+            // Increase the implicit sequence number in the block array.
+            increaseSequenceNumber();
+
+            ad[0] = type;
+            ad[3] = (byte)(length >> 8);
+            ad[4] = (byte)(length & 0xFF);
+
+            return ad;
+        }
+    }
+
+    interface MAC {
+        MacAlg macAlg();
+
+        /**
+         * Compute and returns the MAC for the remaining data
+         * in this ByteBuffer.
+         *
+         * On return, the bb position == limit, and limit will
+         * have not changed.
+         *
+         * @param type record type
+         * @param bb a ByteBuffer in which the position and limit
+         *          demarcate the data to be MAC'd.
+         * @param isSimulated if true, simulate the MAC computation
+         * @param sequence the explicit sequence number, or null if using
+         *        the implicit sequence number for the computation
+         *
+         * @return the MAC result
+         */
+        byte[] compute(byte type, ByteBuffer bb,
+                byte[] sequence, boolean isSimulated);
+
+
+        /**
+         * Compute and returns the MAC for the remaining data
+         * in this ByteBuffer.
+         *
+         * On return, the bb position == limit, and limit will
+         * have not changed.
+         *
+         * @param type record type
+         * @param bb a ByteBuffer in which the position and limit
+         *        demarcate the data to be MAC'd.
+         * @param isSimulated if true, simulate the MAC computation
+         *
+         * @return the MAC result
+         */
+        default byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
+            return compute(type, bb, null, isSimulated);
+        }
+    }
+
+    private class MacImpl implements MAC {
+        // internal identifier for the MAC algorithm
+        private final MacAlg macAlg;
+
+        // JCE Mac object
+        private final Mac mac;
+
+        private MacImpl() {
+            macAlg = MacAlg.M_NULL;
+            mac = null;
+        }
+
+        private MacImpl(ProtocolVersion protocolVersion, MacAlg macAlg,
+                SecretKey key) throws NoSuchAlgorithmException,
+                        InvalidKeyException {
+            if (macAlg == null) {
+                throw new RuntimeException("Null MacAlg");
+            }
+
+            // using SSL MAC computation?
+            boolean useSSLMac = (protocolVersion.id < ProtocolVersion.TLS10.id);
+            String algorithm;
+            switch (macAlg) {
+                case M_MD5:
+                    algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
+                    break;
+                case M_SHA:
+                    algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
+                    break;
+                case M_SHA256:
+                    algorithm = "HmacSHA256";    // TLS 1.2+
+                    break;
+                case M_SHA384:
+                    algorithm = "HmacSHA384";    // TLS 1.2+
+                    break;
+                default:
+                    throw new RuntimeException("Unknown MacAlg " + macAlg);
+            }
+
+            Mac m = JsseJce.getMac(algorithm);
+            m.init(key);
+            this.macAlg = macAlg;
+            this.mac = m;
+        }
+
+        @Override
+        public MacAlg macAlg() {
+            return macAlg;
         }
 
-        return copy;
+        @Override
+        public byte[] compute(byte type, ByteBuffer bb,
+                byte[] sequence, boolean isSimulated) {
+
+            if (macAlg.size == 0) {
+                return new byte[0];
+            }
+
+            if (!isSimulated) {
+                // Uses the explicit sequence number for the computation.
+                byte[] additional =
+                    acquireAuthenticationBytes(type, bb.remaining(), sequence);
+                mac.update(additional);
+            }
+            mac.update(bb);
+
+            return mac.doFinal();
+        }
+    }
+
+    // NULL SSL MAC
+    private static final
+            class SSLNullMac extends SSLNullAuthenticator implements MAC {
+        private final MacImpl macImpl;
+        public SSLNullMac() {
+            super();
+            this.macImpl = new MacImpl();
+        }
+
+        @Override
+        public MacAlg macAlg() {
+            return macImpl.macAlg;
+        }
+
+        @Override
+        public byte[] compute(byte type, ByteBuffer bb,
+                byte[] sequence, boolean isSimulated) {
+            return macImpl.compute(type, bb, sequence, isSimulated);
+        }
+    }
+
+    // For SSL 3.0
+    private static final
+            class SSL30Mac extends SSL30Authenticator implements MAC {
+        private final MacImpl macImpl;
+        public SSL30Mac(ProtocolVersion protocolVersion,
+                MacAlg macAlg, SecretKey key) throws NoSuchAlgorithmException,
+                        InvalidKeyException {
+            super();
+            this.macImpl = new MacImpl(protocolVersion, macAlg, key);
+        }
+
+        @Override
+        public MacAlg macAlg() {
+            return macImpl.macAlg;
+        }
+
+        @Override
+        public byte[] compute(byte type, ByteBuffer bb,
+                byte[] sequence, boolean isSimulated) {
+            return macImpl.compute(type, bb, sequence, isSimulated);
+        }
+    }
+
+    // For TLS 1.0 - 1.2
+    private static final
+            class TLS10Mac extends TLS10Authenticator implements MAC {
+        private final MacImpl macImpl;
+        public TLS10Mac(ProtocolVersion protocolVersion,
+                MacAlg macAlg, SecretKey key) throws NoSuchAlgorithmException,
+                        InvalidKeyException {
+            super(protocolVersion);
+            this.macImpl = new MacImpl(protocolVersion, macAlg, key);
+        }
+
+        @Override
+        public MacAlg macAlg() {
+            return macImpl.macAlg;
+        }
+
+        @Override
+        public byte[] compute(byte type, ByteBuffer bb,
+                byte[] sequence, boolean isSimulated) {
+            return macImpl.compute(type, bb, sequence, isSimulated);
+        }
+    }
+
+    // NULL DTLS MAC
+    private static final
+            class DTLSNullMac extends DTLSNullAuthenticator implements MAC {
+        private final MacImpl macImpl;
+        public DTLSNullMac() {
+            super();
+            this.macImpl = new MacImpl();
+        }
+
+        @Override
+        public MacAlg macAlg() {
+            return macImpl.macAlg;
+        }
+
+        @Override
+        public byte[] compute(byte type, ByteBuffer bb,
+                byte[] sequence, boolean isSimulated) {
+            return macImpl.compute(type, bb, sequence, isSimulated);
+        }
+    }
+
+    // DTLS 1.0/1.2
+    private static final class DTLS10Mac
+            extends DTLS10Authenticator implements MAC {
+        private final MacImpl macImpl;
+        public DTLS10Mac(ProtocolVersion protocolVersion,
+                MacAlg macAlg, SecretKey key) throws NoSuchAlgorithmException,
+                        InvalidKeyException {
+            super(protocolVersion);
+            this.macImpl = new MacImpl(protocolVersion, macAlg, key);
+        }
+
+        @Override
+        public MacAlg macAlg() {
+            return macImpl.macAlg;
+        }
+
+        @Override
+        public byte[] compute(byte type, ByteBuffer bb,
+                byte[] sequence, boolean isSimulated) {
+            return macImpl.compute(type, bb, sequence, isSimulated);
+        }
     }
 
     static final long toLong(byte[] recordEnS) {
--- a/src/java.base/share/classes/sun/security/ssl/BaseSSLSocketImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/BaseSSLSocketImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,24 +26,23 @@
 package sun.security.ssl;
 
 import java.io.*;
+import java.net.*;
 import java.nio.channels.SocketChannel;
-import java.net.*;
 import java.util.Set;
-
 import javax.net.ssl.*;
 
 /**
- * Abstract base class for SSLSocketImpl. Its purpose is to house code with
- * no SSL related logic (or no logic at all). This makes SSLSocketImpl shorter
- * and easier to read. It contains a few constants and static methods plus
- * overridden java.net.Socket methods.
+ * Abstract base class for SSLSocketImpl.
+ *
+ * Its purpose is to house code with no SSL related logic (or no logic at all).
+ * This makes SSLSocketImpl shorter and easier to read. It contains a few
+ * constants and static methods plus overridden java.net.Socket methods.
  *
  * Methods are defined final to ensure that they are not accidentally
  * overridden in SSLSocketImpl.
  *
  * @see javax.net.ssl.SSLSocket
  * @see SSLSocketImpl
- *
  */
 abstract class BaseSSLSocketImpl extends SSLSocket {
 
@@ -92,7 +91,7 @@
                                 "com.sun.net.ssl.requireCloseNotify";
 
     static final boolean requireCloseNotify =
-                                Debug.getBooleanProperty(PROP_NAME, false);
+                                Utilities.getBooleanProperty(PROP_NAME, false);
 
     //
     // MISC SOCKET METHODS
--- a/src/java.base/share/classes/sun/security/ssl/ByteBufferInputStream.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-/*
- * Copyright (c) 2003, 2014, 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.nio.*;
-
-/**
- * A simple InputStream which uses ByteBuffers as it's backing store.
- * <P>
- * The only IOException should come if the InputStream has been closed.
- * All other IOException should not occur because all the data is local.
- * Data reads on an exhausted ByteBuffer returns a -1.
- *
- * @author  Brad Wetmore
- */
-class ByteBufferInputStream extends InputStream {
-
-    ByteBuffer bb;
-
-    ByteBufferInputStream(ByteBuffer bb) {
-        this.bb = bb;
-    }
-
-    /**
-     * Returns a byte from the ByteBuffer.
-     *
-     * Increments position().
-     */
-    @Override
-    public int read() throws IOException {
-
-        if (bb == null) {
-            throw new IOException("read on a closed InputStream");
-        }
-
-        if (bb.remaining() == 0) {
-            return -1;
-        }
-
-        return (bb.get() & 0xFF);   // need to be in the range 0 to 255
-    }
-
-    /**
-     * Returns a byte array from the ByteBuffer.
-     *
-     * Increments position().
-     */
-    @Override
-    public int read(byte[] b) throws IOException {
-
-        if (bb == null) {
-            throw new IOException("read on a closed InputStream");
-        }
-
-        return read(b, 0, b.length);
-    }
-
-    /**
-     * Returns a byte array from the ByteBuffer.
-     *
-     * Increments position().
-     */
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-
-        if (bb == null) {
-            throw new IOException("read on a closed InputStream");
-        }
-
-        if (b == null) {
-            throw new NullPointerException();
-        } else if (off < 0 || len < 0 || len > b.length - off) {
-            throw new IndexOutOfBoundsException();
-        } else if (len == 0) {
-            return 0;
-        }
-
-        int length = Math.min(bb.remaining(), len);
-        if (length == 0) {
-            return -1;
-        }
-
-        bb.get(b, off, length);
-        return length;
-    }
-
-    /**
-     * Skips over and discards <code>n</code> bytes of data from this input
-     * stream.
-     */
-    @Override
-    public long skip(long n) throws IOException {
-
-        if (bb == null) {
-            throw new IOException("skip on a closed InputStream");
-        }
-
-        if (n <= 0) {
-            return 0;
-        }
-
-        /*
-         * ByteBuffers have at most an int, so lose the upper bits.
-         * The contract allows this.
-         */
-        int nInt = (int) n;
-        int skip = Math.min(bb.remaining(), nInt);
-
-        bb.position(bb.position() + skip);
-
-        return nInt;
-    }
-
-    /**
-     * Returns the number of bytes that can be read (or skipped over)
-     * from this input stream without blocking by the next caller of a
-     * method for this input stream.
-     */
-    @Override
-    public int available() throws IOException {
-
-        if (bb == null) {
-            throw new IOException("available on a closed InputStream");
-        }
-
-        return bb.remaining();
-    }
-
-    /**
-     * Closes this input stream and releases any system resources associated
-     * with the stream.
-     *
-     * @exception  IOException  if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        bb = null;
-    }
-
-    /**
-     * Marks the current position in this input stream.
-     */
-    @Override
-    public synchronized void mark(int readlimit) {}
-
-    /**
-     * Repositions this stream to the position at the time the
-     * <code>mark</code> method was last called on this input stream.
-     */
-    @Override
-    public synchronized void reset() throws IOException {
-        throw new IOException("mark/reset not supported");
-    }
-
-    /**
-     * Tests if this input stream supports the <code>mark</code> and
-     * <code>reset</code> methods.
-     */
-    @Override
-    public boolean markSupported() {
-        return false;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SignatureAlgorithmsExtension.SignatureSchemesSpec;
+
+/**
+ * Pack of the "signature_algorithms_cert" extensions.
+ */
+final class CertSignAlgsExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHCertSignatureSchemesProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHCertSignatureSchemesConsumer();
+    static final HandshakeConsumer chOnTradeConsumer =
+            new CHCertSignatureSchemesUpdate();
+
+    static final HandshakeProducer crNetworkProducer =
+            new CRCertSignatureSchemesProducer();
+    static final ExtensionConsumer crOnLoadConsumer =
+            new CRCertSignatureSchemesConsumer();
+    static final HandshakeConsumer crOnTradeConsumer =
+            new CRCertSignatureSchemesUpdate();
+
+    static final SSLStringizer ssStringizer =
+            new CertSignatureSchemesStringizer();
+
+    private static final
+            class CertSignatureSchemesStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new SignatureSchemesSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "signature_algorithms_cert" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHCertSignatureSchemesProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHCertSignatureSchemesProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable " +
+                            "signature_algorithms_cert extension");
+                }
+
+                return null;    // ignore the extension
+            }
+
+            // Produce the extension.
+            if (chc.localSupportedSignAlgs == null) {
+                chc.localSupportedSignAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            chc.algorithmConstraints, chc.activeProtocols);
+            }
+
+            int vectorLen = SignatureScheme.sizeInRecord() *
+                    chc.localSupportedSignAlgs.size();
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (SignatureScheme ss : chc.localSupportedSignAlgs) {
+                Record.putInt16(m, ss.id);
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT,
+                    new SignatureSchemesSpec(chc.localSupportedSignAlgs));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "signature_algorithms_cert" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHCertSignatureSchemesConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHCertSignatureSchemesConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable " +
+                            "signature_algorithms_cert extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            SignatureSchemesSpec spec;
+            try {
+                spec = new SignatureSchemesSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT, spec);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * After session creation consuming of a "signature_algorithms_cert"
+     * extension in the ClientHello handshake message.
+     */
+    private static final class CHCertSignatureSchemesUpdate
+            implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CHCertSignatureSchemesUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            SignatureSchemesSpec spec = (SignatureSchemesSpec)
+                    shc.handshakeExtensions.get(
+                            SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
+            if (spec == null) {
+                // Ignore, no signature_algorithms_cert extension requested.
+                return;
+            }
+
+            // update the context
+            List<SignatureScheme> shemes =
+                    SignatureScheme.getSupportedAlgorithms(
+                            shc.algorithmConstraints, shc.negotiatedProtocol,
+                            spec.signatureSchemes);
+            shc.peerRequestedCertSignSchemes = shemes;
+            shc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+
+            if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) {
+                if (shc.sslConfig.clientAuthType !=
+                        ClientAuthType.CLIENT_AUTH_NONE) {
+                    shc.handshakeProducers.putIfAbsent(
+                            SSLHandshake.CERTIFICATE_REQUEST.id,
+                            SSLHandshake.CERTIFICATE_REQUEST);
+                }
+                shc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
+                        SSLHandshake.CERTIFICATE);
+                shc.handshakeProducers.putIfAbsent(
+                        SSLHandshake.CERTIFICATE_VERIFY.id,
+                        SSLHandshake.CERTIFICATE_VERIFY);
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "signature_algorithms_cert" extension in
+     * the CertificateRequest handshake message.
+     */
+    private static final
+            class CRCertSignatureSchemesProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CRCertSignatureSchemesProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable " +
+                            "signature_algorithms_cert extension");
+                }
+                return null;    // ignore the extension
+            }
+
+            // Produce the extension.
+            if (shc.localSupportedSignAlgs == null) {
+                shc.localSupportedSignAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            shc.algorithmConstraints, shc.activeProtocols);
+            }
+
+            int vectorLen = SignatureScheme.sizeInRecord() *
+                    shc.localSupportedSignAlgs.size();
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (SignatureScheme ss : shc.localSupportedSignAlgs) {
+                Record.putInt16(m, ss.id);
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT,
+                    new SignatureSchemesSpec(shc.localSupportedSignAlgs));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "signature_algorithms_cert" extension in
+     * the CertificateRequest handshake message.
+     */
+    private static final
+            class CRCertSignatureSchemesConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CRCertSignatureSchemesConsumer() {
+            // blank
+        }
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable " +
+                            "signature_algorithms_cert extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            SignatureSchemesSpec spec;
+            try {
+                spec = new SignatureSchemesSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT, spec);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * After session creation consuming of a "signature_algorithms_cert"
+     * extension in the CertificateRequest handshake message.
+     */
+    private static final class CRCertSignatureSchemesUpdate
+            implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CRCertSignatureSchemesUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            SignatureSchemesSpec spec = (SignatureSchemesSpec)
+                    chc.handshakeExtensions.get(
+                            SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT);
+            if (spec == null) {
+                // Ignore, no "signature_algorithms_cert" extension requested.
+                return;
+            }
+
+            // update the context
+            List<SignatureScheme> shemes =
+                    SignatureScheme.getSupportedAlgorithms(
+                            chc.algorithmConstraints, chc.negotiatedProtocol,
+                            spec.signatureSchemes);
+            chc.peerRequestedCertSignSchemes = shemes;
+            chc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,1219 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.io.ByteArrayInputStream;
+import java.nio.ByteBuffer;
+import java.security.cert.Extension;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+import sun.security.provider.certpath.OCSPResponse;
+import sun.security.provider.certpath.ResponderId;
+import static sun.security.ssl.SSLExtension.CH_STATUS_REQUEST;
+import static sun.security.ssl.SSLExtension.CH_STATUS_REQUEST_V2;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import static sun.security.ssl.SSLExtension.SH_STATUS_REQUEST;
+import static sun.security.ssl.SSLExtension.SH_STATUS_REQUEST_V2;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerValue;
+import sun.security.util.HexDumpEncoder;
+
+/**
+ * Pack of "status_request" and "status_request_v2" extensions.
+ */
+final class CertStatusExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHCertStatusReqProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHCertStatusReqConsumer();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHCertStatusReqProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHCertStatusReqConsumer();
+
+    static final HandshakeProducer ctNetworkProducer =
+            new CTCertStatusResponseProducer();
+    static final ExtensionConsumer ctOnLoadConsumer =
+            new CTCertStatusResponseConsumer();
+
+    static final SSLStringizer certStatusReqStringizer =
+            new CertStatusRequestStringizer();
+
+    static final HandshakeProducer chV2NetworkProducer =
+            new CHCertStatusReqV2Producer();
+    static final ExtensionConsumer chV2OnLoadConsumer =
+            new CHCertStatusReqV2Consumer();
+
+    static final HandshakeProducer shV2NetworkProducer =
+            new SHCertStatusReqV2Producer();
+    static final ExtensionConsumer shV2OnLoadConsumer =
+            new SHCertStatusReqV2Consumer();
+
+    static final SSLStringizer certStatusReqV2Stringizer =
+            new CertStatusRequestsStringizer();
+
+    static final SSLStringizer certStatusRespStringizer =
+            new CertStatusRespStringizer();
+
+    /**
+     * The "status_request" extension.
+     *
+     * RFC6066 defines the TLS extension,"status_request" (type 0x5),
+     * which allows the client to request that the server perform OCSP
+     * on the client's behalf.
+     *
+     * The "extension data" field of this extension contains a
+     * "CertificateStatusRequest" structure:
+     *
+     *      struct {
+     *          CertificateStatusType status_type;
+     *          select (status_type) {
+     *              case ocsp: OCSPStatusRequest;
+     *          } request;
+     *      } CertificateStatusRequest;
+     *
+     *      enum { ocsp(1), (255) } CertificateStatusType;
+     *
+     *      struct {
+     *          ResponderID responder_id_list<0..2^16-1>;
+     *          Extensions  request_extensions;
+     *      } OCSPStatusRequest;
+     *
+     *      opaque ResponderID<1..2^16-1>;
+     *      opaque Extensions<0..2^16-1>;
+     */
+    static final class CertStatusRequestSpec implements SSLExtensionSpec {
+        static final CertStatusRequestSpec DEFAULT =
+                new CertStatusRequestSpec(OCSPStatusRequest.EMPTY_OCSP);
+
+        final CertStatusRequest statusRequest;
+
+        private CertStatusRequestSpec(CertStatusRequest statusRequest) {
+            this.statusRequest = statusRequest;
+        }
+
+        private CertStatusRequestSpec(ByteBuffer buffer) throws IOException {
+            // Is it a empty extension_data?
+            if (buffer.remaining() == 0) {
+                // server response
+                this.statusRequest = null;
+                return;
+            }
+
+            if (buffer.remaining() < 1) {
+                throw new SSLProtocolException(
+                    "Invalid status_request extension: insufficient data");
+            }
+
+            byte statusType = (byte)Record.getInt8(buffer);
+            byte[] encoded = new byte[buffer.remaining()];
+            if (encoded.length != 0) {
+                buffer.get(encoded);
+            }
+            if (statusType == CertStatusRequestType.OCSP.id) {
+                this.statusRequest = new OCSPStatusRequest(statusType, encoded);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.info(
+                        "Unknown certificate status request " +
+                        "(status type: " + statusType + ")");
+                }
+
+                this.statusRequest = new CertStatusRequest(statusType, encoded);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return statusRequest == null ?
+                        "<empty>" : statusRequest.toString();
+        }
+    }
+
+    /**
+     * Defines the CertificateStatus response structure as outlined in
+     * RFC 6066.  This will contain a status response type, plus a single,
+     * non-empty OCSP response in DER-encoded form.
+     *
+     * struct {
+     *     CertificateStatusType status_type;
+     *     select (status_type) {
+     *         case ocsp: OCSPResponse;
+     *     } response;
+     * } CertificateStatus;
+     */
+    static final class CertStatusResponseSpec implements SSLExtensionSpec {
+        final CertStatusResponse statusResponse;
+
+        private CertStatusResponseSpec(CertStatusResponse resp) {
+            this.statusResponse = resp;
+        }
+
+        private CertStatusResponseSpec(ByteBuffer buffer) throws IOException {
+            if (buffer.remaining() < 2) {
+                throw new SSLProtocolException(
+                    "Invalid status_request extension: insufficient data");
+            }
+
+            // Get the status type (1 byte) and response data (vector)
+            byte type = (byte)Record.getInt8(buffer);
+            byte[] respData = Record.getBytes24(buffer);
+
+            // Create the CertStatusResponse based on the type
+            if (type == CertStatusRequestType.OCSP.id) {
+                this.statusResponse = new OCSPStatusResponse(type, respData);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.info(
+                        "Unknown certificate status response " +
+                        "(status type: " + type + ")");
+                }
+
+                this.statusResponse = new CertStatusResponse(type, respData);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return statusResponse == null ?
+                        "<empty>" : statusResponse.toString();
+        }
+    }
+
+    private static final
+            class CertStatusRequestStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new CertStatusRequestSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    private static final
+            class CertStatusRespStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new CertStatusResponseSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                 // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    static enum CertStatusRequestType {
+        OCSP        ((byte)0x01,    "ocsp"),        // RFC 6066/6961
+        OCSP_MULTI  ((byte)0x02,    "ocsp_multi");  // RFC 6961
+
+        final byte id;
+        final String name;
+
+        private CertStatusRequestType(byte id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        /**
+         * Returns the enum constant of the specified id (see RFC 6066).
+         */
+        static CertStatusRequestType valueOf(byte id) {
+            for (CertStatusRequestType srt : CertStatusRequestType.values()) {
+                if (srt.id == id) {
+                    return srt;
+                }
+            }
+
+            return null;
+        }
+
+        static String nameOf(byte id) {
+            for (CertStatusRequestType srt : CertStatusRequestType.values()) {
+                if (srt.id == id) {
+                    return srt.name;
+                }
+            }
+
+            return "UNDEFINED-CERT-STATUS-TYPE(" + id + ")";
+        }
+    }
+
+    static class CertStatusRequest {
+        final byte statusType;
+        final byte[] encodedRequest;
+
+        protected CertStatusRequest(byte statusType, byte[] encodedRequest) {
+            this.statusType = statusType;
+            this.encodedRequest = encodedRequest;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"certificate status type\": {0}\n" +
+                "\"encoded certificate status\": '{'\n" +
+                "{1}\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            String encoded = hexEncoder.encodeBuffer(encodedRequest);
+
+            Object[] messageFields = {
+                CertStatusRequestType.nameOf(statusType),
+                Utilities.indent(encoded)
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /*
+     * RFC6066 defines the TLS extension,"status_request" (type 0x5),
+     * which allows the client to request that the server perform OCSP
+     * on the client's behalf.
+     *
+     * The RFC defines an OCSPStatusRequest structure:
+     *
+     *      struct {
+     *          ResponderID responder_id_list<0..2^16-1>;
+     *          Extensions  request_extensions;
+     *      } OCSPStatusRequest;
+     */
+    static final class OCSPStatusRequest extends CertStatusRequest {
+        static final OCSPStatusRequest EMPTY_OCSP;
+        static final OCSPStatusRequest EMPTY_OCSP_MULTI;
+
+        final List<ResponderId> responderIds;
+        final List<Extension> extensions;
+        private final int ridListLen;
+        private final int extListLen;
+
+        static {
+            OCSPStatusRequest ocspReq = null;
+            OCSPStatusRequest multiReq = null;
+
+            try {
+                ocspReq = new OCSPStatusRequest(
+                        CertStatusRequestType.OCSP.id,
+                        new byte[] {0x00, 0x00, 0x00, 0x00});
+                multiReq = new OCSPStatusRequest(
+                    CertStatusRequestType.OCSP_MULTI.id,
+                    new byte[] {0x00, 0x00, 0x00, 0x00});
+            } catch (IOException ioe) {
+                // unlikely
+            }
+
+            EMPTY_OCSP = ocspReq;
+            EMPTY_OCSP_MULTI = multiReq;
+        }
+
+        private OCSPStatusRequest(byte statusType,
+                byte[] encoded) throws IOException {
+            super(statusType, encoded);
+
+            if (encoded == null || encoded.length < 4) {
+                                        //  2: length of responder_id_list
+                                        // +2: length of request_extensions
+                throw new SSLProtocolException(
+                        "Invalid OCSP status request: insufficient data");
+            }
+
+            List<ResponderId> rids = new ArrayList<>();
+            List<Extension> exts = new ArrayList<>();
+            ByteBuffer m = ByteBuffer.wrap(encoded);
+
+            this.ridListLen = Record.getInt16(m);
+            if (m.remaining() < (ridListLen + 2)) {
+                throw new SSLProtocolException(
+                        "Invalid OCSP status request: insufficient data");
+            }
+
+            int ridListBytesRemaining = ridListLen;
+            while (ridListBytesRemaining >= 2) {    // 2: length of responder_id
+                byte[] ridBytes = Record.getBytes16(m);
+                try {
+                    rids.add(new ResponderId(ridBytes));
+                } catch (IOException ioe) {
+                    throw new SSLProtocolException(
+                        "Invalid OCSP status request: invalid responder ID");
+                }
+                ridListBytesRemaining -= ridBytes.length + 2;
+            }
+
+            if (ridListBytesRemaining != 0) {
+                    throw new SSLProtocolException(
+                        "Invalid OCSP status request: incomplete data");
+            }
+
+            byte[] extListBytes = Record.getBytes16(m);
+            this.extListLen = extListBytes.length;
+            if (extListLen > 0) {
+                try {
+                    DerInputStream dis = new DerInputStream(extListBytes);
+                    DerValue[] extSeqContents =
+                            dis.getSequence(extListBytes.length);
+                    for (DerValue extDerVal : extSeqContents) {
+                        exts.add(new sun.security.x509.Extension(extDerVal));
+                    }
+                } catch (IOException ioe) {
+                    throw new SSLProtocolException(
+                        "Invalid OCSP status request: invalid extension");
+                }
+            }
+
+            this.responderIds = rids;
+            this.extensions = exts;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"certificate status type\": {0}\n" +
+                "\"OCSP status request\": '{'\n" +
+                "{1}\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            MessageFormat requestFormat = new MessageFormat(
+                "\"responder_id\": {0}\n" +
+                "\"request extensions\": '{'\n" +
+                "{1}\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            String ridStr = "<empty>";
+            if (!responderIds.isEmpty()) {
+                ridStr = responderIds.toString();
+            }
+
+            String extsStr = "<empty>";
+            if (!extensions.isEmpty()) {
+                StringBuilder extBuilder = new StringBuilder(512);
+                boolean isFirst = true;
+                for (Extension ext : this.extensions) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        extBuilder.append(",\n");
+                    }
+                    extBuilder.append(
+                            "{\n" + Utilities.indent(ext.toString()) + "}");
+                }
+
+                extsStr = extBuilder.toString();
+            }
+
+            Object[] requestFields = {
+                    ridStr,
+                    Utilities.indent(extsStr)
+                };
+            String ocspStatusRequest = requestFormat.format(requestFields);
+
+            Object[] messageFields = {
+                    CertStatusRequestType.nameOf(statusType),
+                    Utilities.indent(ocspStatusRequest)
+                };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    static class CertStatusResponse {
+        final byte statusType;
+        final byte[] encodedResponse;
+
+        protected CertStatusResponse(byte statusType, byte[] respDer) {
+            this.statusType = statusType;
+            this.encodedResponse = respDer;
+        }
+
+        byte[] toByteArray() throws IOException {
+            // Create a byte array large enough to handle the status_type
+            // field (1) + OCSP length (3) + OCSP data (variable)
+            byte[] outData = new byte[encodedResponse.length + 4];
+            ByteBuffer buf = ByteBuffer.wrap(outData);
+            Record.putInt8(buf, statusType);
+            Record.putBytes24(buf, encodedResponse);
+            return buf.array();
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"certificate status response type\": {0}\n" +
+                "\"encoded certificate status\": '{'\n" +
+                "{1}\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            String encoded = hexEncoder.encodeBuffer(encodedResponse);
+
+            Object[] messageFields = {
+                CertStatusRequestType.nameOf(statusType),
+                Utilities.indent(encoded)
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    static final class OCSPStatusResponse extends CertStatusResponse {
+        final OCSPResponse ocspResponse;
+
+        private OCSPStatusResponse(byte statusType,
+                byte[] encoded) throws IOException {
+            super(statusType, encoded);
+
+            // The DER-encoded OCSP response must not be zero length
+            if (encoded == null || encoded.length < 1) {
+                throw new SSLProtocolException(
+                        "Invalid OCSP status response: insufficient data");
+            }
+
+            // Otherwise, make an OCSPResponse object from the data
+            ocspResponse = new OCSPResponse(encoded);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"certificate status response type\": {0}\n" +
+                "\"OCSP status response\": '{'\n" +
+                "{1}\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            Object[] messageFields = {
+                CertStatusRequestType.nameOf(statusType),
+                Utilities.indent(ocspResponse.toString())
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * Network data producer of a "status_request" extension in the
+     * ClientHello handshake message.
+     */
+    private static final
+            class CHCertStatusReqProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHCertStatusReqProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            if (!chc.sslContext.isStaplingEnabled(true)) {
+                return null;
+            }
+
+            if (!chc.sslConfig.isAvailable(CH_STATUS_REQUEST)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " +
+                        CH_STATUS_REQUEST.name);
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            //
+            // We are using empty OCSPStatusRequest at present. May extend to
+            // support specific responder or extensions later.
+            byte[] extData = new byte[] {0x01, 0x00, 0x00, 0x00, 0x00};
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    CH_STATUS_REQUEST, CertStatusRequestSpec.DEFAULT);
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "status_request" extension in the
+     * ClientHello handshake message.
+     */
+    private static final
+            class CHCertStatusReqConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHCertStatusReqConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            if (!shc.sslConfig.isAvailable(CH_STATUS_REQUEST)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Ignore unavailable extension: " +
+                        CH_STATUS_REQUEST.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            CertStatusRequestSpec spec;
+            try {
+                spec = new CertStatusRequestSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(CH_STATUS_REQUEST, spec);
+            if (!shc.isResumption &&
+                    !shc.negotiatedProtocol.useTLS13PlusSpec()) {
+                shc.handshakeProducers.put(SSLHandshake.CERTIFICATE_STATUS.id,
+                    SSLHandshake.CERTIFICATE_STATUS);
+            }   // Otherwise, the certificate status presents in server cert.
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * Network data producer of a "status_request" extension in the
+     * ServerHello handshake message.
+     */
+    private static final
+            class SHCertStatusReqProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHCertStatusReqProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // The StaplingParameters in the ServerHandshakeContext will
+            // contain the info about what kind of stapling (if any) to
+            // perform and whether this status_request extension should be
+            // produced or the status_request_v2 (found in a different producer)
+            // No explicit check is required for isStaplingEnabled here.  If
+            // it is false then stapleParams will be null.  If it is true
+            // then stapleParams may or may not be false and the check below
+            // is sufficient.
+            if ((shc.stapleParams == null) ||
+                    (shc.stapleParams.statusRespExt !=
+                    SSLExtension.CH_STATUS_REQUEST)) {
+                return null;    // Do not produce status_request in ServerHello
+            }
+
+            // In response to "status_request" extension request only.
+            CertStatusRequestSpec spec = (CertStatusRequestSpec)
+                    shc.handshakeExtensions.get(CH_STATUS_REQUEST);
+            if (spec == null) {
+                // Ignore, no status_request extension requested.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable extension: " +
+                        CH_STATUS_REQUEST.name);
+                }
+
+                return null;        // ignore the extension
+            }
+
+            // Is it a session resuming?
+            if (shc.isResumption) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "No status_request response for session resuming");
+                }
+
+                return null;        // ignore the extension
+            }
+
+            // The "extension_data" in the extended ServerHello handshake
+            // message MUST be empty.
+            byte[] extData = new byte[0];
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SH_STATUS_REQUEST, CertStatusRequestSpec.DEFAULT);
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "status_request" extension in the
+     * ServerHello handshake message.
+     */
+    private static final
+            class SHCertStatusReqConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHCertStatusReqConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to "status_request" extension request only.
+            CertStatusRequestSpec requestedCsr = (CertStatusRequestSpec)
+                    chc.handshakeExtensions.get(CH_STATUS_REQUEST);
+            if (requestedCsr == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected status_request extension in ServerHello");
+            }
+
+            // Parse the extension.
+            if (buffer.hasRemaining()) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                  "Invalid status_request extension in ServerHello message: " +
+                  "the extension data must be empty");
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SH_STATUS_REQUEST, CertStatusRequestSpec.DEFAULT);
+            chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
+                    SSLHandshake.CERTIFICATE_STATUS);
+
+            // Since we've received a legitimate status_request in the
+            // ServerHello, stapling is active if it's been enabled.
+            chc.staplingActive = chc.sslContext.isStaplingEnabled(true);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * The "status_request_v2" extension.
+     *
+     * RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),
+     * which allows the client to request that the server perform OCSP
+     * on the client's behalf.
+     *
+     * The RFC defines an CertStatusReqItemV2 structure:
+     *
+     *      struct {
+     *          CertificateStatusType status_type;
+     *          uint16 request_length;
+     *          select (status_type) {
+     *              case ocsp: OCSPStatusRequest;
+     *              case ocsp_multi: OCSPStatusRequest;
+     *          } request;
+     *      } CertificateStatusRequestItemV2;
+     *
+     *      enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+     *      struct {
+     *        ResponderID responder_id_list<0..2^16-1>;
+     *        Extensions request_extensions;
+     *      } OCSPStatusRequest;
+     *
+     *      opaque ResponderID<1..2^16-1>;
+     *      opaque Extensions<0..2^16-1>;
+     *
+     *      struct {
+     *        CertificateStatusRequestItemV2
+     *                         certificate_status_req_list<1..2^16-1>;
+     *      } CertificateStatusRequestListV2;
+     */
+    static final class CertStatusRequestV2Spec implements SSLExtensionSpec {
+        static final CertStatusRequestV2Spec DEFAULT =
+                new CertStatusRequestV2Spec(new CertStatusRequest[] {
+                        OCSPStatusRequest.EMPTY_OCSP_MULTI});
+
+        final CertStatusRequest[] certStatusRequests;
+
+        private CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests) {
+            this.certStatusRequests = certStatusRequests;
+        }
+
+        private CertStatusRequestV2Spec(ByteBuffer message) throws IOException {
+            // Is it a empty extension_data?
+            if (message.remaining() == 0) {
+                // server response
+                this.certStatusRequests = new CertStatusRequest[0];
+                return;
+            }
+
+            if (message.remaining() < 5) {  //  2: certificate_status_req_list
+                                            // +1: status_type
+                                            // +2: request_length
+                throw new SSLProtocolException(
+                    "Invalid status_request_v2 extension: insufficient data");
+            }
+
+            int listLen = Record.getInt16(message);
+            if (listLen <= 0) {
+                throw new SSLProtocolException(
+                    "certificate_status_req_list length must be positive " +
+                    "(received length: " + listLen + ")");
+            }
+
+            int remaining = listLen;
+            List<CertStatusRequest> statusRequests = new ArrayList<>();
+            while (remaining > 0) {
+                byte statusType = (byte)Record.getInt8(message);
+                int requestLen = Record.getInt16(message);
+
+                if (message.remaining() < requestLen) {
+                    throw new SSLProtocolException(
+                            "Invalid status_request_v2 extension: " +
+                            "insufficient data (request_length=" + requestLen +
+                            ", remining=" + message.remaining() + ")");
+                }
+
+                byte[] encoded = new byte[requestLen];
+                if (encoded.length != 0) {
+                    message.get(encoded);
+                }
+                remaining -= 3;     // 1(status type) + 2(request_length) bytes
+                remaining -= requestLen;
+
+                if (statusType == CertStatusRequestType.OCSP.id ||
+                        statusType == CertStatusRequestType.OCSP_MULTI.id) {
+                    if (encoded.length < 4) {
+                                        //  2: length of responder_id_list
+                                        // +2: length of request_extensions
+                        throw new SSLProtocolException(
+                            "Invalid status_request_v2 extension: " +
+                            "insufficient data");
+                    }
+                    statusRequests.add(
+                            new OCSPStatusRequest(statusType, encoded));
+                } else {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.info(
+                                "Unknown certificate status request " +
+                                "(status type: " + statusType + ")");
+                    }
+                    statusRequests.add(
+                            new CertStatusRequest(statusType, encoded));
+                }
+            }
+
+            certStatusRequests =
+                    statusRequests.toArray(new CertStatusRequest[0]);
+        }
+
+        @Override
+        public String toString() {
+            if (certStatusRequests == null || certStatusRequests.length == 0) {
+                return "<empty>";
+            } else {
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"cert status request\": '{'\n{0}\n'}'", Locale.ENGLISH);
+
+                StringBuilder builder = new StringBuilder(512);
+                boolean isFirst = true;
+                for (CertStatusRequest csr : certStatusRequests) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        builder.append(", ");
+                    }
+                    Object[] messageFields = {
+                            Utilities.indent(csr.toString())
+                        };
+                    builder.append(messageFormat.format(messageFields));
+                }
+
+                return builder.toString();
+            }
+        }
+    }
+
+    private static final
+            class CertStatusRequestsStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new CertStatusRequestV2Spec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "status_request_v2" extension in the
+     * ClientHello handshake message.
+     */
+    private static final
+            class CHCertStatusReqV2Producer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHCertStatusReqV2Producer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            if (!chc.sslContext.isStaplingEnabled(true)) {
+                return null;
+            }
+
+            if (!chc.sslConfig.isAvailable(CH_STATUS_REQUEST_V2)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable status_request_v2 extension");
+                }
+
+                return null;
+            }
+
+            // Produce the extension.
+            //
+            // We are using empty OCSPStatusRequest at present. May extend to
+            // support specific responder or extensions later.
+            byte[] extData = new byte[] {
+                0x00, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00};
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    CH_STATUS_REQUEST_V2, CertStatusRequestV2Spec.DEFAULT);
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "status_request_v2" extension in the
+     * ClientHello handshake message.
+     */
+    private static final
+            class CHCertStatusReqV2Consumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHCertStatusReqV2Consumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            if (!shc.sslConfig.isAvailable(CH_STATUS_REQUEST_V2)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable status_request_v2 extension");
+                }
+
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            CertStatusRequestV2Spec spec;
+            try {
+                spec = new CertStatusRequestV2Spec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(CH_STATUS_REQUEST_V2, spec);
+            if (!shc.isResumption) {
+                shc.handshakeProducers.putIfAbsent(
+                        SSLHandshake.CERTIFICATE_STATUS.id,
+                        SSLHandshake.CERTIFICATE_STATUS);
+            }
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * Network data producer of a "status_request_v2" extension in the
+     * ServerHello handshake message.
+     */
+    private static final
+            class SHCertStatusReqV2Producer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHCertStatusReqV2Producer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            // The StaplingParameters in the ServerHandshakeContext will
+            // contain the info about what kind of stapling (if any) to
+            // perform and whether this status_request extension should be
+            // produced or the status_request_v2 (found in a different producer)
+            // No explicit check is required for isStaplingEnabled here.  If
+            // it is false then stapleParams will be null.  If it is true
+            // then stapleParams may or may not be false and the check below
+            // is sufficient.
+            if ((shc.stapleParams == null) ||
+                    (shc.stapleParams.statusRespExt !=
+                    SSLExtension.CH_STATUS_REQUEST_V2)) {
+                return null;    // Do not produce status_request_v2 in SH
+            }
+
+            // In response to "status_request_v2" extension request only
+            CertStatusRequestV2Spec spec = (CertStatusRequestV2Spec)
+                    shc.handshakeExtensions.get(CH_STATUS_REQUEST_V2);
+            if (spec == null) {
+                // Ignore, no status_request_v2 extension requested.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable status_request_v2 extension");
+                }
+
+                return null;        // ignore the extension
+            }
+
+            // Is it a session resuming?
+            if (shc.isResumption) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "No status_request_v2 response for session resumption");
+                }
+                return null;        // ignore the extension
+            }
+
+            // The "extension_data" in the extended ServerHello handshake
+            // message MUST be empty.
+            byte[] extData = new byte[0];
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SH_STATUS_REQUEST_V2, CertStatusRequestV2Spec.DEFAULT);
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "status_request_v2" extension in the
+     * ServerHello handshake message.
+     */
+    private static final
+            class SHCertStatusReqV2Consumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHCertStatusReqV2Consumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consumption happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to "status_request" extension request only
+            CertStatusRequestV2Spec requestedCsr = (CertStatusRequestV2Spec)
+                    chc.handshakeExtensions.get(CH_STATUS_REQUEST_V2);
+            if (requestedCsr == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected status_request_v2 extension in ServerHello");
+            }
+
+            // Parse the extension.
+            if (buffer.hasRemaining()) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                  "Invalid status_request_v2 extension in ServerHello: " +
+                  "the extension data must be empty");
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SH_STATUS_REQUEST_V2, CertStatusRequestV2Spec.DEFAULT);
+            chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,
+                    SSLHandshake.CERTIFICATE_STATUS);
+
+            // Since we've received a legitimate status_request in the
+            // ServerHello, stapling is active if it's been enabled.
+            chc.staplingActive = chc.sslContext.isStaplingEnabled(true);
+
+            // No impact on session resumption.
+        }
+    }
+
+    private static final
+            class CTCertStatusResponseProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CTCertStatusResponseProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            byte[] producedData = null;
+
+            // Stapling needs to be active and have valid data to proceed
+            if (shc.stapleParams == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Stapling is disabled for this connection");
+                }
+                return null;
+            }
+
+            // There needs to be a non-null CertificateEntry to proceed
+            if (shc.currentCertEntry == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest("Found null CertificateEntry in context");
+                }
+                return null;
+            }
+
+            // Pull the certificate from the CertificateEntry and find
+            // a response from the response map.  If one exists we will
+            // staple it.
+            try {
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                X509Certificate x509Cert =
+                        (X509Certificate)cf.generateCertificate(
+                                new ByteArrayInputStream(
+                                        shc.currentCertEntry.encoded));
+                byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert);
+                if (respBytes == null) {
+                    // We're done with this entry.  Clear it from the context
+                    if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,handshake,verbose")) {
+                        SSLLogger.finest("No status response found for " +
+                                x509Cert.getSubjectX500Principal());
+                    }
+                    shc.currentCertEntry = null;
+                    return null;
+                }
+
+                // Build a proper response buffer from the stapling information
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {
+                    SSLLogger.finest("Found status response for " +
+                            x509Cert.getSubjectX500Principal() +
+                            ", response length: " + respBytes.length);
+                }
+                CertStatusResponse certResp = (shc.stapleParams.statReqType ==
+                        CertStatusRequestType.OCSP) ?
+                        new OCSPStatusResponse(shc.stapleParams.statReqType.id,
+                                respBytes) :
+                        new CertStatusResponse(shc.stapleParams.statReqType.id,
+                                respBytes);
+                producedData = certResp.toByteArray();
+            } catch (CertificateException ce) {
+                shc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                        "Failed to parse server certificates", ce);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.BAD_CERT_STATUS_RESPONSE,
+                        "Failed to parse certificate status response", ioe);
+            }
+
+            // Clear the pinned CertificateEntry from the context
+            shc.currentCertEntry = null;
+            return producedData;
+        }
+    }
+
+    private static final
+        class CTCertStatusResponseConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CTCertStatusResponseConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consumption happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Parse the extension.
+            CertStatusResponseSpec spec;
+            try {
+                spec = new CertStatusResponseSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.DECODE_ERROR, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            if (chc.sslContext.isStaplingEnabled(true)) {
+                // Activate stapling
+                chc.staplingActive = true;
+            } else {
+                // Do no further processing of stapled responses
+                return;
+            }
+
+            // Get response list from the session.  This is unmodifiable
+            // so we need to create a new list.  Then add this new response
+            // to the end and submit it back to the session object.
+            if ((chc.handshakeSession != null) && (!chc.isResumption)) {
+                List<byte[]> respList = new ArrayList<>(
+                        chc.handshakeSession.getStatusResponses());
+                respList.add(spec.statusResponse.encodedResponse);
+                chc.handshakeSession.setStatusResponses(respList);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {
+                    SSLLogger.finest(
+                            "Ignoring stapled data on resumed session");
+                }
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/CertStatusReqExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-import java.util.Objects;
-
-/*
- * RFC6066 defines the TLS extension,"status_request" (type 0x5),
- * which allows the client to request that the server perform OCSP
- * on the client's behalf.
- * The "extension data" field of this extension contains a
- * "CertificateStatusRequest" structure:
- *
- *      struct {
- *          CertificateStatusType status_type;
- *          select (status_type) {
- *              case ocsp: OCSPStatusRequest;
- *          } request;
- *      } CertificateStatusRequest;
- *
- *      enum { ocsp(1), (255) } CertificateStatusType;
- *
- *      struct {
- *          ResponderID responder_id_list<0..2^16-1>;
- *          Extensions  request_extensions;
- *      } OCSPStatusRequest;
- *
- *      opaque ResponderID<1..2^16-1>;
- *      opaque Extensions<0..2^16-1>;
- */
-
-final class CertStatusReqExtension extends HelloExtension {
-
-    private final StatusRequestType statReqType;
-    private final StatusRequest request;
-
-
-    /**
-     * Construct the default status request extension object.  The default
-     * object results in a status_request extension where the extension
-     * data segment is zero-length.  This is used primarily in ServerHello
-     * messages where the server asserts it can do RFC 6066 status stapling.
-     */
-    CertStatusReqExtension() {
-        super(ExtensionType.EXT_STATUS_REQUEST);
-        statReqType = null;
-        request = null;
-    }
-
-    /**
-     * Construct the status request extension object given a request type
-     *      and {@code StatusRequest} object.
-     *
-     * @param reqType a {@code StatusRequestExtType object correspoding
-     *      to the underlying {@code StatusRequest} object.  A value of
-     *      {@code null} is not allowed.
-     * @param statReq the {@code StatusRequest} object used to provide the
-     *      encoding for the TLS extension.  A value of {@code null} is not
-     *      allowed.
-     *
-     * @throws IllegalArgumentException if the provided {@code StatusRequest}
-     *      does not match the type.
-     * @throws NullPointerException if either the {@code reqType} or
-     *      {@code statReq} arguments are {@code null}.
-     */
-    CertStatusReqExtension(StatusRequestType reqType, StatusRequest statReq) {
-        super(ExtensionType.EXT_STATUS_REQUEST);
-
-        statReqType = Objects.requireNonNull(reqType,
-                "Unallowed null value for status_type");
-        request = Objects.requireNonNull(statReq,
-                "Unallowed null value for request");
-
-        // There is currently only one known status type (OCSP)
-        // We can add more clauses to cover other types in the future
-        if (statReqType == StatusRequestType.OCSP) {
-            if (!(statReq instanceof OCSPStatusRequest)) {
-                throw new IllegalArgumentException("StatusRequest not " +
-                        "of type OCSPStatusRequest");
-            }
-        }
-    }
-
-    /**
-     * Construct the {@code CertStatusReqExtension} object from data read from
-     *      a {@code HandshakeInputStream}
-     *
-     * @param s the {@code HandshakeInputStream} providing the encoded data
-     * @param len the length of the extension data
-     *
-     * @throws IOException if any decoding errors happen during object
-     *      construction.
-     */
-    CertStatusReqExtension(HandshakeInStream s, int len) throws IOException {
-        super(ExtensionType.EXT_STATUS_REQUEST);
-
-        if (len > 0) {
-            // Obtain the status type (first byte)
-            statReqType = StatusRequestType.get(s.getInt8());
-            if (statReqType == StatusRequestType.OCSP) {
-                request = new OCSPStatusRequest(s);
-            } else {
-                // This is a status_type we don't understand.  Create
-                // an UnknownStatusRequest in order to preserve the data
-                request = new UnknownStatusRequest(s, len - 1);
-            }
-        } else {
-            // Treat this as a zero-length extension (i.e. from a ServerHello
-            statReqType = null;
-            request = null;
-        }
-    }
-
-    /**
-     * Return the length of the encoded extension, including extension type,
-     *      extension length and status_type fields.
-     *
-     * @return the length in bytes, including the extension type and
-     *      length fields.
-     */
-    @Override
-    int length() {
-        return (statReqType != null ? 5 + request.length() : 4);
-    }
-
-    /**
-     * Send the encoded TLS extension through a {@code HandshakeOutputStream}
-     *
-     * @param s the {@code HandshakeOutputStream} used to send the encoded data
-     *
-     * @throws IOException tf any errors occur during the encoding process
-     */
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(this.length() - 4);
-
-        if (statReqType != null) {
-            s.putInt8(statReqType.id);
-            request.send(s);
-        }
-    }
-
-    /**
-     * Create a string representation of this {@code CertStatusReqExtension}
-     *
-     * @return the string representation of this {@code CertStatusReqExtension}
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder("Extension ").append(type);
-        if (statReqType != null) {
-            sb.append(": ").append(statReqType).append(", ").append(request);
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * Return the type field for this {@code CertStatusReqExtension}
-     *
-     * @return the {@code StatusRequestType} for this extension.  {@code null}
-     *      will be returned if the default constructor is used to create
-     *      a zero length status_request extension (found in ServerHello
-     *      messages)
-     */
-    StatusRequestType getType() {
-        return statReqType;
-    }
-
-    /**
-     * Get the underlying {@code StatusRequest} for this
-     *      {@code CertStatusReqExtension}
-     *
-     * @return the {@code StatusRequest} or {@code null} if the default
-     * constructor was used to create this extension.
-     */
-    StatusRequest getRequest() {
-        return request;
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/CertStatusReqItemV2.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-import java.nio.ByteBuffer;
-import java.util.Objects;
-import javax.net.ssl.SSLException;
-
-/*
- * RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),
- * which allows the client to request that the server perform OCSP
- * on the client's behalf.
- *
- * The RFC defines an CertStatusReqItemV2 structure:
- *
- *      struct {
- *          CertificateStatusType status_type;
- *          uint16 request_length;
- *          select (status_type) {
- *              case ocsp: OCSPStatusRequest;
- *              case ocsp_multi: OCSPStatusRequest;
- *          } request;
- *      } CertificateStatusRequestItemV2;
- *
- *      enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
- */
-
-final class CertStatusReqItemV2 {
-
-    private final StatusRequestType statReqType;
-    private final StatusRequest request;
-
-    /**
-     * Construct a {@code CertStatusReqItemV2} object using a type value
-     *      and empty ResponderId and Extension lists.
-     *
-     * @param reqType the type of request (e.g. ocsp).  A {@code null} value
-     *      is not allowed.
-     * @param statReq the {@code StatusRequest} object used to provide the
-     *      encoding for this {@code CertStatusReqItemV2}.  A {@code null}
-     *      value is not allowed.
-     *
-     * @throws IllegalArgumentException if the provided {@code StatusRequest}
-     *      does not match the type.
-     * @throws NullPointerException if either the reqType or statReq arguments
-     *      are {@code null}.
-     */
-    CertStatusReqItemV2(StatusRequestType reqType, StatusRequest statReq) {
-        statReqType = Objects.requireNonNull(reqType,
-                "Unallowed null value for status_type");
-        request = Objects.requireNonNull(statReq,
-                "Unallowed null value for request");
-
-        // There is currently only one known status type (OCSP)
-        // We can add more clauses to cover other types in the future
-        if (statReqType.equals(StatusRequestType.OCSP) ||
-                statReqType.equals(StatusRequestType.OCSP_MULTI)) {
-            if (!(statReq instanceof OCSPStatusRequest)) {
-                throw new IllegalArgumentException("StatusRequest not " +
-                        "of type OCSPStatusRequest");
-            }
-        }
-    }
-
-    /**
-     * Construct a {@code CertStatusReqItemV2} object from encoded bytes
-     *
-     * @param requestBytes the encoded bytes for the {@code CertStatusReqItemV2}
-     *
-     * @throws IOException if any decoding errors take place
-     * @throws IllegalArgumentException if the parsed reqType value is not a
-     *      supported status request type.
-     */
-    CertStatusReqItemV2(byte[] reqItemBytes) throws IOException {
-        ByteBuffer reqBuf = ByteBuffer.wrap(reqItemBytes);
-        statReqType = StatusRequestType.get(reqBuf.get());
-        int requestLength = Short.toUnsignedInt(reqBuf.getShort());
-
-        if (requestLength == reqBuf.remaining()) {
-            byte[] statReqBytes = new byte[requestLength];
-            reqBuf.get(statReqBytes);
-            if (statReqType == StatusRequestType.OCSP ||
-                    statReqType == StatusRequestType.OCSP_MULTI) {
-                request = new OCSPStatusRequest(statReqBytes);
-            } else {
-                request = new UnknownStatusRequest(statReqBytes);
-            }
-        } else {
-            throw new SSLException("Incorrect request_length: " +
-                    "Expected " + reqBuf.remaining() + ", got " +
-                    requestLength);
-        }
-    }
-
-    /**
-     * Construct an {@code CertStatusReqItemV2} object from data read from
-     * a {@code HandshakeInputStream}
-     *
-     * @param s the {@code HandshakeInputStream} providing the encoded data
-     *
-     * @throws IOException if any decoding errors happen during object
-     *      construction.
-     * @throws IllegalArgumentException if the parsed reqType value is not a
-     *      supported status request type.
-     */
-    CertStatusReqItemV2(HandshakeInStream in) throws IOException {
-        statReqType = StatusRequestType.get(in.getInt8());
-        int requestLength = in.getInt16();
-
-        if (statReqType == StatusRequestType.OCSP ||
-                statReqType == StatusRequestType.OCSP_MULTI) {
-            request = new OCSPStatusRequest(in);
-        } else {
-            request = new UnknownStatusRequest(in, requestLength);
-        }
-    }
-
-    /**
-     * Return the length of this {@code CertStatusReqItemV2} in its encoded form
-     *
-     * @return the encoded length of this {@code CertStatusReqItemV2}
-     */
-    int length() {
-        // The length is the status type (1 byte) + the request length
-        // field (2 bytes) + the StatusRequest data length.
-        return request.length() + 3;
-    }
-
-    /**
-     * Send the encoded {@code CertStatusReqItemV2} through a
-     *      {@code HandshakeOutputStream}
-     *
-     * @param s the {@code HandshakeOutputStream} used to send the encoded data
-     *
-     * @throws IOException if any errors occur during the encoding process
-     */
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt8(statReqType.id);
-        s.putInt16(request.length());
-        request.send(s);
-    }
-
-    /**
-     * Create a string representation of this {@code CertStatusReqItemV2}
-     *
-     * @return the string representation of this {@code CertStatusReqItemV2}
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("CertStatusReqItemV2: ").append(statReqType).append(", ");
-        sb.append(request.toString());
-
-        return sb.toString();
-    }
-
-    /**
-     * Return the type field for this {@code CertStatusReqItemV2}
-     *
-     * @return the {@code StatusRequestType} for this extension.
-     */
-    StatusRequestType getType() {
-        return statReqType;
-    }
-
-    /**
-     * Get the underlying {@code StatusRequest} for this
-     *      {@code CertStatusReqItemV2}
-     *
-     * @return the {@code StatusRequest}
-     */
-    StatusRequest getRequest() {
-        return request;
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/CertStatusReqListV2Extension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,220 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-import java.util.List;
-import java.util.Collections;
-import java.util.ArrayList;
-import java.util.Objects;
-import javax.net.ssl.SSLException;
-
-/*
- * RFC6066 defines the TLS extension,"status_request" (type 0x5),
- * which allows the client to request that the server perform OCSP
- * on the client's behalf.
- * The "extension data" field of this extension contains a
- * "CertificateStatusRequest" structure:
- *
- *      struct {
- *          CertificateStatusType status_type;
- *          select (status_type) {
- *              case ocsp: OCSPStatusRequest;
- *          } request;
- *      } CertificateStatusRequest;
- *
- *      enum { ocsp(1), (255) } CertificateStatusType;
- *
- *      struct {
- *          ResponderID responder_id_list<0..2^16-1>;
- *          Extensions  request_extensions;
- *      } OCSPStatusRequest;
- *
- *      opaque ResponderID<1..2^16-1>;
- *      opaque Extensions<0..2^16-1>;
- */
-
-final class CertStatusReqListV2Extension extends HelloExtension {
-
-    private final List<CertStatusReqItemV2> itemList;
-    private final int itemListLength;
-
-    /**
-     * Construct a default {@code CertStatusReqListV2Extension}.  The default
-     * object results in a status_request_v2 extension where the extension
-     * data segment is zero-length.  This is used primarily in ServerHello
-     * messages where the server asserts it can do RFC 6961 status stapling.
-     */
-    CertStatusReqListV2Extension() {
-        super(ExtensionType.EXT_STATUS_REQUEST_V2);
-        itemList = Collections.emptyList();
-        itemListLength = 0;
-    }
-
-    /**
-     * Construct a {@code CertStatusReqListV2Extension} from a provided list
-     *      of {@code CertStatusReqItemV2} objects.
-     *
-     * @param reqList a {@code List} containing one or more
-     *      {@code CertStatusReqItemV2} objects to be included in this TLS
-     *      Hello extension.  Passing an empty list will result in the encoded
-     *      extension having a zero-length extension_data segment, and is
-     *      the same as using the default constructor.
-     *
-     * @throws NullPointerException if reqList is {@code null}
-     */
-    CertStatusReqListV2Extension(List<CertStatusReqItemV2> reqList) {
-        super(ExtensionType.EXT_STATUS_REQUEST_V2);
-        Objects.requireNonNull(reqList,
-                "Unallowed null value for certificate_status_req_list");
-        itemList = Collections.unmodifiableList(new ArrayList<>(reqList));
-        itemListLength = calculateListLength();
-    }
-
-    /**
-     *  Construct the {@code CertStatusReqListV2Extension} object from data
-     *      read from a {@code HandshakeInputStream}
-     *
-     * @param s the {@code HandshakeInputStream} providing the encoded data
-     * @param len the length of the extension data
-     *
-     * @throws IOException if any decoding errors happen during object
-     *      construction.
-     */
-    CertStatusReqListV2Extension(HandshakeInStream s, int len)
-            throws IOException {
-        super(ExtensionType.EXT_STATUS_REQUEST_V2);
-
-        if (len <= 0) {
-            // Handle the empty extension data case (from a ServerHello)
-            itemList = Collections.emptyList();
-            itemListLength = 0;
-        } else {
-            List<CertStatusReqItemV2> workingList = new ArrayList<>();
-
-            itemListLength = s.getInt16();
-            if (itemListLength <= 0) {
-                throw new SSLException("certificate_status_req_list length " +
-                        "must be greater than zero (received length: " +
-                        itemListLength + ")");
-            }
-
-            int totalRead = 0;
-            CertStatusReqItemV2 reqItem;
-            do {
-                reqItem = new CertStatusReqItemV2(s);
-                totalRead += reqItem.length();
-            } while (workingList.add(reqItem) && totalRead < itemListLength);
-
-            // If for some reason the add returns false, we may not have read
-            // all the necessary bytes from the stream.  Check this and throw
-            // an exception if we terminated the loop early.
-            if (totalRead != itemListLength) {
-                throw new SSLException("Not all certificate_status_req_list " +
-                        "bytes were read: expected " + itemListLength +
-                        ", read " + totalRead);
-            }
-
-            itemList = Collections.unmodifiableList(workingList);
-        }
-    }
-
-    /**
-     * Get the list of {@code CertStatusReqItemV2} objects for this extension
-     *
-     * @return an unmodifiable list of {@code CertStatusReqItemV2} objects
-     */
-    List<CertStatusReqItemV2> getRequestItems() {
-        return itemList;
-    }
-
-    /**
-     * Return the length of the encoded extension, including extension type
-     *      and extension length fields.
-     *
-     * @return the length in bytes, including the extension type and
-     *      extension_data length.
-     */
-    @Override
-    int length() {
-        return (itemList.isEmpty() ? 4 : itemListLength + 6);
-    }
-
-    /**
-     * Send the encoded {@code CertStatusReqListV2Extension} through a
-     *      {@code HandshakeOutputStream}
-     *
-     * @param s the {@code HandshakeOutputStream} used to send the encoded data
-     *
-     * @throws IOException if any errors occur during the encoding process
-     */
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(this.length() - 4);
-        if (itemListLength > 0) {
-            s.putInt16(itemListLength);
-            for (CertStatusReqItemV2 item : itemList) {
-                item.send(s);
-            }
-        }
-    }
-
-    /**
-     * Create a string representation of this
-     *      {@code CertStatusReqListV2Extension}
-     *
-     * @return the string representation of this
-     *      {@code CertStatusReqListV2Extension}
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder("Extension ").append(type);
-        for (CertStatusReqItemV2 item : itemList) {
-            sb.append("\n").append(item);
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * Determine the length of the certificate_status_req_list field in
-     * the status_request_v2 extension.
-     *
-     * @return the total encoded length of all items in the list, or 0 if the
-     *      encapsulating extension_data is zero-length (from a ServerHello)
-     */
-    private int calculateListLength() {
-        int listLen = 0;
-
-        for (CertStatusReqItemV2 item : itemList) {
-            listLen += item.length();
-        }
-
-        return listLen;
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,1370 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.PublicKey;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertPathValidatorException.BasicReason;
+import java.security.cert.CertPathValidatorException.Reason;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
+import sun.security.ssl.ClientHello.ClientHelloMessage;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+
+/**
+ * Pack of the CertificateMessage handshake message.
+ */
+final class CertificateMessage {
+    static final SSLConsumer t12HandshakeConsumer =
+        new T12CertificateConsumer();
+    static final HandshakeProducer t12HandshakeProducer =
+        new T12CertificateProducer();
+
+    static final SSLConsumer t13HandshakeConsumer =
+        new T13CertificateConsumer();
+    static final HandshakeProducer t13HandshakeProducer =
+        new T13CertificateProducer();
+
+    /**
+     * The Certificate handshake message for TLS 1.2 and previous
+     * SSL/TLS protocol versions.
+     *
+     * In server mode, the certificate handshake message is sent whenever the
+     * agreed-upon key exchange method uses certificates for authentication.
+     * In client mode, this message is only sent if the server requests a
+     * certificate for client authentication.
+     *
+     *       opaque ASN.1Cert<1..2^24-1>;
+     *
+     * SSL 3.0:
+     *       struct {
+     *           ASN.1Cert certificate_list<1..2^24-1>;
+     *       } Certificate;
+     * Note: For SSL 3.0 client authentication, if no suitable certificate
+     * is available, the client should send a no_certificate alert instead.
+     * This alert is only a warning; however, the server may respond with
+     * a fatal handshake failure alert if client authentication is required.
+     *
+     * TLS 1.0/1.1/1.2:
+     *       struct {
+     *           ASN.1Cert certificate_list<0..2^24-1>;
+     *       } Certificate;
+     */
+    static final class T12CertificateMessage extends HandshakeMessage {
+        final List<byte[]> encodedCertChain;
+
+        T12CertificateMessage(HandshakeContext handshakeContext,
+                X509Certificate[] certChain) throws SSLException {
+            super(handshakeContext);
+
+            List<byte[]> encodedCerts = new ArrayList<>(certChain.length);
+            for (X509Certificate cert : certChain) {
+                try {
+                    encodedCerts.add(cert.getEncoded());
+                } catch (CertificateEncodingException cee) {
+                    // unlikely
+                    handshakeContext.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Could not encode certificate (" +
+                            cert.getSubjectX500Principal() + ")", cee);
+                    break;
+                }
+            }
+
+            this.encodedCertChain = encodedCerts;
+        }
+
+        T12CertificateMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            int listLen = Record.getInt24(m);
+            if (listLen > m.remaining()) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Error parsing certificate message:no sufficient data");
+            }
+            if (listLen > 0) {
+                List<byte[]> encodedCerts = new LinkedList<>();
+                while (listLen > 0) {
+                    byte[] encodedCert = Record.getBytes24(m);
+                    listLen -= (3 + encodedCert.length);
+                    encodedCerts.add(encodedCert);
+                }
+                this.encodedCertChain = encodedCerts;
+            } else {
+                this.encodedCertChain = Collections.emptyList();
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE;
+        }
+
+        @Override
+        public int messageLength() {
+            int msgLen = 3;
+            for (byte[] encodedCert : encodedCertChain) {
+                msgLen += (encodedCert.length + 3);
+            }
+
+            return msgLen;
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            int listLen = 0;
+            for (byte[] encodedCert : encodedCertChain) {
+                listLen += (encodedCert.length + 3);
+            }
+
+            hos.putInt24(listLen);
+            for (byte[] encodedCert : encodedCertChain) {
+                hos.putBytes24(encodedCert);
+            }
+        }
+
+        @Override
+        public String toString() {
+            if (encodedCertChain.isEmpty()) {
+                return "\"Certificates\": <empty list>";
+            }
+
+            Object[] x509Certs = new Object[encodedCertChain.size()];
+            try {
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                int i = 0;
+                for (byte[] encodedCert : encodedCertChain) {
+                    Object obj;
+                    try {
+                        obj = (X509Certificate)cf.generateCertificate(
+                                    new ByteArrayInputStream(encodedCert));
+                    } catch (CertificateException ce) {
+                        obj = encodedCert;
+                    }
+                    x509Certs[i++] = obj;
+                }
+            } catch (CertificateException ce) {
+                // no X.509 certificate factory service
+                int i = 0;
+                for (byte[] encodedCert : encodedCertChain) {
+                    x509Certs[i++] = encodedCert;
+                }
+            }
+
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"Certificates\": [\n" +
+                    "{0}\n" +
+                    "]",
+                    Locale.ENGLISH);
+            Object[] messageFields = {
+                SSLLogger.toString(x509Certs)
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "Certificate" handshake message producer for TLS 1.2 and
+     * previous SSL/TLS protocol versions.
+     */
+    private static final
+            class T12CertificateProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T12CertificateProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+            if (hc.sslConfig.isClientMode) {
+                return onProduceCertificate(
+                        (ClientHandshakeContext)context, message);
+            } else {
+                return onProduceCertificate(
+                        (ServerHandshakeContext)context, message);
+            }
+        }
+
+        private byte[] onProduceCertificate(ServerHandshakeContext shc,
+                SSLHandshake.HandshakeMessage message) throws IOException {
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    break;
+                }
+            }
+
+            if (x509Possession == null) {       // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No expected X.509 certificate for server authentication");
+
+                return null;        // make the compiler happy
+            }
+
+            shc.handshakeSession.setLocalPrivateKey(
+                    x509Possession.popPrivateKey);
+            shc.handshakeSession.setLocalCertificates(x509Possession.popCerts);
+            T12CertificateMessage cm =
+                    new T12CertificateMessage(shc, x509Possession.popCerts);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced server Certificate handshake message", cm);
+            }
+
+            // Output the handshake message.
+            cm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+
+        private byte[] onProduceCertificate(ClientHandshakeContext chc,
+                SSLHandshake.HandshakeMessage message) throws IOException {
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : chc.handshakePossessions) {
+                if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    break;
+                }
+            }
+
+            // Report to the server if no appropriate cert was found.  For
+            // SSL 3.0, send a no_certificate alert;  TLS 1.0/1.1/1.2 uses
+            // an empty cert chain instead.
+            if (x509Possession == null) {
+                if (chc.negotiatedProtocol.useTLS10PlusSpec()) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "No X.509 certificate for client authentication, " +
+                            "use empty Certificate message instead");
+                    }
+
+                    x509Possession =
+                            new X509Possession(null, new X509Certificate[0]);
+                } else {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "No X.509 certificate for client authentication, " +
+                            "send a no_certificate alert");
+                    }
+
+                    chc.conContext.warning(Alert.NO_CERTIFICATE);
+                    return null;
+                }
+            }
+
+            chc.handshakeSession.setLocalPrivateKey(
+                    x509Possession.popPrivateKey);
+            if (x509Possession.popCerts != null &&
+                    x509Possession.popCerts.length != 0) {
+                chc.handshakeSession.setLocalCertificates(
+                        x509Possession.popCerts);
+            } else {
+                chc.handshakeSession.setLocalCertificates(null);
+            }
+            T12CertificateMessage cm =
+                    new T12CertificateMessage(chc, x509Possession.popCerts);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced client Certificate handshake message", cm);
+            }
+
+            // Output the handshake message.
+            cm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "Certificate" handshake message consumer for TLS 1.2 and
+     * previous SSL/TLS protocol versions.
+     */
+    static final
+            class T12CertificateConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T12CertificateConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+
+            // clean up this consumer
+            hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id);
+
+            T12CertificateMessage cm = new T12CertificateMessage(hc, message);
+            if (hc.sslConfig.isClientMode) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Consuming server Certificate handshake message", cm);
+                }
+                onCertificate((ClientHandshakeContext)context, cm);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Consuming client Certificate handshake message", cm);
+                }
+                onCertificate((ServerHandshakeContext)context, cm);
+            }
+        }
+
+        private void onCertificate(ServerHandshakeContext shc,
+                T12CertificateMessage certificateMessage )throws IOException {
+            List<byte[]> encodedCerts = certificateMessage.encodedCertChain;
+            if (encodedCerts == null || encodedCerts.isEmpty()) {
+                if (shc.sslConfig.clientAuthType !=
+                        ClientAuthType.CLIENT_AUTH_REQUESTED) {
+                    // unexpected or require client authentication
+                    shc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                        "Empty server certificate chain");
+                } else {
+                    return;
+                }
+            }
+
+            X509Certificate[] x509Certs =
+                    new X509Certificate[encodedCerts.size()];
+            try {
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                int i = 0;
+                for (byte[] encodedCert : encodedCerts) {
+                    x509Certs[i++] = (X509Certificate)cf.generateCertificate(
+                                    new ByteArrayInputStream(encodedCert));
+                }
+            } catch (CertificateException ce) {
+                shc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                    "Failed to parse server certificates", ce);
+            }
+
+            checkClientCerts(shc, x509Certs);
+
+            //
+            // update
+            //
+            shc.handshakeCredentials.add(
+                new X509Credentials(x509Certs[0].getPublicKey(), x509Certs));
+            shc.handshakeSession.setPeerCertificates(x509Certs);
+        }
+
+        private void onCertificate(ClientHandshakeContext chc,
+                T12CertificateMessage certificateMessage) throws IOException {
+            List<byte[]> encodedCerts = certificateMessage.encodedCertChain;
+            if (encodedCerts == null || encodedCerts.isEmpty()) {
+                chc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                    "Empty server certificate chain");
+            }
+
+            X509Certificate[] x509Certs =
+                    new X509Certificate[encodedCerts.size()];
+            try {
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                int i = 0;
+                for (byte[] encodedCert : encodedCerts) {
+                    x509Certs[i++] = (X509Certificate)cf.generateCertificate(
+                                    new ByteArrayInputStream(encodedCert));
+                }
+            } catch (CertificateException ce) {
+                chc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                    "Failed to parse server certificates", ce);
+            }
+
+            // Allow server certificate change in client side during
+            // renegotiation after a session-resumption abbreviated
+            // initial handshake?
+            //
+            // DO NOT need to check allowUnsafeServerCertChange here. We only
+            // reserve server certificates when allowUnsafeServerCertChange is
+            // false.
+            if (chc.reservedServerCerts != null &&
+                    !chc.handshakeSession.useExtendedMasterSecret) {
+                // It is not necessary to check the certificate update if
+                // endpoint identification is enabled.
+                String identityAlg = chc.sslConfig.identificationProtocol;
+                if ((identityAlg == null || identityAlg.length() == 0) &&
+                        !isIdentityEquivalent(x509Certs[0],
+                                chc.reservedServerCerts[0])) {
+                    chc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                            "server certificate change is restricted " +
+                            "during renegotiation");
+                }
+            }
+
+            // ask the trust manager to verify the chain
+            if (chc.staplingActive) {
+                // Defer the certificate check until after we've received the
+                // CertificateStatus message.  If that message doesn't come in
+                // immediately following this message we will execute the
+                // check from CertificateStatus' absent handler.
+                chc.deferredCerts = x509Certs;
+            } else {
+                // We're not doing stapling, so perform the check right now
+                checkServerCerts(chc, x509Certs);
+            }
+
+            //
+            // update
+            //
+            chc.handshakeCredentials.add(
+                new X509Credentials(x509Certs[0].getPublicKey(), x509Certs));
+            chc.handshakeSession.setPeerCertificates(x509Certs);
+        }
+
+        /*
+         * Whether the certificates can represent the same identity?
+         *
+         * The certificates can be used to represent the same identity:
+         *     1. If the subject alternative names of IP address are present
+         *        in both certificates, they should be identical; otherwise,
+         *     2. if the subject alternative names of DNS name are present in
+         *        both certificates, they should be identical; otherwise,
+         *     3. if the subject fields are present in both certificates, the
+         *        certificate subjects and issuers should be identical.
+         */
+        private static boolean isIdentityEquivalent(X509Certificate thisCert,
+                X509Certificate prevCert) {
+            if (thisCert.equals(prevCert)) {
+                return true;
+            }
+
+            // check subject alternative names
+            Collection<List<?>> thisSubjectAltNames = null;
+            try {
+                thisSubjectAltNames = thisCert.getSubjectAlternativeNames();
+            } catch (CertificateParsingException cpe) {
+                if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
+                    SSLLogger.fine(
+                        "Attempt to obtain subjectAltNames extension failed!");
+                }
+            }
+
+            Collection<List<?>> prevSubjectAltNames = null;
+            try {
+                prevSubjectAltNames = prevCert.getSubjectAlternativeNames();
+            } catch (CertificateParsingException cpe) {
+                if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
+                    SSLLogger.fine(
+                        "Attempt to obtain subjectAltNames extension failed!");
+                }
+            }
+
+            if (thisSubjectAltNames != null && prevSubjectAltNames != null) {
+                // check the iPAddress field in subjectAltName extension
+                //
+                // 7: subject alternative name of type IP.
+                Collection<String> thisSubAltIPAddrs =
+                            getSubjectAltNames(thisSubjectAltNames, 7);
+                Collection<String> prevSubAltIPAddrs =
+                            getSubjectAltNames(prevSubjectAltNames, 7);
+                if (thisSubAltIPAddrs != null && prevSubAltIPAddrs != null &&
+                    isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs)) {
+                    return true;
+                }
+
+                // check the dNSName field in subjectAltName extension
+                // 2: subject alternative name of type IP.
+                Collection<String> thisSubAltDnsNames =
+                            getSubjectAltNames(thisSubjectAltNames, 2);
+                Collection<String> prevSubAltDnsNames =
+                            getSubjectAltNames(prevSubjectAltNames, 2);
+                if (thisSubAltDnsNames != null && prevSubAltDnsNames != null &&
+                    isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames)) {
+                    return true;
+                }
+            }
+
+            // check the certificate subject and issuer
+            X500Principal thisSubject = thisCert.getSubjectX500Principal();
+            X500Principal prevSubject = prevCert.getSubjectX500Principal();
+            X500Principal thisIssuer = thisCert.getIssuerX500Principal();
+            X500Principal prevIssuer = prevCert.getIssuerX500Principal();
+
+            return (!thisSubject.getName().isEmpty() &&
+                    !prevSubject.getName().isEmpty() &&
+                    thisSubject.equals(prevSubject) &&
+                    thisIssuer.equals(prevIssuer));
+        }
+
+        /*
+         * Returns the subject alternative name of the specified type in the
+         * subjectAltNames extension of a certificate.
+         *
+         * Note that only those subjectAltName types that use String data
+         * should be passed into this function.
+         */
+        private static Collection<String> getSubjectAltNames(
+                Collection<List<?>> subjectAltNames, int type) {
+            HashSet<String> subAltDnsNames = null;
+            for (List<?> subjectAltName : subjectAltNames) {
+                int subjectAltNameType = (Integer)subjectAltName.get(0);
+                if (subjectAltNameType == type) {
+                    String subAltDnsName = (String)subjectAltName.get(1);
+                    if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) {
+                        if (subAltDnsNames == null) {
+                            subAltDnsNames =
+                                    new HashSet<>(subjectAltNames.size());
+                        }
+                        subAltDnsNames.add(subAltDnsName);
+                    }
+                }
+            }
+
+            return subAltDnsNames;
+        }
+
+        private static boolean isEquivalent(Collection<String> thisSubAltNames,
+                Collection<String> prevSubAltNames) {
+            for (String thisSubAltName : thisSubAltNames) {
+                for (String prevSubAltName : prevSubAltNames) {
+                    // Only allow the exactly match.  No wildcard character
+                    // checking.
+                    if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         * Perform client-side checking of server certificates.
+         *
+         * @param certs an array of {@code X509Certificate} objects presented
+         *      by the server in the ServerCertificate message.
+         *
+         * @throws IOException if a failure occurs during validation or
+         *      the trust manager associated with the {@code SSLContext} is not
+         *      an {@code X509ExtendedTrustManager}.
+         */
+        static void checkServerCerts(ClientHandshakeContext chc,
+                X509Certificate[] certs) throws IOException {
+
+            X509TrustManager tm = chc.sslContext.getX509TrustManager();
+
+            // find out the key exchange algorithm used
+            // use "RSA" for non-ephemeral "RSA_EXPORT"
+            String keyExchangeString;
+            if (chc.negotiatedCipherSuite.keyExchange ==
+                    CipherSuite.KeyExchange.K_RSA_EXPORT ||
+                    chc.negotiatedCipherSuite.keyExchange ==
+                            CipherSuite.KeyExchange.K_DHE_RSA_EXPORT) {
+                keyExchangeString = CipherSuite.KeyExchange.K_RSA.name;
+            } else {
+                keyExchangeString = chc.negotiatedCipherSuite.keyExchange.name;
+            }
+
+            try {
+                if (tm instanceof X509ExtendedTrustManager) {
+                    if (chc.conContext.transport instanceof SSLEngine) {
+                        SSLEngine engine = (SSLEngine)chc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkServerTrusted(
+                            certs.clone(),
+                            keyExchangeString,
+                            engine);
+                    } else {
+                        SSLSocket socket = (SSLSocket)chc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkServerTrusted(
+                            certs.clone(),
+                            keyExchangeString,
+                            socket);
+                    }
+                } else {
+                    // Unlikely to happen, because we have wrapped the old
+                    // X509TrustManager with the new X509ExtendedTrustManager.
+                    throw new CertificateException(
+                            "Improper X509TrustManager implementation");
+                }
+
+                // Once the server certificate chain has been validated, set
+                // the certificate chain in the TLS session.
+                chc.handshakeSession.setPeerCertificates(certs);
+            } catch (CertificateException ce) {
+                chc.conContext.fatal(getCertificateAlert(chc, ce), ce);
+            }
+        }
+
+        private static void checkClientCerts(ServerHandshakeContext shc,
+                X509Certificate[] certs) throws IOException {
+            X509TrustManager tm = shc.sslContext.getX509TrustManager();
+
+            // find out the types of client authentication used
+            PublicKey key = certs[0].getPublicKey();
+            String keyAlgorithm = key.getAlgorithm();
+            String authType;
+            switch (keyAlgorithm) {
+                case "RSA":
+                case "DSA":
+                case "EC":
+                case "RSASSA-PSS":
+                    authType = keyAlgorithm;
+                    break;
+                default:
+                    // unknown public key type
+                    authType = "UNKNOWN";
+            }
+
+            try {
+                if (tm instanceof X509ExtendedTrustManager) {
+                    if (shc.conContext.transport instanceof SSLEngine) {
+                        SSLEngine engine = (SSLEngine)shc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkClientTrusted(
+                            certs.clone(),
+                            authType,
+                            engine);
+                    } else {
+                        SSLSocket socket = (SSLSocket)shc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkClientTrusted(
+                            certs.clone(),
+                            authType,
+                            socket);
+                    }
+                } else {
+                    // Unlikely to happen, because we have wrapped the old
+                    // X509TrustManager with the new X509ExtendedTrustManager.
+                    throw new CertificateException(
+                            "Improper X509TrustManager implementation");
+                }
+            } catch (CertificateException ce) {
+                shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce);
+            }
+        }
+
+        /**
+         * When a failure happens during certificate checking from an
+         * {@link X509TrustManager}, determine what TLS alert description
+         * to use.
+         *
+         * @param cexc The exception thrown by the {@link X509TrustManager}
+         *
+         * @return A byte value corresponding to a TLS alert description number.
+         */
+        private static Alert getCertificateAlert(
+                ClientHandshakeContext chc, CertificateException cexc) {
+            // The specific reason for the failure will determine how to
+            // set the alert description value
+            Alert alert = Alert.CERTIFICATE_UNKNOWN;
+
+            Throwable baseCause = cexc.getCause();
+            if (baseCause instanceof CertPathValidatorException) {
+                CertPathValidatorException cpve =
+                        (CertPathValidatorException)baseCause;
+                Reason reason = cpve.getReason();
+                if (reason == BasicReason.REVOKED) {
+                    alert = chc.staplingActive ?
+                            Alert.BAD_CERT_STATUS_RESPONSE :
+                            Alert.CERTIFICATE_REVOKED;
+                } else if (
+                        reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
+                    alert = chc.staplingActive ?
+                            Alert.BAD_CERT_STATUS_RESPONSE :
+                            Alert.CERTIFICATE_UNKNOWN;
+                }
+            }
+
+            return alert;
+        }
+
+    }
+
+    /**
+     * The certificate entry used in Certificate handshake message for TLS 1.3.
+     */
+    static final class CertificateEntry {
+        final byte[] encoded;       // encoded cert or public key
+        private final SSLExtensions extensions;
+
+        CertificateEntry(byte[] encoded, SSLExtensions extensions) {
+            this.encoded = encoded;
+            this.extensions = extensions;
+        }
+
+        private int getEncodedSize() {
+            int extLen = extensions.length();
+            if (extLen == 0) {
+                extLen = 2;     // empty extensions
+            }
+            return 3 + encoded.length + extLen;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\n'{'\n" +
+                "{0}\n" +                       // X.509 certificate
+                "  \"extensions\": '{'\n" +
+                "{1}\n" +
+                "  '}'\n" +
+                "'}',", Locale.ENGLISH);
+
+            Object x509Certs;
+            try {
+                // Don't support certificate type extension (RawPublicKey) yet.
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                x509Certs =
+                    cf.generateCertificate(new ByteArrayInputStream(encoded));
+            } catch (CertificateException ce) {
+                // no X.509 certificate factory service
+                x509Certs = encoded;
+            }
+
+            Object[] messageFields = {
+                SSLLogger.toString(x509Certs),
+                Utilities.indent(extensions.toString(), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The Certificate handshake message for TLS 1.3.
+     */
+    static final class T13CertificateMessage extends HandshakeMessage {
+        private final byte[] requestContext;
+        private final List<CertificateEntry> certEntries;
+
+        T13CertificateMessage(HandshakeContext context,
+                byte[] requestContext, X509Certificate[] certificates)
+                throws SSLException, CertificateException  {
+            super(context);
+
+            this.requestContext = requestContext.clone();
+            this.certEntries = new LinkedList<>();
+            for (X509Certificate cert : certificates) {
+                byte[] encoded = cert.getEncoded();
+                SSLExtensions extensions = new SSLExtensions(this);
+                certEntries.add(new CertificateEntry(encoded, extensions));
+            }
+        }
+
+        T13CertificateMessage(HandshakeContext handshakeContext,
+                byte[] requestContext, List<CertificateEntry> certificates) {
+            super(handshakeContext);
+
+            this.requestContext = requestContext.clone();
+            this.certEntries = certificates;
+        }
+
+        T13CertificateMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // struct {
+            //      opaque certificate_request_context<0..2^8-1>;
+            //      CertificateEntry certificate_list<0..2^24-1>;
+            //  } Certificate;
+            if (m.remaining() < 4) {
+                throw new SSLProtocolException(
+                        "Invalid Certificate message: " +
+                        "insufficient data (length=" + m.remaining() + ")");
+            }
+            this.requestContext = Record.getBytes8(m);
+
+            if (m.remaining() < 3) {
+                throw new SSLProtocolException(
+                        "Invalid Certificate message: " +
+                        "insufficient certificate entries data (length=" +
+                        m.remaining() + ")");
+            }
+
+            int listLen = Record.getInt24(m);
+            if (listLen != m.remaining()) {
+                throw new SSLProtocolException(
+                    "Invalid Certificate message: " +
+                    "incorrect list length (length=" + listLen + ")");
+            }
+
+            SSLExtension[] enabledExtensions =
+                handshakeContext.sslConfig.getEnabledExtensions(
+                        SSLHandshake.CERTIFICATE);
+            List<CertificateEntry> certList = new LinkedList<>();
+            while (m.hasRemaining()) {
+                // Note: support only X509 CertificateType right now.
+                byte[] encodedCert = Record.getBytes24(m);
+                if (encodedCert.length == 0) {
+                    throw new SSLProtocolException(
+                        "Invalid Certificate message: empty cert_data");
+                }
+
+                SSLExtensions extensions =
+                        new SSLExtensions(this, m, enabledExtensions);
+                certList.add(new CertificateEntry(encodedCert, extensions));
+            }
+
+            this.certEntries = Collections.unmodifiableList(certList);
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE;
+        }
+
+        @Override
+        public int messageLength() {
+            int msgLen = 4 + requestContext.length;
+            for (CertificateEntry entry : certEntries) {
+                msgLen += entry.getEncodedSize();
+            }
+
+            return msgLen;
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            int entryListLen = 0;
+            for (CertificateEntry entry : certEntries) {
+                entryListLen += entry.getEncodedSize();
+            }
+
+            hos.putBytes8(requestContext);
+            hos.putInt24(entryListLen);
+            for (CertificateEntry entry : certEntries) {
+                hos.putBytes24(entry.encoded);
+                // Is it an empty extensions?
+                if (entry.extensions.length() == 0) {
+                    hos.putInt16(0);
+                } else {
+                    entry.extensions.send(hos);
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"Certificate\": '{'\n" +
+                "  \"certificate_request_context\": \"{0}\",\n" +
+                "  \"certificate_list\": [{1}\n]\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            StringBuilder builder = new StringBuilder(512);
+            for (CertificateEntry entry : certEntries) {
+                builder.append(entry.toString());
+            }
+
+            Object[] messageFields = {
+                Utilities.toHexString(requestContext),
+                Utilities.indent(builder.toString())
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "Certificate" handshake message producer for TLS 1.3.
+     */
+    private static final
+            class T13CertificateProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T13CertificateProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+            if (hc.sslConfig.isClientMode) {
+                return onProduceCertificate(
+                        (ClientHandshakeContext)context, message);
+            } else {
+                return onProduceCertificate(
+                        (ServerHandshakeContext)context, message);
+            }
+        }
+
+        private byte[] onProduceCertificate(ServerHandshakeContext shc,
+                HandshakeMessage message) throws IOException {
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            SSLPossession pos = choosePossession(shc, clientHello);
+            if (pos == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No available authentication scheme");
+                return null;    // make the complier happy
+            }
+
+            if (!(pos instanceof X509Possession)) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No X.509 certificate for server authentication");
+            }
+
+            X509Possession x509Possession = (X509Possession)pos;
+            X509Certificate[] localCerts = x509Possession.popCerts;
+            if (localCerts == null || localCerts.length == 0) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No X.509 certificate for server authentication");
+                return null;    // make the complier happy
+            }
+
+            // update the context
+            shc.handshakePossessions.add(x509Possession);
+            shc.handshakeSession.setLocalPrivateKey(
+                    x509Possession.popPrivateKey);
+            shc.handshakeSession.setLocalCertificates(localCerts);
+            T13CertificateMessage cm;
+            try {
+                cm = new T13CertificateMessage(shc, (new byte[0]), localCerts);
+            } catch (SSLException | CertificateException ce) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Failed to produce server Certificate message", ce);
+                return null;    // make the complier happy
+            }
+
+            // Check the OCSP stapling extensions and attempt
+            // to get responses.  If the resulting stapleParams is non
+            // null, it implies that stapling is enabled on the server side.
+            shc.stapleParams = StatusResponseManager.processStapling(shc);
+            shc.staplingActive = (shc.stapleParams != null);
+
+            // Process extensions for each CertificateEntry.
+            // Since there can be multiple CertificateEntries within a
+            // single CT message, we will pin a specific CertificateEntry
+            // into the ServerHandshakeContext so individual extension
+            // producers know which X509Certificate it is processing in
+            // each call.
+            SSLExtension[] enabledCTExts = shc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.CERTIFICATE,
+                    Arrays.asList(ProtocolVersion.PROTOCOLS_OF_13));
+            for (CertificateEntry certEnt : cm.certEntries) {
+                shc.currentCertEntry = certEnt;
+                certEnt.extensions.produce(shc, enabledCTExts);
+            }
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced server Certificate message", cm);
+            }
+
+            // Output the handshake message.
+            cm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+
+        private static SSLPossession choosePossession(
+                HandshakeContext hc,
+                ClientHelloMessage clientHello) throws IOException {
+            if (hc.peerRequestedCertSignSchemes == null ||
+                    hc.peerRequestedCertSignSchemes.isEmpty()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "No signature_algorithms(_cert) in ClientHello");
+                }
+                return null;
+            }
+
+            Collection<String> checkedKeyTypes = new HashSet<>();
+            for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) {
+                if (checkedKeyTypes.contains(ss.keyAlgorithm)) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unsupported authentication scheme: " + ss.name);
+                    }
+                    continue;
+                }
+
+                // Don't select a signature scheme unless we will be able to
+                // produce a CertificateVerify message later
+                if (SignatureScheme.getPreferableAlgorithm(
+                    hc.peerRequestedSignatureSchemes,
+                    ss, hc.negotiatedProtocol) == null) {
+
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unable to produce CertificateVerify for " +
+                            "signature scheme: " + ss.name);
+                    }
+                    checkedKeyTypes.add(ss.keyAlgorithm);
+                    continue;
+                }
+
+                SSLAuthentication ka = X509Authentication.valueOf(ss);
+                if (ka == null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unsupported authentication scheme: " + ss.name);
+                    }
+                    checkedKeyTypes.add(ss.keyAlgorithm);
+                    continue;
+                }
+
+                SSLPossession pos = ka.createPossession(hc);
+                if (pos == null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Unavailable authentication scheme: " + ss.name);
+                    }
+                    continue;
+                }
+
+                return pos;
+            }
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.warning("No available authentication scheme");
+            }
+            return null;
+        }
+
+        private byte[] onProduceCertificate(ClientHandshakeContext chc,
+                HandshakeMessage message) throws IOException {
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+            SSLPossession pos = choosePossession(chc, clientHello);
+            X509Certificate[] localCerts;
+            if (pos == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("No available client authentication scheme");
+                }
+                localCerts = new X509Certificate[0];
+            } else {
+                chc.handshakePossessions.add(pos);
+                if (!(pos instanceof X509Possession)) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "No X.509 certificate for client authentication");
+                    }
+                    localCerts = new X509Certificate[0];
+                } else {
+                    X509Possession x509Possession = (X509Possession)pos;
+                    localCerts = x509Possession.popCerts;
+                    chc.handshakeSession.setLocalPrivateKey(
+                            x509Possession.popPrivateKey);
+                }
+            }
+
+            if (localCerts != null && localCerts.length != 0) {
+                chc.handshakeSession.setLocalCertificates(localCerts);
+            } else {
+                chc.handshakeSession.setLocalCertificates(null);
+            }
+
+            T13CertificateMessage cm;
+            try {
+                cm = new T13CertificateMessage(
+                        chc, chc.certRequestContext, localCerts);
+            } catch (SSLException | CertificateException ce) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Failed to produce client Certificate message", ce);
+                return null;
+            }
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced client Certificate message", cm);
+            }
+
+            // Output the handshake message.
+            cm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "Certificate" handshake message consumer for TLS 1.3.
+     */
+    private static final class T13CertificateConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T13CertificateConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+
+            // clean up this consumer
+            hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id);
+            T13CertificateMessage cm = new T13CertificateMessage(hc, message);
+            if (hc.sslConfig.isClientMode) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Consuming server Certificate handshake message", cm);
+                }
+                onConsumeCertificate((ClientHandshakeContext)context, cm);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Consuming client Certificate handshake message", cm);
+                }
+                onConsumeCertificate((ServerHandshakeContext)context, cm);
+            }
+        }
+
+        private void onConsumeCertificate(ServerHandshakeContext shc,
+                T13CertificateMessage certificateMessage )throws IOException {
+            if (certificateMessage.certEntries == null ||
+                    certificateMessage.certEntries.isEmpty()) {
+                if (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED) {
+                    shc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                        "Empty client certificate chain");
+                } else {
+                    // optional client authentication
+                    return;
+                }
+            }
+
+            // check client certificate entries
+            X509Certificate[] cliCerts =
+                    checkClientCerts(shc, certificateMessage.certEntries);
+
+            //
+            // update
+            //
+            shc.handshakeCredentials.add(
+                new X509Credentials(cliCerts[0].getPublicKey(), cliCerts));
+            shc.handshakeSession.setPeerCertificates(cliCerts);
+        }
+
+        private void onConsumeCertificate(ClientHandshakeContext chc,
+                T13CertificateMessage certificateMessage )throws IOException {
+            if (certificateMessage.certEntries == null ||
+                    certificateMessage.certEntries.isEmpty()) {
+                chc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                    "Empty server certificate chain");
+            }
+
+            // Each CertificateEntry will have its own set of extensions
+            // which must be consumed.
+            SSLExtension[] enabledExtensions =
+                chc.sslConfig.getEnabledExtensions(SSLHandshake.CERTIFICATE);
+            for (CertificateEntry certEnt : certificateMessage.certEntries) {
+                certEnt.extensions.consumeOnLoad(chc, enabledExtensions);
+            }
+
+            // check server certificate entries
+            X509Certificate[] srvCerts =
+                    checkServerCerts(chc, certificateMessage.certEntries);
+
+            //
+            // update
+            //
+            chc.handshakeCredentials.add(
+                new X509Credentials(srvCerts[0].getPublicKey(), srvCerts));
+            chc.handshakeSession.setPeerCertificates(srvCerts);
+        }
+
+        private static X509Certificate[] checkClientCerts(
+                ServerHandshakeContext shc,
+                List<CertificateEntry> certEntries) throws IOException {
+            X509Certificate[] certs =
+                    new X509Certificate[certEntries.size()];
+            try {
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                int i = 0;
+                for (CertificateEntry entry : certEntries) {
+                    certs[i++] = (X509Certificate)cf.generateCertificate(
+                                    new ByteArrayInputStream(entry.encoded));
+                }
+            } catch (CertificateException ce) {
+                shc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                    "Failed to parse server certificates", ce);
+            }
+
+            // find out the types of client authentication used
+            String keyAlgorithm = certs[0].getPublicKey().getAlgorithm();
+            String authType;
+            switch (keyAlgorithm) {
+                case "RSA":
+                case "DSA":
+                case "EC":
+                case "RSASSA-PSS":
+                    authType = keyAlgorithm;
+                    break;
+                default:
+                    // unknown public key type
+                    authType = "UNKNOWN";
+            }
+
+            try {
+                X509TrustManager tm = shc.sslContext.getX509TrustManager();
+                if (tm instanceof X509ExtendedTrustManager) {
+                    if (shc.conContext.transport instanceof SSLEngine) {
+                        SSLEngine engine = (SSLEngine)shc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkClientTrusted(
+                            certs.clone(),
+                            authType,
+                            engine);
+                    } else {
+                        SSLSocket socket = (SSLSocket)shc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkClientTrusted(
+                            certs.clone(),
+                            authType,
+                            socket);
+                    }
+                } else {
+                    // Unlikely to happen, because we have wrapped the old
+                    // X509TrustManager with the new X509ExtendedTrustManager.
+                    throw new CertificateException(
+                            "Improper X509TrustManager implementation");
+                }
+
+                // Once the client certificate chain has been validated, set
+                // the certificate chain in the TLS session.
+                shc.handshakeSession.setPeerCertificates(certs);
+            } catch (CertificateException ce) {
+                shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce);
+            }
+
+            return certs;
+        }
+
+        private static X509Certificate[] checkServerCerts(
+                ClientHandshakeContext chc,
+                List<CertificateEntry> certEntries) throws IOException {
+            X509Certificate[] certs =
+                    new X509Certificate[certEntries.size()];
+            try {
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                int i = 0;
+                for (CertificateEntry entry : certEntries) {
+                    certs[i++] = (X509Certificate)cf.generateCertificate(
+                                    new ByteArrayInputStream(entry.encoded));
+                }
+            } catch (CertificateException ce) {
+                chc.conContext.fatal(Alert.BAD_CERTIFICATE,
+                    "Failed to parse server certificates", ce);
+            }
+
+            // find out the types of server authentication used
+            //
+            // Note that the "UNKNOWN" authentication type is sufficient to
+            // check the required digitalSignature KeyUsage for TLS 1.3.
+            String authType = "UNKNOWN";
+
+            try {
+                X509TrustManager tm = chc.sslContext.getX509TrustManager();
+                if (tm instanceof X509ExtendedTrustManager) {
+                    if (chc.conContext.transport instanceof SSLEngine) {
+                        SSLEngine engine = (SSLEngine)chc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkServerTrusted(
+                            certs.clone(),
+                            authType,
+                            engine);
+                    } else {
+                        SSLSocket socket = (SSLSocket)chc.conContext.transport;
+                        ((X509ExtendedTrustManager)tm).checkServerTrusted(
+                            certs.clone(),
+                            authType,
+                            socket);
+                    }
+                } else {
+                    // Unlikely to happen, because we have wrapped the old
+                    // X509TrustManager with the new X509ExtendedTrustManager.
+                    throw new CertificateException(
+                            "Improper X509TrustManager implementation");
+                }
+
+                // Once the server certificate chain has been validated, set
+                // the certificate chain in the TLS session.
+                chc.handshakeSession.setPeerCertificates(certs);
+            } catch (CertificateException ce) {
+                chc.conContext.fatal(getCertificateAlert(chc, ce), ce);
+            }
+
+            return certs;
+        }
+
+        /**
+         * When a failure happens during certificate checking from an
+         * {@link X509TrustManager}, determine what TLS alert description
+         * to use.
+         *
+         * @param cexc The exception thrown by the {@link X509TrustManager}
+         *
+         * @return A byte value corresponding to a TLS alert description number.
+         */
+        private static Alert getCertificateAlert(
+                ClientHandshakeContext chc, CertificateException cexc) {
+            // The specific reason for the failure will determine how to
+            // set the alert description value
+            Alert alert = Alert.CERTIFICATE_UNKNOWN;
+
+            Throwable baseCause = cexc.getCause();
+            if (baseCause instanceof CertPathValidatorException) {
+                CertPathValidatorException cpve =
+                        (CertPathValidatorException)baseCause;
+                Reason reason = cpve.getReason();
+                if (reason == BasicReason.REVOKED) {
+                    alert = chc.staplingActive ?
+                            Alert.BAD_CERT_STATUS_RESPONSE :
+                            Alert.CERTIFICATE_REVOKED;
+                } else if (
+                        reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
+                    alert = chc.staplingActive ?
+                            Alert.BAD_CERT_STATUS_RESPONSE :
+                            Alert.CERTIFICATE_UNKNOWN;
+                }
+            }
+
+            return alert;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,890 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.security.auth.x500.X500Principal;
+import sun.security.ssl.CipherSuite.KeyExchange;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.X509Authentication.X509Possession;
+
+/**
+ * Pack of the CertificateRequest handshake message.
+ */
+final class CertificateRequest {
+    static final SSLConsumer t10HandshakeConsumer =
+        new T10CertificateRequestConsumer();
+    static final HandshakeProducer t10HandshakeProducer =
+        new T10CertificateRequestProducer();
+
+    static final SSLConsumer t12HandshakeConsumer =
+        new T12CertificateRequestConsumer();
+    static final HandshakeProducer t12HandshakeProducer =
+        new T12CertificateRequestProducer();
+
+    static final SSLConsumer t13HandshakeConsumer =
+        new T13CertificateRequestConsumer();
+    static final HandshakeProducer t13HandshakeProducer =
+        new T13CertificateRequestProducer();
+
+    // TLS 1.2 and prior versions
+    private static enum ClientCertificateType {
+        // RFC 2246
+        RSA_SIGN            ((byte)0x01, "rsa_sign", "RSA", true),
+        DSS_SIGN            ((byte)0x02, "dss_sign", "DSA", true),
+        RSA_FIXED_DH        ((byte)0x03, "rsa_fixed_dh"),
+        DSS_FIXED_DH        ((byte)0x04, "dss_fixed_dh"),
+
+        // RFC 4346
+        RSA_EPHEMERAL_DH    ((byte)0x05, "rsa_ephemeral_dh"),
+        DSS_EPHEMERAL_DH    ((byte)0x06, "dss_ephemeral_dh"),
+        FORTEZZA_DMS        ((byte)0x14, "fortezza_dms"),
+
+        // RFC 4492
+        ECDSA_SIGN          ((byte)0x40, "ecdsa_sign",
+                                             "EC", JsseJce.isEcAvailable()),
+        RSA_FIXED_ECDH      ((byte)0x41, "rsa_fixed_ecdh"),
+        ECDSA_FIXED_ECDH    ((byte)0x42, "ecdsa_fixed_ecdh");
+
+        private static final byte[] CERT_TYPES =
+                JsseJce.isEcAvailable() ? new byte[] {
+                        ECDSA_SIGN.id,
+                        RSA_SIGN.id,
+                        DSS_SIGN.id
+                    } :  new byte[] {
+                        RSA_SIGN.id,
+                        DSS_SIGN.id
+                    };
+
+        final byte id;
+        final String name;
+        final String keyAlgorithm;
+        final boolean isAvailable;
+
+        private ClientCertificateType(byte id, String name) {
+            this(id, name, null, false);
+        }
+
+        private ClientCertificateType(byte id, String name,
+                String keyAlgorithm, boolean isAvailable) {
+            this.id = id;
+            this.name = name;
+            this.keyAlgorithm = keyAlgorithm;
+            this.isAvailable = isAvailable;
+        }
+
+        private static String nameOf(byte id) {
+            for (ClientCertificateType cct : ClientCertificateType.values()) {
+                if (cct.id == id) {
+                    return cct.name;
+                }
+            }
+            return "UNDEFINED-CLIENT-CERTIFICATE-TYPE(" + (int)id + ")";
+        }
+
+        private static ClientCertificateType valueOf(byte id) {
+            for (ClientCertificateType cct : ClientCertificateType.values()) {
+                if (cct.id == id) {
+                    return cct;
+                }
+            }
+
+            return null;
+        }
+
+        private static String[] getKeyTypes(byte[] ids) {
+            ArrayList<String> keyTypes = new ArrayList<>(3);
+            for (byte id : ids) {
+                ClientCertificateType cct = ClientCertificateType.valueOf(id);
+                if (cct.isAvailable) {
+                    keyTypes.add(cct.keyAlgorithm);
+                }
+            }
+
+            return keyTypes.toArray(new String[0]);
+        }
+    }
+
+    /**
+     * The "CertificateRequest" handshake message for SSL 3.0 and TLS 1.0/1.1.
+     */
+    static final class T10CertificateRequestMessage extends HandshakeMessage {
+        final byte[] types;                 // certificate types
+        final List<byte[]> authorities;     // certificate authorities
+
+        T10CertificateRequestMessage(HandshakeContext handshakeContext,
+                X509Certificate[] trustedCerts, KeyExchange keyExchange) {
+            super(handshakeContext);
+
+            this.authorities = new ArrayList<>(trustedCerts.length);
+            for (X509Certificate cert : trustedCerts) {
+                X500Principal x500Principal = cert.getSubjectX500Principal();
+                authorities.add(x500Principal.getEncoded());
+            }
+
+            this.types = ClientCertificateType.CERT_TYPES;
+        }
+
+        T10CertificateRequestMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // struct {
+            //     ClientCertificateType certificate_types<1..2^8-1>;
+            //     DistinguishedName certificate_authorities<0..2^16-1>;
+            // } CertificateRequest;
+            if (m.remaining() < 4) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Incorrect CertificateRequest message: no sufficient data");
+            }
+            this.types = Record.getBytes8(m);
+
+            int listLen = Record.getInt16(m);
+            if (listLen > m.remaining()) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Incorrect CertificateRequest message:no sufficient data");
+            }
+
+            if (listLen > 0) {
+                this.authorities = new LinkedList<>();
+                while (listLen > 0) {
+                    // opaque DistinguishedName<1..2^16-1>;
+                    byte[] encoded = Record.getBytes16(m);
+                    listLen -= (2 + encoded.length);
+                    authorities.add(encoded);
+                }
+            } else {
+                this.authorities = Collections.emptyList();
+            }
+        }
+
+        String[] getKeyTypes() {
+            return  ClientCertificateType.getKeyTypes(types);
+        }
+
+        X500Principal[] getAuthorities() {
+            List<X500Principal> principals =
+                    new ArrayList<>(authorities.size());
+            for (byte[] encoded : authorities) {
+                X500Principal principal = new X500Principal(encoded);
+                principals.add(principal);
+            }
+
+            return principals.toArray(new X500Principal[0]);
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_REQUEST;
+        }
+
+        @Override
+        public int messageLength() {
+            int len = 1 + types.length + 2;
+            for (byte[] encoded : authorities) {
+                len += encoded.length + 2;
+            }
+            return len;
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes8(types);
+
+            int listLen = 0;
+            for (byte[] encoded : authorities) {
+                listLen += encoded.length + 2;
+            }
+
+            hos.putInt16(listLen);
+            for (byte[] encoded : authorities) {
+                hos.putBytes16(encoded);
+            }
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"CertificateRequest\": '{'\n" +
+                    "  \"certificate types\": {0}\n" +
+                    "  \"certificate authorities\": {1}\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            List<String> typeNames = new ArrayList<>(types.length);
+            for (byte type : types) {
+                typeNames.add(ClientCertificateType.nameOf(type));
+            }
+
+            List<String> authorityNames = new ArrayList<>(authorities.size());
+            for (byte[] encoded : authorities) {
+                X500Principal principal = new X500Principal(encoded);
+                authorityNames.add(principal.toString());
+            }
+            Object[] messageFields = {
+                typeNames,
+                authorityNames
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "CertificateRequest" handshake message producer for SSL 3.0 and
+     * TLS 1.0/1.1.
+     */
+    private static final
+            class T10CertificateRequestProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T10CertificateRequestProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            X509Certificate[] caCerts =
+                    shc.sslContext.getX509TrustManager().getAcceptedIssuers();
+            T10CertificateRequestMessage crm = new T10CertificateRequestMessage(
+                    shc, caCerts, shc.negotiatedCipherSuite.keyExchange);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced CertificateRequest handshake message", crm);
+            }
+
+            // Output the handshake message.
+            crm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            //
+            // update
+            //
+            shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id,
+                    SSLHandshake.CERTIFICATE);
+            shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "CertificateRequest" handshake message consumer for SSL 3.0 and
+     * TLS 1.0/1.1.
+     */
+    private static final
+            class T10CertificateRequestConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T10CertificateRequestConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id);
+
+            T10CertificateRequestMessage crm =
+                    new T10CertificateRequestMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming CertificateRequest handshake message", crm);
+            }
+
+            //
+            // validate
+            //
+            // blank
+
+            //
+            // update
+            //
+
+            // An empty client Certificate handshake message may be allow.
+            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
+                    SSLHandshake.CERTIFICATE);
+
+            X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
+            String clientAlias = null;
+            if (chc.conContext.transport instanceof SSLSocketImpl) {
+                clientAlias = km.chooseClientAlias(crm.getKeyTypes(),
+                    crm.getAuthorities(), (SSLSocket)chc.conContext.transport);
+            } else if (chc.conContext.transport instanceof SSLEngineImpl) {
+                clientAlias = km.chooseEngineClientAlias(crm.getKeyTypes(),
+                    crm.getAuthorities(), (SSLEngine)chc.conContext.transport);
+            }
+
+
+            if (clientAlias == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("No available client authentication");
+                }
+                return;
+            }
+
+            PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias);
+            if (clientPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("No available client private key");
+                }
+                return;
+            }
+
+            X509Certificate[] clientCerts = km.getCertificateChain(clientAlias);
+            if ((clientCerts == null) || (clientCerts.length == 0)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("No available client certificate");
+                }
+                return;
+            }
+
+            chc.handshakePossessions.add(
+                    new X509Possession(clientPrivateKey, clientCerts));
+            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+        }
+    }
+
+    /**
+     * The CertificateRequest handshake message for TLS 1.2.
+     */
+    static final class T12CertificateRequestMessage extends HandshakeMessage {
+        final byte[] types;                 // certificate types
+        final int[] algorithmIds;           // supported signature algorithms
+        final List<byte[]> authorities;     // certificate authorities
+
+        T12CertificateRequestMessage(HandshakeContext handshakeContext,
+                X509Certificate[] trustedCerts, KeyExchange keyExchange,
+                List<SignatureScheme> signatureSchemes) throws IOException {
+            super(handshakeContext);
+
+            this.types = ClientCertificateType.CERT_TYPES;
+
+            if (signatureSchemes == null || signatureSchemes.isEmpty()) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "No signature algorithms specified for " +
+                        "CertificateRequest hanshake message");
+            }
+            this.algorithmIds = new int[signatureSchemes.size()];
+            int i = 0;
+            for (SignatureScheme scheme : signatureSchemes) {
+                algorithmIds[i++] = scheme.id;
+            }
+
+            this.authorities = new ArrayList<>(trustedCerts.length);
+            for (X509Certificate cert : trustedCerts) {
+                X500Principal x500Principal = cert.getSubjectX500Principal();
+                authorities.add(x500Principal.getEncoded());
+            }
+        }
+
+        T12CertificateRequestMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // struct {
+            //     ClientCertificateType certificate_types<1..2^8-1>;
+            //     SignatureAndHashAlgorithm
+            //       supported_signature_algorithms<2..2^16-2>;
+            //     DistinguishedName certificate_authorities<0..2^16-1>;
+            // } CertificateRequest;
+
+            // certificate_authorities
+            if (m.remaining() < 8) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid CertificateRequest handshake message: " +
+                        "no sufficient data");
+            }
+            this.types = Record.getBytes8(m);
+
+            // supported_signature_algorithms
+            if (m.remaining() < 6) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid CertificateRequest handshake message: " +
+                        "no sufficient data");
+            }
+
+            byte[] algs = Record.getBytes16(m);
+            if (algs == null || algs.length == 0 || (algs.length & 0x01) != 0) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid CertificateRequest handshake message: " +
+                        "incomplete signature algorithms");
+            }
+
+            this.algorithmIds = new int[(algs.length >> 1)];
+            for (int i = 0, j = 0; i < algs.length;) {
+                byte hash = algs[i++];
+                byte sign = algs[i++];
+                algorithmIds[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF);
+            }
+
+            // certificate_authorities
+            if (m.remaining() < 2) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid CertificateRequest handshake message: " +
+                        "no sufficient data");
+            }
+
+            int listLen = Record.getInt16(m);
+            if (listLen > m.remaining()) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid CertificateRequest message: no sufficient data");
+            }
+
+            if (listLen > 0) {
+                this.authorities = new LinkedList<>();
+                while (listLen > 0) {
+                    // opaque DistinguishedName<1..2^16-1>;
+                    byte[] encoded = Record.getBytes16(m);
+                    listLen -= (2 + encoded.length);
+                    authorities.add(encoded);
+                }
+            } else {
+                this.authorities = Collections.emptyList();
+            }
+        }
+
+        String[] getKeyTypes() {
+            return ClientCertificateType.getKeyTypes(types);
+        }
+
+        X500Principal[] getAuthorities() {
+            List<X500Principal> principals =
+                    new ArrayList<>(authorities.size());
+            for (byte[] encoded : authorities) {
+                X500Principal principal = new X500Principal(encoded);
+                principals.add(principal);
+            }
+
+            return principals.toArray(new X500Principal[0]);
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_REQUEST;
+        }
+
+        @Override
+        public int messageLength() {
+            int len = 1 + types.length + 2 + (algorithmIds.length << 1) + 2;
+            for (byte[] encoded : authorities) {
+                len += encoded.length + 2;
+            }
+            return len;
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes8(types);
+
+            int listLen = 0;
+            for (byte[] encoded : authorities) {
+                listLen += encoded.length + 2;
+            }
+
+            hos.putInt16(algorithmIds.length << 1);
+            for (int algorithmId : algorithmIds) {
+                hos.putInt16(algorithmId);
+            }
+
+            hos.putInt16(listLen);
+            for (byte[] encoded : authorities) {
+                hos.putBytes16(encoded);
+            }
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"CertificateRequest\": '{'\n" +
+                    "  \"certificate types\": {0}\n" +
+                    "  \"supported signature algorithms\": {1}\n" +
+                    "  \"certificate authorities\": {2}\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            List<String> typeNames = new ArrayList<>(types.length);
+            for (byte type : types) {
+                typeNames.add(ClientCertificateType.nameOf(type));
+            }
+
+            List<String> algorithmNames = new ArrayList<>(algorithmIds.length);
+            for (int algorithmId : algorithmIds) {
+                algorithmNames.add(SignatureScheme.nameOf(algorithmId));
+            }
+
+            List<String> authorityNames = new ArrayList<>(authorities.size());
+            for (byte[] encoded : authorities) {
+                X500Principal principal = new X500Principal(encoded);
+                authorityNames.add(principal.toString());
+            }
+            Object[] messageFields = {
+                typeNames,
+                algorithmNames,
+                authorityNames
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "CertificateRequest" handshake message producer for TLS 1.2.
+     */
+    private static final
+            class T12CertificateRequestProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T12CertificateRequestProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            if (shc.localSupportedSignAlgs == null) {
+                shc.localSupportedSignAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            shc.algorithmConstraints, shc.activeProtocols);
+            }
+
+            if (shc.localSupportedSignAlgs == null ||
+                    shc.localSupportedSignAlgs.isEmpty()) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No supported signature algorithm");
+            }
+
+            X509Certificate[] caCerts =
+                    shc.sslContext.getX509TrustManager().getAcceptedIssuers();
+            T12CertificateRequestMessage crm = new T12CertificateRequestMessage(
+                    shc, caCerts, shc.negotiatedCipherSuite.keyExchange,
+                    shc.localSupportedSignAlgs);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced CertificateRequest handshake message", crm);
+            }
+
+            // Output the handshake message.
+            crm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            //
+            // update
+            //
+            shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id,
+                    SSLHandshake.CERTIFICATE);
+            shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "CertificateRequest" handshake message consumer for TLS 1.2.
+     */
+    private static final
+            class T12CertificateRequestConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T12CertificateRequestConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id);
+
+            T12CertificateRequestMessage crm =
+                    new T12CertificateRequestMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming CertificateRequest handshake message", crm);
+            }
+
+            //
+            // validate
+            //
+            // blank
+
+            //
+            // update
+            //
+
+            // An empty client Certificate handshake message may be allow.
+            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
+                    SSLHandshake.CERTIFICATE);
+
+            List<SignatureScheme> sss = new LinkedList<>();
+            for (int id : crm.algorithmIds) {
+                SignatureScheme ss = SignatureScheme.valueOf(id);
+                if (ss != null) {
+                    sss.add(ss);
+                }
+            }
+            chc.peerRequestedSignatureSchemes = sss;
+            chc.peerRequestedCertSignSchemes = sss;     // use the same schemes
+            chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
+
+            X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
+            String clientAlias = null;
+            if (chc.conContext.transport instanceof SSLSocketImpl) {
+                clientAlias = km.chooseClientAlias(crm.getKeyTypes(),
+                    crm.getAuthorities(), (SSLSocket)chc.conContext.transport);
+            } else if (chc.conContext.transport instanceof SSLEngineImpl) {
+                clientAlias = km.chooseEngineClientAlias(crm.getKeyTypes(),
+                    crm.getAuthorities(), (SSLEngine)chc.conContext.transport);
+            }
+
+            if (clientAlias == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("No available client authentication");
+                }
+                return;
+            }
+
+            PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias);
+            if (clientPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("No available client private key");
+                }
+                return;
+            }
+
+            X509Certificate[] clientCerts = km.getCertificateChain(clientAlias);
+            if ((clientCerts == null) || (clientCerts.length == 0)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("No available client certificate");
+                }
+                return;
+            }
+
+            chc.handshakePossessions.add(
+                    new X509Possession(clientPrivateKey, clientCerts));
+            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+        }
+    }
+
+    /**
+     * The CertificateRequest handshake message for TLS 1.3.
+     */
+    static final class T13CertificateRequestMessage extends HandshakeMessage {
+        private final byte[] requestContext;
+        private final SSLExtensions extensions;
+
+        T13CertificateRequestMessage(
+                HandshakeContext handshakeContext) throws IOException {
+            super(handshakeContext);
+
+            this.requestContext = new byte[0];
+            this.extensions = new SSLExtensions(this);
+        }
+
+        T13CertificateRequestMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // struct {
+            //      opaque certificate_request_context<0..2^8-1>;
+            //      Extension extensions<2..2^16-1>;
+            //  } CertificateRequest;
+            if (m.remaining() < 5) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid CertificateRequest handshake message: " +
+                        "no sufficient data");
+            }
+            this.requestContext = Record.getBytes8(m);
+
+            if (m.remaining() < 4) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid CertificateRequest handshake message: " +
+                        "no sufficient extensions data");
+            }
+            SSLExtension[] enabledExtensions =
+                handshakeContext.sslConfig.getEnabledExtensions(
+                        SSLHandshake.CERTIFICATE_REQUEST);
+            this.extensions = new SSLExtensions(this, m, enabledExtensions);
+        }
+
+        @Override
+        SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_REQUEST;
+        }
+
+        @Override
+        int messageLength() {
+            // In TLS 1.3, use of certain extensions is mandatory.
+            return 1 + requestContext.length + extensions.length();
+        }
+
+        @Override
+        void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes8(requestContext);
+
+            // In TLS 1.3, use of certain extensions is mandatory.
+            extensions.send(hos);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"CertificateRequest\": '{'\n" +
+                "  \"certificate_request_context\": \"{0}\",\n" +
+                "  \"extensions\": [\n" +
+                "{1}\n" +
+                "  ]\n" +
+                "'}'",
+                Locale.ENGLISH);
+            Object[] messageFields = {
+                Utilities.toHexString(requestContext),
+                Utilities.indent(Utilities.indent(extensions.toString()))
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "CertificateRequest" handshake message producer for TLS 1.3.
+     */
+    private static final
+            class T13CertificateRequestProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T13CertificateRequestProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            T13CertificateRequestMessage crm =
+                    new T13CertificateRequestMessage(shc);
+            // Produce extensions for CertificateRequest handshake message.
+            SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.CERTIFICATE_REQUEST, shc.negotiatedProtocol);
+            crm.extensions.produce(shc, extTypes);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced CertificateRequest message", crm);
+            }
+
+            // Output the handshake message.
+            crm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            //
+            // update
+            //
+            shc.certRequestContext = crm.requestContext.clone();
+            shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id,
+                    SSLHandshake.CERTIFICATE);
+            shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "CertificateRequest" handshake message consumer for TLS 1.3.
+     */
+    private static final
+            class T13CertificateRequestConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T13CertificateRequestConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id);
+
+            T13CertificateRequestMessage crm =
+                    new T13CertificateRequestMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming CertificateRequest handshake message", crm);
+            }
+
+            //
+            // validate
+            //
+            SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.CERTIFICATE_REQUEST);
+            crm.extensions.consumeOnLoad(chc, extTypes);
+
+            //
+            // update
+            //
+            crm.extensions.consumeOnTrade(chc, extTypes);
+
+            //
+            // produce
+            //
+            chc.certRequestContext = crm.requestContext.clone();
+            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
+                    SSLHandshake.CERTIFICATE);
+            chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import javax.net.ssl.SSLHandshakeException;
+import java.security.cert.X509Certificate;
+import sun.security.provider.certpath.OCSPResponse;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import static sun.security.ssl.CertStatusExtension.*;
+import static sun.security.ssl.CertificateMessage.*;
+
+/**
+ * Consumers and producers for the CertificateStatus handshake message.
+ * This message takes one of two related but slightly different forms,
+ * depending on the type of stapling selected by the server.  The message
+ * data will be of the form(s):
+ *
+ *  [status_request, RFC 6066]
+ *
+ *  struct {
+ *      CertificateStatusType status_type;
+ *      select (status_type) {
+ *          case ocsp: OCSPResponse;
+ *      } response;
+ *  } CertificateStatus;
+ *
+ *  opaque OCSPResponse<1..2^24-1>;
+ *
+ *  [status_request_v2, RFC 6961]
+ *
+ *  struct {
+ *      CertificateStatusType status_type;
+ *      select (status_type) {
+ *        case ocsp: OCSPResponse;
+ *        case ocsp_multi: OCSPResponseList;
+ *      } response;
+ *  } CertificateStatus;
+ *
+ *  opaque OCSPResponse<0..2^24-1>;
+ *
+ *  struct {
+ *      OCSPResponse ocsp_response_list<1..2^24-1>;
+ *  } OCSPResponseList;
+ */
+final class CertificateStatus {
+    static final SSLConsumer handshakeConsumer =
+            new CertificateStatusConsumer();
+    static final HandshakeProducer handshakeProducer =
+            new CertificateStatusProducer();
+    static final HandshakeAbsence handshakeAbsence =
+            new CertificateStatusAbsence();
+
+    /**
+     * The CertificateStatus handshake message.
+     */
+    static final class CertificateStatusMessage extends HandshakeMessage {
+
+        final CertStatusRequestType statusType;
+        int encodedResponsesLen = 0;
+        int messageLength = -1;
+        final List<byte[]> encodedResponses = new ArrayList<>();
+
+        CertificateStatusMessage(HandshakeContext handshakeContext) {
+            super(handshakeContext);
+
+            ServerHandshakeContext shc =
+                    (ServerHandshakeContext)handshakeContext;
+
+            // Get the Certificates from the SSLContextImpl amd the Stapling
+            // parameters
+            StatusResponseManager.StaplingParameters stapleParams =
+                    shc.stapleParams;
+            if (stapleParams == null) {
+                throw new IllegalArgumentException(
+                        "Unexpected null stapling parameters");
+            }
+
+            X509Certificate[] certChain =
+                (X509Certificate[])shc.handshakeSession.getLocalCertificates();
+            if (certChain == null) {
+                throw new IllegalArgumentException(
+                        "Unexpected null certificate chain");
+            }
+
+            // Walk the certificate list and add the correct encoded responses
+            // to the encoded responses list
+            statusType = stapleParams.statReqType;
+            if (statusType == CertStatusRequestType.OCSP) {
+                // Just worry about the first cert in the chain
+                byte[] resp = stapleParams.responseMap.get(certChain[0]);
+                if (resp == null) {
+                    // A not-found return status means we should include
+                    // a zero-length response in CertificateStatus.
+                    // This is highly unlikely to happen in practice.
+                    resp = new byte[0];
+                }
+                encodedResponses.add(resp);
+                encodedResponsesLen += resp.length + 3;
+            } else if (statusType == CertStatusRequestType.OCSP_MULTI) {
+                for (X509Certificate cert : certChain) {
+                    byte[] resp = stapleParams.responseMap.get(cert);
+                    if (resp == null) {
+                        resp = new byte[0];
+                    }
+                    encodedResponses.add(resp);
+                    encodedResponsesLen += resp.length + 3;
+                }
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported StatusResponseType: " + statusType);
+            }
+
+            messageLength = messageLength();
+        }
+
+        CertificateStatusMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            statusType = CertStatusRequestType.valueOf((byte)Record.getInt8(m));
+            if (statusType == CertStatusRequestType.OCSP) {
+                byte[] respDER = Record.getBytes24(m);
+                // Convert the incoming bytes to a OCSPResponse strucutre
+                if (respDER.length > 0) {
+                    encodedResponses.add(respDER);
+                    encodedResponsesLen = 3 + respDER.length;
+                } else {
+                    handshakeContext.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Zero-length OCSP Response");
+                }
+            } else if (statusType == CertStatusRequestType.OCSP_MULTI) {
+                int respListLen = Record.getInt24(m);
+                encodedResponsesLen = respListLen;
+
+                // Add each OCSP reponse into the array list in the order
+                // we receive them off the wire.  A zero-length array is
+                // allowed for ocsp_multi, and means that a response for
+                // a given certificate is not available.
+                while (respListLen > 0) {
+                    byte[] respDER = Record.getBytes24(m);
+                    encodedResponses.add(respDER);
+                    respListLen -= (respDER.length + 3);
+                }
+
+                if (respListLen != 0) {
+                    handshakeContext.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Bad OCSP response list length");
+                }
+            } else {
+                handshakeContext.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Unsupported StatusResponseType: " + statusType);
+            }
+            messageLength = messageLength();
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_STATUS;
+        }
+
+        @Override
+        public int messageLength() {
+            int len = 1;
+
+            if (messageLength == -1) {
+                if (statusType == CertStatusRequestType.OCSP) {
+                    len += encodedResponsesLen;
+                } else if (statusType == CertStatusRequestType.OCSP_MULTI) {
+                    len += 3 + encodedResponsesLen;
+                }
+                messageLength = len;
+            }
+
+            return messageLength;
+        }
+
+        @Override
+        public void send(HandshakeOutStream s) throws IOException {
+            s.putInt8(statusType.id);
+            if (statusType == CertStatusRequestType.OCSP) {
+                s.putBytes24(encodedResponses.get(0));
+            } else if (statusType == CertStatusRequestType.OCSP_MULTI) {
+                s.putInt24(encodedResponsesLen);
+                for (byte[] respBytes : encodedResponses) {
+                    if (respBytes != null) {
+                        s.putBytes24(respBytes);
+                    } else {
+                        s.putBytes24(null);
+                    }
+                }
+            } else {
+                // It is highly unlikely that we will fall into this section
+                // of the code.
+                throw new SSLHandshakeException("Unsupported status_type: " +
+                        statusType.id);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            // Stringify the encoded OCSP response list
+            for (byte[] respDER : encodedResponses) {
+                if (respDER.length > 0) {
+                    try {
+                        OCSPResponse oResp = new OCSPResponse(respDER);
+                        sb.append(oResp.toString()).append("\n");
+                    } catch (IOException ioe) {
+                        sb.append("OCSP Response Exception: ").append(ioe)
+                                .append("\n");
+                    }
+                } else {
+                    sb.append("<Zero-length entry>\n");
+                }
+            }
+
+            MessageFormat messageFormat = new MessageFormat(
+                "\"CertificateStatus\": '{'\n" +
+                "  \"type\"                : \"{0}\",\n" +
+                "  \"responses \"          : [\n" + "{1}\n" + "  ]\n" +
+                "'}'",
+                Locale.ENGLISH);
+            Object[] messageFields = {
+                statusType.name,
+                Utilities.indent(Utilities.indent(sb.toString()))
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The CertificateStatus handshake message consumer.
+     */
+    private static final class CertificateStatusConsumer
+            implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private CertificateStatusConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            CertificateStatusMessage cst =
+                    new CertificateStatusMessage(chc, message);
+
+            // Log the message
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming server CertificateStatus handshake message",
+                        cst);
+            }
+
+            // Pin the received responses to the SSLSessionImpl.  It will
+            // be retrieved by the X509TrustManagerImpl during the certficicate
+            // checking phase.
+            chc.handshakeSession.setStatusResponses(cst.encodedResponses);
+
+            // Now perform the check
+            T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
+        }
+    }
+
+    /**
+     * The CertificateStatus handshake message consumer.
+     */
+    private static final class CertificateStatusProducer
+            implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CertificateStatusProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // Only the server-side should be a producer of this message
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // If stapling is not active, immediately return without producing
+            // a message or any further processing.
+            if (!shc.staplingActive) {
+                return null;
+            }
+
+            // Create the CertificateStatus message from info in the
+            CertificateStatusMessage csm = new CertificateStatusMessage(shc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced server CertificateStatus handshake message", csm);
+            }
+
+            // Output the handshake message.
+            csm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    private static final class CertificateStatusAbsence
+            implements HandshakeAbsence {
+        // Prevent instantiation of this class
+        private CertificateStatusAbsence() {
+            // blank
+        }
+
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Processing should only continue if stapling is active
+            if (chc.staplingActive) {
+                // Because OCSP stapling is active, it means two things
+                // if we're here: 1) The server hello asserted the
+                // status_request[_v2] extension.  2) The CertificateStatus
+                // message was not sent.  This means that cert path checking
+                // was deferred, but must happen immediately.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Server did not send CertificateStatus, " +
+                            "checking cert chain without status info.");
+                }
+                T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,1143 @@
+ /*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.*;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Locale;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.HexDumpEncoder;
+
+/**
+ * Pack of the CertificateVerify handshake message.
+ */
+final class CertificateVerify {
+    static final SSLConsumer s30HandshakeConsumer =
+        new S30CertificateVerifyConsumer();
+    static final HandshakeProducer s30HandshakeProducer =
+        new S30CertificateVerifyProducer();
+
+    static final SSLConsumer t10HandshakeConsumer =
+        new T10CertificateVerifyConsumer();
+    static final HandshakeProducer t10HandshakeProducer =
+        new T10CertificateVerifyProducer();
+
+    static final SSLConsumer t12HandshakeConsumer =
+        new T12CertificateVerifyConsumer();
+    static final HandshakeProducer t12HandshakeProducer =
+        new T12CertificateVerifyProducer();
+
+    static final SSLConsumer t13HandshakeConsumer =
+        new T13CertificateVerifyConsumer();
+    static final HandshakeProducer t13HandshakeProducer =
+        new T13CertificateVerifyProducer();
+
+    /**
+     * The CertificateVerify handshake message (SSL 3.0).
+     */
+    static final class S30CertificateVerifyMessage extends HandshakeMessage {
+        // signature bytes
+        private final byte[] signature;
+
+        S30CertificateVerifyMessage(HandshakeContext context,
+                X509Possession x509Possession) throws IOException {
+            super(context);
+
+            // This happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            byte[] temproary = null;
+            String algorithm = x509Possession.popPrivateKey.getAlgorithm();
+            try {
+                Signature signer =
+                        getSignature(algorithm, x509Possession.popPrivateKey);
+                byte[] hashes = chc.handshakeHash.digest(algorithm,
+                        chc.handshakeSession.getMasterSecret());
+                signer.update(hashes);
+                temproary = signer.sign();
+            } catch (NoSuchAlgorithmException nsae) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" + algorithm +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (GeneralSecurityException gse) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot produce CertificateVerify signature", gse);
+            }
+
+            this.signature = temproary;
+        }
+
+        S30CertificateVerifyMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+
+            // This happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            //  digitally-signed struct {
+            //    select(SignatureAlgorithm) {
+            //        case anonymous: struct { };
+            //        case rsa:
+            //            opaque md5_hash[16];
+            //            opaque sha_hash[20];
+            //        case dsa:
+            //            opaque sha_hash[20];
+            //    };
+            //  } Signature;
+            if (m.remaining() < 2) {
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid CertificateVerify message: no sufficient data");
+            }
+
+            // read and verify the signature
+            this.signature = Record.getBytes16(m);
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials cd : shc.handshakeCredentials) {
+                if (cd instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)cd;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null ||
+                    x509Credentials.popPublicKey == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No X509 credentials negotiated for CertificateVerify");
+            }
+
+            String algorithm = x509Credentials.popPublicKey.getAlgorithm();
+            try {
+                Signature signer =
+                        getSignature(algorithm, x509Credentials.popPublicKey);
+                byte[] hashes = shc.handshakeHash.digest(algorithm,
+                        shc.handshakeSession.getMasterSecret());
+                signer.update(hashes);
+                if (!signer.verify(signature)) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid CertificateVerify message: invalid signature");
+                }
+            } catch (NoSuchAlgorithmException nsae) {
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" + algorithm +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (GeneralSecurityException gse) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot verify CertificateVerify signature", gse);
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_VERIFY;
+        }
+
+        @Override
+        public int messageLength() {
+            return 2 + signature.length;    //  2: length of signature
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes16(signature);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"CertificateVerify\": '{'\n" +
+                    "  \"signature\": '{'\n" +
+                    "{0}\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(signature), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+
+        /*
+         * Get the Signature object appropriate for verification using the
+         * given signature algorithm.
+         */
+        private static Signature getSignature(String algorithm,
+                Key key) throws GeneralSecurityException {
+            Signature signer = null;
+            switch (algorithm) {
+                case "RSA":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA);
+                    break;
+                case "DSA":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA);
+                    break;
+                case "EC":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA);
+                    break;
+                default:
+                    throw new SignatureException("Unrecognized algorithm: "
+                        + algorithm);
+            }
+
+            if (signer != null) {
+                if (key instanceof PublicKey) {
+                    signer.initVerify((PublicKey)(key));
+                } else {
+                    signer.initSign((PrivateKey)key);
+                }
+            }
+
+            return signer;
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message producer.
+     */
+    private static final
+            class S30CertificateVerifyProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private S30CertificateVerifyProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : chc.handshakePossessions) {
+                if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    break;
+                }
+            }
+
+            if (x509Possession == null ||
+                    x509Possession.popPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "No X.509 credentials negotiated for CertificateVerify");
+                }
+
+                return null;
+            }
+
+            S30CertificateVerifyMessage cvm =
+                    new S30CertificateVerifyMessage(chc, x509Possession);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced CertificateVerify handshake message", cvm);
+            }
+
+            // Output the handshake message.
+            cvm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message consumer.
+     */
+    private static final
+            class S30CertificateVerifyConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private S30CertificateVerifyConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            S30CertificateVerifyMessage cvm =
+                    new S30CertificateVerifyMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming CertificateVerify handshake message", cvm);
+            }
+
+            //
+            // update
+            //
+            // Need no additional validation.
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+
+    /**
+     * The CertificateVerify handshake message (TLS 1.0/1.1).
+     */
+    static final class T10CertificateVerifyMessage extends HandshakeMessage {
+        // signature bytes
+        private final byte[] signature;
+
+        T10CertificateVerifyMessage(HandshakeContext context,
+                X509Possession x509Possession) throws IOException {
+            super(context);
+
+            // This happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            byte[] temproary = null;
+            String algorithm = x509Possession.popPrivateKey.getAlgorithm();
+            try {
+                Signature signer =
+                        getSignature(algorithm, x509Possession.popPrivateKey);
+                byte[] hashes = chc.handshakeHash.digest(algorithm);
+                signer.update(hashes);
+                temproary = signer.sign();
+            } catch (NoSuchAlgorithmException nsae) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" + algorithm +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (GeneralSecurityException gse) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "Cannot produce CertificateVerify signature", gse);
+            }
+
+            this.signature = temproary;
+        }
+
+        T10CertificateVerifyMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+
+            // This happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            //  digitally-signed struct {
+            //    select(SignatureAlgorithm) {
+            //        case anonymous: struct { };
+            //        case rsa:
+            //            opaque md5_hash[16];
+            //            opaque sha_hash[20];
+            //        case dsa:
+            //            opaque sha_hash[20];
+            //    };
+            //  } Signature;
+            if (m.remaining() < 2) {
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid CertificateVerify message: no sufficient data");
+            }
+
+            // read and verify the signature
+            this.signature = Record.getBytes16(m);
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials cd : shc.handshakeCredentials) {
+                if (cd instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)cd;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null ||
+                    x509Credentials.popPublicKey == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No X509 credentials negotiated for CertificateVerify");
+            }
+
+            String algorithm = x509Credentials.popPublicKey.getAlgorithm();
+            try {
+                Signature signer =
+                        getSignature(algorithm, x509Credentials.popPublicKey);
+                byte[] hashes = shc.handshakeHash.digest(algorithm);
+                signer.update(hashes);
+                if (!signer.verify(signature)) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid CertificateVerify message: invalid signature");
+                }
+            } catch (NoSuchAlgorithmException nsae) {
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" + algorithm +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (GeneralSecurityException gse) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot verify CertificateVerify signature", gse);
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_VERIFY;
+        }
+
+        @Override
+        public int messageLength() {
+            return 2 + signature.length;    //  2: length of signature
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes16(signature);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"CertificateVerify\": '{'\n" +
+                    "  \"signature\": '{'\n" +
+                    "{0}\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(signature), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+
+        /*
+         * Get the Signature object appropriate for verification using the
+         * given signature algorithm.
+         */
+        private static Signature getSignature(String algorithm,
+                Key key) throws GeneralSecurityException {
+            Signature signer = null;
+            switch (algorithm) {
+                case "RSA":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA);
+                    break;
+                case "DSA":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA);
+                    break;
+                case "EC":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA);
+                    break;
+                default:
+                    throw new SignatureException("Unrecognized algorithm: "
+                        + algorithm);
+            }
+
+            if (signer != null) {
+                if (key instanceof PublicKey) {
+                    signer.initVerify((PublicKey)(key));
+                } else {
+                    signer.initSign((PrivateKey)key);
+                }
+            }
+
+            return signer;
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message producer.
+     */
+    private static final
+            class T10CertificateVerifyProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T10CertificateVerifyProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : chc.handshakePossessions) {
+                if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    break;
+                }
+            }
+
+            if (x509Possession == null ||
+                    x509Possession.popPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "No X.509 credentials negotiated for CertificateVerify");
+                }
+
+                return null;
+            }
+
+            T10CertificateVerifyMessage cvm =
+                    new T10CertificateVerifyMessage(chc, x509Possession);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced CertificateVerify handshake message", cvm);
+            }
+
+            // Output the handshake message.
+            cvm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message consumer.
+     */
+    private static final
+            class T10CertificateVerifyConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T10CertificateVerifyConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            T10CertificateVerifyMessage cvm =
+                    new T10CertificateVerifyMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming CertificateVerify handshake message", cvm);
+            }
+
+            //
+            // update
+            //
+            // Need no additional validation.
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.        }
+        }
+    }
+
+    /**
+     * The CertificateVerify handshake message (TLS 1.2).
+     */
+    static final class T12CertificateVerifyMessage extends HandshakeMessage {
+        // the signature algorithm
+        private final SignatureScheme signatureScheme;
+
+        // signature bytes
+        private final byte[] signature;
+
+        T12CertificateVerifyMessage(HandshakeContext context,
+                X509Possession x509Possession) throws IOException {
+            super(context);
+
+            // This happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            this.signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                    chc.peerRequestedSignatureSchemes,
+                    x509Possession.popPrivateKey,
+                    chc.negotiatedProtocol);
+            if (signatureScheme == null) {
+                // Unlikely, the credentials generator should have
+                // selected the preferable signature algorithm properly.
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No preferred signature algorithm for CertificateVerify");
+            }
+
+            byte[] temproary = null;
+            try {
+                Signature signer =
+                    signatureScheme.getSignature(x509Possession.popPrivateKey);
+                signer.update(chc.handshakeHash.archived());
+                temproary = signer.sign();
+            } catch (NoSuchAlgorithmException |
+                    InvalidAlgorithmParameterException nsae) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" +
+                        signatureScheme.name +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (InvalidKeyException | SignatureException ikse) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot produce CertificateVerify signature", ikse);
+            }
+
+            this.signature = temproary;
+        }
+
+        T12CertificateVerifyMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // This happens in server side only.
+            ServerHandshakeContext shc =
+                    (ServerHandshakeContext)handshakeContext;
+
+            // struct {
+            //     SignatureAndHashAlgorithm algorithm;
+            //     opaque signature<0..2^16-1>;
+            // } DigitallySigned;
+            if (m.remaining() < 4) {
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid CertificateVerify message: no sufficient data");
+            }
+
+            // SignatureAndHashAlgorithm algorithm
+            int ssid = Record.getInt16(m);
+            this.signatureScheme = SignatureScheme.valueOf(ssid);
+            if (signatureScheme == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid signature algorithm (" + ssid +
+                        ") used in CertificateVerify handshake message");
+            }
+
+            if (!shc.localSupportedSignAlgs.contains(signatureScheme)) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Unsupported signature algorithm (" +
+                        signatureScheme.name +
+                        ") used in CertificateVerify handshake message");
+            }
+
+            // read and verify the signature
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials cd : shc.handshakeCredentials) {
+                if (cd instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)cd;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null ||
+                    x509Credentials.popPublicKey == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No X509 credentials negotiated for CertificateVerify");
+            }
+
+            // opaque signature<0..2^16-1>;
+            this.signature = Record.getBytes16(m);
+            try {
+                Signature signer =
+                    signatureScheme.getSignature(x509Credentials.popPublicKey);
+                signer.update(shc.handshakeHash.archived());
+                if (!signer.verify(signature)) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid CertificateVerify signature");
+                }
+            } catch (NoSuchAlgorithmException |
+                    InvalidAlgorithmParameterException nsae) {
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" +
+                        signatureScheme.name +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (InvalidKeyException | SignatureException ikse) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot verify CertificateVerify signature", ikse);
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_VERIFY;
+        }
+
+        @Override
+        public int messageLength() {
+            return 4 + signature.length;    //  2: signature algorithm
+                                            // +2: length of signature
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putInt16(signatureScheme.id);
+            hos.putBytes16(signature);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"CertificateVerify\": '{'\n" +
+                    "  \"signature algorithm\": {0}\n" +
+                    "  \"signature\": '{'\n" +
+                    "{1}\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                signatureScheme.name,
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(signature), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message producer.
+     */
+    private static final
+            class T12CertificateVerifyProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T12CertificateVerifyProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : chc.handshakePossessions) {
+                if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    break;
+                }
+            }
+
+            if (x509Possession == null ||
+                    x509Possession.popPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "No X.509 credentials negotiated for CertificateVerify");
+                }
+
+                return null;
+            }
+
+            T12CertificateVerifyMessage cvm =
+                    new T12CertificateVerifyMessage(chc, x509Possession);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced CertificateVerify handshake message", cvm);
+            }
+
+            // Output the handshake message.
+            cvm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message consumer.
+     */
+    private static final
+            class T12CertificateVerifyConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T12CertificateVerifyConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            T12CertificateVerifyMessage cvm =
+                    new T12CertificateVerifyMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming CertificateVerify handshake message", cvm);
+            }
+
+            //
+            // update
+            //
+            // Need no additional validation.
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+
+    /**
+     * The CertificateVerify handshake message (TLS 1.3).
+     */
+    static final class T13CertificateVerifyMessage extends HandshakeMessage {
+        private static final byte[] serverSignHead = new byte[] {
+            // repeated 0x20 for 64 times
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+
+            // "TLS 1.3, server CertificateVerify" + 0x00
+            (byte)0x54, (byte)0x4c, (byte)0x53, (byte)0x20,
+            (byte)0x31, (byte)0x2e, (byte)0x33, (byte)0x2c,
+            (byte)0x20, (byte)0x73, (byte)0x65, (byte)0x72,
+            (byte)0x76, (byte)0x65, (byte)0x72, (byte)0x20,
+            (byte)0x43, (byte)0x65, (byte)0x72, (byte)0x74,
+            (byte)0x69, (byte)0x66, (byte)0x69, (byte)0x63,
+            (byte)0x61, (byte)0x74, (byte)0x65, (byte)0x56,
+            (byte)0x65, (byte)0x72, (byte)0x69, (byte)0x66,
+            (byte)0x79, (byte)0x00
+        };
+
+        private static final byte[] clientSignHead = new byte[] {
+            // repeated 0x20 for 64 times
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+            (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20,
+
+            // "TLS 1.3, client CertificateVerify" + 0x00
+            (byte)0x54, (byte)0x4c, (byte)0x53, (byte)0x20,
+            (byte)0x31, (byte)0x2e, (byte)0x33, (byte)0x2c,
+            (byte)0x20, (byte)0x63, (byte)0x6c, (byte)0x69,
+            (byte)0x65, (byte)0x6e, (byte)0x74, (byte)0x20,
+            (byte)0x43, (byte)0x65, (byte)0x72, (byte)0x74,
+            (byte)0x69, (byte)0x66, (byte)0x69, (byte)0x63,
+            (byte)0x61, (byte)0x74, (byte)0x65, (byte)0x56,
+            (byte)0x65, (byte)0x72, (byte)0x69, (byte)0x66,
+            (byte)0x79, (byte)0x00
+        };
+
+
+        // the signature algorithm
+        private final SignatureScheme signatureScheme;
+
+        // signature bytes
+        private final byte[] signature;
+
+        T13CertificateVerifyMessage(HandshakeContext context,
+                X509Possession x509Possession) throws IOException {
+            super(context);
+
+            this.signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                    context.peerRequestedSignatureSchemes,
+                    x509Possession.popPrivateKey,
+                    context.negotiatedProtocol);
+            if (signatureScheme == null) {
+                // Unlikely, the credentials generator should have
+                // selected the preferable signature algorithm properly.
+                context.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No preferred signature algorithm for CertificateVerify");
+            }
+
+            byte[] hashValue = context.handshakeHash.digest();
+            byte[] contentCovered;
+            if (context.sslConfig.isClientMode) {
+                contentCovered = Arrays.copyOf(clientSignHead,
+                        clientSignHead.length + hashValue.length);
+                System.arraycopy(hashValue, 0, contentCovered,
+                        clientSignHead.length, hashValue.length);
+            } else {
+                contentCovered = Arrays.copyOf(serverSignHead,
+                        serverSignHead.length + hashValue.length);
+                System.arraycopy(hashValue, 0, contentCovered,
+                        serverSignHead.length, hashValue.length);
+            }
+
+            byte[] temproary = null;
+            try {
+                Signature signer =
+                    signatureScheme.getSignature(x509Possession.popPrivateKey);
+                signer.update(contentCovered);
+                temproary = signer.sign();
+            } catch (NoSuchAlgorithmException |
+                    InvalidAlgorithmParameterException nsae) {
+                context.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" +
+                        signatureScheme.name +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (InvalidKeyException | SignatureException ikse) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot produce CertificateVerify signature", ikse);
+            }
+
+            this.signature = temproary;
+        }
+
+        T13CertificateVerifyMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+             super(context);
+
+            // struct {
+            //     SignatureAndHashAlgorithm algorithm;
+            //     opaque signature<0..2^16-1>;
+            // } DigitallySigned;
+            if (m.remaining() < 4) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid CertificateVerify message: no sufficient data");
+            }
+
+            // SignatureAndHashAlgorithm algorithm
+            int ssid = Record.getInt16(m);
+            this.signatureScheme = SignatureScheme.valueOf(ssid);
+            if (signatureScheme == null) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid signature algorithm (" + ssid +
+                        ") used in CertificateVerify handshake message");
+            }
+
+            if (!context.localSupportedSignAlgs.contains(signatureScheme)) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Unsupported signature algorithm (" +
+                        signatureScheme.name +
+                        ") used in CertificateVerify handshake message");
+            }
+
+            // read and verify the signature
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials cd : context.handshakeCredentials) {
+                if (cd instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)cd;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null ||
+                    x509Credentials.popPublicKey == null) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No X509 credentials negotiated for CertificateVerify");
+            }
+
+            // opaque signature<0..2^16-1>;
+            this.signature = Record.getBytes16(m);
+
+            byte[] hashValue = context.handshakeHash.digest();
+            byte[] contentCovered;
+            if (context.sslConfig.isClientMode) {
+                contentCovered = Arrays.copyOf(serverSignHead,
+                        serverSignHead.length + hashValue.length);
+                System.arraycopy(hashValue, 0, contentCovered,
+                        serverSignHead.length, hashValue.length);
+            } else {
+                contentCovered = Arrays.copyOf(clientSignHead,
+                        clientSignHead.length + hashValue.length);
+                System.arraycopy(hashValue, 0, contentCovered,
+                        clientSignHead.length, hashValue.length);
+            }
+
+            try {
+                Signature signer =
+                    signatureScheme.getSignature(x509Credentials.popPublicKey);
+                signer.update(contentCovered);
+                if (!signer.verify(signature)) {
+                    context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid CertificateVerify signature");
+                }
+            } catch (NoSuchAlgorithmException |
+                    InvalidAlgorithmParameterException nsae) {
+                context.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm (" +
+                        signatureScheme.name +
+                        ") used in CertificateVerify handshake message", nsae);
+            } catch (InvalidKeyException | SignatureException ikse) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot verify CertificateVerify signature", ikse);
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CERTIFICATE_VERIFY;
+        }
+
+        @Override
+        public int messageLength() {
+            return 4 + signature.length;    //  2: signature algorithm
+                                            // +2: length of signature
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putInt16(signatureScheme.id);
+            hos.putBytes16(signature);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"CertificateVerify\": '{'\n" +
+                    "  \"signature algorithm\": {0}\n" +
+                    "  \"signature\": '{'\n" +
+                    "{1}\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                signatureScheme.name,
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(signature), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message producer.
+     */
+    private static final
+            class T13CertificateVerifyProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T13CertificateVerifyProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : hc.handshakePossessions) {
+                if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    break;
+                }
+            }
+
+            if (x509Possession == null ||
+                    x509Possession.popPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "No X.509 credentials negotiated for CertificateVerify");
+                }
+
+                return null;
+            }
+
+            if (hc.sslConfig.isClientMode) {
+                return onProduceCertificateVerify(
+                        (ClientHandshakeContext)context, x509Possession);
+            } else {
+                return onProduceCertificateVerify(
+                        (ServerHandshakeContext)context, x509Possession);
+            }
+        }
+
+        private byte[] onProduceCertificateVerify(ServerHandshakeContext shc,
+                X509Possession x509Possession) throws IOException {
+            T13CertificateVerifyMessage cvm =
+                    new T13CertificateVerifyMessage(shc, x509Possession);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced server CertificateVerify handshake message", cvm);
+            }
+
+            // Output the handshake message.
+            cvm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+
+        private byte[] onProduceCertificateVerify(ClientHandshakeContext chc,
+                X509Possession x509Possession) throws IOException {
+            T13CertificateVerifyMessage cvm =
+                    new T13CertificateVerifyMessage(chc, x509Possession);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced client CertificateVerify handshake message", cvm);
+            }
+
+            // Output the handshake message.
+            cvm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "CertificateVerify" handshake message consumer.
+     */
+    private static final
+            class T13CertificateVerifyConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T13CertificateVerifyConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The producing happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+            T13CertificateVerifyMessage cvm =
+                    new T13CertificateVerifyMessage(hc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming CertificateVerify handshake message", cvm);
+            }
+
+            //
+            // update
+            //
+            // Need no additional validation.
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.net.ssl.SSLException;
+import sun.security.ssl.SSLCipher.SSLReadCipher;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SSLTrafficKeyDerivation.LegacyTrafficKeyDerivation;
+
+/**
+ * Pack of the ChangeCipherSpec message.
+ */
+final class ChangeCipherSpec {
+    static final SSLConsumer t10Consumer =
+            new T10ChangeCipherSpecConsumer();
+    static final HandshakeProducer t10Producer =
+            new T10ChangeCipherSpecProducer();
+    static final SSLConsumer t13Consumer =
+            new T13ChangeCipherSpecConsumer();
+
+    /**
+     * The "ChangeCipherSpec" message producer.
+     */
+    private static final
+            class T10ChangeCipherSpecProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T10ChangeCipherSpecProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            HandshakeContext hc = (HandshakeContext)context;
+            SSLKeyDerivation kd = hc.handshakeKeyDerivation;
+
+            if (!(kd instanceof LegacyTrafficKeyDerivation)) {
+                throw new UnsupportedOperationException("Not supported.");
+            }
+            LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd;
+            CipherSuite ncs = hc.negotiatedCipherSuite;
+            Authenticator writeAuthenticator;
+            if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) {
+                writeAuthenticator =
+                        Authenticator.valueOf(hc.negotiatedProtocol);
+            } else {
+                try {
+                    writeAuthenticator = Authenticator.valueOf(
+                            hc.negotiatedProtocol, ncs.macAlg,
+                            tkd.getTrafficKey(hc.sslConfig.isClientMode ?
+                                    "clientMacKey" : "serverMacKey"));
+                } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+                    // unlikely
+                    throw new SSLException("Algorithm missing:  ", e);
+                }
+            }
+
+            SecretKey writeKey =
+                    tkd.getTrafficKey(hc.sslConfig.isClientMode ?
+                                    "clientWriteKey" : "serverWriteKey");
+            SecretKey writeIv =
+                    tkd.getTrafficKey(hc.sslConfig.isClientMode ?
+                                    "clientWriteIv" : "serverWriteIv");
+            IvParameterSpec iv = (writeIv == null) ? null :
+                    new IvParameterSpec(writeIv.getEncoded());
+            SSLWriteCipher writeCipher;
+            try {
+                writeCipher = ncs.bulkCipher.createWriteCipher(
+                        writeAuthenticator,
+                        hc.negotiatedProtocol, writeKey, iv,
+                        hc.sslContext.getSecureRandom());
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                throw new SSLException("Algorithm missing:  ", gse);
+            }
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced ChangeCipherSpec message");
+            }
+
+            hc.conContext.outputRecord.changeWriteCiphers(writeCipher, true);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "ChangeCipherSpec" message producer.
+     */
+    private static final
+            class T10ChangeCipherSpecConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T10ChangeCipherSpecConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            TransportContext tc = (TransportContext)context;
+
+            // This consumer can be used only once.
+            tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
+
+            // parse
+            if (message.remaining() != 1 || message.get() != 1) {
+                tc.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Malformed or unexpected ChangeCipherSpec message");
+            }
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Consuming ChangeCipherSpec message");
+            }
+
+            // validate
+            if (tc.handshakeContext == null) {
+                tc.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Unexpected ChangeCipherSpec message");
+            }
+
+
+            HandshakeContext hc = tc.handshakeContext;
+
+            if (hc.handshakeKeyDerivation == null) {
+                tc.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected ChangeCipherSpec message");
+            }
+
+            SSLKeyDerivation kd = hc.handshakeKeyDerivation;
+            if (kd instanceof LegacyTrafficKeyDerivation) {
+                LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd;
+                CipherSuite ncs = hc.negotiatedCipherSuite;
+                Authenticator readAuthenticator;
+                if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) {
+                    readAuthenticator =
+                            Authenticator.valueOf(hc.negotiatedProtocol);
+                } else {
+                    try {
+                        readAuthenticator = Authenticator.valueOf(
+                                hc.negotiatedProtocol, ncs.macAlg,
+                                tkd.getTrafficKey(hc.sslConfig.isClientMode ?
+                                        "serverMacKey" : "clientMacKey"));
+                    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+                        // unlikely
+                        throw new SSLException("Algorithm missing:  ", e);
+                    }
+                }
+
+                SecretKey readKey =
+                        tkd.getTrafficKey(hc.sslConfig.isClientMode ?
+                                        "serverWriteKey" : "clientWriteKey");
+                SecretKey readIv =
+                        tkd.getTrafficKey(hc.sslConfig.isClientMode ?
+                                        "serverWriteIv" : "clientWriteIv");
+                IvParameterSpec iv = (readIv == null) ? null :
+                        new IvParameterSpec(readIv.getEncoded());
+                SSLReadCipher readCipher;
+                try {
+                    readCipher = ncs.bulkCipher.createReadCipher(
+                            readAuthenticator,
+                            hc.negotiatedProtocol, readKey, iv,
+                            hc.sslContext.getSecureRandom());
+                } catch (GeneralSecurityException gse) {
+                    // unlikely
+                    throw new SSLException("Algorithm missing:  ", gse);
+                }
+                tc.inputRecord.changeReadCiphers(readCipher);
+            } else {
+                throw new UnsupportedOperationException("Not supported.");
+            }
+        }
+    }
+
+    private static final
+            class T13ChangeCipherSpecConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T13ChangeCipherSpecConsumer() {
+            // blank
+        }
+
+        // An implementation may receive an unencrypted record of type
+        // change_cipher_spec consisting of the single byte value 0x01
+        // at any time after the first ClientHello message has been
+        // sent or received and before the peer's Finished message has
+        // been received and MUST simply drop it without further
+        // processing.
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            TransportContext tc = (TransportContext)context;
+
+            // This consumer can be used only once.
+            tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
+
+            // parse
+            if (message.remaining() != 1 || message.get() != 1) {
+                tc.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Malformed or unexpected ChangeCipherSpec message");
+            }
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Consuming ChangeCipherSpec message");
+            }
+
+            // no further processing
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/CipherBox.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1150 +0,0 @@
-/*
- * Copyright (c) 1996, 2015, 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.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.Hashtable;
-import java.util.Arrays;
-
-import java.security.*;
-import javax.crypto.*;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.GCMParameterSpec;
-
-import java.nio.*;
-
-import sun.security.ssl.CipherSuite.*;
-import static sun.security.ssl.CipherSuite.*;
-import static sun.security.ssl.CipherSuite.CipherType.*;
-
-import sun.security.util.HexDumpEncoder;
-
-
-/**
- * This class handles bulk data enciphering/deciphering for each SSLv3
- * message.  This provides data confidentiality.  Stream ciphers (such
- * as RC4) don't need to do padding; block ciphers (e.g. DES) need it.
- *
- * Individual instances are obtained by calling the static method
- * newCipherBox(), which should only be invoked by BulkCipher.newCipher().
- *
- * In RFC 2246, with bock ciphers in CBC mode, the Initialization
- * Vector (IV) for the first record is generated with the other keys
- * and secrets when the security parameters are set.  The IV for
- * subsequent records is the last ciphertext block from the previous
- * record.
- *
- * In RFC 4346, the implicit Initialization Vector (IV) is replaced
- * with an explicit IV to protect against CBC attacks.  RFC 4346
- * recommends two algorithms used to generated the per-record IV.
- * The implementation uses the algorithm (2)(b), as described at
- * section 6.2.3.2 of RFC 4346.
- *
- * The usage of IV in CBC block cipher can be illustrated in
- * the following diagrams.
- *
- *   (random)
- *        R         P1                    IV        C1
- *        |          |                     |         |
- *  SIV---+    |-----+    |-...            |-----    |------
- *        |    |     |    |                |    |    |     |
- *     +----+  |  +----+  |             +----+  |  +----+  |
- *     | Ek |  |  + Ek +  |             | Dk |  |  | Dk |  |
- *     +----+  |  +----+  |             +----+  |  +----+  |
- *        |    |     |    |                |    |    |     |
- *        |----|     |----|           SIV--+    |----|     |-...
- *        |          |                     |       |
- *       IV         C1                     R      P1
- *                                     (discard)
- *
- *       CBC Encryption                    CBC Decryption
- *
- * NOTE that any ciphering involved in key exchange (e.g. with RSA) is
- * handled separately.
- *
- * @author David Brownell
- * @author Andreas Sterbenz
- */
-final class CipherBox {
-
-    // A CipherBox that implements the identity operation
-    static final CipherBox NULL = new CipherBox();
-
-    /* Class and subclass dynamic debugging support */
-    private static final Debug debug = Debug.getInstance("ssl");
-
-    // the protocol version this cipher conforms to
-    private final ProtocolVersion protocolVersion;
-
-    // cipher object
-    private final Cipher cipher;
-
-    /**
-     * secure random
-     */
-    private SecureRandom random;
-
-    /**
-     * fixed IV, the implicit nonce of AEAD cipher suite, only apply to
-     * AEAD cipher suites
-     */
-    private final byte[] fixedIv;
-
-    /**
-     * the key, reserved only for AEAD cipher initialization
-     */
-    private final Key key;
-
-    /**
-     * the operation mode, reserved for AEAD cipher initialization
-     */
-    private final int mode;
-
-    /**
-     * the authentication tag size, only apply to AEAD cipher suites
-     */
-    private final int tagSize;
-
-    /**
-     * the record IV length, only apply to AEAD cipher suites
-     */
-    private final int recordIvSize;
-
-    /**
-     * cipher type
-     */
-    private final CipherType cipherType;
-
-    /**
-     * Fixed masks of various block size, as the initial decryption IVs
-     * for TLS 1.1 or later.
-     *
-     * For performance, we do not use random IVs. As the initial decryption
-     * IVs will be discarded by TLS decryption processes, so the fixed masks
-     * do not hurt cryptographic strength.
-     */
-    private static Hashtable<Integer, IvParameterSpec> masks;
-
-    /**
-     * NULL cipherbox. Identity operation, no encryption.
-     */
-    private CipherBox() {
-        this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
-        this.cipher = null;
-        this.cipherType = NULL_CIPHER;
-        this.fixedIv = new byte[0];
-        this.key = null;
-        this.mode = Cipher.ENCRYPT_MODE;    // choose at random
-        this.random = null;
-        this.tagSize = 0;
-        this.recordIvSize = 0;
-    }
-
-    /**
-     * Construct a new CipherBox using the cipher transformation.
-     *
-     * @exception NoSuchAlgorithmException if no appropriate JCE Cipher
-     * implementation could be found.
-     */
-    private CipherBox(ProtocolVersion protocolVersion, BulkCipher bulkCipher,
-            SecretKey key, IvParameterSpec iv, SecureRandom random,
-            boolean encrypt) throws NoSuchAlgorithmException {
-        try {
-            this.protocolVersion = protocolVersion;
-            this.cipher = JsseJce.getCipher(bulkCipher.transformation);
-            this.mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
-
-            if (random == null) {
-                random = JsseJce.getSecureRandom();
-            }
-            this.random = random;
-            this.cipherType = bulkCipher.cipherType;
-
-            /*
-             * RFC 4346 recommends two algorithms used to generated the
-             * per-record IV. The implementation uses the algorithm (2)(b),
-             * as described at section 6.2.3.2 of RFC 4346.
-             *
-             * As we don't care about the initial IV value for TLS 1.1 or
-             * later, so if the "iv" parameter is null, we use the default
-             * value generated by Cipher.init() for encryption, and a fixed
-             * mask for decryption.
-             */
-            if (iv == null && bulkCipher.ivSize != 0 &&
-                    mode == Cipher.DECRYPT_MODE &&
-                    protocolVersion.useTLS11PlusSpec()) {
-                iv = getFixedMask(bulkCipher.ivSize);
-            }
-
-            if (cipherType == AEAD_CIPHER) {
-                // AEAD must completely initialize the cipher for each packet,
-                // and so we save initialization parameters for packet
-                // processing time.
-
-                // Set the tag size for AEAD cipher
-                tagSize = bulkCipher.tagSize;
-
-                // Reserve the key for AEAD cipher initialization
-                this.key = key;
-
-                fixedIv = iv.getIV();
-                if (fixedIv == null ||
-                        fixedIv.length != bulkCipher.fixedIvSize) {
-                    throw new RuntimeException("Improper fixed IV for AEAD");
-                }
-
-                // Set the record IV length for AEAD cipher
-                recordIvSize = bulkCipher.ivSize - bulkCipher.fixedIvSize;
-
-                // DON'T initialize the cipher for AEAD!
-            } else {
-                // CBC only requires one initialization during its lifetime
-                // (future packets/IVs set the proper CBC state), so we can
-                // initialize now.
-
-                // Zeroize the variables that only apply to AEAD cipher
-                this.tagSize = 0;
-                this.fixedIv = new byte[0];
-                this.recordIvSize = 0;
-                this.key = null;
-
-                // Initialize the cipher
-                cipher.init(mode, key, iv, random);
-            }
-        } catch (NoSuchAlgorithmException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new NoSuchAlgorithmException
-                    ("Could not create cipher " + bulkCipher, e);
-        } catch (ExceptionInInitializerError e) {
-            throw new NoSuchAlgorithmException
-                    ("Could not create cipher " + bulkCipher, e);
-        }
-    }
-
-    /*
-     * Factory method to obtain a new CipherBox object.
-     */
-    static CipherBox newCipherBox(ProtocolVersion version, BulkCipher cipher,
-            SecretKey key, IvParameterSpec iv, SecureRandom random,
-            boolean encrypt) throws NoSuchAlgorithmException {
-        if (cipher.allowed == false) {
-            throw new NoSuchAlgorithmException("Unsupported cipher " + cipher);
-        }
-
-        if (cipher == BulkCipher.B_NULL) {
-            return NULL;
-        } else {
-            return new CipherBox(version, cipher, key, iv, random, encrypt);
-        }
-    }
-
-    /*
-     * Get a fixed mask, as the initial decryption IVs for TLS 1.1 or later.
-     */
-    private static IvParameterSpec getFixedMask(int ivSize) {
-        if (masks == null) {
-            masks = new Hashtable<Integer, IvParameterSpec>(5);
-        }
-
-        IvParameterSpec iv = masks.get(ivSize);
-        if (iv == null) {
-            iv = new IvParameterSpec(new byte[ivSize]);
-            masks.put(ivSize, iv);
-        }
-
-        return iv;
-    }
-
-    /*
-     * Encrypts a block of data, returning the size of the
-     * resulting block.
-     */
-    int encrypt(byte[] buf, int offset, int len) {
-        if (cipher == null) {
-            return len;
-        }
-
-        try {
-            int blockSize = cipher.getBlockSize();
-            if (cipherType == BLOCK_CIPHER) {
-                len = addPadding(buf, offset, len, blockSize);
-            }
-
-            if (debug != null && Debug.isOn("plaintext")) {
-                try {
-                    HexDumpEncoder hd = new HexDumpEncoder();
-
-                    System.out.println(
-                        "Padded plaintext before ENCRYPTION:  len = "
-                        + len);
-                    hd.encodeBuffer(
-                        new ByteArrayInputStream(buf, offset, len),
-                        System.out);
-                } catch (IOException e) { }
-            }
-
-
-            if (cipherType == AEAD_CIPHER) {
-                try {
-                    return cipher.doFinal(buf, offset, len, buf, offset);
-                } catch (IllegalBlockSizeException | BadPaddingException ibe) {
-                    // unlikely to happen
-                    throw new RuntimeException(
-                        "Cipher error in AEAD mode in JCE provider " +
-                        cipher.getProvider().getName(), ibe);
-                }
-            } else {
-                int newLen = cipher.update(buf, offset, len, buf, offset);
-                if (newLen != len) {
-                    // catch BouncyCastle buffering error
-                    throw new RuntimeException("Cipher buffering error " +
-                        "in JCE provider " + cipher.getProvider().getName());
-                }
-                return newLen;
-            }
-        } catch (ShortBufferException e) {
-            // unlikely to happen, we should have enough buffer space here
-            throw new ArrayIndexOutOfBoundsException(e.toString());
-        }
-    }
-
-    /*
-     * Encrypts a ByteBuffer block of data, returning the size of the
-     * resulting block.
-     *
-     * The byte buffers position and limit initially define the amount
-     * to encrypt.  On return, the position and limit are
-     * set to last position padded/encrypted.  The limit may have changed
-     * because of the added padding bytes.
-     */
-    int encrypt(ByteBuffer bb, int outLimit) {
-
-        int len = bb.remaining();
-
-        if (cipher == null) {
-            bb.position(bb.limit());
-            return len;
-        }
-
-        int pos = bb.position();
-
-        int blockSize = cipher.getBlockSize();
-        if (cipherType == BLOCK_CIPHER) {
-            // addPadding adjusts pos/limit
-            len = addPadding(bb, blockSize);
-            bb.position(pos);
-        }
-
-        if (debug != null && Debug.isOn("plaintext")) {
-            try {
-                HexDumpEncoder hd = new HexDumpEncoder();
-
-                System.out.println(
-                    "Padded plaintext before ENCRYPTION:  len = "
-                    + len);
-                hd.encodeBuffer(bb.duplicate(), System.out);
-
-            } catch (IOException e) { }
-        }
-
-        /*
-         * Encrypt "in-place".  This does not add its own padding.
-         */
-        ByteBuffer dup = bb.duplicate();
-        if (cipherType == AEAD_CIPHER) {
-            try {
-                int outputSize = cipher.getOutputSize(dup.remaining());
-                if (outputSize > bb.remaining()) {
-                    // need to expand the limit of the output buffer for
-                    // the authentication tag.
-                    //
-                    // DON'T worry about the buffer's capacity, we have
-                    // reserved space for the authentication tag.
-                    if (outLimit < pos + outputSize) {
-                        // unlikely to happen
-                        throw new ShortBufferException(
-                                    "need more space in output buffer");
-                    }
-                    bb.limit(pos + outputSize);
-                }
-                int newLen = cipher.doFinal(dup, bb);
-                if (newLen != outputSize) {
-                    throw new RuntimeException(
-                            "Cipher buffering error in JCE provider " +
-                            cipher.getProvider().getName());
-                }
-                return newLen;
-            } catch (IllegalBlockSizeException |
-                           BadPaddingException | ShortBufferException ibse) {
-                // unlikely to happen
-                throw new RuntimeException(
-                        "Cipher error in AEAD mode in JCE provider " +
-                        cipher.getProvider().getName(), ibse);
-            }
-        } else {
-            int newLen;
-            try {
-                newLen = cipher.update(dup, bb);
-            } catch (ShortBufferException sbe) {
-                // unlikely to happen
-                throw new RuntimeException("Cipher buffering error " +
-                    "in JCE provider " + cipher.getProvider().getName());
-            }
-
-            if (bb.position() != dup.position()) {
-                throw new RuntimeException("bytebuffer padding error");
-            }
-
-            if (newLen != len) {
-                // catch BouncyCastle buffering error
-                throw new RuntimeException("Cipher buffering error " +
-                    "in JCE provider " + cipher.getProvider().getName());
-            }
-            return newLen;
-        }
-    }
-
-
-    /*
-     * Decrypts a block of data, returning the size of the
-     * resulting block if padding was required.
-     *
-     * For SSLv3 and TLSv1.0, with block ciphers in CBC mode the
-     * Initialization Vector (IV) for the first record is generated by
-     * the handshake protocol, the IV for subsequent records is the
-     * last ciphertext block from the previous record.
-     *
-     * From TLSv1.1, the implicit IV is replaced with an explicit IV to
-     * protect against CBC attacks.
-     *
-     * Differentiating between bad_record_mac and decryption_failed alerts
-     * may permit certain attacks against CBC mode. It is preferable to
-     * uniformly use the bad_record_mac alert to hide the specific type of
-     * the error.
-     */
-    int decrypt(byte[] buf, int offset, int len,
-            int tagLen) throws BadPaddingException {
-        if (cipher == null) {
-            return len;
-        }
-
-        try {
-            int newLen;
-            if (cipherType == AEAD_CIPHER) {
-                try {
-                    newLen = cipher.doFinal(buf, offset, len, buf, offset);
-                } catch (IllegalBlockSizeException ibse) {
-                    // unlikely to happen
-                    throw new RuntimeException(
-                        "Cipher error in AEAD mode in JCE provider " +
-                        cipher.getProvider().getName(), ibse);
-                }
-            } else {
-                newLen = cipher.update(buf, offset, len, buf, offset);
-                if (newLen != len) {
-                    // catch BouncyCastle buffering error
-                    throw new RuntimeException("Cipher buffering error " +
-                        "in JCE provider " + cipher.getProvider().getName());
-                }
-            }
-            if (debug != null && Debug.isOn("plaintext")) {
-                try {
-                    HexDumpEncoder hd = new HexDumpEncoder();
-
-                    System.out.println(
-                        "Padded plaintext after DECRYPTION:  len = "
-                        + newLen);
-                    hd.encodeBuffer(
-                        new ByteArrayInputStream(buf, offset, newLen),
-                        System.out);
-                } catch (IOException e) { }
-            }
-
-            if (cipherType == BLOCK_CIPHER) {
-                int blockSize = cipher.getBlockSize();
-                newLen = removePadding(
-                    buf, offset, newLen, tagLen, blockSize, protocolVersion);
-
-                if (protocolVersion.useTLS11PlusSpec()) {
-                    if (newLen < blockSize) {
-                        throw new BadPaddingException("The length after " +
-                        "padding removal (" + newLen + ") should be larger " +
-                        "than <" + blockSize + "> since explicit IV used");
-                    }
-                }
-            }
-            return newLen;
-        } catch (ShortBufferException e) {
-            // unlikely to happen, we should have enough buffer space here
-            throw new ArrayIndexOutOfBoundsException(e.toString());
-        }
-    }
-
-    /*
-     * Decrypts a block of data, returning the size of the
-     * resulting block if padding was required.  position and limit
-     * point to the end of the decrypted/depadded data.  The initial
-     * limit and new limit may be different, given we may
-     * have stripped off some padding bytes.
-     *
-     *  @see decrypt(byte[], int, int)
-     */
-    int decrypt(ByteBuffer bb, int tagLen) throws BadPaddingException {
-
-        int len = bb.remaining();
-
-        if (cipher == null) {
-            bb.position(bb.limit());
-            return len;
-        }
-
-        try {
-            /*
-             * Decrypt "in-place".
-             */
-            int pos = bb.position();
-            ByteBuffer dup = bb.duplicate();
-            int newLen;
-            if (cipherType == AEAD_CIPHER) {
-                try {
-                    newLen = cipher.doFinal(dup, bb);
-                } catch (IllegalBlockSizeException ibse) {
-                    // unlikely to happen
-                    throw new RuntimeException(
-                        "Cipher error in AEAD mode \"" + ibse.getMessage() +
-                        " \"in JCE provider " + cipher.getProvider().getName());
-                }
-            } else {
-                newLen = cipher.update(dup, bb);
-                if (newLen != len) {
-                    // catch BouncyCastle buffering error
-                    throw new RuntimeException("Cipher buffering error " +
-                        "in JCE provider " + cipher.getProvider().getName());
-                }
-            }
-
-            // reset the limit to the end of the decryted data
-            bb.limit(pos + newLen);
-
-            if (debug != null && Debug.isOn("plaintext")) {
-                try {
-                    HexDumpEncoder hd = new HexDumpEncoder();
-
-                    System.out.println(
-                        "Padded plaintext after DECRYPTION:  len = "
-                        + newLen);
-
-                    hd.encodeBuffer(
-                        bb.duplicate().position(pos), System.out);
-                } catch (IOException e) { }
-            }
-
-            /*
-             * Remove the block padding.
-             */
-            if (cipherType == BLOCK_CIPHER) {
-                int blockSize = cipher.getBlockSize();
-                bb.position(pos);
-                newLen = removePadding(bb, tagLen, blockSize, protocolVersion);
-
-                // check the explicit IV of TLS v1.1 or later
-                if (protocolVersion.useTLS11PlusSpec()) {
-                    if (newLen < blockSize) {
-                        throw new BadPaddingException("The length after " +
-                        "padding removal (" + newLen + ") should be larger " +
-                        "than <" + blockSize + "> since explicit IV used");
-                    }
-
-                    // reset the position to the end of the decrypted data
-                    bb.position(bb.limit());
-                }
-            }
-            return newLen;
-        } catch (ShortBufferException e) {
-            // unlikely to happen, we should have enough buffer space here
-            throw new ArrayIndexOutOfBoundsException(e.toString());
-        }
-    }
-
-    private static int addPadding(byte[] buf, int offset, int len,
-            int blockSize) {
-        int     newlen = len + 1;
-        byte    pad;
-        int     i;
-
-        if ((newlen % blockSize) != 0) {
-            newlen += blockSize - 1;
-            newlen -= newlen % blockSize;
-        }
-        pad = (byte) (newlen - len);
-
-        if (buf.length < (newlen + offset)) {
-            throw new IllegalArgumentException("no space to pad buffer");
-        }
-
-        /*
-         * TLS version of the padding works for both SSLv3 and TLSv1
-         */
-        for (i = 0, offset += len; i < pad; i++) {
-            buf [offset++] = (byte) (pad - 1);
-        }
-        return newlen;
-    }
-
-    /*
-     * Apply the padding to the buffer.
-     *
-     * Limit is advanced to the new buffer length.
-     * Position is equal to limit.
-     */
-    private static int addPadding(ByteBuffer bb, int blockSize) {
-
-        int     len = bb.remaining();
-        int     offset = bb.position();
-
-        int     newlen = len + 1;
-        byte    pad;
-        int     i;
-
-        if ((newlen % blockSize) != 0) {
-            newlen += blockSize - 1;
-            newlen -= newlen % blockSize;
-        }
-        pad = (byte) (newlen - len);
-
-        /*
-         * Update the limit to what will be padded.
-         */
-        bb.limit(newlen + offset);
-
-        /*
-         * TLS version of the padding works for both SSLv3 and TLSv1
-         */
-        for (i = 0, offset += len; i < pad; i++) {
-            bb.put(offset++, (byte) (pad - 1));
-        }
-
-        bb.position(offset);
-        bb.limit(offset);
-
-        return newlen;
-    }
-
-    /*
-     * A constant-time check of the padding.
-     *
-     * NOTE that we are checking both the padding and the padLen bytes here.
-     *
-     * The caller MUST ensure that the len parameter is a positive number.
-     */
-    private static int[] checkPadding(
-            byte[] buf, int offset, int len, byte pad) {
-
-        if (len <= 0) {
-            throw new RuntimeException("padding len must be positive");
-        }
-
-        // An array of hits is used to prevent Hotspot optimization for
-        // the purpose of a constant-time check.
-        int[] results = {0, 0};    // {missed #, matched #}
-        for (int i = 0; i <= 256;) {
-            for (int j = 0; j < len && i <= 256; j++, i++) {     // j <= i
-                if (buf[offset + j] != pad) {
-                    results[0]++;       // mismatched padding data
-                } else {
-                    results[1]++;       // matched padding data
-                }
-            }
-        }
-
-        return results;
-    }
-
-    /*
-     * A constant-time check of the padding.
-     *
-     * NOTE that we are checking both the padding and the padLen bytes here.
-     *
-     * The caller MUST ensure that the bb parameter has remaining.
-     */
-    private static int[] checkPadding(ByteBuffer bb, byte pad) {
-
-        if (!bb.hasRemaining()) {
-            throw new RuntimeException("hasRemaining() must be positive");
-        }
-
-        // An array of hits is used to prevent Hotspot optimization for
-        // the purpose of a constant-time check.
-        int[] results = {0, 0};    // {missed #, matched #}
-        bb.mark();
-        for (int i = 0; i <= 256; bb.reset()) {
-            for (; bb.hasRemaining() && i <= 256; i++) {
-                if (bb.get() != pad) {
-                    results[0]++;       // mismatched padding data
-                } else {
-                    results[1]++;       // matched padding data
-                }
-            }
-        }
-
-        return results;
-    }
-
-    /*
-     * Typical TLS padding format for a 64 bit block cipher is as follows:
-     *   xx xx xx xx xx xx xx 00
-     *   xx xx xx xx xx xx 01 01
-     *   ...
-     *   xx 06 06 06 06 06 06 06
-     *   07 07 07 07 07 07 07 07
-     * TLS also allows any amount of padding from 1 and 256 bytes as long
-     * as it makes the data a multiple of the block size
-     */
-    private static int removePadding(byte[] buf, int offset, int len,
-            int tagLen, int blockSize,
-            ProtocolVersion protocolVersion) throws BadPaddingException {
-
-        // last byte is length byte (i.e. actual padding length - 1)
-        int padOffset = offset + len - 1;
-        int padLen = buf[padOffset] & 0xFF;
-
-        int newLen = len - (padLen + 1);
-        if ((newLen - tagLen) < 0) {
-            // If the buffer is not long enough to contain the padding plus
-            // a MAC tag, do a dummy constant-time padding check.
-            //
-            // Note that it is a dummy check, so we won't care about what is
-            // the actual padding data.
-            checkPadding(buf, offset, len, (byte)(padLen & 0xFF));
-
-            throw new BadPaddingException("Invalid Padding length: " + padLen);
-        }
-
-        // The padding data should be filled with the padding length value.
-        int[] results = checkPadding(buf, offset + newLen,
-                        padLen + 1, (byte)(padLen & 0xFF));
-        if (protocolVersion.useTLS10PlusSpec()) {
-            if (results[0] != 0) {          // padding data has invalid bytes
-                throw new BadPaddingException("Invalid TLS padding data");
-            }
-        } else { // SSLv3
-            // SSLv3 requires 0 <= length byte < block size
-            // some implementations do 1 <= length byte <= block size,
-            // so accept that as well
-            // v3 does not require any particular value for the other bytes
-            if (padLen > blockSize) {
-                throw new BadPaddingException("Padding length (" +
-                padLen + ") of SSLv3 message should not be bigger " +
-                "than the block size (" + blockSize + ")");
-            }
-        }
-        return newLen;
-    }
-
-    /*
-     * Position/limit is equal the removed padding.
-     */
-    private static int removePadding(ByteBuffer bb,
-            int tagLen, int blockSize,
-            ProtocolVersion protocolVersion) throws BadPaddingException {
-
-        int len = bb.remaining();
-        int offset = bb.position();
-
-        // last byte is length byte (i.e. actual padding length - 1)
-        int padOffset = offset + len - 1;
-        int padLen = bb.get(padOffset) & 0xFF;
-
-        int newLen = len - (padLen + 1);
-        if ((newLen - tagLen) < 0) {
-            // If the buffer is not long enough to contain the padding plus
-            // a MAC tag, do a dummy constant-time padding check.
-            //
-            // Note that it is a dummy check, so we won't care about what is
-            // the actual padding data.
-            checkPadding(bb.duplicate(), (byte)(padLen & 0xFF));
-
-            throw new BadPaddingException("Invalid Padding length: " + padLen);
-        }
-
-        // The padding data should be filled with the padding length value.
-        int[] results = checkPadding(
-                bb.duplicate().position(offset + newLen),
-                (byte)(padLen & 0xFF));
-        if (protocolVersion.useTLS10PlusSpec()) {
-            if (results[0] != 0) {          // padding data has invalid bytes
-                throw new BadPaddingException("Invalid TLS padding data");
-            }
-        } else { // SSLv3
-            // SSLv3 requires 0 <= length byte < block size
-            // some implementations do 1 <= length byte <= block size,
-            // so accept that as well
-            // v3 does not require any particular value for the other bytes
-            if (padLen > blockSize) {
-                throw new BadPaddingException("Padding length (" +
-                padLen + ") of SSLv3 message should not be bigger " +
-                "than the block size (" + blockSize + ")");
-            }
-        }
-
-        /*
-         * Reset buffer limit to remove padding.
-         */
-        bb.position(offset + newLen);
-        bb.limit(offset + newLen);
-
-        return newLen;
-    }
-
-    /*
-     * Dispose of any intermediate state in the underlying cipher.
-     * For PKCS11 ciphers, this will release any attached sessions, and
-     * thus make finalization faster.
-     */
-    void dispose() {
-        try {
-            if (cipher != null) {
-                // ignore return value.
-                cipher.doFinal();
-            }
-        } catch (Exception e) {
-            // swallow all types of exceptions.
-        }
-    }
-
-    /*
-     * Does the cipher use CBC mode?
-     *
-     * @return true if the cipher use CBC mode, false otherwise.
-     */
-    boolean isCBCMode() {
-        return cipherType == BLOCK_CIPHER;
-    }
-
-    /*
-     * Does the cipher use AEAD mode?
-     *
-     * @return true if the cipher use AEAD mode, false otherwise.
-     */
-    boolean isAEADMode() {
-        return cipherType == AEAD_CIPHER;
-    }
-
-    /*
-     * Is the cipher null?
-     *
-     * @return true if the cipher is null, false otherwise.
-     */
-    boolean isNullCipher() {
-        return cipher == null;
-    }
-
-    /*
-     * Gets the explicit nonce/IV size of the cipher.
-     *
-     * The returned value is the SecurityParameters.record_iv_length in
-     * RFC 4346/5246.  It is the size of explicit IV for CBC mode, and the
-     * size of explicit nonce for AEAD mode.
-     *
-     * @return the explicit nonce size of the cipher.
-     */
-    int getExplicitNonceSize() {
-        switch (cipherType) {
-            case BLOCK_CIPHER:
-                // For block ciphers, the explicit IV length is of length
-                // SecurityParameters.record_iv_length, which is equal to
-                // the SecurityParameters.block_size.
-                if (protocolVersion.useTLS11PlusSpec()) {
-                    return cipher.getBlockSize();
-                }
-                break;
-            case AEAD_CIPHER:
-                return recordIvSize;
-                        // It is also the length of sequence number, which is
-                        // used as the nonce_explicit for AEAD cipher suites.
-        }
-
-        return 0;
-    }
-
-    /*
-     * Applies the explicit nonce/IV to this cipher. This method is used to
-     * decrypt an SSL/TLS input record.
-     *
-     * The returned value is the SecurityParameters.record_iv_length in
-     * RFC 4346/5246.  It is the size of explicit IV for CBC mode, and the
-     * size of explicit nonce for AEAD mode.
-     *
-     * @param  authenticator the authenticator to get the additional
-     *         authentication data
-     * @param  contentType the content type of the input record
-     * @param  bb the byte buffer to get the explicit nonce from
-     *
-     * @return the explicit nonce size of the cipher.
-     */
-    int applyExplicitNonce(Authenticator authenticator, byte contentType,
-            ByteBuffer bb, byte[] sequence) throws BadPaddingException {
-        switch (cipherType) {
-            case BLOCK_CIPHER:
-                // sanity check length of the ciphertext
-                int tagLen = (authenticator instanceof MAC) ?
-                                    ((MAC)authenticator).MAClen() : 0;
-                if (tagLen != 0) {
-                    if (!sanityCheck(tagLen, bb.remaining())) {
-                        throw new BadPaddingException(
-                                "ciphertext sanity check failed");
-                    }
-                }
-
-                // For block ciphers, the explicit IV length is of length
-                // SecurityParameters.record_iv_length, which is equal to
-                // the SecurityParameters.block_size.
-                if (protocolVersion.useTLS11PlusSpec()) {
-                    return cipher.getBlockSize();
-                }
-                break;
-            case AEAD_CIPHER:
-                if (bb.remaining() < (recordIvSize + tagSize)) {
-                    throw new BadPaddingException(
-                        "Insufficient buffer remaining for AEAD cipher " +
-                        "fragment (" + bb.remaining() + "). Needs to be " +
-                        "more than or equal to IV size (" + recordIvSize +
-                         ") + tag size (" + tagSize + ")");
-                }
-
-                // initialize the AEAD cipher for the unique IV
-                byte[] iv = Arrays.copyOf(fixedIv,
-                                    fixedIv.length + recordIvSize);
-                bb.get(iv, fixedIv.length, recordIvSize);
-                bb.position(bb.position() - recordIvSize);
-                GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
-                try {
-                    cipher.init(mode, key, spec, random);
-                } catch (InvalidKeyException |
-                            InvalidAlgorithmParameterException ikae) {
-                    // unlikely to happen
-                    throw new RuntimeException(
-                                "invalid key or spec in GCM mode", ikae);
-                }
-
-                // update the additional authentication data
-                byte[] aad = authenticator.acquireAuthenticationBytes(
-                        contentType, bb.remaining() - recordIvSize - tagSize,
-                        sequence);
-                cipher.updateAAD(aad);
-
-                return recordIvSize;
-                        // It is also the length of sequence number, which is
-                        // used as the nonce_explicit for AEAD cipher suites.
-        }
-
-       return 0;
-    }
-
-    /*
-     * Creates the explicit nonce/IV to this cipher. This method is used to
-     * encrypt an SSL/TLS output record.
-     *
-     * The size of the returned array is the SecurityParameters.record_iv_length
-     * in RFC 4346/5246.  It is the size of explicit IV for CBC mode, and the
-     * size of explicit nonce for AEAD mode.
-     *
-     * @param  authenticator the authenticator to get the additional
-     *         authentication data
-     * @param  contentType the content type of the input record
-     * @param  fragmentLength the fragment length of the output record, it is
-     *         the TLSCompressed.length in RFC 4346/5246.
-     *
-     * @return the explicit nonce of the cipher.
-     */
-    byte[] createExplicitNonce(Authenticator authenticator,
-            byte contentType, int fragmentLength) {
-
-        byte[] nonce = new byte[0];
-        switch (cipherType) {
-            case BLOCK_CIPHER:
-                if (protocolVersion.useTLS11PlusSpec()) {
-                    // For block ciphers, the explicit IV length is of length
-                    // SecurityParameters.record_iv_length, which is equal to
-                    // the SecurityParameters.block_size.
-                    //
-                    // Generate a random number as the explicit IV parameter.
-                    nonce = new byte[cipher.getBlockSize()];
-                    random.nextBytes(nonce);
-                }
-                break;
-            case AEAD_CIPHER:
-                // To be unique and aware of overflow-wrap, sequence number
-                // is used as the nonce_explicit of AEAD cipher suites.
-                nonce = authenticator.sequenceNumber();
-
-                // initialize the AEAD cipher for the unique IV
-                byte[] iv = Arrays.copyOf(fixedIv,
-                                            fixedIv.length + nonce.length);
-                System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length);
-                GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
-                try {
-                    cipher.init(mode, key, spec, random);
-                } catch (InvalidKeyException |
-                            InvalidAlgorithmParameterException ikae) {
-                    // unlikely to happen
-                    throw new RuntimeException(
-                                "invalid key or spec in GCM mode", ikae);
-                }
-
-                // Update the additional authentication data, using the
-                // implicit sequence number of the authenticator.
-                byte[] aad = authenticator.acquireAuthenticationBytes(
-                                        contentType, fragmentLength, null);
-                cipher.updateAAD(aad);
-                break;
-        }
-
-        return nonce;
-    }
-
-    // See also CipherSuite.calculatePacketSize().
-    int calculatePacketSize(int fragmentSize, int macLen, int headerSize) {
-        int packetSize = fragmentSize;
-        if (cipher != null) {
-            int blockSize = cipher.getBlockSize();
-            switch (cipherType) {
-                case BLOCK_CIPHER:
-                    packetSize += macLen;
-                    packetSize += 1;        // 1 byte padding length field
-                    packetSize +=           // use the minimal padding
-                            (blockSize - (packetSize % blockSize)) % blockSize;
-                    if (protocolVersion.useTLS11PlusSpec()) {
-                        packetSize += blockSize;        // explicit IV
-                    }
-
-                    break;
-                case AEAD_CIPHER:
-                    packetSize += recordIvSize;
-                    packetSize += tagSize;
-
-                    break;
-                default:    // NULL_CIPHER or STREAM_CIPHER
-                    packetSize += macLen;
-            }
-        }
-
-        return packetSize + headerSize;
-    }
-
-    // See also CipherSuite.calculateFragSize().
-    int calculateFragmentSize(int packetLimit, int macLen, int headerSize) {
-        int fragLen = packetLimit - headerSize;
-        if (cipher != null) {
-            int blockSize = cipher.getBlockSize();
-            switch (cipherType) {
-                case BLOCK_CIPHER:
-                    if (protocolVersion.useTLS11PlusSpec()) {
-                        fragLen -= blockSize;           // explicit IV
-                    }
-                    fragLen -= (fragLen % blockSize);   // cannot hold a block
-                    // No padding for a maximum fragment.
-                    fragLen -= 1;       // 1 byte padding length field: 0x00
-                    fragLen -= macLen;
-
-                    break;
-                case AEAD_CIPHER:
-                    fragLen -= recordIvSize;
-                    fragLen -= tagSize;
-
-                    break;
-                default:    // NULL_CIPHER or STREAM_CIPHER
-                    fragLen -= macLen;
-            }
-        }
-
-        return fragLen;
-    }
-
-    // Estimate the maximum fragment size of a received packet.
-    int estimateFragmentSize(int packetSize, int macLen, int headerSize) {
-        int fragLen = packetSize - headerSize;
-        if (cipher != null) {
-            int blockSize = cipher.getBlockSize();
-            switch (cipherType) {
-                case BLOCK_CIPHER:
-                    if (protocolVersion.useTLS11PlusSpec()) {
-                        fragLen -= blockSize;       // explicit IV
-                    }
-                    // No padding for a maximum fragment.
-                    fragLen -= 1;       // 1 byte padding length field: 0x00
-                    fragLen -= macLen;
-
-                    break;
-                case AEAD_CIPHER:
-                    fragLen -= recordIvSize;
-                    fragLen -= tagSize;
-
-                    break;
-                default:    // NULL_CIPHER or STREAM_CIPHER
-                    fragLen -= macLen;
-            }
-        }
-
-        return fragLen;
-    }
-
-    /**
-     * Sanity check the length of a fragment before decryption.
-     *
-     * In CBC mode, check that the fragment length is one or multiple times
-     * of the block size of the cipher suite, and is at least one (one is the
-     * smallest size of padding in CBC mode) bigger than the tag size of the
-     * MAC algorithm except the explicit IV size for TLS 1.1 or later.
-     *
-     * In non-CBC mode, check that the fragment length is not less than the
-     * tag size of the MAC algorithm.
-     *
-     * @return true if the length of a fragment matches above requirements
-     */
-    private boolean sanityCheck(int tagLen, int fragmentLen) {
-        if (!isCBCMode()) {
-            return fragmentLen >= tagLen;
-        }
-
-        int blockSize = cipher.getBlockSize();
-        if ((fragmentLen % blockSize) == 0) {
-            int minimal = tagLen + 1;
-            minimal = (minimal >= blockSize) ? minimal : blockSize;
-            if (protocolVersion.useTLS11PlusSpec()) {
-                minimal += blockSize;   // plus the size of the explicit IV
-            }
-
-            return (fragmentLen >= minimal);
-        }
-
-        return false;
-    }
-
-}
--- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,171 +23,939 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
-import java.util.*;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.InvalidKeyException;
-import java.security.SecureRandom;
-import java.security.KeyManagementException;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import static sun.security.ssl.CipherSuite.HashAlg.*;
 import static sun.security.ssl.CipherSuite.KeyExchange.*;
-import static sun.security.ssl.CipherSuite.PRF.*;
-import static sun.security.ssl.CipherSuite.CipherType.*;
 import static sun.security.ssl.CipherSuite.MacAlg.*;
-import static sun.security.ssl.CipherSuite.BulkCipher.*;
-import static sun.security.ssl.JsseJce.*;
-import static sun.security.ssl.NamedGroupType.*;
+import static sun.security.ssl.SSLCipher.*;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
 
 /**
- * An SSL/TLS CipherSuite. Constants for the standard key exchange, cipher,
- * and mac algorithms are also defined in this class.
- *
- * The CipherSuite class and the inner classes defined in this file roughly
- * follow the type safe enum pattern described in Effective Java. This means:
- *
- *  . instances are immutable, classes are final
- *
- *  . there is a unique instance of every value, i.e. there are never two
- *    instances representing the same CipherSuite, etc. This means equality
- *    tests can be performed using == instead of equals() (although that works
- *    as well). [A minor exception are *unsupported* CipherSuites read from a
- *    handshake message, but this is usually irrelevant]
+ * Enum for SSL/(D)TLS cipher suites.
  *
- *  . instances are obtained using the static valueOf() factory methods.
- *
- *  . properties are defined as final variables and made available as
- *    package private variables without method accessors
- *
- *  . if the member variable allowed is false, the given algorithm is either
- *    unavailable or disabled at compile time
- *
+ * Please refer to the "TLS Cipher Suite Registry" section for more details
+ * about each cipher suite:
+ *     https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
  */
-final class CipherSuite implements Comparable<CipherSuite> {
+enum CipherSuite {
+    //
+    // in preference order
+    //
+
+    // Definition of the CipherSuites that are enabled by default.
+    //
+    // They are listed in preference order, most preferred first, using
+    // the following criteria:
+    // 1. Prefer Suite B compliant cipher suites, see RFC6460 (To be
+    //    changed later, see below).
+    // 2. Prefer the stronger bulk cipher, in the order of AES_256(GCM),
+    //    AES_128(GCM), AES_256, AES_128, 3DES-EDE.
+    // 3. Prefer the stronger MAC algorithm, in the order of SHA384,
+    //    SHA256, SHA, MD5.
+    // 4. Prefer the better performance of key exchange and digital
+    //    signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA,
+    //    RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS.
+
+    TLS_AES_128_GCM_SHA256(
+            0x1301, true, "TLS_AES_128_GCM_SHA256",
+            ProtocolVersion.PROTOCOLS_OF_13, B_AES_128_GCM_IV, H_SHA256),
+    TLS_AES_256_GCM_SHA384(
+            0x1302, true, "TLS_AES_256_GCM_SHA384",
+            ProtocolVersion.PROTOCOLS_OF_13, B_AES_256_GCM_IV, H_SHA384),
+
+    // Suite B compliant cipher suites, see RFC 6460.
+    //
+    // Note that, at present this provider is not Suite B compliant. The
+    // preference order of the GCM cipher suites does not follow the spec
+    // of RFC 6460.  In this section, only two cipher suites are listed
+    // so that applications can make use of Suite-B compliant cipher
+    // suite firstly.
+    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(
+            0xC02C, true, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_ECDSA, B_AES_256_GCM, M_NULL, H_SHA384),
+    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(
+            0xC02B, true, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_ECDSA, B_AES_128_GCM, M_NULL, H_SHA256),
+
+    // AES_256(GCM)
+    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(
+            0xC030, true, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_RSA, B_AES_256_GCM, M_NULL, H_SHA384),
+    TLS_RSA_WITH_AES_256_GCM_SHA384(
+            0x009D, true, "TLS_RSA_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_RSA, B_AES_256_GCM, M_NULL, H_SHA384),
+    TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(
+            0xC02E, true, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_ECDSA, B_AES_256_GCM, M_NULL, H_SHA384),
+    TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(
+            0xC032, true, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_RSA, B_AES_256_GCM, M_NULL, H_SHA384),
+    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(
+            0x009F, true, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_RSA, B_AES_256_GCM, M_NULL, H_SHA384),
+    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(
+            0x00A3, true, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_DSS, B_AES_256_GCM, M_NULL, H_SHA384),
+
+    // AES_128(GCM)
+    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(
+            0xC02F, true, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_RSA, B_AES_128_GCM, M_NULL, H_SHA256),
+    TLS_RSA_WITH_AES_128_GCM_SHA256(
+            0x009C, true, "TLS_RSA_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_RSA, B_AES_128_GCM, M_NULL, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(
+            0xC02D, true, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_ECDSA, B_AES_128_GCM, M_NULL, H_SHA256),
+    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(
+            0xC031, true, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_RSA, B_AES_128_GCM, M_NULL, H_SHA256),
+    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(
+            0x009E, true, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_RSA, B_AES_128_GCM, M_NULL, H_SHA256),
+    TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(
+            0x00A2, true, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_DSS, B_AES_128_GCM, M_NULL, H_SHA256),
+
+    // AES_256(CBC)
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(
+            0xC024, true, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_ECDSA, B_AES_256, M_SHA384, H_SHA384),
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(
+            0xC028, true, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_RSA, B_AES_256, M_SHA384, H_SHA384),
+    TLS_RSA_WITH_AES_256_CBC_SHA256(
+            0x003D, true, "TLS_RSA_WITH_AES_256_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_RSA, B_AES_256, M_SHA256, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(
+            0xC026, true, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_ECDSA, B_AES_256, M_SHA384, H_SHA384),
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(
+            0xC02A, true, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_RSA, B_AES_256, M_SHA384, H_SHA384),
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(
+            0x006B, true, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_RSA, B_AES_256, M_SHA256, H_SHA256),
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(
+            0x006A, true, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_DSS, B_AES_256, M_SHA256, H_SHA256),
+
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(
+            0xC00A, true, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_ECDSA, B_AES_256, M_SHA, H_SHA256),
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(
+            0xC014, true, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_RSA, B_AES_256, M_SHA, H_SHA256),
+    TLS_RSA_WITH_AES_256_CBC_SHA(
+            0x0035, true, "TLS_RSA_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_RSA, B_AES_256, M_SHA, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(
+            0xC005, true, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ECDSA, B_AES_256, M_SHA, H_SHA256),
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(
+            0xC00F, true, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_RSA, B_AES_256, M_SHA, H_SHA256),
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA(
+            0x0039, true, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DHE_RSA, B_AES_256, M_SHA, H_SHA256),
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA(
+            0x0038, true, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DHE_DSS, B_AES_256, M_SHA, H_SHA256),
 
-    // minimum priority for supported CipherSuites
-    static final int SUPPORTED_SUITES_PRIORITY = 1;
+    // AES_128(CBC)
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(
+            0xC023, true, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_ECDSA, B_AES_128, M_SHA256, H_SHA256),
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(
+            0xC027, true, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDHE_RSA, B_AES_128, M_SHA256, H_SHA256),
+    TLS_RSA_WITH_AES_128_CBC_SHA256(
+            0x003C, true, "TLS_RSA_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_RSA, B_AES_128, M_SHA256, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(
+            0xC025, true, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_ECDSA, B_AES_128, M_SHA256, H_SHA256),
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(
+            0xC029, true, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_ECDH_RSA, B_AES_128, M_SHA256, H_SHA256),
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(
+            0x0067, true, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_RSA, B_AES_128, M_SHA256, H_SHA256),
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(
+            0x0040, true, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DHE_DSS, B_AES_128, M_SHA256, H_SHA256),
+
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(
+            0xC009, true, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_ECDSA, B_AES_128, M_SHA, H_SHA256),
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(
+            0xC013, true, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_RSA, B_AES_128, M_SHA, H_SHA256),
+    TLS_RSA_WITH_AES_128_CBC_SHA(
+            0x002F, true, "TLS_RSA_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_RSA, B_AES_128, M_SHA, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(
+            0xC004, true, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ECDSA, B_AES_128, M_SHA, H_SHA256),
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(
+            0xC00E, true, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_RSA, B_AES_128, M_SHA, H_SHA256),
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA(
+            0x0033, true, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DHE_RSA, B_AES_128, M_SHA, H_SHA256),
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA(
+            0x0032, true, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DHE_DSS, B_AES_128, M_SHA, H_SHA256),
+
+    // 3DES_EDE
+    TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA(
+            0xC008, true, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_ECDSA, B_3DES, M_SHA, H_SHA256),
+    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA(
+            0xC012, true, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_RSA, B_3DES, M_SHA, H_SHA256),
+    SSL_RSA_WITH_3DES_EDE_CBC_SHA(
+            0x000A, true, "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+                          "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_RSA, B_3DES, M_SHA, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA(
+            0xC003, true, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ECDSA, B_3DES, M_SHA, H_SHA256),
+    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA(
+            0xC00D, true, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_RSA, B_3DES, M_SHA, H_SHA256),
+    SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA(
+            0x0016, true, "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+                          "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DHE_RSA, B_3DES, M_SHA, H_SHA256),
+    SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA(
+            0x0013, true, "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+                          "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DHE_DSS, B_3DES, M_SHA, H_SHA256),
+
+    // Renegotiation protection request Signalling Cipher Suite Value (SCSV).
+    TLS_EMPTY_RENEGOTIATION_INFO_SCSV(        //  RFC 5746, TLS 1.2 and prior
+            0x00FF, true, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_SCSV, B_NULL, M_NULL, H_NONE),
 
-    // minimum priority for default enabled CipherSuites
-    static final int DEFAULT_SUITES_PRIORITY = 300;
+    // Definition of the CipherSuites that are supported but not enabled
+    // by default.
+    // They are listed in preference order, preferred first, using the
+    // following criteria:
+    // 1. If a cipher suite has been obsoleted, we put it at the end of
+    //    the list.
+    // 2. Prefer the stronger bulk cipher, in the order of AES_256,
+    //    AES_128, 3DES-EDE, RC-4, DES, DES40, RC4_40, NULL.
+    // 3. Prefer the stronger MAC algorithm, in the order of SHA384,
+    //    SHA256, SHA, MD5.
+    // 4. Prefer the better performance of key exchange and digital
+    //    signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA,
+    //    RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS, anonymous.
+    TLS_DH_anon_WITH_AES_256_GCM_SHA384(
+            0x00A7, false, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DH_ANON, B_AES_256_GCM, M_NULL, H_SHA384),
+    TLS_DH_anon_WITH_AES_128_GCM_SHA256(
+            0x00A6, false, "TLS_DH_anon_WITH_AES_128_GCM_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DH_ANON, B_AES_128_GCM, M_NULL, H_SHA256),
+    TLS_DH_anon_WITH_AES_256_CBC_SHA256(
+            0x006D, false, "TLS_DH_anon_WITH_AES_256_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DH_ANON, B_AES_256, M_SHA256, H_SHA256),
+    TLS_ECDH_anon_WITH_AES_256_CBC_SHA(
+            0xC019, false, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ANON, B_AES_256, M_SHA, H_SHA256),
+    TLS_DH_anon_WITH_AES_256_CBC_SHA(
+            0x003A, false, "TLS_DH_anon_WITH_AES_256_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DH_ANON, B_AES_256, M_SHA, H_SHA256),
+    TLS_DH_anon_WITH_AES_128_CBC_SHA256(
+            0x006C, false, "TLS_DH_anon_WITH_AES_128_CBC_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_DH_ANON, B_AES_128, M_SHA256, H_SHA256),
+    TLS_ECDH_anon_WITH_AES_128_CBC_SHA(
+            0xC018, false, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ANON, B_AES_128, M_SHA, H_SHA256),
+    TLS_DH_anon_WITH_AES_128_CBC_SHA(
+            0x0034, false, "TLS_DH_anon_WITH_AES_128_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DH_ANON, B_AES_128, M_SHA, H_SHA256),
+    TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA(
+            0xC017, false, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ANON, B_3DES, M_SHA, H_SHA256),
+    SSL_DH_anon_WITH_3DES_EDE_CBC_SHA(
+            0x001B, false, "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
+                           "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_DH_ANON, B_3DES, M_SHA, H_SHA256),
+
+    // RC4
+    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA(
+            0xC007, false, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_ECDHE_ECDSA, B_RC4_128, M_SHA, H_SHA256),
+    TLS_ECDHE_RSA_WITH_RC4_128_SHA(
+            0xC011, false, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_ECDHE_RSA, B_RC4_128, M_SHA, H_SHA256),
+    SSL_RSA_WITH_RC4_128_SHA(
+            0x0005, false, "SSL_RSA_WITH_RC4_128_SHA",
+                           "TLS_RSA_WITH_RC4_128_SHA",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_RSA, B_RC4_128, M_SHA, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_RC4_128_SHA(
+            0xC002, false, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_ECDH_ECDSA, B_RC4_128, M_SHA, H_SHA256),
+    TLS_ECDH_RSA_WITH_RC4_128_SHA(
+            0xC00C, false, "TLS_ECDH_RSA_WITH_RC4_128_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_ECDH_RSA, B_RC4_128, M_SHA, H_SHA256),
+    SSL_RSA_WITH_RC4_128_MD5(
+            0x0004, false, "SSL_RSA_WITH_RC4_128_MD5",
+                           "TLS_RSA_WITH_RC4_128_MD5",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_RSA, B_RC4_128, M_MD5, H_SHA256),
+    TLS_ECDH_anon_WITH_RC4_128_SHA(
+            0xC016, false, "TLS_ECDH_anon_WITH_RC4_128_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_ECDH_ANON, B_RC4_128, M_SHA, H_SHA256),
+    SSL_DH_anon_WITH_RC4_128_MD5(
+            0x0018, false, "SSL_DH_anon_WITH_RC4_128_MD5",
+                           "TLS_DH_anon_WITH_RC4_128_MD5",
+            ProtocolVersion.PROTOCOLS_TO_TLS12,
+            K_DH_ANON, B_RC4_128, M_MD5, H_SHA256),
+
+    // weak cipher suites obsoleted in TLS 1.2 [RFC 5246]
+    SSL_RSA_WITH_DES_CBC_SHA(
+            0x0009, false, "SSL_RSA_WITH_DES_CBC_SHA",
+                           "TLS_RSA_WITH_DES_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_11,
+            K_RSA, B_DES, M_SHA, H_NONE),
+    SSL_DHE_RSA_WITH_DES_CBC_SHA(
+            0x0015, false, "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+                           "TLS_DHE_RSA_WITH_DES_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_11,
+            K_DHE_RSA, B_DES, M_SHA, H_NONE),
+    SSL_DHE_DSS_WITH_DES_CBC_SHA(
+            0x0012, false, "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+                           "TLS_DHE_DSS_WITH_DES_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_11,
+            K_DHE_DSS, B_DES, M_SHA, H_NONE),
+    SSL_DH_anon_WITH_DES_CBC_SHA(
+            0x001A, false, "SSL_DH_anon_WITH_DES_CBC_SHA",
+                           "TLS_DH_anon_WITH_DES_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_11,
+            K_DH_ANON, B_DES, M_SHA, H_NONE),
 
-    private static final boolean ALLOW_ECC = Debug.getBooleanProperty
-        ("com.sun.net.ssl.enableECC", true);
+    // weak cipher suites obsoleted in TLS 1.1  [RFC 4346]
+    SSL_RSA_EXPORT_WITH_DES40_CBC_SHA(
+            0x0008, false, "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+                           "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_RSA_EXPORT, B_DES_40, M_SHA, H_NONE),
+    SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA(
+            0x0014, false, "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+                           "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_DHE_RSA_EXPORT, B_DES_40, M_SHA, H_NONE),
+    SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(
+            0x0011, false, "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+                           "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_DHE_DSS_EXPORT, B_DES_40, M_SHA, H_NONE),
+    SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA(
+            0x0019, false, "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+                           "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_DH_ANON_EXPORT, B_DES_40, M_SHA, H_NONE),
+    SSL_RSA_EXPORT_WITH_RC4_40_MD5(
+            0x0003, false, "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+                           "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_RSA_EXPORT, B_DES_40, M_MD5, H_NONE),
+    SSL_DH_anon_EXPORT_WITH_RC4_40_MD5(
+            0x0017, false, "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+                           "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_DH_ANON, B_DES_40, M_MD5, H_NONE),
+
+    // no traffic encryption cipher suites
+    TLS_RSA_WITH_NULL_SHA256(
+            0x003B, false, "TLS_RSA_WITH_NULL_SHA256", "",
+            ProtocolVersion.PROTOCOLS_OF_12,
+            K_RSA, B_NULL, M_SHA256, H_SHA256),
+    TLS_ECDHE_ECDSA_WITH_NULL_SHA(
+            0xC006, false, "TLS_ECDHE_ECDSA_WITH_NULL_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_ECDSA, B_NULL, M_SHA, H_SHA256),
+    TLS_ECDHE_RSA_WITH_NULL_SHA(
+            0xC010, false, "TLS_ECDHE_RSA_WITH_NULL_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDHE_RSA, B_NULL, M_SHA, H_SHA256),
+    SSL_RSA_WITH_NULL_SHA(
+            0x0002, false, "SSL_RSA_WITH_NULL_SHA",
+                           "TLS_RSA_WITH_NULL_SHA",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_RSA, B_NULL, M_SHA, H_SHA256),
+    TLS_ECDH_ECDSA_WITH_NULL_SHA(
+            0xC001, false, "TLS_ECDH_ECDSA_WITH_NULL_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ECDSA, B_NULL, M_SHA, H_SHA256),
+    TLS_ECDH_RSA_WITH_NULL_SHA(
+            0xC00B, false, "TLS_ECDH_RSA_WITH_NULL_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_RSA, B_NULL, M_SHA, H_SHA256),
+    TLS_ECDH_anon_WITH_NULL_SHA(
+            0xC015, false, "TLS_ECDH_anon_WITH_NULL_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_ECDH_ANON, B_NULL, M_SHA, H_SHA256),
+    SSL_RSA_WITH_NULL_MD5(
+            0x0001, false, "SSL_RSA_WITH_NULL_MD5",
+                           "TLS_RSA_WITH_NULL_MD5",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_RSA, B_NULL, M_MD5, H_SHA256),
+
+    // Definition of the CipherSuites that are not supported but the names
+    // are known.
+    TLS_CHACHA20_POLY1305_SHA256(                    // TLS 1.3
+            "TLS_CHACHA20_POLY1305_SHA256", 0x1303),
+    TLS_AES_128_CCM_SHA256(                          // TLS 1.3
+            "TLS_AES_128_CCM_SHA256", 0x1304),
+    TLS_AES_128_CCM_8_SHA256(                        // TLS 1.3
+            "TLS_AES_128_CCM_8_SHA256", 0x1305),
 
-    // Map Integer(id) -> CipherSuite
-    // contains all known CipherSuites
-    private static final Map<Integer,CipherSuite> idMap;
+    // remaining unsupported ciphersuites defined in RFC2246.
+    CS_0006("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",           0x0006),
+    CS_0007("SSL_RSA_WITH_IDEA_CBC_SHA",                    0x0007),
+    CS_000B("SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",         0x000b),
+    CS_000C("SSL_DH_DSS_WITH_DES_CBC_SHA",                  0x000c),
+    CS_000D("SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",             0x000d),
+    CS_000E("SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",         0x000e),
+    CS_000F("SSL_DH_RSA_WITH_DES_CBC_SHA",                  0x000f),
+    CS_0010("SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",             0x0010),
+
+    // SSL 3.0 Fortezza ciphersuites
+    CS_001C("SSL_FORTEZZA_DMS_WITH_NULL_SHA",               0x001c),
+    CS_001D("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA",       0x001d),
+
+    // 1024/56 bit exportable ciphersuites from expired internet draft
+    CS_0062("SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",          0x0062),
+    CS_0063("SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA",      0x0063),
+    CS_0064("SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",           0x0064),
+    CS_0065("SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA",       0x0065),
+    CS_0066("SSL_DHE_DSS_WITH_RC4_128_SHA",                 0x0066),
+
+    // Netscape old and new SSL 3.0 FIPS ciphersuites
+    // see http://www.mozilla.org/projects/security/pki/nss/ssl/fips-ssl-ciphersuites.html
+    CS_FFE0("NETSCAPE_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",      0xffe0),
+    CS_FFE1("NETSCAPE_RSA_FIPS_WITH_DES_CBC_SHA",           0xffe1),
+    CS_FEFE("SSL_RSA_FIPS_WITH_DES_CBC_SHA",                0xfefe),
+    CS_FEFF("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",           0xfeff),
+
+    // Unsupported Kerberos cipher suites from RFC 2712
+    CS_001E("TLS_KRB5_WITH_DES_CBC_SHA",                    0x001E),
+    CS_001F("TLS_KRB5_WITH_3DES_EDE_CBC_SHA",               0x001F),
+    CS_0020("TLS_KRB5_WITH_RC4_128_SHA",                    0x0020),
+    CS_0021("TLS_KRB5_WITH_IDEA_CBC_SHA",                   0x0021),
+    CS_0022("TLS_KRB5_WITH_DES_CBC_MD5",                    0x0022),
+    CS_0023("TLS_KRB5_WITH_3DES_EDE_CBC_MD5",               0x0023),
+    CS_0024("TLS_KRB5_WITH_RC4_128_MD5",                    0x0024),
+    CS_0025("TLS_KRB5_WITH_IDEA_CBC_MD5",                   0x0025),
+    CS_0026("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",          0x0026),
+    CS_0027("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",          0x0027),
+    CS_0028("TLS_KRB5_EXPORT_WITH_RC4_40_SHA",              0x0028),
+    CS_0029("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",          0x0029),
+    CS_002A("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",          0x002a),
+    CS_002B("TLS_KRB5_EXPORT_WITH_RC4_40_MD5",              0x002B),
+
+    // Unsupported cipher suites from RFC 4162
+    CS_0096("TLS_RSA_WITH_SEED_CBC_SHA",                    0x0096),
+    CS_0097("TLS_DH_DSS_WITH_SEED_CBC_SHA",                 0x0097),
+    CS_0098("TLS_DH_RSA_WITH_SEED_CBC_SHA",                 0x0098),
+    CS_0099("TLS_DHE_DSS_WITH_SEED_CBC_SHA",                0x0099),
+    CS_009A("TLS_DHE_RSA_WITH_SEED_CBC_SHA",                0x009a),
+    CS_009B("TLS_DH_anon_WITH_SEED_CBC_SHA",                0x009b),
+
+    // Unsupported cipher suites from RFC 4279
+    CS_008A("TLS_PSK_WITH_RC4_128_SHA",                     0x008a),
+    CS_008B("TLS_PSK_WITH_3DES_EDE_CBC_SHA",                0x008b),
+    CS_008C("TLS_PSK_WITH_AES_128_CBC_SHA",                 0x008c),
+    CS_008D("TLS_PSK_WITH_AES_256_CBC_SHA",                 0x008d),
+    CS_008E("TLS_DHE_PSK_WITH_RC4_128_SHA",                 0x008e),
+    CS_008F("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",            0x008f),
+    CS_0090("TLS_DHE_PSK_WITH_AES_128_CBC_SHA",             0x0090),
+    CS_0091("TLS_DHE_PSK_WITH_AES_256_CBC_SHA",             0x0091),
+    CS_0092("TLS_RSA_PSK_WITH_RC4_128_SHA",                 0x0092),
+    CS_0093("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",            0x0093),
+    CS_0094("TLS_RSA_PSK_WITH_AES_128_CBC_SHA",             0x0094),
+    CS_0095("TLS_RSA_PSK_WITH_AES_256_CBC_SHA",             0x0095),
+
+    // Unsupported cipher suites from RFC 4785
+    CS_002C("TLS_PSK_WITH_NULL_SHA",                        0x002c),
+    CS_002D("TLS_DHE_PSK_WITH_NULL_SHA",                    0x002d),
+    CS_002E("TLS_RSA_PSK_WITH_NULL_SHA",                    0x002e),
+
+    // Unsupported cipher suites from RFC 5246
+    CS_0030("TLS_DH_DSS_WITH_AES_128_CBC_SHA",              0x0030),
+    CS_0031("TLS_DH_RSA_WITH_AES_128_CBC_SHA",              0x0031),
+    CS_0036("TLS_DH_DSS_WITH_AES_256_CBC_SHA",              0x0036),
+    CS_0037("TLS_DH_RSA_WITH_AES_256_CBC_SHA",              0x0037),
+    CS_003E("TLS_DH_DSS_WITH_AES_128_CBC_SHA256",           0x003e),
+    CS_003F("TLS_DH_RSA_WITH_AES_128_CBC_SHA256",           0x003f),
+    CS_0068("TLS_DH_DSS_WITH_AES_256_CBC_SHA256",           0x0068),
+    CS_0069("TLS_DH_RSA_WITH_AES_256_CBC_SHA256",           0x0069),
+
+    // Unsupported cipher suites from RFC 5288
+    CS_00A0("TLS_DH_RSA_WITH_AES_128_GCM_SHA256",           0x00a0),
+    CS_00A1("TLS_DH_RSA_WITH_AES_256_GCM_SHA384",           0x00a1),
+    CS_00A4("TLS_DH_DSS_WITH_AES_128_GCM_SHA256",           0x00a4),
+    CS_00A5("TLS_DH_DSS_WITH_AES_256_GCM_SHA384",           0x00a5),
 
-    // Map String(name) -> CipherSuite
-    // contains only supported CipherSuites (i.e. allowed == true)
-    private static final Map<String,CipherSuite> nameMap;
+    // Unsupported cipher suites from RFC 5487
+    CS_00A8("TLS_PSK_WITH_AES_128_GCM_SHA256",              0x00a8),
+    CS_00A9("TLS_PSK_WITH_AES_256_GCM_SHA384",              0x00a9),
+    CS_00AA("TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",          0x00aa),
+    CS_00AB("TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",          0x00ab),
+    CS_00AC("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",          0x00ac),
+    CS_00AD("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",          0x00ad),
+    CS_00AE("TLS_PSK_WITH_AES_128_CBC_SHA256",              0x00ae),
+    CS_00AF("TLS_PSK_WITH_AES_256_CBC_SHA384",              0x00af),
+    CS_00B0("TLS_PSK_WITH_NULL_SHA256",                     0x00b0),
+    CS_00B1("TLS_PSK_WITH_NULL_SHA384",                     0x00b1),
+    CS_00B2("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",          0x00b2),
+    CS_00B3("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",          0x00b3),
+    CS_00B4("TLS_DHE_PSK_WITH_NULL_SHA256",                 0x00b4),
+    CS_00B5("TLS_DHE_PSK_WITH_NULL_SHA384",                 0x00b5),
+    CS_00B6("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",          0x00b6),
+    CS_00B7("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",          0x00b7),
+    CS_00B8("TLS_RSA_PSK_WITH_NULL_SHA256",                 0x00b8),
+    CS_00B9("TLS_RSA_PSK_WITH_NULL_SHA384",                 0x00b9),
+
+    // Unsupported cipher suites from RFC 5932
+    CS_0041("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",            0x0041),
+    CS_0042("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",         0x0042),
+    CS_0043("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",         0x0043),
+    CS_0044("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",        0x0044),
+    CS_0045("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",        0x0045),
+    CS_0046("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",        0x0046),
+    CS_0084("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",            0x0084),
+    CS_0085("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",         0x0085),
+    CS_0086("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",         0x0086),
+    CS_0087("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",        0x0087),
+    CS_0088("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",        0x0088),
+    CS_0089("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",        0x0089),
+    CS_00BA("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",         0x00ba),
+    CS_00BB("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",      0x00bb),
+    CS_00BC("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",      0x00bc),
+    CS_00BD("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",     0x00bd),
+    CS_00BE("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",     0x00be),
+    CS_00BF("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",     0x00bf),
+    CS_00C0("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",         0x00c0),
+    CS_00C1("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",      0x00c1),
+    CS_00C2("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",      0x00c2),
+    CS_00C3("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",     0x00c3),
+    CS_00C4("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",     0x00c4),
+    CS_00C5("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",     0x00c5),
+
+    // TLS Fallback Signaling Cipher Suite Value (SCSV) RFC 7507
+    CS_5600("TLS_FALLBACK_SCSV",                            0x5600),
+
+    // Unsupported cipher suites from RFC 5054
+    CS_C01A("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",            0xc01a),
+    CS_C01B("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",        0xc01b),
+    CS_C01C("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",        0xc01c),
+    CS_C01D("TLS_SRP_SHA_WITH_AES_128_CBC_SHA",             0xc01d),
+    CS_C01E("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",         0xc01e),
+    CS_C01F("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",         0xc01f),
+    CS_C020("TLS_SRP_SHA_WITH_AES_256_CBC_SHA",             0xc020),
+    CS_C021("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",         0xc021),
+    CS_C022("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",         0xc022),
+
+    // Unsupported cipher suites from RFC 5489
+    CS_C033("TLS_ECDHE_PSK_WITH_RC4_128_SHA",               0xc033),
+    CS_C034("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",          0xc034),
+    CS_C035("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",           0xc035),
+    CS_C036("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",           0xc036),
+    CS_C037("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",        0xc037),
+    CS_C038("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",        0xc038),
+    CS_C039("TLS_ECDHE_PSK_WITH_NULL_SHA",                  0xc039),
+    CS_C03A("TLS_ECDHE_PSK_WITH_NULL_SHA256",               0xc03a),
+    CS_C03B("TLS_ECDHE_PSK_WITH_NULL_SHA384",               0xc03b),
 
-    // Protocol defined CipherSuite name, e.g. SSL_RSA_WITH_RC4_128_MD5
-    // we use TLS_* only for new CipherSuites, still SSL_* for old ones
-    final String name;
+    // Unsupported cipher suites from RFC 6209
+    CS_C03C("TLS_RSA_WITH_ARIA_128_CBC_SHA256",             0xc03c),
+    CS_C03D("TLS_RSA_WITH_ARIA_256_CBC_SHA384",             0xc03d),
+    CS_C03E("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256",          0xc03e),
+    CS_C03F("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384",          0xc03f),
+    CS_C040("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256",          0xc040),
+    CS_C041("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384",          0xc041),
+    CS_C042("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256",         0xc042),
+    CS_C043("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384",         0xc043),
+    CS_C044("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256",         0xc044),
+    CS_C045("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384",         0xc045),
+    CS_C046("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256",         0xc046),
+    CS_C047("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384",         0xc047),
+    CS_C048("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256",     0xc048),
+    CS_C049("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384",     0xc049),
+    CS_C04A("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256",      0xc04a),
+    CS_C04B("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384",      0xc04b),
+    CS_C04C("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256",       0xc04c),
+    CS_C04D("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384",       0xc04d),
+    CS_C04E("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256",        0xc04e),
+    CS_C04F("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384",        0xc04f),
+    CS_C050("TLS_RSA_WITH_ARIA_128_GCM_SHA256",             0xc050),
+    CS_C051("TLS_RSA_WITH_ARIA_256_GCM_SHA384",             0xc051),
+    CS_C052("TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256",         0xc052),
+    CS_C053("TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384",         0xc053),
+    CS_C054("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256",          0xc054),
+    CS_C055("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384",          0xc055),
+    CS_C056("TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256",         0xc056),
+    CS_C057("TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384",         0xc057),
+    CS_C058("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256",          0xc058),
+    CS_C059("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384",          0xc059),
+    CS_C05A("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256",         0xc05a),
+    CS_C05B("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384",         0xc05b),
+    CS_C05C("TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256",     0xc05c),
+    CS_C05D("TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384",     0xc05d),
+    CS_C05E("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256",      0xc05e),
+    CS_C05F("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384",      0xc05f),
+    CS_C060("TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256",       0xc060),
+    CS_C061("TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384",       0xc061),
+    CS_C062("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256",        0xc062),
+    CS_C063("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384",        0xc063),
+    CS_C064("TLS_PSK_WITH_ARIA_128_CBC_SHA256",             0xc064),
+    CS_C065("TLS_PSK_WITH_ARIA_256_CBC_SHA384",             0xc065),
+    CS_C066("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256",         0xc066),
+    CS_C067("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384",         0xc067),
+    CS_C068("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256",         0xc068),
+    CS_C069("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384",         0xc069),
+    CS_C06A("TLS_PSK_WITH_ARIA_128_GCM_SHA256",             0xc06a),
+    CS_C06B("TLS_PSK_WITH_ARIA_256_GCM_SHA384",             0xc06b),
+    CS_C06C("TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256",         0xc06c),
+    CS_C06D("TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384",         0xc06d),
+    CS_C06E("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256",         0xc06e),
+    CS_C06F("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384",         0xc06f),
+    CS_C070("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256",       0xc070),
+    CS_C071("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384",       0xc071),
 
-    // id in 16 bit MSB format, i.e. 0x0004 for SSL_RSA_WITH_RC4_128_MD5
+    // Unsupported cipher suites from RFC 6367
+    CS_C072("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc072),
+    CS_C073("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc073),
+    CS_C074("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",  0xc074),
+    CS_C075("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",  0xc075),
+    CS_C076("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",   0xc076),
+    CS_C077("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",   0xc077),
+    CS_C078("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256",    0xc078),
+    CS_C079("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384",    0xc079),
+    CS_C07A("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",         0xc07a),
+    CS_C07B("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",         0xc07b),
+    CS_C07C("TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",     0xc07c),
+    CS_C07D("TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",     0xc07d),
+    CS_C07E("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256",      0xc07e),
+    CS_C07F("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384",      0xc07f),
+    CS_C080("TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256",     0xc080),
+    CS_C081("TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384",     0xc081),
+    CS_C082("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256",      0xc082),
+    CS_C083("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384",      0xc083),
+    CS_C084("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",     0xc084),
+    CS_C085("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",     0xc085),
+    CS_C086("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc086),
+    CS_C087("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc087),
+    CS_C088("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",  0xc088),
+    CS_C089("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",  0xc089),
+    CS_C08A("TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",   0xc08a),
+    CS_C08B("TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",   0xc08b),
+    CS_C08C("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256",    0xc08c),
+    CS_C08D("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384",    0xc08d),
+    CS_C08E("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",         0xc08e),
+    CS_C08F("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",         0xc08f),
+    CS_C090("TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256",     0xc090),
+    CS_C091("TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384",     0xc091),
+    CS_C092("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",     0xc092),
+    CS_C093("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",     0xc093),
+    CS_C094("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",         0xc094),
+    CS_C095("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",         0xc095),
+    CS_C096("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",     0xc096),
+    CS_C097("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",     0xc097),
+    CS_C098("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",     0xc098),
+    CS_C099("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",     0xc099),
+    CS_C09A("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",   0xc09a),
+    CS_C09B("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",   0xc09b),
+
+    // Unsupported cipher suites from RFC 6655
+    CS_C09C("TLS_RSA_WITH_AES_128_CCM",                     0xc09c),
+    CS_C09D("TLS_RSA_WITH_AES_256_CCM",                     0xc09d),
+    CS_C09E("TLS_DHE_RSA_WITH_AES_128_CCM",                 0xc09e),
+    CS_C09F("TLS_DHE_RSA_WITH_AES_256_CCM",                 0xc09f),
+    CS_C0A0("TLS_RSA_WITH_AES_128_CCM_8",                   0xc0A0),
+    CS_C0A1("TLS_RSA_WITH_AES_256_CCM_8",                   0xc0A1),
+    CS_C0A2("TLS_DHE_RSA_WITH_AES_128_CCM_8",               0xc0A2),
+    CS_C0A3("TLS_DHE_RSA_WITH_AES_256_CCM_8",               0xc0A3),
+    CS_C0A4("TLS_PSK_WITH_AES_128_CCM",                     0xc0A4),
+    CS_C0A5("TLS_PSK_WITH_AES_256_CCM",                     0xc0A5),
+    CS_C0A6("TLS_DHE_PSK_WITH_AES_128_CCM",                 0xc0A6),
+    CS_C0A7("TLS_DHE_PSK_WITH_AES_256_CCM",                 0xc0A7),
+    CS_C0A8("TLS_PSK_WITH_AES_128_CCM_8",                   0xc0A8),
+    CS_C0A9("TLS_PSK_WITH_AES_256_CCM_8",                   0xc0A9),
+    CS_C0AA("TLS_PSK_DHE_WITH_AES_128_CCM_8",               0xc0Aa),
+    CS_C0AB("TLS_PSK_DHE_WITH_AES_256_CCM_8",               0xc0Ab),
+
+    // Unsupported cipher suites from RFC 7251
+    CS_C0AC("TLS_ECDHE_ECDSA_WITH_AES_128_CCM",             0xc0Ac),
+    CS_C0AD("TLS_ECDHE_ECDSA_WITH_AES_256_CCM",             0xc0Ad),
+    CS_C0AE("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",           0xc0Ae),
+    CS_C0AF("TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",           0xc0Af),
+
+    C_NULL("SSL_NULL_WITH_NULL_NULL", 0x0000);
+
     final int id;
-
-    // priority for the internal default preference order. the higher the
-    // better. Each supported CipherSuite *must* have a unique priority.
-    // Ciphersuites with priority >= DEFAULT_SUITES_PRIORITY are enabled
-    // by default
-    final int priority;
+    final boolean isDefaultEnabled;
+    final String name;
+    final List<String> aliases;
+    final List<ProtocolVersion> supportedProtocols;
+    final KeyExchange keyExchange;
+    final SSLCipher bulkCipher;
+    final MacAlg macAlg;
+    final HashAlg hashAlg;
 
-    // key exchange, bulk cipher, mac and prf algorithms. See those
-    // classes below.
-    final KeyExchange keyExchange;
-    final BulkCipher cipher;
-    final MacAlg macAlg;
-    final PRF prfAlg;
-
-    // whether a CipherSuite qualifies as exportable under 512/40 bit rules.
-    // TLS 1.1+ (RFC 4346) must not negotiate to these suites.
     final boolean exportable;
 
-    // true iff implemented and enabled at compile time
-    final boolean allowed;
+    // known but unsupported cipher suite
+    private CipherSuite(String name, int id) {
+        this(id, false, name, "",
+                ProtocolVersion.PROTOCOLS_EMPTY, null, null, null, null);
+    }
+
+    // TLS 1.3 cipher suite
+    private CipherSuite(int id, boolean isDefaultEnabled,
+            String name, ProtocolVersion[] supportedProtocols,
+            SSLCipher bulkCipher, HashAlg hashAlg) {
+        this(id, isDefaultEnabled, name, "",
+                supportedProtocols, null, bulkCipher, M_NULL, hashAlg);
+    }
 
-    // obsoleted since protocol version
-    //
-    // TLS version is used.  If checking DTLS versions, please map to
-    // TLS version firstly.  See ProtocolVersion.mapToTLSProtocol().
-    final int obsoleted;
+    private CipherSuite(int id, boolean isDefaultEnabled,
+            String name, String aliases,
+            ProtocolVersion[] supportedProtocols,
+            KeyExchange keyExchange, SSLCipher cipher,
+            MacAlg macAlg, HashAlg hashAlg) {
+        this.id = id;
+        this.isDefaultEnabled = isDefaultEnabled;
+        this.name = name;
+        if (aliases.isEmpty()) {
+            this.aliases = Arrays.asList(aliases.split(","));
+        } else {
+            this.aliases = Collections.emptyList();
+        }
+        this.supportedProtocols = Arrays.asList(supportedProtocols);
+        this.keyExchange = keyExchange;
+        this.bulkCipher = cipher;
+        this.macAlg = macAlg;
+        this.hashAlg = hashAlg;
 
-    // supported since protocol version (TLS version is used)
-    //
-    // TLS version is used.  If checking DTLS versions, please map to
-    // TLS version firstly.  See ProtocolVersion.mapToTLSProtocol().
-    final int supported;
+        this.exportable = (cipher == null ? false : cipher.exportable);
+    }
+
+    static CipherSuite nameOf(String ciperSuiteName) {
+        for (CipherSuite cs : CipherSuite.values()) {
+            if (cs.name.equals(ciperSuiteName) ||
+                    cs.aliases.contains(ciperSuiteName)) {
+                return cs;
+            }
+        }
+
+        return null;
+    }
 
-    /**
-     * Constructor for implemented CipherSuites.
-     */
-    private CipherSuite(String name, int id, int priority,
-            KeyExchange keyExchange, BulkCipher cipher, MacAlg mac,
-            boolean allowed, int obsoleted, int supported, PRF prfAlg) {
-        this.name = name;
-        this.id = id;
-        this.priority = priority;
-        this.keyExchange = keyExchange;
-        this.cipher = cipher;
-        this.macAlg = mac;
-        this.exportable = cipher.exportable;
-        allowed &= keyExchange.allowed;
-        allowed &= cipher.allowed;
-        this.allowed = allowed;
-        this.obsoleted = obsoleted;
-        this.supported = supported;
-        this.prfAlg = prfAlg;
+    static CipherSuite valueOf(int id) {
+        for (CipherSuite cs : CipherSuite.values()) {
+            if (cs.id == id) {
+                return cs;
+            }
+        }
+
+        return null;
+    }
+
+    static String nameOf(int id) {
+        for (CipherSuite cs : CipherSuite.values()) {
+            if (cs.id == id) {
+                return cs.name;
+            }
+        }
+
+        return "UNKNOWN-CIPHER-SUITE(" + Utilities.byte16HexString(id) + ")";
+    }
+
+    static Collection<CipherSuite> allowedCipherSuites() {
+        Collection<CipherSuite> cipherSuites = new LinkedList<>();
+        for (CipherSuite cs : CipherSuite.values()) {
+            if (!cs.supportedProtocols.isEmpty()) {
+                cipherSuites.add(cs);
+            } else {
+                // values() is ordered, remaining cipher suites are
+                // not supported.
+                break;
+            }
+        }
+        return cipherSuites;
+    }
+
+    static Collection<CipherSuite> defaultCipherSuites() {
+        Collection<CipherSuite> cipherSuites = new LinkedList<>();
+        for (CipherSuite cs : CipherSuite.values()) {
+            if (cs.isDefaultEnabled) {
+                cipherSuites.add(cs);
+            } else {
+                // values() is ordered, remaining cipher suites are
+                // not enabled.
+                break;
+            }
+        }
+        return cipherSuites;
     }
 
     /**
-     * Constructor for unimplemented CipherSuites.
+     * Validates and converts an array of cipher suite names.
+     *
+     * @throws IllegalArgumentException when one or more of the ciphers named
+     *         by the parameter is not supported, or when the parameter is null.
      */
-    private CipherSuite(String name, int id) {
-        this.name = name;
-        this.id = id;
-        this.allowed = false;
+    static List<CipherSuite> validValuesOf(String[] names) {
+        if (names == null) {
+            throw new IllegalArgumentException("CipherSuites cannot be null");
+        }
+
+        List<CipherSuite> cipherSuites = new ArrayList<>(names.length);
+        for (String name : names) {
+            if (name == null) {
+                throw new IllegalArgumentException(
+                    "The specified CipherSuites array contain null element");
+            }
 
-        this.priority = 0;
-        this.keyExchange = null;
-        this.cipher = null;
-        this.macAlg = null;
-        this.exportable = false;
-        this.obsoleted = ProtocolVersion.LIMIT_MAX_VALUE;
-        this.supported = ProtocolVersion.LIMIT_MIN_VALUE;
-        this.prfAlg = P_NONE;
+            boolean found = false;
+            for (CipherSuite cs : CipherSuite.values()) {
+                if (!cs.supportedProtocols.isEmpty()) {
+                    if (cs.name.equals(name) ||
+                            cs.aliases.contains(name)) {
+                        cipherSuites.add(cs);
+                        found = true;
+                        break;
+                    }
+                } else {
+                    // values() is ordered, remaining cipher suites are
+                    // not supported.
+                    break;
+                }
+            }
+            if (!found) {
+                throw new IllegalArgumentException(
+                        "Unsupported CipherSuite: "  + name);
+            }
+        }
+
+        return Collections.unmodifiableList(cipherSuites);
     }
 
-    /**
-     * Return whether this CipherSuite is available for use. A
-     * CipherSuite may be unavailable even if it is supported
-     * (i.e. allowed == true) if the required JCE cipher is not installed.
-     */
+    static String[] namesOf(List<CipherSuite> cipherSuites) {
+        String[] names = new String[cipherSuites.size()];
+        int i = 0;
+        for (CipherSuite cipherSuite : cipherSuites) {
+            names[i++] = cipherSuite.name;
+        }
+
+        return names;
+    }
+
     boolean isAvailable() {
-        return allowed && keyExchange.isAvailable() && cipher.isAvailable();
+        // Note: keyExchange is null for TLS 1.3 CipherSuites.
+        return !supportedProtocols.isEmpty() &&
+                (keyExchange == null || keyExchange.isAvailable()) &&
+                bulkCipher != null && bulkCipher.isAvailable();
+    }
+
+    public boolean supports(ProtocolVersion protocolVersion) {
+        return supportedProtocols.contains(protocolVersion);
     }
 
     boolean isNegotiable() {
-        return this != C_SCSV && isAvailable();
+        return this != TLS_EMPTY_RENEGOTIATION_INFO_SCSV && isAvailable();
     }
 
-    // See also CipherBox.calculatePacketSize().
+    boolean isAnonymous() {
+        return (keyExchange != null && keyExchange.isAnonymous);
+    }
+
+    // See also SSLWriteCipher.calculatePacketSize().
     int calculatePacketSize(int fragmentSize,
             ProtocolVersion protocolVersion, boolean isDTLS) {
-
         int packetSize = fragmentSize;
-        if (cipher != B_NULL) {
-            int blockSize = cipher.ivSize;
-            switch (cipher.cipherType) {
+        if (bulkCipher != null && bulkCipher != B_NULL) {
+            int blockSize = bulkCipher.ivSize;
+            switch (bulkCipher.cipherType) {
                 case BLOCK_CIPHER:
                     packetSize += macAlg.size;
                     packetSize += 1;        // 1 byte padding length field
@@ -198,13 +966,17 @@
                     }
 
                     break;
-            case AEAD_CIPHER:
-                packetSize += cipher.ivSize - cipher.fixedIvSize;   // record IV
-                packetSize += cipher.tagSize;
+                case AEAD_CIPHER:
+                    if (protocolVersion == ProtocolVersion.TLS12 ||
+                            protocolVersion == ProtocolVersion.DTLS12) {
+                        packetSize +=
+                                bulkCipher.ivSize - bulkCipher.fixedIvSize;
+                    }
+                    packetSize += bulkCipher.tagSize;
 
-                break;
-            default:    // NULL_CIPHER or STREAM_CIPHER
-                packetSize += macAlg.size;
+                    break;
+                default:    // NULL_CIPHER or STREAM_CIPHER
+                    packetSize += macAlg.size;
             }
         }
 
@@ -215,29 +987,28 @@
     // See also CipherBox.calculateFragmentSize().
     int calculateFragSize(int packetLimit,
             ProtocolVersion protocolVersion, boolean isDTLS) {
-
         int fragSize = packetLimit -
                 (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize);
-        if (cipher != B_NULL) {
-            int blockSize = cipher.ivSize;
-            switch (cipher.cipherType) {
-            case BLOCK_CIPHER:
-                if (protocolVersion.useTLS11PlusSpec()) {
-                    fragSize -= blockSize;              // explicit IV
-                }
-                fragSize -= (fragSize % blockSize);     // cannot hold a block
-                // No padding for a maximum fragment.
-                fragSize -= 1;        // 1 byte padding length field: 0x00
-                fragSize -= macAlg.size;
+        if (bulkCipher != null && bulkCipher != B_NULL) {
+            int blockSize = bulkCipher.ivSize;
+            switch (bulkCipher.cipherType) {
+                case BLOCK_CIPHER:
+                    if (protocolVersion.useTLS11PlusSpec()) {
+                        fragSize -= blockSize;          // explicit IV
+                    }
+                    fragSize -= (fragSize % blockSize); // cannot hold a block
+                    // No padding for a maximum fragment.
+                    fragSize -= 1;        // 1 byte padding length field: 0x00
+                    fragSize -= macAlg.size;
 
-                break;
-            case AEAD_CIPHER:
-                fragSize -= cipher.tagSize;
-                fragSize -= cipher.ivSize - cipher.fixedIvSize;     // record IV
+                    break;
+                case AEAD_CIPHER:
+                    fragSize -= bulkCipher.tagSize;
+                    fragSize -= bulkCipher.ivSize - bulkCipher.fixedIvSize;
 
-                break;
-            default:    // NULL_CIPHER or STREAM_CIPHER
-                fragSize -= macAlg.size;
+                    break;
+                default:    // NULL_CIPHER or STREAM_CIPHER
+                    fragSize -= macAlg.size;
             }
         }
 
@@ -245,172 +1016,48 @@
     }
 
     /**
-     * Compares CipherSuites based on their priority. Has the effect of
-     * sorting CipherSuites when put in a sorted collection, which is
-     * used by CipherSuiteList. Follows standard Comparable contract.
-     *
-     * Note that for unsupported CipherSuites parsed from a handshake
-     * message we violate the equals() contract.
-     */
-    @Override
-    public int compareTo(CipherSuite o) {
-        return o.priority - priority;
-    }
-
-    /**
-     * Returns this.name.
-     */
-    @Override
-    public String toString() {
-        return name;
-    }
-
-    /**
-     * Return a CipherSuite for the given name. The returned CipherSuite
-     * is supported by this implementation but may not actually be
-     * currently useable. See isAvailable().
-     *
-     * @exception IllegalArgumentException if the CipherSuite is unknown or
-     * unsupported.
-     */
-    static CipherSuite valueOf(String s) {
-        if (s == null) {
-            throw new IllegalArgumentException("Name must not be null");
-        }
-
-        CipherSuite c = nameMap.get(s);
-        if ((c == null) || (c.allowed == false)) {
-            throw new IllegalArgumentException("Unsupported ciphersuite " + s);
-        }
-
-        return c;
-    }
-
-    /**
-     * Return a CipherSuite with the given ID. A temporary object is
-     * constructed if the ID is unknown. Use isAvailable() to verify that
-     * the CipherSuite can actually be used.
-     */
-    static CipherSuite valueOf(int id1, int id2) {
-        id1 &= 0xff;
-        id2 &= 0xff;
-        int id = (id1 << 8) | id2;
-        CipherSuite c = idMap.get(id);
-        if (c == null) {
-            String h1 = Integer.toString(id1, 16);
-            String h2 = Integer.toString(id2, 16);
-            c = new CipherSuite("Unknown 0x" + h1 + ":0x" + h2, id);
-        }
-        return c;
-    }
-
-    // for use by SSLContextImpl only
-    static Collection<CipherSuite> allowedCipherSuites() {
-        return nameMap.values();
-    }
-
-    /*
-     * Use this method when all of the values need to be specified.
-     * This is primarily used when defining a new ciphersuite for
-     * TLS 1.2+ that doesn't use the "default" PRF.
-     */
-    private static void add(String name, int id, int priority,
-            KeyExchange keyExchange, BulkCipher cipher, MacAlg mac,
-            boolean allowed, int obsoleted, int supported, PRF prf) {
-
-        CipherSuite c = new CipherSuite(name, id, priority, keyExchange,
-            cipher, mac, allowed, obsoleted, supported, prf);
-        if (idMap.put(id, c) != null) {
-            throw new RuntimeException("Duplicate ciphersuite definition: "
-                                        + id + ", " + name);
-        }
-        if (c.allowed) {
-            if (nameMap.put(name, c) != null) {
-                throw new RuntimeException("Duplicate ciphersuite definition: "
-                                            + id + ", " + name);
-            }
-        }
-    }
-
-    /*
-     * Use this method when there is no lower protocol limit where this
-     * suite can be used, and the PRF is P_SHA256.  That is, the
-     * existing ciphersuites.  From RFC 5246:
-     *
-     *     All cipher suites in this document use P_SHA256.
-     */
-    private static void add(String name, int id, int priority,
-            KeyExchange keyExchange, BulkCipher cipher, MacAlg mac,
-            boolean allowed, int obsoleted) {
-        PRF prf = obsoleted < ProtocolVersion.TLS12.v ? P_NONE : P_SHA256;
-
-        add(name, id, priority, keyExchange, cipher, mac, allowed, obsoleted,
-            ProtocolVersion.LIMIT_MIN_VALUE, prf);
-    }
-
-    /*
-     * Use this method when there is no upper protocol limit.  That is,
-     * suites which have not been obsoleted.
-     */
-    private static void add(String name, int id, int priority,
-            KeyExchange keyExchange, BulkCipher cipher, MacAlg mac,
-            boolean allowed) {
-        add(name, id, priority, keyExchange, cipher, mac, allowed,
-                ProtocolVersion.LIMIT_MAX_VALUE);
-    }
-
-    /*
-     * Use this method to define an unimplemented suite.  This provides
-     * a number<->name mapping that can be used for debugging.
-     */
-    private static void add(String name, int id) {
-        CipherSuite c = new CipherSuite(name, id);
-        if (idMap.put(id, c) != null) {
-            throw new RuntimeException("Duplicate ciphersuite definition: "
-                                        + id + ", " + name);
-        }
-    }
-
-    /**
      * An SSL/TLS key exchange algorithm.
      */
     static enum KeyExchange {
+        K_NULL          ("NULL",           false, true,   NAMED_GROUP_NONE),
+        K_RSA           ("RSA",            true,  false,  NAMED_GROUP_NONE),
+        K_RSA_EXPORT    ("RSA_EXPORT",     true,  false,  NAMED_GROUP_NONE),
+        K_DH_RSA        ("DH_RSA",         false, false,  NAMED_GROUP_NONE),
+        K_DH_DSS        ("DH_DSS",         false, false,  NAMED_GROUP_NONE),
+        K_DHE_DSS       ("DHE_DSS",        true,  false,  NAMED_GROUP_FFDHE),
+        K_DHE_DSS_EXPORT("DHE_DSS_EXPORT", true,  false,  NAMED_GROUP_NONE),
+        K_DHE_RSA       ("DHE_RSA",        true,  false,  NAMED_GROUP_FFDHE),
+        K_DHE_RSA_EXPORT("DHE_RSA_EXPORT", true,  false,  NAMED_GROUP_NONE),
+        K_DH_ANON       ("DH_anon",        true,  true,   NAMED_GROUP_FFDHE),
+        K_DH_ANON_EXPORT("DH_anon_EXPORT", true,  true,   NAMED_GROUP_NONE),
 
-        // key exchange algorithms
-        K_NULL       ("NULL",       false,      NAMED_GROUP_NONE),
-        K_RSA        ("RSA",        true,       NAMED_GROUP_NONE),
-        K_RSA_EXPORT ("RSA_EXPORT", true,       NAMED_GROUP_NONE),
-        K_DH_RSA     ("DH_RSA",     false,      NAMED_GROUP_NONE),
-        K_DH_DSS     ("DH_DSS",     false,      NAMED_GROUP_NONE),
-        K_DHE_DSS    ("DHE_DSS",    true,       NAMED_GROUP_FFDHE),
-        K_DHE_RSA    ("DHE_RSA",    true,       NAMED_GROUP_FFDHE),
-        K_DH_ANON    ("DH_anon",    true,       NAMED_GROUP_FFDHE),
-
-        K_ECDH_ECDSA ("ECDH_ECDSA",  ALLOW_ECC, NAMED_GROUP_ECDHE),
-        K_ECDH_RSA   ("ECDH_RSA",    ALLOW_ECC, NAMED_GROUP_ECDHE),
-        K_ECDHE_ECDSA("ECDHE_ECDSA", ALLOW_ECC, NAMED_GROUP_ECDHE),
-        K_ECDHE_RSA  ("ECDHE_RSA",   ALLOW_ECC, NAMED_GROUP_ECDHE),
-        K_ECDH_ANON  ("ECDH_anon",   ALLOW_ECC, NAMED_GROUP_ECDHE),
-
-        // Kerberos cipher suites
-        K_KRB5       ("KRB5", true,             NAMED_GROUP_NONE),
-        K_KRB5_EXPORT("KRB5_EXPORT", true,      NAMED_GROUP_NONE),
+        K_ECDH_ECDSA    ("ECDH_ECDSA",     true,  false,  NAMED_GROUP_ECDHE),
+        K_ECDH_RSA      ("ECDH_RSA",       true,  false,  NAMED_GROUP_ECDHE),
+        K_ECDHE_ECDSA   ("ECDHE_ECDSA",    true,  false,  NAMED_GROUP_ECDHE),
+        K_ECDHE_RSA     ("ECDHE_RSA",      true,  false,  NAMED_GROUP_ECDHE),
+        K_ECDH_ANON     ("ECDH_anon",      true,  true,   NAMED_GROUP_ECDHE),
 
         // renegotiation protection request signaling cipher suite
-        K_SCSV       ("SCSV",        true,      NAMED_GROUP_NONE);
+        K_SCSV          ("SCSV",           true,  true,   NAMED_GROUP_NONE);
 
         // name of the key exchange algorithm, e.g. DHE_DSS
         final String name;
         final boolean allowed;
         final NamedGroupType groupType;
         private final boolean alwaysAvailable;
+        private final boolean isAnonymous;
 
-        KeyExchange(String name, boolean allowed, NamedGroupType groupType) {
+        KeyExchange(String name, boolean allowed,
+                boolean isAnonymous, NamedGroupType groupType) {
             this.name = name;
-            this.allowed = allowed;
+            if (groupType == NAMED_GROUP_ECDHE) {
+                this.allowed = JsseJce.ALLOW_ECC;
+            } else {
+                this.allowed = allowed;
+            }
             this.groupType = groupType;
-            this.alwaysAvailable = allowed &&
-                (!name.startsWith("EC")) && (!name.startsWith("KRB"));
+            this.alwaysAvailable = allowed && (!name.startsWith("EC"));
+            this.isAnonymous = isAnonymous;
         }
 
         boolean isAvailable() {
@@ -420,8 +1067,6 @@
 
             if (groupType == NAMED_GROUP_ECDHE) {
                 return (allowed && JsseJce.isEcAvailable());
-            } else if (name.startsWith("KRB")) {
-                return (allowed && JsseJce.isKerberosAvailable());
             } else {
                 return allowed;
             }
@@ -433,192 +1078,6 @@
         }
     }
 
-    static enum CipherType {
-        NULL_CIPHER,           // null cipher
-        STREAM_CIPHER,         // stream cipher
-        BLOCK_CIPHER,          // block cipher in CBC mode
-        AEAD_CIPHER            // AEAD cipher
-    }
-
-    /**
-     * An SSL/TLS bulk cipher algorithm. One instance per combination of
-     * cipher and key length.
-     *
-     * Also contains a factory method to obtain in initialized CipherBox
-     * for this algorithm.
-     */
-    static enum BulkCipher {
-
-        // export strength ciphers
-        B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true),
-        B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true),
-        B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false),
-        B_DES_40(CIPHER_DES,  BLOCK_CIPHER, 5, 8, 8, 0, true),
-
-        // domestic strength ciphers
-        B_RC4_128(CIPHER_RC4, STREAM_CIPHER, 16, 0, 0, true),
-        B_DES(CIPHER_DES, BLOCK_CIPHER, 8, 8, 0, true),
-        B_3DES(CIPHER_3DES, BLOCK_CIPHER, 24, 8, 0, true),
-        B_IDEA("IDEA", BLOCK_CIPHER, 16, 8, 0, false),
-        B_AES_128(CIPHER_AES, BLOCK_CIPHER, 16, 16, 0, true),
-        B_AES_256(CIPHER_AES, BLOCK_CIPHER, 32, 16, 0, true),
-        B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 12, 4, true),
-        B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 12, 4, true);
-
-        // descriptive name including key size, e.g. AES/128
-        final String description;
-
-        // JCE cipher transformation string, e.g. AES/CBC/NoPadding
-        final String transformation;
-
-        // algorithm name, e.g. AES
-        final String algorithm;
-
-        // supported and compile time enabled. Also see isAvailable()
-        final boolean allowed;
-
-        // number of bytes of entropy in the key
-        final int keySize;
-
-        // length of the actual cipher key in bytes.
-        // for non-exportable ciphers, this is the same as keySize
-        final int expandedKeySize;
-
-        // size of the IV
-        final int ivSize;
-
-        // size of fixed IV
-        //
-        // record_iv_length = ivSize - fixedIvSize
-        final int fixedIvSize;
-
-        // exportable under 512/40 bit rules
-        final boolean exportable;
-
-        // Is the cipher algorithm of Cipher Block Chaining (CBC) mode?
-        final CipherType cipherType;
-
-        // size of the authentication tag, only applicable to cipher suites in
-        // Galois Counter Mode (GCM)
-        //
-        // As far as we know, all supported GCM cipher suites use 128-bits
-        // authentication tags.
-        final int tagSize = 16;
-
-        // The secure random used to detect the cipher availability.
-        private static final SecureRandom secureRandom;
-
-        // runtime availability
-        private final boolean isAvailable;
-
-        static {
-            try {
-                secureRandom = JsseJce.getSecureRandom();
-            } catch (KeyManagementException kme) {
-                throw new RuntimeException(kme);
-            }
-        }
-
-        BulkCipher(String transformation, CipherType cipherType, int keySize,
-                int expandedKeySize, int ivSize,
-                int fixedIvSize, boolean allowed) {
-
-            this.transformation = transformation;
-            String[] splits = transformation.split("/");
-            this.algorithm = splits[0];
-            this.cipherType = cipherType;
-            this.description = this.algorithm + "/" + (keySize << 3);
-            this.keySize = keySize;
-            this.ivSize = ivSize;
-            this.fixedIvSize = fixedIvSize;
-            this.allowed = allowed;
-
-            this.expandedKeySize = expandedKeySize;
-            this.exportable = true;
-
-            // availability of this bulk cipher
-            //
-            // Currently all supported ciphers except AES are always available
-            // via the JSSE internal implementations. We also assume AES/128 of
-            // CBC mode is always available since it is shipped with the SunJCE
-            // provider.  However, AES/256 is unavailable when the default JCE
-            // policy jurisdiction files are installed because of key length
-            // restrictions.
-            this.isAvailable =
-                    allowed ? isUnlimited(keySize, transformation) : false;
-        }
-
-        BulkCipher(String transformation, CipherType cipherType, int keySize,
-                int ivSize, int fixedIvSize, boolean allowed) {
-            this.transformation = transformation;
-            String[] splits = transformation.split("/");
-            this.algorithm = splits[0];
-            this.cipherType = cipherType;
-            this.description = this.algorithm + "/" + (keySize << 3);
-            this.keySize = keySize;
-            this.ivSize = ivSize;
-            this.fixedIvSize = fixedIvSize;
-            this.allowed = allowed;
-
-            this.expandedKeySize = keySize;
-            this.exportable = false;
-
-            // availability of this bulk cipher
-            //
-            // Currently all supported ciphers except AES are always available
-            // via the JSSE internal implementations. We also assume AES/128 of
-            // CBC mode is always available since it is shipped with the SunJCE
-            // provider.  However, AES/256 is unavailable when the default JCE
-            // policy jurisdiction files are installed because of key length
-            // restrictions.
-            this.isAvailable =
-                    allowed ? isUnlimited(keySize, transformation) : false;
-        }
-
-        /**
-         * Return an initialized CipherBox for this BulkCipher.
-         * IV must be null for stream ciphers.
-         *
-         * @exception NoSuchAlgorithmException if anything goes wrong
-         */
-        CipherBox newCipher(ProtocolVersion version, SecretKey key,
-                IvParameterSpec iv, SecureRandom random,
-                boolean encrypt) throws NoSuchAlgorithmException {
-            return CipherBox.newCipherBox(version, this,
-                                            key, iv, random, encrypt);
-        }
-
-        /**
-         * Test if this bulk cipher is available. For use by CipherSuite.
-         */
-        boolean isAvailable() {
-            return this.isAvailable;
-        }
-
-        private static boolean isUnlimited(int keySize, String transformation) {
-            int keySizeInBits = keySize * 8;
-            if (keySizeInBits > 128) {    // need the JCE unlimited
-                                          // strength jurisdiction policy
-                try {
-                    if (Cipher.getMaxAllowedKeyLength(
-                            transformation) < keySizeInBits) {
-
-                        return false;
-                    }
-                } catch (Exception e) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        @Override
-        public String toString() {
-            return description;
-        }
-    }
-
     /**
      * An SSL/TLS key MAC algorithm.
      *
@@ -626,7 +1085,6 @@
      * for this algorithm.
      */
     static enum MacAlg {
-        // MACs
         M_NULL      ("NULL",     0,   0,   0),
         M_MD5       ("MD5",     16,  64,   9),
         M_SHA       ("SHA",     20,  64,   9),
@@ -653,17 +1111,6 @@
             this.minimalPaddingSize = minimalPaddingSize;
         }
 
-        /**
-         * Return an initialized MAC for this MacAlg. ProtocolVersion
-         * must either be SSL30 (SSLv3 custom MAC) or TLS10 (std. HMAC).
-         *
-         * @exception NoSuchAlgorithmException if anything goes wrong
-         */
-        MAC newMac(ProtocolVersion protocolVersion, SecretKey secret)
-                throws NoSuchAlgorithmException, InvalidKeyException {
-            return new MAC(this, protocolVersion, secret);
-        }
-
         @Override
         public String toString() {
             return name;
@@ -671,975 +1118,29 @@
     }
 
     /**
-     * PRFs (PseudoRandom Function) from TLS specifications.
-     *
-     * TLS 1.1- uses a single MD5/SHA1-based PRF algorithm for generating
-     * the necessary material.
+     * The hash algorithms used for PRF (PseudoRandom Function) or HKDF.
      *
-     * In TLS 1.2+, all existing/known CipherSuites use SHA256, however
-     * new Ciphersuites (e.g. RFC 5288) can define specific PRF hash
-     * algorithms.
+     * Note that TLS 1.1- uses a single MD5/SHA1-based PRF algorithm for
+     * generating the necessary material.
      */
-    static enum PRF {
+    static enum HashAlg {
+        H_NONE      ("NONE",    0,    0),
+        H_SHA256    ("SHA-256", 32,  64),
+        H_SHA384    ("SHA-384", 48, 128);
 
-        // PRF algorithms
-        P_NONE(     "NONE",  0,   0),
-        P_SHA256("SHA-256", 32,  64),
-        P_SHA384("SHA-384", 48, 128),
-        P_SHA512("SHA-512", 64, 128);  // not currently used.
+        final String name;
+        final int hashLength;
+        final int blockSize;
 
-        // PRF characteristics
-        private final String prfHashAlg;
-        private final int prfHashLength;
-        private final int prfBlockSize;
-
-        PRF(String prfHashAlg, int prfHashLength, int prfBlockSize) {
-            this.prfHashAlg = prfHashAlg;
-            this.prfHashLength = prfHashLength;
-            this.prfBlockSize = prfBlockSize;
+        HashAlg(String hashAlg, int hashLength, int blockSize) {
+            this.name = hashAlg;
+            this.hashLength = hashLength;
+            this.blockSize = blockSize;
         }
 
-        String getPRFHashAlg() {
-            return prfHashAlg;
-        }
-
-        int getPRFHashLength() {
-            return prfHashLength;
-        }
-
-        int getPRFBlockSize() {
-            return prfBlockSize;
+        @Override
+        public String toString() {
+            return name;
         }
     }
-
-    static {
-        idMap = new HashMap<Integer,CipherSuite>();
-        nameMap = new HashMap<String,CipherSuite>();
-
-        final boolean F = false;
-        final boolean T = true;
-        // N: ciphersuites only allowed if we are not in FIPS mode
-        final boolean N = (SunJSSE.isFIPS() == false);
-
-        /*
-         * TLS Cipher Suite Registry, as of November 2015.
-         *
-         * http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
-         *
-         * Range      Registration Procedures   Notes
-         * 000-191    Standards Action          Refers to value of first byte
-         * 192-254    Specification Required    Refers to value of first byte
-         * 255        Reserved for Private Use  Refers to value of first byte
-         *
-         * Value      Description                                   Reference
-         * 0x00,0x00  TLS_NULL_WITH_NULL_NULL                       [RFC5246]
-         * 0x00,0x01  TLS_RSA_WITH_NULL_MD5                         [RFC5246]
-         * 0x00,0x02  TLS_RSA_WITH_NULL_SHA                         [RFC5246]
-         * 0x00,0x03  TLS_RSA_EXPORT_WITH_RC4_40_MD5                [RFC4346]
-         * 0x00,0x04  TLS_RSA_WITH_RC4_128_MD5                      [RFC5246]
-         * 0x00,0x05  TLS_RSA_WITH_RC4_128_SHA                      [RFC5246]
-         * 0x00,0x06  TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5            [RFC4346]
-         * 0x00,0x07  TLS_RSA_WITH_IDEA_CBC_SHA                     [RFC5469]
-         * 0x00,0x08  TLS_RSA_EXPORT_WITH_DES40_CBC_SHA             [RFC4346]
-         * 0x00,0x09  TLS_RSA_WITH_DES_CBC_SHA                      [RFC5469]
-         * 0x00,0x0A  TLS_RSA_WITH_3DES_EDE_CBC_SHA                 [RFC5246]
-         * 0x00,0x0B  TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA          [RFC4346]
-         * 0x00,0x0C  TLS_DH_DSS_WITH_DES_CBC_SHA                   [RFC5469]
-         * 0x00,0x0D  TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA              [RFC5246]
-         * 0x00,0x0E  TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA          [RFC4346]
-         * 0x00,0x0F  TLS_DH_RSA_WITH_DES_CBC_SHA                   [RFC5469]
-         * 0x00,0x10  TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA              [RFC5246]
-         * 0x00,0x11  TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA         [RFC4346]
-         * 0x00,0x12  TLS_DHE_DSS_WITH_DES_CBC_SHA                  [RFC5469]
-         * 0x00,0x13  TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA             [RFC5246]
-         * 0x00,0x14  TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA         [RFC4346]
-         * 0x00,0x15  TLS_DHE_RSA_WITH_DES_CBC_SHA                  [RFC5469]
-         * 0x00,0x16  TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA             [RFC5246]
-         * 0x00,0x17  TLS_DH_anon_EXPORT_WITH_RC4_40_MD5            [RFC4346]
-         * 0x00,0x18  TLS_DH_anon_WITH_RC4_128_MD5                  [RFC5246]
-         * 0x00,0x19  TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA         [RFC4346]
-         * 0x00,0x1A  TLS_DH_anon_WITH_DES_CBC_SHA                  [RFC5469]
-         * 0x00,0x1B  TLS_DH_anon_WITH_3DES_EDE_CBC_SHA             [RFC5246]
-         * 0x00,0x1C-1D Reserved to avoid conflicts with SSLv3      [RFC5246]
-         * 0x00,0x1E  TLS_KRB5_WITH_DES_CBC_SHA                     [RFC2712]
-         * 0x00,0x1F  TLS_KRB5_WITH_3DES_EDE_CBC_SHA                [RFC2712]
-         * 0x00,0x20  TLS_KRB5_WITH_RC4_128_SHA                     [RFC2712]
-         * 0x00,0x21  TLS_KRB5_WITH_IDEA_CBC_SHA                    [RFC2712]
-         * 0x00,0x22  TLS_KRB5_WITH_DES_CBC_MD5                     [RFC2712]
-         * 0x00,0x23  TLS_KRB5_WITH_3DES_EDE_CBC_MD5                [RFC2712]
-         * 0x00,0x24  TLS_KRB5_WITH_RC4_128_MD5                     [RFC2712]
-         * 0x00,0x25  TLS_KRB5_WITH_IDEA_CBC_MD5                    [RFC2712]
-         * 0x00,0x26  TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA           [RFC2712]
-         * 0x00,0x27  TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA           [RFC2712]
-         * 0x00,0x28  TLS_KRB5_EXPORT_WITH_RC4_40_SHA               [RFC2712]
-         * 0x00,0x29  TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5           [RFC2712]
-         * 0x00,0x2A  TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5           [RFC2712]
-         * 0x00,0x2B  TLS_KRB5_EXPORT_WITH_RC4_40_MD5               [RFC2712]
-         * 0x00,0x2C  TLS_PSK_WITH_NULL_SHA                         [RFC4785]
-         * 0x00,0x2D  TLS_DHE_PSK_WITH_NULL_SHA                     [RFC4785]
-         * 0x00,0x2E  TLS_RSA_PSK_WITH_NULL_SHA                     [RFC4785]
-         * 0x00,0x2F  TLS_RSA_WITH_AES_128_CBC_SHA                  [RFC5246]
-         * 0x00,0x30  TLS_DH_DSS_WITH_AES_128_CBC_SHA               [RFC5246]
-         * 0x00,0x31  TLS_DH_RSA_WITH_AES_128_CBC_SHA               [RFC5246]
-         * 0x00,0x32  TLS_DHE_DSS_WITH_AES_128_CBC_SHA              [RFC5246]
-         * 0x00,0x33  TLS_DHE_RSA_WITH_AES_128_CBC_SHA              [RFC5246]
-         * 0x00,0x34  TLS_DH_anon_WITH_AES_128_CBC_SHA              [RFC5246]
-         * 0x00,0x35  TLS_RSA_WITH_AES_256_CBC_SHA                  [RFC5246]
-         * 0x00,0x36  TLS_DH_DSS_WITH_AES_256_CBC_SHA               [RFC5246]
-         * 0x00,0x37  TLS_DH_RSA_WITH_AES_256_CBC_SHA               [RFC5246]
-         * 0x00,0x38  TLS_DHE_DSS_WITH_AES_256_CBC_SHA              [RFC5246]
-         * 0x00,0x39  TLS_DHE_RSA_WITH_AES_256_CBC_SHA              [RFC5246]
-         * 0x00,0x3A  TLS_DH_anon_WITH_AES_256_CBC_SHA              [RFC5246]
-         * 0x00,0x3B  TLS_RSA_WITH_NULL_SHA256                      [RFC5246]
-         * 0x00,0x3C  TLS_RSA_WITH_AES_128_CBC_SHA256               [RFC5246]
-         * 0x00,0x3D  TLS_RSA_WITH_AES_256_CBC_SHA256               [RFC5246]
-         * 0x00,0x3E  TLS_DH_DSS_WITH_AES_128_CBC_SHA256            [RFC5246]
-         * 0x00,0x3F  TLS_DH_RSA_WITH_AES_128_CBC_SHA256            [RFC5246]
-         * 0x00,0x40  TLS_DHE_DSS_WITH_AES_128_CBC_SHA256           [RFC5246]
-         * 0x00,0x41  TLS_RSA_WITH_CAMELLIA_128_CBC_SHA             [RFC5932]
-         * 0x00,0x42  TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA          [RFC5932]
-         * 0x00,0x43  TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA          [RFC5932]
-         * 0x00,0x44  TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA         [RFC5932]
-         * 0x00,0x45  TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA         [RFC5932]
-         * 0x00,0x46  TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA         [RFC5932]
-         * 0x00,0x47-4F Reserved to avoid conflicts with
-         *            deployed implementations                  [Pasi_Eronen]
-         * 0x00,0x50-58 Reserved to avoid conflicts             [Pasi Eronen]
-         * 0x00,0x59-5C Reserved to avoid conflicts with
-         *            deployed implementations                  [Pasi_Eronen]
-         * 0x00,0x5D-5F Unassigned
-         * 0x00,0x60-66 Reserved to avoid conflicts with widely
-         *            deployed implementations                  [Pasi_Eronen]
-         * 0x00,0x67  TLS_DHE_RSA_WITH_AES_128_CBC_SHA256           [RFC5246]
-         * 0x00,0x68  TLS_DH_DSS_WITH_AES_256_CBC_SHA256            [RFC5246]
-         * 0x00,0x69  TLS_DH_RSA_WITH_AES_256_CBC_SHA256            [RFC5246]
-         * 0x00,0x6A  TLS_DHE_DSS_WITH_AES_256_CBC_SHA256           [RFC5246]
-         * 0x00,0x6B  TLS_DHE_RSA_WITH_AES_256_CBC_SHA256           [RFC5246]
-         * 0x00,0x6C  TLS_DH_anon_WITH_AES_128_CBC_SHA256           [RFC5246]
-         * 0x00,0x6D  TLS_DH_anon_WITH_AES_256_CBC_SHA256           [RFC5246]
-         * 0x00,0x6E-83 Unassigned
-         * 0x00,0x84  TLS_RSA_WITH_CAMELLIA_256_CBC_SHA             [RFC5932]
-         * 0x00,0x85  TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA          [RFC5932]
-         * 0x00,0x86  TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA          [RFC5932]
-         * 0x00,0x87  TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA         [RFC5932]
-         * 0x00,0x88  TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA         [RFC5932]
-         * 0x00,0x89  TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA         [RFC5932]
-         * 0x00,0x8A  TLS_PSK_WITH_RC4_128_SHA                      [RFC4279]
-         * 0x00,0x8B  TLS_PSK_WITH_3DES_EDE_CBC_SHA                 [RFC4279]
-         * 0x00,0x8C  TLS_PSK_WITH_AES_128_CBC_SHA                  [RFC4279]
-         * 0x00,0x8D  TLS_PSK_WITH_AES_256_CBC_SHA                  [RFC4279]
-         * 0x00,0x8E  TLS_DHE_PSK_WITH_RC4_128_SHA                  [RFC4279]
-         * 0x00,0x8F  TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA             [RFC4279]
-         * 0x00,0x90  TLS_DHE_PSK_WITH_AES_128_CBC_SHA              [RFC4279]
-         * 0x00,0x91  TLS_DHE_PSK_WITH_AES_256_CBC_SHA              [RFC4279]
-         * 0x00,0x92  TLS_RSA_PSK_WITH_RC4_128_SHA                  [RFC4279]
-         * 0x00,0x93  TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA             [RFC4279]
-         * 0x00,0x94  TLS_RSA_PSK_WITH_AES_128_CBC_SHA              [RFC4279]
-         * 0x00,0x95  TLS_RSA_PSK_WITH_AES_256_CBC_SHA              [RFC4279]
-         * 0x00,0x96  TLS_RSA_WITH_SEED_CBC_SHA                     [RFC4162]
-         * 0x00,0x97  TLS_DH_DSS_WITH_SEED_CBC_SHA                  [RFC4162]
-         * 0x00,0x98  TLS_DH_RSA_WITH_SEED_CBC_SHA                  [RFC4162]
-         * 0x00,0x99  TLS_DHE_DSS_WITH_SEED_CBC_SHA                 [RFC4162]
-         * 0x00,0x9A  TLS_DHE_RSA_WITH_SEED_CBC_SHA                 [RFC4162]
-         * 0x00,0x9B  TLS_DH_anon_WITH_SEED_CBC_SHA                 [RFC4162]
-         * 0x00,0x9C  TLS_RSA_WITH_AES_128_GCM_SHA256               [RFC5288]
-         * 0x00,0x9D  TLS_RSA_WITH_AES_256_GCM_SHA384               [RFC5288]
-         * 0x00,0x9E  TLS_DHE_RSA_WITH_AES_128_GCM_SHA256           [RFC5288]
-         * 0x00,0x9F  TLS_DHE_RSA_WITH_AES_256_GCM_SHA384           [RFC5288]
-         * 0x00,0xA0  TLS_DH_RSA_WITH_AES_128_GCM_SHA256            [RFC5288]
-         * 0x00,0xA1  TLS_DH_RSA_WITH_AES_256_GCM_SHA384            [RFC5288]
-         * 0x00,0xA2  TLS_DHE_DSS_WITH_AES_128_GCM_SHA256           [RFC5288]
-         * 0x00,0xA3  TLS_DHE_DSS_WITH_AES_256_GCM_SHA384           [RFC5288]
-         * 0x00,0xA4  TLS_DH_DSS_WITH_AES_128_GCM_SHA256            [RFC5288]
-         * 0x00,0xA5  TLS_DH_DSS_WITH_AES_256_GCM_SHA384            [RFC5288]
-         * 0x00,0xA6  TLS_DH_anon_WITH_AES_128_GCM_SHA256           [RFC5288]
-         * 0x00,0xA7  TLS_DH_anon_WITH_AES_256_GCM_SHA384           [RFC5288]
-         * 0x00,0xA8  TLS_PSK_WITH_AES_128_GCM_SHA256               [RFC5487]
-         * 0x00,0xA9  TLS_PSK_WITH_AES_256_GCM_SHA384               [RFC5487]
-         * 0x00,0xAA  TLS_DHE_PSK_WITH_AES_128_GCM_SHA256           [RFC5487]
-         * 0x00,0xAB  TLS_DHE_PSK_WITH_AES_256_GCM_SHA384           [RFC5487]
-         * 0x00,0xAC  TLS_RSA_PSK_WITH_AES_128_GCM_SHA256           [RFC5487]
-         * 0x00,0xAD  TLS_RSA_PSK_WITH_AES_256_GCM_SHA384           [RFC5487]
-         * 0x00,0xAE  TLS_PSK_WITH_AES_128_CBC_SHA256               [RFC5487]
-         * 0x00,0xAF  TLS_PSK_WITH_AES_256_CBC_SHA384               [RFC5487]
-         * 0x00,0xB0  TLS_PSK_WITH_NULL_SHA256                      [RFC5487]
-         * 0x00,0xB1  TLS_PSK_WITH_NULL_SHA384                      [RFC5487]
-         * 0x00,0xB2  TLS_DHE_PSK_WITH_AES_128_CBC_SHA256           [RFC5487]
-         * 0x00,0xB3  TLS_DHE_PSK_WITH_AES_256_CBC_SHA384           [RFC5487]
-         * 0x00,0xB4  TLS_DHE_PSK_WITH_NULL_SHA256                  [RFC5487]
-         * 0x00,0xB5  TLS_DHE_PSK_WITH_NULL_SHA384                  [RFC5487]
-         * 0x00,0xB6  TLS_RSA_PSK_WITH_AES_128_CBC_SHA256           [RFC5487]
-         * 0x00,0xB7  TLS_RSA_PSK_WITH_AES_256_CBC_SHA384           [RFC5487]
-         * 0x00,0xB8  TLS_RSA_PSK_WITH_NULL_SHA256                  [RFC5487]
-         * 0x00,0xB9  TLS_RSA_PSK_WITH_NULL_SHA384                  [RFC5487]
-         * 0x00,0xBA  TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256          [RFC5932]
-         * 0x00,0xBB  TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256       [RFC5932]
-         * 0x00,0xBC  TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256       [RFC5932]
-         * 0x00,0xBD  TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256      [RFC5932]
-         * 0x00,0xBE  TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256      [RFC5932]
-         * 0x00,0xBF  TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256      [RFC5932]
-         * 0x00,0xC0  TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256          [RFC5932]
-         * 0x00,0xC1  TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256       [RFC5932]
-         * 0x00,0xC2  TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256       [RFC5932]
-         * 0x00,0xC3  TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256      [RFC5932]
-         * 0x00,0xC4  TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256      [RFC5932]
-         * 0x00,0xC5  TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256      [RFC5932]
-         * 0x00,0xC6-FE         Unassigned
-         * 0x00,0xFF  TLS_EMPTY_RENEGOTIATION_INFO_SCSV             [RFC5746]
-         * 0x01-55,*  Unassigned
-         * 0x56,0x00  TLS_FALLBACK_SCSV                             [RFC7507]
-         * 0x56,0x01-0xC0,0x00  Unassigned
-         * 0xC0,0x01  TLS_ECDH_ECDSA_WITH_NULL_SHA                  [RFC4492]
-         * 0xC0,0x02  TLS_ECDH_ECDSA_WITH_RC4_128_SHA               [RFC4492]
-         * 0xC0,0x03  TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA          [RFC4492]
-         * 0xC0,0x04  TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA           [RFC4492]
-         * 0xC0,0x05  TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA           [RFC4492]
-         * 0xC0,0x06  TLS_ECDHE_ECDSA_WITH_NULL_SHA                 [RFC4492]
-         * 0xC0,0x07  TLS_ECDHE_ECDSA_WITH_RC4_128_SHA              [RFC4492]
-         * 0xC0,0x08  TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA         [RFC4492]
-         * 0xC0,0x09  TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA          [RFC4492]
-         * 0xC0,0x0A  TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA          [RFC4492]
-         * 0xC0,0x0B  TLS_ECDH_RSA_WITH_NULL_SHA                    [RFC4492]
-         * 0xC0,0x0C  TLS_ECDH_RSA_WITH_RC4_128_SHA                 [RFC4492]
-         * 0xC0,0x0D  TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA            [RFC4492]
-         * 0xC0,0x0E  TLS_ECDH_RSA_WITH_AES_128_CBC_SHA             [RFC4492]
-         * 0xC0,0x0F  TLS_ECDH_RSA_WITH_AES_256_CBC_SHA             [RFC4492]
-         * 0xC0,0x10  TLS_ECDHE_RSA_WITH_NULL_SHA                   [RFC4492]
-         * 0xC0,0x11  TLS_ECDHE_RSA_WITH_RC4_128_SHA                [RFC4492]
-         * 0xC0,0x12  TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA           [RFC4492]
-         * 0xC0,0x13  TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA            [RFC4492]
-         * 0xC0,0x14  TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA            [RFC4492]
-         * 0xC0,0x15  TLS_ECDH_anon_WITH_NULL_SHA                   [RFC4492]
-         * 0xC0,0x16  TLS_ECDH_anon_WITH_RC4_128_SHA                [RFC4492]
-         * 0xC0,0x17  TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA           [RFC4492]
-         * 0xC0,0x18  TLS_ECDH_anon_WITH_AES_128_CBC_SHA            [RFC4492]
-         * 0xC0,0x19  TLS_ECDH_anon_WITH_AES_256_CBC_SHA            [RFC4492]
-         * 0xC0,0x1A  TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA             [RFC5054]
-         * 0xC0,0x1B  TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA         [RFC5054]
-         * 0xC0,0x1C  TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA         [RFC5054]
-         * 0xC0,0x1D  TLS_SRP_SHA_WITH_AES_128_CBC_SHA              [RFC5054]
-         * 0xC0,0x1E  TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA          [RFC5054]
-         * 0xC0,0x1F  TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA          [RFC5054]
-         * 0xC0,0x20  TLS_SRP_SHA_WITH_AES_256_CBC_SHA              [RFC5054]
-         * 0xC0,0x21  TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA          [RFC5054]
-         * 0xC0,0x22  TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA          [RFC5054]
-         * 0xC0,0x23  TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256       [RFC5289]
-         * 0xC0,0x24  TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384       [RFC5289]
-         * 0xC0,0x25  TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256        [RFC5289]
-         * 0xC0,0x26  TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384        [RFC5289]
-         * 0xC0,0x27  TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256         [RFC5289]
-         * 0xC0,0x28  TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384         [RFC5289]
-         * 0xC0,0x29  TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256          [RFC5289]
-         * 0xC0,0x2A  TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384          [RFC5289]
-         * 0xC0,0x2B  TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256       [RFC5289]
-         * 0xC0,0x2C  TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384       [RFC5289]
-         * 0xC0,0x2D  TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256        [RFC5289]
-         * 0xC0,0x2E  TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384        [RFC5289]
-         * 0xC0,0x2F  TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256         [RFC5289]
-         * 0xC0,0x30  TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384         [RFC5289]
-         * 0xC0,0x31  TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256          [RFC5289]
-         * 0xC0,0x32  TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384          [RFC5289]
-         * 0xC0,0x33  TLS_ECDHE_PSK_WITH_RC4_128_SHA                [RFC5489]
-         * 0xC0,0x34  TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA           [RFC5489]
-         * 0xC0,0x35  TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA            [RFC5489]
-         * 0xC0,0x36  TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA            [RFC5489]
-         * 0xC0,0x37  TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256         [RFC5489]
-         * 0xC0,0x38  TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384         [RFC5489]
-         * 0xC0,0x39  TLS_ECDHE_PSK_WITH_NULL_SHA                   [RFC5489]
-         * 0xC0,0x3A  TLS_ECDHE_PSK_WITH_NULL_SHA256                [RFC5489]
-         * 0xC0,0x3B  TLS_ECDHE_PSK_WITH_NULL_SHA384                [RFC5489]
-         * 0xC0,0x3C  TLS_RSA_WITH_ARIA_128_CBC_SHA256              [RFC6209]
-         * 0xC0,0x3D  TLS_RSA_WITH_ARIA_256_CBC_SHA384              [RFC6209]
-         * 0xC0,0x3E  TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256           [RFC6209]
-         * 0xC0,0x3F  TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384           [RFC6209]
-         * 0xC0,0x40  TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256           [RFC6209]
-         * 0xC0,0x41  TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384           [RFC6209]
-         * 0xC0,0x42  TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256          [RFC6209]
-         * 0xC0,0x43  TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384          [RFC6209]
-         * 0xC0,0x44  TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256          [RFC6209]
-         * 0xC0,0x45  TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384          [RFC6209]
-         * 0xC0,0x46  TLS_DH_anon_WITH_ARIA_128_CBC_SHA256          [RFC6209]
-         * 0xC0,0x47  TLS_DH_anon_WITH_ARIA_256_CBC_SHA384          [RFC6209]
-         * 0xC0,0x48  TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256      [RFC6209]
-         * 0xC0,0x49  TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384      [RFC6209]
-         * 0xC0,0x4A  TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256       [RFC6209]
-         * 0xC0,0x4B  TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384       [RFC6209]
-         * 0xC0,0x4C  TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256        [RFC6209]
-         * 0xC0,0x4D  TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384        [RFC6209]
-         * 0xC0,0x4E  TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256         [RFC6209]
-         * 0xC0,0x4F  TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384         [RFC6209]
-         * 0xC0,0x50  TLS_RSA_WITH_ARIA_128_GCM_SHA256              [RFC6209]
-         * 0xC0,0x51  TLS_RSA_WITH_ARIA_256_GCM_SHA384              [RFC6209]
-         * 0xC0,0x52  TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256          [RFC6209]
-         * 0xC0,0x53  TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384          [RFC6209]
-         * 0xC0,0x54  TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256           [RFC6209]
-         * 0xC0,0x55  TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384           [RFC6209]
-         * 0xC0,0x56  TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256          [RFC6209]
-         * 0xC0,0x57  TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384          [RFC6209]
-         * 0xC0,0x58  TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256           [RFC6209]
-         * 0xC0,0x59  TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384           [RFC6209]
-         * 0xC0,0x5A  TLS_DH_anon_WITH_ARIA_128_GCM_SHA256          [RFC6209]
-         * 0xC0,0x5B  TLS_DH_anon_WITH_ARIA_256_GCM_SHA384          [RFC6209]
-         * 0xC0,0x5C  TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256      [RFC6209]
-         * 0xC0,0x5D  TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384      [RFC6209]
-         * 0xC0,0x5E  TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256       [RFC6209]
-         * 0xC0,0x5F  TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384       [RFC6209]
-         * 0xC0,0x60  TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256        [RFC6209]
-         * 0xC0,0x61  TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384        [RFC6209]
-         * 0xC0,0x62  TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256         [RFC6209]
-         * 0xC0,0x63  TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384         [RFC6209]
-         * 0xC0,0x64  TLS_PSK_WITH_ARIA_128_CBC_SHA256              [RFC6209]
-         * 0xC0,0x65  TLS_PSK_WITH_ARIA_256_CBC_SHA384              [RFC6209]
-         * 0xC0,0x66  TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256          [RFC6209]
-         * 0xC0,0x67  TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384          [RFC6209]
-         * 0xC0,0x68  TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256          [RFC6209]
-         * 0xC0,0x69  TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384          [RFC6209]
-         * 0xC0,0x6A  TLS_PSK_WITH_ARIA_128_GCM_SHA256              [RFC6209]
-         * 0xC0,0x6B  TLS_PSK_WITH_ARIA_256_GCM_SHA384              [RFC6209]
-         * 0xC0,0x6C  TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256          [RFC6209]
-         * 0xC0,0x6D  TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384          [RFC6209]
-         * 0xC0,0x6E  TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256          [RFC6209]
-         * 0xC0,0x6F  TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384          [RFC6209]
-         * 0xC0,0x70  TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256        [RFC6209]
-         * 0xC0,0x71  TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384        [RFC6209]
-         * 0xC0,0x72  TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256  [RFC6367]
-         * 0xC0,0x73  TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384  [RFC6367]
-         * 0xC0,0x74  TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256   [RFC6367]
-         * 0xC0,0x75  TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384   [RFC6367]
-         * 0xC0,0x76  TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256    [RFC6367]
-         * 0xC0,0x77  TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384    [RFC6367]
-         * 0xC0,0x78  TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256     [RFC6367]
-         * 0xC0,0x79  TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384     [RFC6367]
-         * 0xC0,0x7A  TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256          [RFC6367]
-         * 0xC0,0x7B  TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384          [RFC6367]
-         * 0xC0,0x7C  TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256      [RFC6367]
-         * 0xC0,0x7D  TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384      [RFC6367]
-         * 0xC0,0x7E  TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256       [RFC6367]
-         * 0xC0,0x7F  TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384       [RFC6367]
-         * 0xC0,0x80  TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256      [RFC6367]
-         * 0xC0,0x81  TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384      [RFC6367]
-         * 0xC0,0x82  TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256       [RFC6367]
-         * 0xC0,0x83  TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384       [RFC6367]
-         * 0xC0,0x84  TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256      [RFC6367]
-         * 0xC0,0x85  TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384      [RFC6367]
-         * 0xC0,0x86  TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256  [RFC6367]
-         * 0xC0,0x87  TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384  [RFC6367]
-         * 0xC0,0x88  TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256   [RFC6367]
-         * 0xC0,0x89  TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384   [RFC6367]
-         * 0xC0,0x8A  TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256    [RFC6367]
-         * 0xC0,0x8B  TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384    [RFC6367]
-         * 0xC0,0x8C  TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256     [RFC6367]
-         * 0xC0,0x8D  TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384     [RFC6367]
-         * 0xC0,0x8E  TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256          [RFC6367]
-         * 0xC0,0x8F  TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384          [RFC6367]
-         * 0xC0,0x90  TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256      [RFC6367]
-         * 0xC0,0x91  TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384      [RFC6367]
-         * 0xC0,0x92  TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256      [RFC6367]
-         * 0xC0,0x93  TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384      [RFC6367]
-         * 0xC0,0x94  TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256          [RFC6367]
-         * 0xC0,0x95  TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384          [RFC6367]
-         * 0xC0,0x96  TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256      [RFC6367]
-         * 0xC0,0x97  TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384      [RFC6367]
-         * 0xC0,0x98  TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256      [RFC6367]
-         * 0xC0,0x99  TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384      [RFC6367]
-         * 0xC0,0x9A  TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256    [RFC6367]
-         * 0xC0,0x9B  TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384    [RFC6367]
-         * 0xC0,0x9C  TLS_RSA_WITH_AES_128_CCM                      [RFC6655]
-         * 0xC0,0x9D  TLS_RSA_WITH_AES_256_CCM                      [RFC6655]
-         * 0xC0,0x9E  TLS_DHE_RSA_WITH_AES_128_CCM                  [RFC6655]
-         * 0xC0,0x9F  TLS_DHE_RSA_WITH_AES_256_CCM                  [RFC6655]
-         * 0xC0,0xA0  TLS_RSA_WITH_AES_128_CCM_8                    [RFC6655]
-         * 0xC0,0xA1  TLS_RSA_WITH_AES_256_CCM_8                    [RFC6655]
-         * 0xC0,0xA2  TLS_DHE_RSA_WITH_AES_128_CCM_8                [RFC6655]
-         * 0xC0,0xA3  TLS_DHE_RSA_WITH_AES_256_CCM_8                [RFC6655]
-         * 0xC0,0xA4  TLS_PSK_WITH_AES_128_CCM                      [RFC6655]
-         * 0xC0,0xA5  TLS_PSK_WITH_AES_256_CCM                      [RFC6655]
-         * 0xC0,0xA6  TLS_DHE_PSK_WITH_AES_128_CCM                  [RFC6655]
-         * 0xC0,0xA7  TLS_DHE_PSK_WITH_AES_256_CCM                  [RFC6655]
-         * 0xC0,0xA8  TLS_PSK_WITH_AES_128_CCM_8                    [RFC6655]
-         * 0xC0,0xA9  TLS_PSK_WITH_AES_256_CCM_8                    [RFC6655]
-         * 0xC0,0xAA  TLS_PSK_DHE_WITH_AES_128_CCM_8                [RFC6655]
-         * 0xC0,0xAB  TLS_PSK_DHE_WITH_AES_256_CCM_8                [RFC6655]
-         * 0xC0,0xAC  TLS_ECDHE_ECDSA_WITH_AES_128_CCM              [RFC7251]
-         * 0xC0,0xAD  TLS_ECDHE_ECDSA_WITH_AES_256_CCM              [RFC7251]
-         * 0xC0,0xAE  TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8            [RFC7251]
-         * 0xC0,0xAF  TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8            [RFC7251]
-         * 0xC0,0xB0-FF  Unassigned
-         * 0xC1-FD,*  Unassigned
-         * 0xFE,0x00-FD Unassigned
-         * 0xFE,0xFE-FF Reserved to avoid conflicts with widely
-         *            deployed implementations                  [Pasi_Eronen]
-         * 0xFF,0x00-FF Reserved for Private Use                [RFC5246]
-         */
-
-        add("SSL_NULL_WITH_NULL_NULL", 0x0000,
-                1,      K_NULL,     B_NULL,     M_NULL,     F);
-
-        /*
-         * Definition of the CipherSuites that are enabled by default.
-         * They are listed in preference order, most preferred first, using
-         * the following criteria:
-         * 1. Prefer Suite B compliant cipher suites, see RFC6460 (To be
-         *    changed later, see below).
-         * 2. Prefer the stronger bulk cipher, in the order of AES_256(GCM),
-         *    AES_128(GCM), AES_256, AES_128, 3DES-EDE.
-         * 3. Prefer the stronger MAC algorithm, in the order of SHA384,
-         *    SHA256, SHA, MD5.
-         * 4. Prefer the better performance of key exchange and digital
-         *    signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA,
-         *    RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS.
-         */
-        int p = DEFAULT_SUITES_PRIORITY * 2;
-
-        // shorten names to fit the following table cleanly.
-        int max = ProtocolVersion.LIMIT_MAX_VALUE;
-        int tls11 = ProtocolVersion.TLS11.v;
-        int tls12 = ProtocolVersion.TLS12.v;
-
-        //  ID           Key Exchange   Cipher     A  obs  suprt  PRF
-        //  ======       ============   =========  =  ===  =====  ========
-
-        // Suite B compliant cipher suites, see RFC 6460.
-        //
-        // Note that, at present this provider is not Suite B compliant. The
-        // preference order of the GCM cipher suites does not follow the spec
-        // of RFC 6460.  In this section, only two cipher suites are listed
-        // so that applications can make use of Suite-B compliant cipher
-        // suite firstly.
-        add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",  0xc02c, --p,
-            K_ECDHE_ECDSA, B_AES_256_GCM, M_NULL,   T, max, tls12, P_SHA384);
-        add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",  0xc02b, --p,
-            K_ECDHE_ECDSA, B_AES_128_GCM, M_NULL,   T, max, tls12, P_SHA256);
-
-        // AES_256(GCM)
-        add("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",    0xc030, --p,
-            K_ECDHE_RSA,   B_AES_256_GCM, M_NULL,   T, max, tls12, P_SHA384);
-        add("TLS_RSA_WITH_AES_256_GCM_SHA384",          0x009d, --p,
-            K_RSA,         B_AES_256_GCM, M_NULL,   T, max, tls12, P_SHA384);
-        add("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",   0xc02e, --p,
-            K_ECDH_ECDSA,  B_AES_256_GCM, M_NULL,   T, max, tls12, P_SHA384);
-        add("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",     0xc032, --p,
-            K_ECDH_RSA,    B_AES_256_GCM, M_NULL,   T, max, tls12, P_SHA384);
-        add("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",      0x009f, --p,
-            K_DHE_RSA,     B_AES_256_GCM, M_NULL,   T, max, tls12, P_SHA384);
-        add("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",      0x00a3, --p,
-            K_DHE_DSS,     B_AES_256_GCM, M_NULL,   T, max, tls12, P_SHA384);
-
-        // AES_128(GCM)
-        add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",    0xc02f, --p,
-            K_ECDHE_RSA,   B_AES_128_GCM, M_NULL,   T, max, tls12, P_SHA256);
-        add("TLS_RSA_WITH_AES_128_GCM_SHA256",          0x009c, --p,
-            K_RSA,         B_AES_128_GCM, M_NULL,   T, max, tls12, P_SHA256);
-        add("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",   0xc02d, --p,
-            K_ECDH_ECDSA,  B_AES_128_GCM, M_NULL,   T, max, tls12, P_SHA256);
-        add("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",     0xc031, --p,
-            K_ECDH_RSA,    B_AES_128_GCM, M_NULL,   T, max, tls12, P_SHA256);
-        add("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",      0x009e, --p,
-            K_DHE_RSA,     B_AES_128_GCM, M_NULL,   T, max, tls12, P_SHA256);
-        add("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",      0x00a2, --p,
-            K_DHE_DSS,     B_AES_128_GCM, M_NULL,   T, max, tls12, P_SHA256);
-
-        // AES_256(CBC)
-        add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",  0xc024, --p,
-            K_ECDHE_ECDSA, B_AES_256,     M_SHA384, T, max, tls12, P_SHA384);
-        add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",    0xc028, --p,
-            K_ECDHE_RSA,   B_AES_256,     M_SHA384, T, max, tls12, P_SHA384);
-        add("TLS_RSA_WITH_AES_256_CBC_SHA256",          0x003d, --p,
-            K_RSA,         B_AES_256,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",   0xc026, --p,
-            K_ECDH_ECDSA,  B_AES_256,     M_SHA384, T, max, tls12, P_SHA384);
-        add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",     0xc02a, --p,
-            K_ECDH_RSA,    B_AES_256,     M_SHA384, T, max, tls12, P_SHA384);
-        add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",      0x006b, --p,
-            K_DHE_RSA,     B_AES_256,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",      0x006a, --p,
-            K_DHE_DSS,     B_AES_256,     M_SHA256, T, max, tls12, P_SHA256);
-
-        add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",     0xC00A, --p,
-            K_ECDHE_ECDSA, B_AES_256,     M_SHA,    T);
-        add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",       0xC014, --p,
-            K_ECDHE_RSA,   B_AES_256,     M_SHA,    T);
-        add("TLS_RSA_WITH_AES_256_CBC_SHA",             0x0035, --p,
-            K_RSA,         B_AES_256,     M_SHA,    T);
-        add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",      0xC005, --p,
-            K_ECDH_ECDSA,  B_AES_256,     M_SHA,    T);
-        add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",        0xC00F, --p,
-            K_ECDH_RSA,    B_AES_256,     M_SHA,    T);
-        add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA",         0x0039, --p,
-            K_DHE_RSA,     B_AES_256,     M_SHA,    T);
-        add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA",         0x0038, --p,
-            K_DHE_DSS,     B_AES_256,     M_SHA,    T);
-
-        // AES_128(CBC)
-        add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",  0xc023, --p,
-            K_ECDHE_ECDSA, B_AES_128,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",    0xc027, --p,
-            K_ECDHE_RSA,   B_AES_128,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_RSA_WITH_AES_128_CBC_SHA256",          0x003c, --p,
-            K_RSA,         B_AES_128,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",   0xc025, --p,
-            K_ECDH_ECDSA,  B_AES_128,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",     0xc029, --p,
-            K_ECDH_RSA,    B_AES_128,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",      0x0067, --p,
-            K_DHE_RSA,     B_AES_128,     M_SHA256, T, max, tls12, P_SHA256);
-        add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",      0x0040, --p,
-            K_DHE_DSS,     B_AES_128,     M_SHA256, T, max, tls12, P_SHA256);
-
-        add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",     0xC009, --p,
-            K_ECDHE_ECDSA, B_AES_128,     M_SHA,    T);
-        add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",       0xC013, --p,
-            K_ECDHE_RSA,   B_AES_128,     M_SHA,    T);
-        add("TLS_RSA_WITH_AES_128_CBC_SHA",             0x002f, --p,
-            K_RSA,         B_AES_128,     M_SHA,    T);
-        add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",      0xC004, --p,
-            K_ECDH_ECDSA,  B_AES_128,     M_SHA,    T);
-        add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",        0xC00E, --p,
-            K_ECDH_RSA,    B_AES_128,     M_SHA,    T);
-        add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA",         0x0033, --p,
-            K_DHE_RSA,     B_AES_128,     M_SHA,    T);
-        add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA",         0x0032, --p,
-            K_DHE_DSS,     B_AES_128,     M_SHA,    T);
-
-        // 3DES_EDE
-        add("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",    0xC008, --p,
-            K_ECDHE_ECDSA, B_3DES,        M_SHA,    T);
-        add("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",      0xC012, --p,
-            K_ECDHE_RSA,   B_3DES,        M_SHA,    T);
-        add("SSL_RSA_WITH_3DES_EDE_CBC_SHA",            0x000a, --p,
-            K_RSA,         B_3DES,        M_SHA,    T);
-        add("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",     0xC003, --p,
-            K_ECDH_ECDSA,  B_3DES,        M_SHA,    T);
-        add("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",       0xC00D, --p,
-            K_ECDH_RSA,    B_3DES,        M_SHA,    T);
-        add("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",        0x0016, --p,
-            K_DHE_RSA,     B_3DES,        M_SHA,    T);
-        add("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",        0x0013, --p,
-            K_DHE_DSS,     B_3DES,        M_SHA,    N);
-
-        // Renegotiation protection request Signalling Cipher Suite Value (SCSV)
-        add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV",        0x00ff, --p,
-            K_SCSV,        B_NULL,        M_NULL,   T);
-
-        /*
-         * Definition of the CipherSuites that are supported but not enabled
-         * by default.
-         * They are listed in preference order, preferred first, using the
-         * following criteria:
-         * 1. CipherSuites for KRB5 need additional KRB5 service
-         *    configuration, and these suites are not common in practice,
-         *    so we put KRB5 based cipher suites at the end of the supported
-         *    list.
-         * 2. If a cipher suite has been obsoleted, we put it at the end of
-         *    the list.
-         * 3. Prefer the stronger bulk cipher, in the order of AES_256,
-         *    AES_128, 3DES-EDE, RC-4, DES, DES40, RC4_40, NULL.
-         * 4. Prefer the stronger MAC algorithm, in the order of SHA384,
-         *    SHA256, SHA, MD5.
-         * 5. Prefer the better performance of key exchange and digital
-         *    signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA,
-         *    RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS, anonymous.
-         */
-        p = DEFAULT_SUITES_PRIORITY;
-
-        add("TLS_DH_anon_WITH_AES_256_GCM_SHA384",      0x00a7, --p,
-            K_DH_ANON,     B_AES_256_GCM, M_NULL,   N, max, tls12, P_SHA384);
-        add("TLS_DH_anon_WITH_AES_128_GCM_SHA256",      0x00a6, --p,
-            K_DH_ANON,     B_AES_128_GCM, M_NULL,   N, max, tls12, P_SHA256);
-
-        add("TLS_DH_anon_WITH_AES_256_CBC_SHA256",      0x006d, --p,
-            K_DH_ANON,     B_AES_256,     M_SHA256, N, max, tls12, P_SHA256);
-        add("TLS_ECDH_anon_WITH_AES_256_CBC_SHA",       0xC019, --p,
-            K_ECDH_ANON,   B_AES_256,     M_SHA,    N);
-        add("TLS_DH_anon_WITH_AES_256_CBC_SHA",         0x003a, --p,
-            K_DH_ANON,     B_AES_256,     M_SHA,    N);
-
-        add("TLS_DH_anon_WITH_AES_128_CBC_SHA256",      0x006c, --p,
-            K_DH_ANON,     B_AES_128,     M_SHA256, N, max, tls12, P_SHA256);
-        add("TLS_ECDH_anon_WITH_AES_128_CBC_SHA",       0xC018, --p,
-            K_ECDH_ANON,   B_AES_128,     M_SHA,    N);
-        add("TLS_DH_anon_WITH_AES_128_CBC_SHA",         0x0034, --p,
-            K_DH_ANON,     B_AES_128,     M_SHA,    N);
-
-        add("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",      0xC017, --p,
-            K_ECDH_ANON,   B_3DES,        M_SHA,    N);
-        add("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",        0x001b, --p,
-            K_DH_ANON,     B_3DES,        M_SHA,    N);
-
-        // RC-4
-        add("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",         0xC007, --p,
-            K_ECDHE_ECDSA, B_RC4_128,     M_SHA,    N);
-        add("TLS_ECDHE_RSA_WITH_RC4_128_SHA",           0xC011, --p,
-            K_ECDHE_RSA,   B_RC4_128,     M_SHA,    N);
-        add("SSL_RSA_WITH_RC4_128_SHA",                 0x0005, --p,
-            K_RSA,         B_RC4_128,     M_SHA,    N);
-        add("TLS_ECDH_ECDSA_WITH_RC4_128_SHA",          0xC002, --p,
-            K_ECDH_ECDSA,  B_RC4_128,     M_SHA,    N);
-        add("TLS_ECDH_RSA_WITH_RC4_128_SHA",            0xC00C, --p,
-            K_ECDH_RSA,    B_RC4_128,     M_SHA,    N);
-        add("SSL_RSA_WITH_RC4_128_MD5",                 0x0004, --p,
-            K_RSA,         B_RC4_128,     M_MD5,    N);
-
-        add("TLS_ECDH_anon_WITH_RC4_128_SHA",           0xC016, --p,
-            K_ECDH_ANON,   B_RC4_128,     M_SHA,    N);
-        add("SSL_DH_anon_WITH_RC4_128_MD5",             0x0018, --p,
-            K_DH_ANON,     B_RC4_128,     M_MD5,    N);
-
-        // weak cipher suites obsoleted in TLS 1.2
-        add("SSL_RSA_WITH_DES_CBC_SHA",                 0x0009, --p,
-            K_RSA,         B_DES,         M_SHA,    N, tls12);
-        add("SSL_DHE_RSA_WITH_DES_CBC_SHA",             0x0015, --p,
-            K_DHE_RSA,     B_DES,         M_SHA,    N, tls12);
-        add("SSL_DHE_DSS_WITH_DES_CBC_SHA",             0x0012, --p,
-            K_DHE_DSS,     B_DES,         M_SHA,    N, tls12);
-        add("SSL_DH_anon_WITH_DES_CBC_SHA",             0x001a, --p,
-            K_DH_ANON,     B_DES,         M_SHA,    N, tls12);
-
-        // weak cipher suites obsoleted in TLS 1.1
-        add("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",        0x0008, --p,
-            K_RSA_EXPORT,  B_DES_40,      M_SHA,    N, tls11);
-        add("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",    0x0014, --p,
-            K_DHE_RSA,     B_DES_40,      M_SHA,    N, tls11);
-        add("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",    0x0011, --p,
-            K_DHE_DSS,     B_DES_40,      M_SHA,    N, tls11);
-        add("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",    0x0019, --p,
-            K_DH_ANON,     B_DES_40,      M_SHA,    N, tls11);
-
-        add("SSL_RSA_EXPORT_WITH_RC4_40_MD5",           0x0003, --p,
-            K_RSA_EXPORT,  B_RC4_40,      M_MD5,    N, tls11);
-        add("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",       0x0017, --p,
-            K_DH_ANON,     B_RC4_40,      M_MD5,    N, tls11);
-
-        add("TLS_RSA_WITH_NULL_SHA256",                 0x003b, --p,
-            K_RSA,         B_NULL,        M_SHA256, N, max, tls12, P_SHA256);
-        add("TLS_ECDHE_ECDSA_WITH_NULL_SHA",            0xC006, --p,
-            K_ECDHE_ECDSA, B_NULL,        M_SHA,    N);
-        add("TLS_ECDHE_RSA_WITH_NULL_SHA",              0xC010, --p,
-            K_ECDHE_RSA,   B_NULL,        M_SHA,    N);
-        add("SSL_RSA_WITH_NULL_SHA",                    0x0002, --p,
-            K_RSA,         B_NULL,        M_SHA,    N);
-        add("TLS_ECDH_ECDSA_WITH_NULL_SHA",             0xC001, --p,
-            K_ECDH_ECDSA,  B_NULL,        M_SHA,    N);
-        add("TLS_ECDH_RSA_WITH_NULL_SHA",               0xC00B, --p,
-            K_ECDH_RSA,    B_NULL,        M_SHA,    N);
-        add("TLS_ECDH_anon_WITH_NULL_SHA",              0xC015, --p,
-            K_ECDH_ANON,   B_NULL,        M_SHA,    N);
-        add("SSL_RSA_WITH_NULL_MD5",                    0x0001, --p,
-            K_RSA,         B_NULL,        M_MD5,    N);
-
-        // Supported Kerberos ciphersuites from RFC2712
-        add("TLS_KRB5_WITH_3DES_EDE_CBC_SHA",           0x001f, --p,
-            K_KRB5,        B_3DES,        M_SHA,    N);
-        add("TLS_KRB5_WITH_3DES_EDE_CBC_MD5",           0x0023, --p,
-            K_KRB5,        B_3DES,        M_MD5,    N);
-        add("TLS_KRB5_WITH_RC4_128_SHA",                0x0020, --p,
-            K_KRB5,        B_RC4_128,     M_SHA,    N);
-        add("TLS_KRB5_WITH_RC4_128_MD5",                0x0024, --p,
-            K_KRB5,        B_RC4_128,     M_MD5,    N);
-        add("TLS_KRB5_WITH_DES_CBC_SHA",                0x001e, --p,
-            K_KRB5,        B_DES,         M_SHA,    N, tls12);
-        add("TLS_KRB5_WITH_DES_CBC_MD5",                0x0022, --p,
-            K_KRB5,        B_DES,         M_MD5,    N, tls12);
-        add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",      0x0026, --p,
-            K_KRB5_EXPORT, B_DES_40,      M_SHA,    N, tls11);
-        add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",      0x0029, --p,
-            K_KRB5_EXPORT, B_DES_40,      M_MD5,    N, tls11);
-        add("TLS_KRB5_EXPORT_WITH_RC4_40_SHA",          0x0028, --p,
-            K_KRB5_EXPORT, B_RC4_40,      M_SHA,    N, tls11);
-        add("TLS_KRB5_EXPORT_WITH_RC4_40_MD5",          0x002b, --p,
-            K_KRB5_EXPORT, B_RC4_40,      M_MD5,    N, tls11);
-
-        /*
-         * Other values from the TLS Cipher Suite Registry, as of August 2010.
-         *
-         * http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
-         *
-         * Range      Registration Procedures   Notes
-         * 000-191    Standards Action          Refers to value of first byte
-         * 192-254    Specification Required    Refers to value of first byte
-         * 255        Reserved for Private Use  Refers to value of first byte
-         */
-
-        // Register the names of a few additional CipherSuites.
-        // Makes them show up as names instead of numbers in
-        // the debug output.
-
-        // remaining unsupported ciphersuites defined in RFC2246.
-        add("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",           0x0006);
-        add("SSL_RSA_WITH_IDEA_CBC_SHA",                    0x0007);
-        add("SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",         0x000b);
-        add("SSL_DH_DSS_WITH_DES_CBC_SHA",                  0x000c);
-        add("SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",             0x000d);
-        add("SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",         0x000e);
-        add("SSL_DH_RSA_WITH_DES_CBC_SHA",                  0x000f);
-        add("SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",             0x0010);
-
-        // SSL 3.0 Fortezza ciphersuites
-        add("SSL_FORTEZZA_DMS_WITH_NULL_SHA",               0x001c);
-        add("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA",       0x001d);
-
-        // 1024/56 bit exportable ciphersuites from expired internet draft
-        add("SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",          0x0062);
-        add("SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA",      0x0063);
-        add("SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",           0x0064);
-        add("SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA",       0x0065);
-        add("SSL_DHE_DSS_WITH_RC4_128_SHA",                 0x0066);
-
-        // Netscape old and new SSL 3.0 FIPS ciphersuites
-        // see http://www.mozilla.org/projects/security/pki/nss/ssl/fips-ssl-ciphersuites.html
-        add("NETSCAPE_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",      0xffe0);
-        add("NETSCAPE_RSA_FIPS_WITH_DES_CBC_SHA",           0xffe1);
-        add("SSL_RSA_FIPS_WITH_DES_CBC_SHA",                0xfefe);
-        add("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",           0xfeff);
-
-        // Unsupported Kerberos cipher suites from RFC 2712
-        add("TLS_KRB5_WITH_IDEA_CBC_SHA",                   0x0021);
-        add("TLS_KRB5_WITH_IDEA_CBC_MD5",                   0x0025);
-        add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",          0x0027);
-        add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",          0x002a);
-
-        // Unsupported cipher suites from RFC 4162
-        add("TLS_RSA_WITH_SEED_CBC_SHA",                    0x0096);
-        add("TLS_DH_DSS_WITH_SEED_CBC_SHA",                 0x0097);
-        add("TLS_DH_RSA_WITH_SEED_CBC_SHA",                 0x0098);
-        add("TLS_DHE_DSS_WITH_SEED_CBC_SHA",                0x0099);
-        add("TLS_DHE_RSA_WITH_SEED_CBC_SHA",                0x009a);
-        add("TLS_DH_anon_WITH_SEED_CBC_SHA",                0x009b);
-
-        // Unsupported cipher suites from RFC 4279
-        add("TLS_PSK_WITH_RC4_128_SHA",                     0x008a);
-        add("TLS_PSK_WITH_3DES_EDE_CBC_SHA",                0x008b);
-        add("TLS_PSK_WITH_AES_128_CBC_SHA",                 0x008c);
-        add("TLS_PSK_WITH_AES_256_CBC_SHA",                 0x008d);
-        add("TLS_DHE_PSK_WITH_RC4_128_SHA",                 0x008e);
-        add("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",            0x008f);
-        add("TLS_DHE_PSK_WITH_AES_128_CBC_SHA",             0x0090);
-        add("TLS_DHE_PSK_WITH_AES_256_CBC_SHA",             0x0091);
-        add("TLS_RSA_PSK_WITH_RC4_128_SHA",                 0x0092);
-        add("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",            0x0093);
-        add("TLS_RSA_PSK_WITH_AES_128_CBC_SHA",             0x0094);
-        add("TLS_RSA_PSK_WITH_AES_256_CBC_SHA",             0x0095);
-
-        // Unsupported cipher suites from RFC 4785
-        add("TLS_PSK_WITH_NULL_SHA",                        0x002c);
-        add("TLS_DHE_PSK_WITH_NULL_SHA",                    0x002d);
-        add("TLS_RSA_PSK_WITH_NULL_SHA",                    0x002e);
-
-        // Unsupported cipher suites from RFC 5246
-        add("TLS_DH_DSS_WITH_AES_128_CBC_SHA",              0x0030);
-        add("TLS_DH_RSA_WITH_AES_128_CBC_SHA",              0x0031);
-        add("TLS_DH_DSS_WITH_AES_256_CBC_SHA",              0x0036);
-        add("TLS_DH_RSA_WITH_AES_256_CBC_SHA",              0x0037);
-        add("TLS_DH_DSS_WITH_AES_128_CBC_SHA256",           0x003e);
-        add("TLS_DH_RSA_WITH_AES_128_CBC_SHA256",           0x003f);
-        add("TLS_DH_DSS_WITH_AES_256_CBC_SHA256",           0x0068);
-        add("TLS_DH_RSA_WITH_AES_256_CBC_SHA256",           0x0069);
-
-        // Unsupported cipher suites from RFC 5288
-        add("TLS_DH_RSA_WITH_AES_128_GCM_SHA256",           0x00a0);
-        add("TLS_DH_RSA_WITH_AES_256_GCM_SHA384",           0x00a1);
-        add("TLS_DH_DSS_WITH_AES_128_GCM_SHA256",           0x00a4);
-        add("TLS_DH_DSS_WITH_AES_256_GCM_SHA384",           0x00a5);
-
-        // Unsupported cipher suites from RFC 5487
-        add("TLS_PSK_WITH_AES_128_GCM_SHA256",              0x00a8);
-        add("TLS_PSK_WITH_AES_256_GCM_SHA384",              0x00a9);
-        add("TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",          0x00aa);
-        add("TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",          0x00ab);
-        add("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",          0x00ac);
-        add("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",          0x00ad);
-        add("TLS_PSK_WITH_AES_128_CBC_SHA256",              0x00ae);
-        add("TLS_PSK_WITH_AES_256_CBC_SHA384",              0x00af);
-        add("TLS_PSK_WITH_NULL_SHA256",                     0x00b0);
-        add("TLS_PSK_WITH_NULL_SHA384",                     0x00b1);
-        add("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",          0x00b2);
-        add("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",          0x00b3);
-        add("TLS_DHE_PSK_WITH_NULL_SHA256",                 0x00b4);
-        add("TLS_DHE_PSK_WITH_NULL_SHA384",                 0x00b5);
-        add("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",          0x00b6);
-        add("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",          0x00b7);
-        add("TLS_RSA_PSK_WITH_NULL_SHA256",                 0x00b8);
-        add("TLS_RSA_PSK_WITH_NULL_SHA384",                 0x00b9);
-
-        // Unsupported cipher suites from RFC 5932
-        add("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",            0x0041);
-        add("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",         0x0042);
-        add("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",         0x0043);
-        add("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",        0x0044);
-        add("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",        0x0045);
-        add("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",        0x0046);
-        add("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",            0x0084);
-        add("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",         0x0085);
-        add("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",         0x0086);
-        add("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",        0x0087);
-        add("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",        0x0088);
-        add("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",        0x0089);
-        add("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",         0x00ba);
-        add("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",      0x00bb);
-        add("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",      0x00bc);
-        add("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",     0x00bd);
-        add("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",     0x00be);
-        add("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",     0x00bf);
-        add("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",         0x00c0);
-        add("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",      0x00c1);
-        add("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",      0x00c2);
-        add("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",     0x00c3);
-        add("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",     0x00c4);
-        add("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",     0x00c5);
-
-        // TLS Fallback Signaling Cipher Suite Value (SCSV) RFC 7507
-        add("TLS_FALLBACK_SCSV", 0x5600);
-
-        // Unsupported cipher suites from RFC 5054
-        add("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",            0xc01a);
-        add("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",        0xc01b);
-        add("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",        0xc01c);
-        add("TLS_SRP_SHA_WITH_AES_128_CBC_SHA",             0xc01d);
-        add("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",         0xc01e);
-        add("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",         0xc01f);
-        add("TLS_SRP_SHA_WITH_AES_256_CBC_SHA",             0xc020);
-        add("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",         0xc021);
-        add("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",         0xc022);
-
-        // Unsupported cipher suites from RFC 5489
-        add("TLS_ECDHE_PSK_WITH_RC4_128_SHA",               0xc033);
-        add("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",          0xc034);
-        add("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",           0xc035);
-        add("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",           0xc036);
-        add("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",        0xc037);
-        add("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",        0xc038);
-        add("TLS_ECDHE_PSK_WITH_NULL_SHA",                  0xc039);
-        add("TLS_ECDHE_PSK_WITH_NULL_SHA256",               0xc03a);
-        add("TLS_ECDHE_PSK_WITH_NULL_SHA384",               0xc03b);
-
-        // Unsupported cipher suites from RFC 6209
-        add("TLS_RSA_WITH_ARIA_128_CBC_SHA256",             0xc03c);
-        add("TLS_RSA_WITH_ARIA_256_CBC_SHA384",             0xc03d);
-        add("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256",          0xc03e);
-        add("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384",          0xc03f);
-        add("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256",          0xc040);
-        add("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384",          0xc041);
-        add("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256",         0xc042);
-        add("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384",         0xc043);
-        add("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256",         0xc044);
-        add("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384",         0xc045);
-        add("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256",         0xc046);
-        add("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384",         0xc047);
-        add("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256",     0xc048);
-        add("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384",     0xc049);
-        add("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256",      0xc04a);
-        add("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384",      0xc04b);
-        add("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256",       0xc04c);
-        add("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384",       0xc04d);
-        add("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256",        0xc04e);
-        add("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384",        0xc04f);
-        add("TLS_RSA_WITH_ARIA_128_GCM_SHA256",             0xc050);
-        add("TLS_RSA_WITH_ARIA_256_GCM_SHA384",             0xc051);
-        add("TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256",         0xc052);
-        add("TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384",         0xc053);
-        add("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256",          0xc054);
-        add("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384",          0xc055);
-        add("TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256",         0xc056);
-        add("TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384",         0xc057);
-        add("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256",          0xc058);
-        add("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384",          0xc059);
-        add("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256",         0xc05a);
-        add("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384",         0xc05b);
-        add("TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256",     0xc05c);
-        add("TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384",     0xc05d);
-        add("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256",      0xc05e);
-        add("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384",      0xc05f);
-        add("TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256",       0xc060);
-        add("TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384",       0xc061);
-        add("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256",        0xc062);
-        add("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384",        0xc063);
-        add("TLS_PSK_WITH_ARIA_128_CBC_SHA256",             0xc064);
-        add("TLS_PSK_WITH_ARIA_256_CBC_SHA384",             0xc065);
-        add("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256",         0xc066);
-        add("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384",         0xc067);
-        add("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256",         0xc068);
-        add("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384",         0xc069);
-        add("TLS_PSK_WITH_ARIA_128_GCM_SHA256",             0xc06a);
-        add("TLS_PSK_WITH_ARIA_256_GCM_SHA384",             0xc06b);
-        add("TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256",         0xc06c);
-        add("TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384",         0xc06d);
-        add("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256",         0xc06e);
-        add("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384",         0xc06f);
-        add("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256",       0xc070);
-        add("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384",       0xc071);
-
-        // Unsupported cipher suites from RFC 6367
-        add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc072);
-        add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc073);
-        add("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",  0xc074);
-        add("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",  0xc075);
-        add("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",   0xc076);
-        add("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",   0xc077);
-        add("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256",    0xc078);
-        add("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384",    0xc079);
-        add("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",         0xc07a);
-        add("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",         0xc07b);
-        add("TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",     0xc07c);
-        add("TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",     0xc07d);
-        add("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256",      0xc07e);
-        add("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384",      0xc07f);
-        add("TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256",     0xc080);
-        add("TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384",     0xc081);
-        add("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256",      0xc082);
-        add("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384",      0xc083);
-        add("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",     0xc084);
-        add("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",     0xc085);
-        add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc086);
-        add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc087);
-        add("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",  0xc088);
-        add("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",  0xc089);
-        add("TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",   0xc08a);
-        add("TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",   0xc08b);
-        add("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256",    0xc08c);
-        add("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384",    0xc08d);
-        add("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",         0xc08e);
-        add("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",         0xc08f);
-        add("TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256",     0xc090);
-        add("TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384",     0xc091);
-        add("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",     0xc092);
-        add("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",     0xc093);
-        add("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",         0xc094);
-        add("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",         0xc095);
-        add("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",     0xc096);
-        add("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",     0xc097);
-        add("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",     0xc098);
-        add("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",     0xc099);
-        add("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",   0xc09a);
-        add("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",   0xc09b);
-
-        // Unsupported cipher suites from RFC 6655
-        add("TLS_RSA_WITH_AES_128_CCM",                     0xc09c);
-        add("TLS_RSA_WITH_AES_256_CCM",                     0xc09d);
-        add("TLS_DHE_RSA_WITH_AES_128_CCM",                 0xc09e);
-        add("TLS_DHE_RSA_WITH_AES_256_CCM",                 0xc09f);
-        add("TLS_RSA_WITH_AES_128_CCM_8",                   0xc0A0);
-        add("TLS_RSA_WITH_AES_256_CCM_8",                   0xc0A1);
-        add("TLS_DHE_RSA_WITH_AES_128_CCM_8",               0xc0A2);
-        add("TLS_DHE_RSA_WITH_AES_256_CCM_8",               0xc0A3);
-        add("TLS_PSK_WITH_AES_128_CCM",                     0xc0A4);
-        add("TLS_PSK_WITH_AES_256_CCM",                     0xc0A5);
-        add("TLS_DHE_PSK_WITH_AES_128_CCM",                 0xc0A6);
-        add("TLS_DHE_PSK_WITH_AES_256_CCM",                 0xc0A7);
-        add("TLS_PSK_WITH_AES_128_CCM_8",                   0xc0A8);
-        add("TLS_PSK_WITH_AES_256_CCM_8",                   0xc0A9);
-        add("TLS_PSK_DHE_WITH_AES_128_CCM_8",               0xc0Aa);
-        add("TLS_PSK_DHE_WITH_AES_256_CCM_8",               0xc0Ab);
-
-        // Unsupported cipher suites from RFC 7251
-        add("TLS_ECDHE_ECDSA_WITH_AES_128_CCM",             0xc0Ac);
-        add("TLS_ECDHE_ECDSA_WITH_AES_256_CCM",             0xc0Ad);
-        add("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",           0xc0Ae);
-        add("TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",           0xc0Af);
-    }
-
-    // ciphersuite SSL_NULL_WITH_NULL_NULL
-    static final CipherSuite C_NULL = CipherSuite.valueOf(0, 0);
-
-    // ciphersuite TLS_EMPTY_RENEGOTIATION_INFO_SCSV
-    static final CipherSuite C_SCSV = CipherSuite.valueOf(0x00, 0xff);
 }
--- a/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-/*
- * Copyright (c) 2002, 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 javax.net.ssl.SSLException;
-import static sun.security.ssl.NamedGroupType.*;
-
-/**
- * A list of CipherSuites. Also maintains the lists of supported and
- * default ciphersuites and supports I/O from handshake streams.
- *
- * Instances of this class are immutable.
- *
- */
-final class CipherSuiteList {
-
-    private final Collection<CipherSuite> cipherSuites;
-    private String[] suiteNames;
-    private final EnumSet<NamedGroupType> groupsTypes =
-            EnumSet.noneOf(NamedGroupType.class);
-
-    // for use by buildAvailableCache() and
-    // Handshaker.getKickstartMessage() only
-    CipherSuiteList(Collection<CipherSuite> cipherSuites) {
-        this.cipherSuites = cipherSuites;
-        for (CipherSuite suite : cipherSuites) {
-            updateGroupTypes(suite);
-        }
-    }
-
-    /**
-     * Create a CipherSuiteList with a single element.
-     */
-    CipherSuiteList(CipherSuite suite) {
-        cipherSuites = new ArrayList<CipherSuite>(1);
-        cipherSuites.add(suite);
-        updateGroupTypes(suite);
-    }
-
-    /**
-     * Construct a CipherSuiteList from a array of names. We don't bother
-     * to eliminate duplicates.
-     *
-     * @exception IllegalArgumentException if the array or any of its elements
-     * is null or if the ciphersuite name is unrecognized or unsupported
-     * using currently installed providers.
-     */
-    CipherSuiteList(String[] names) {
-        if (names == null) {
-            throw new IllegalArgumentException("CipherSuites may not be null");
-        }
-        cipherSuites = new ArrayList<CipherSuite>(names.length);
-        for (int i = 0; i < names.length; i++) {
-            String suiteName = names[i];
-            CipherSuite suite = CipherSuite.valueOf(suiteName);
-            if (suite.isAvailable() == false) {
-                throw new IllegalArgumentException("Cannot support "
-                    + suiteName + " with currently installed providers");
-            }
-            cipherSuites.add(suite);
-            updateGroupTypes(suite);
-        }
-    }
-
-    /**
-     * Read a CipherSuiteList from a HandshakeInStream in V3 ClientHello
-     * format. Does not check if the listed ciphersuites are known or
-     * supported.
-     */
-    CipherSuiteList(HandshakeInStream in) throws IOException {
-        byte[] bytes = in.getBytes16();
-        if ((bytes.length & 1) != 0) {
-            throw new SSLException("Invalid ClientHello message");
-        }
-        cipherSuites = new ArrayList<CipherSuite>(bytes.length >> 1);
-        for (int i = 0; i < bytes.length; i += 2) {
-            CipherSuite suite = CipherSuite.valueOf(bytes[i], bytes[i+1]);
-            cipherSuites.add(suite);
-            updateGroupTypes(suite);
-        }
-    }
-
-    // Please don't use this method except constructors.
-    private void updateGroupTypes(CipherSuite cipherSuite) {
-        if (cipherSuite.keyExchange != null && (!cipherSuite.exportable)) {
-            NamedGroupType groupType = cipherSuite.keyExchange.groupType;
-            if ((groupType != NAMED_GROUP_NONE) &&
-                    (!groupsTypes.contains(groupType))) {
-                groupsTypes.add(groupType);
-            }
-        }
-    }
-
-    /**
-     * Return whether this list contains the given CipherSuite.
-     */
-    boolean contains(CipherSuite suite) {
-        return cipherSuites.contains(suite);
-    }
-
-    // Return whether this list contains cipher suites of a named group type.
-    boolean contains(NamedGroupType groupType) {
-        return groupsTypes.contains(groupType);
-    }
-
-    /**
-     * Return an Iterator for the CipherSuites in this list.
-     */
-    Iterator<CipherSuite> iterator() {
-        return cipherSuites.iterator();
-    }
-
-    /**
-     * Return a reference to the internal Collection of CipherSuites.
-     * The Collection MUST NOT be modified.
-     */
-    Collection<CipherSuite> collection() {
-        return cipherSuites;
-    }
-
-    /**
-     * Return the number of CipherSuites in this list.
-     */
-    int size() {
-        return cipherSuites.size();
-    }
-
-    /**
-     * Return an array with the names of the CipherSuites in this list.
-     */
-    synchronized String[] toStringArray() {
-        if (suiteNames == null) {
-            suiteNames = new String[cipherSuites.size()];
-            int i = 0;
-            for (CipherSuite c : cipherSuites) {
-                suiteNames[i++] = c.name;
-            }
-        }
-        return suiteNames.clone();
-    }
-
-    @Override
-    public String toString() {
-        return cipherSuites.toString();
-    }
-
-    /**
-     * Write this list to an HandshakeOutStream in V3 ClientHello format.
-     */
-    void send(HandshakeOutStream s) throws IOException {
-        byte[] suiteBytes = new byte[cipherSuites.size() * 2];
-        int i = 0;
-        for (CipherSuite c : cipherSuites) {
-            suiteBytes[i] = (byte)(c.id >> 8);
-            suiteBytes[i+1] = (byte)c.id;
-            i += 2;
-        }
-        s.putBytes16(suiteBytes);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CipherType.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+/**
+ * Enum for SSL/(D)TLS cipher types.
+ */
+enum CipherType {
+    NULL_CIPHER,           // null cipher
+    STREAM_CIPHER,         // stream cipher
+    BLOCK_CIPHER,          // block cipher in CBC mode
+    AEAD_CIPHER            // AEAD cipher
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/Ciphertext.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/Ciphertext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,120 +26,30 @@
 package sun.security.ssl;
 
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-import static sun.security.ssl.HandshakeMessage.*;
 
 /*
- * enumeration of record type
+ * Ciphertext
  */
 final class Ciphertext {
     static final Ciphertext CIPHERTEXT_NULL = new Ciphertext();
 
-    RecordType recordType;
-    long recordSN;
+    final byte contentType;
+    final byte handshakeType;
+    final long recordSN;
 
     HandshakeStatus handshakeStatus;    // null if not used or not handshaking
 
-    Ciphertext() {
-        this.recordType = null;
+    private Ciphertext() {
+        this.contentType = 0;
+        this.handshakeType = -1;
         this.recordSN = -1L;
         this.handshakeStatus = null;
     }
 
-    Ciphertext(RecordType recordType, long recordSN) {
-        this.recordType = recordType;
+    Ciphertext(byte contentType, byte handshakeType, long recordSN) {
+        this.contentType = contentType;
+        this.handshakeType = handshakeType;
         this.recordSN = recordSN;
         this.handshakeStatus = null;
     }
-
-    static enum RecordType {
-        RECORD_CHANGE_CIPHER_SPEC (
-                Record.ct_change_cipher_spec, ht_not_applicable),
-        RECORD_ALERT (
-                Record.ct_alert, ht_not_applicable),
-        RECORD_HELLO_REQUEST (
-                Record.ct_handshake, ht_hello_request),
-        RECORD_CLIENT_HELLO (
-                Record.ct_handshake, ht_client_hello),
-        RECORD_SERVER_HELLO (
-                Record.ct_handshake, ht_server_hello),
-        RECORD_HELLO_VERIFY_REQUEST (
-                Record.ct_handshake, ht_hello_verify_request),
-        RECORD_NEW_SESSION_TICKET (
-                Record.ct_handshake, ht_new_session_ticket),
-        RECORD_CERTIFICATE (
-                Record.ct_handshake, ht_certificate),
-        RECORD_SERVER_KEY_EXCHANGE (
-                Record.ct_handshake, ht_server_key_exchange),
-        RECORD_CERTIFICATE_REQUEST (
-                Record.ct_handshake, ht_certificate_request),
-        RECORD_SERVER_HELLO_DONE (
-                Record.ct_handshake, ht_server_hello_done),
-        RECORD_CERTIFICATE_VERIFY (
-                Record.ct_handshake, ht_certificate_verify),
-        RECORD_CLIENT_KEY_EXCHANGE (
-                Record.ct_handshake, ht_client_key_exchange),
-        RECORD_FINISHED (
-                Record.ct_handshake, ht_finished),
-        RECORD_CERTIFICATE_URL (
-                Record.ct_handshake, ht_certificate_url),
-        RECORD_CERTIFICATE_STATUS (
-                Record.ct_handshake, ht_certificate_status),
-        RECORD_SUPPLIEMENTAL_DATA (
-                Record.ct_handshake, ht_supplemental_data),
-        RECORD_APPLICATION_DATA (
-                Record.ct_application_data, ht_not_applicable);
-
-        byte contentType;
-        byte handshakeType;
-
-        private RecordType(byte contentType, byte handshakeType) {
-            this.contentType = contentType;
-            this.handshakeType = handshakeType;
-        }
-
-        static RecordType valueOf(byte contentType, byte handshakeType) {
-            if (contentType == Record.ct_change_cipher_spec) {
-                return RECORD_CHANGE_CIPHER_SPEC;
-            } else if (contentType == Record.ct_alert) {
-                return RECORD_ALERT;
-            } else if (contentType == Record.ct_application_data) {
-                return RECORD_APPLICATION_DATA;
-            } else if (handshakeType == ht_hello_request) {
-                return RECORD_HELLO_REQUEST;
-            } else if (handshakeType == ht_client_hello) {
-                return RECORD_CLIENT_HELLO;
-            } else if (handshakeType == ht_server_hello) {
-                return RECORD_SERVER_HELLO;
-            } else if (handshakeType == ht_hello_verify_request) {
-                return RECORD_HELLO_VERIFY_REQUEST;
-            } else if (handshakeType == ht_new_session_ticket) {
-                return RECORD_NEW_SESSION_TICKET;
-            } else if (handshakeType == ht_certificate) {
-                return RECORD_CERTIFICATE;
-            } else if (handshakeType == ht_server_key_exchange) {
-                return RECORD_SERVER_KEY_EXCHANGE;
-            } else if (handshakeType == ht_certificate_request) {
-                return RECORD_CERTIFICATE_REQUEST;
-            } else if (handshakeType == ht_server_hello_done) {
-                return RECORD_SERVER_HELLO_DONE;
-            } else if (handshakeType == ht_certificate_verify) {
-                return RECORD_CERTIFICATE_VERIFY;
-            } else if (handshakeType == ht_client_key_exchange) {
-                return RECORD_CLIENT_KEY_EXCHANGE;
-            } else if (handshakeType == ht_finished) {
-                return RECORD_FINISHED;
-            } else if (handshakeType == ht_certificate_url) {
-                return RECORD_CERTIFICATE_URL;
-            } else if (handshakeType == ht_certificate_status) {
-                return RECORD_CERTIFICATE_STATUS;
-            } else if (handshakeType == ht_supplemental_data) {
-                return RECORD_SUPPLIEMENTAL_DATA;
-            }
-
-            // otherwise, invalid record type
-            throw new IllegalArgumentException(
-                    "Invalid record type (ContentType:" + contentType +
-                    ", HandshakeType:" + handshakeType + ")");
-        }
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.security.cert.X509Certificate;
+
+import sun.security.ssl.ClientHello.ClientHelloMessage;
+
+class ClientHandshakeContext extends HandshakeContext {
+    /*
+     * Allow unsafe server certificate change?
+     *
+     * Server certificate change during SSL/TLS renegotiation may be considered
+     * unsafe, as described in the Triple Handshake attacks:
+     *
+     *     https://secure-resumption.com/tlsauth.pdf
+     *
+     * Endpoint identification (See
+     * SSLParameters.getEndpointIdentificationAlgorithm()) is a pretty nice
+     * guarantee that the server certificate change in renegotiation is legal.
+     * However, endpoint identification is only enabled for HTTPS and LDAP
+     * over SSL/TLS by default.  It is not enough to protect SSL/TLS
+     * connections other than HTTPS and LDAP.
+     *
+     * The renegotiation indication extension (See RFC 5746) is a pretty
+     * strong guarantee that the endpoints on both client and server sides
+     * are identical on the same connection.  However, the Triple Handshake
+     * attacks can bypass this guarantee if there is a session-resumption
+     * handshake between the initial full handshake and the renegotiation
+     * full handshake.
+     *
+     * Server certificate change may be unsafe and should be restricted if
+     * endpoint identification is not enabled and the previous handshake is
+     * a session-resumption abbreviated initial handshake, unless the
+     * identities represented by both certificates can be regraded as the
+     * same (See isIdentityEquivalent()).
+     *
+     * Considering the compatibility impact and the actual requirements to
+     * support server certificate change in practice, the system property,
+     * jdk.tls.allowUnsafeServerCertChange, is used to define whether unsafe
+     * server certificate change in renegotiation is allowed or not.  The
+     * default value of the system property is "false".  To mitigate the
+     * compatibility impact, applications may want to set the system
+     * property to "true" at their own risk.
+     *
+     * If the value of the system property is "false", server certificate
+     * change in renegotiation after a session-resumption abbreviated initial
+     * handshake is restricted (See isIdentityEquivalent()).
+     *
+     * If the system property is set to "true" explicitly, the restriction on
+     * server certificate change in renegotiation is disabled.
+     */
+    static final boolean allowUnsafeServerCertChange =
+            Utilities.getBooleanProperty(
+                    "jdk.tls.allowUnsafeServerCertChange", false);
+
+    /*
+     * the reserved server certificate chain in previous handshaking
+     *
+     * The server certificate chain is only reserved if the previous
+     * handshake is a session-resumption abbreviated initial handshake.
+     */
+    X509Certificate[] reservedServerCerts = null;
+
+    X509Certificate[] deferredCerts;
+
+    ClientHelloMessage initialClientHelloMsg = null;
+
+    ClientHandshakeContext(SSLContextImpl sslContext,
+            TransportContext conContext) throws IOException {
+        super(sslContext, conContext);
+    }
+
+    @Override
+    void kickstart() throws IOException {
+        if (kickstartMessageDelivered) {
+            return;
+        }
+
+        SSLHandshake.kickstart(this);
+        kickstartMessageDelivered = true;
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2004 +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.math.BigInteger;
-import java.security.*;
-import java.util.*;
-
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.ECParameterSpec;
-
-import java.security.cert.X509Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertPathValidatorException.Reason;
-import java.security.cert.CertPathValidatorException.BasicReason;
-import javax.security.auth.x500.X500Principal;
-
-import javax.crypto.SecretKey;
-
-import javax.net.ssl.*;
-
-import sun.security.ssl.HandshakeMessage.*;
-import static sun.security.ssl.CipherSuite.KeyExchange.*;
-
-/**
- * ClientHandshaker does the protocol handshaking from the point
- * of view of a client.  It is driven asychronously by handshake messages
- * as delivered by the parent Handshaker class, and also uses
- * common functionality (e.g. key generation) that is provided there.
- *
- * @author David Brownell
- */
-final class ClientHandshaker extends Handshaker {
-
-    // constants for subject alt names of type DNS and IP
-    private static final int ALTNAME_DNS = 2;
-    private static final int ALTNAME_IP  = 7;
-
-    // the server's public key from its certificate.
-    private PublicKey serverKey;
-
-    // the server's ephemeral public key from the server key exchange message
-    // for ECDHE/ECDH_anon and RSA_EXPORT.
-    private PublicKey ephemeralServerKey;
-
-    // server's ephemeral public value for DHE/DH_anon key exchanges
-    private BigInteger          serverDH;
-
-    private DHCrypt             dh;
-
-    private ECDHCrypt ecdh;
-
-    private CertificateRequest  certRequest;
-
-    private boolean serverKeyExchangeReceived;
-
-    private boolean staplingActive = false;
-    private X509Certificate[] deferredCerts;
-
-    /*
-     * The RSA PreMasterSecret needs to know the version of
-     * ClientHello that was used on this handshake.  This represents
-     * the "max version" this client is supporting.  In the
-     * case of an initial handshake, it's the max version enabled,
-     * but in the case of a resumption attempt, it's the version
-     * of the session we're trying to resume.
-     */
-    private ProtocolVersion maxProtocolVersion;
-
-    // To switch off the SNI extension.
-    private static final boolean enableSNIExtension =
-            Debug.getBooleanProperty("jsse.enableSNIExtension", true);
-
-    /*
-     * Allow unsafe server certificate change?
-     *
-     * Server certificate change during SSL/TLS renegotiation may be considered
-     * unsafe, as described in the Triple Handshake attacks:
-     *
-     *     https://secure-resumption.com/tlsauth.pdf
-     *
-     * Endpoint identification (See
-     * SSLParameters.getEndpointIdentificationAlgorithm()) is a pretty nice
-     * guarantee that the server certificate change in renegotiation is legal.
-     * However, endpoing identification is only enabled for HTTPS and LDAP
-     * over SSL/TLS by default.  It is not enough to protect SSL/TLS
-     * connections other than HTTPS and LDAP.
-     *
-     * The renegotiation indication extension (See RFC 5764) is a pretty
-     * strong guarantee that the endpoints on both client and server sides
-     * are identical on the same connection.  However, the Triple Handshake
-     * attacks can bypass this guarantee if there is a session-resumption
-     * handshake between the initial full handshake and the renegotiation
-     * full handshake.
-     *
-     * Server certificate change may be unsafe and should be restricted if
-     * endpoint identification is not enabled and the previous handshake is
-     * a session-resumption abbreviated initial handshake, unless the
-     * identities represented by both certificates can be regraded as the
-     * same (See isIdentityEquivalent()).
-     *
-     * Considering the compatibility impact and the actual requirements to
-     * support server certificate change in practice, the system property,
-     * jdk.tls.allowUnsafeServerCertChange, is used to define whether unsafe
-     * server certificate change in renegotiation is allowed or not.  The
-     * default value of the system property is "false".  To mitigate the
-     * compactibility impact, applications may want to set the system
-     * property to "true" at their own risk.
-     *
-     * If the value of the system property is "false", server certificate
-     * change in renegotiation after a session-resumption abbreviated initial
-     * handshake is restricted (See isIdentityEquivalent()).
-     *
-     * If the system property is set to "true" explicitly, the restriction on
-     * server certificate change in renegotiation is disabled.
-     */
-    private static final boolean allowUnsafeServerCertChange =
-        Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false);
-
-    // To switch off the max_fragment_length extension.
-    private static final boolean enableMFLExtension =
-            Debug.getBooleanProperty("jsse.enableMFLExtension", false);
-
-    // To switch off the supported_groups extension for DHE cipher suite.
-    private static final boolean enableFFDHE =
-            Debug.getBooleanProperty("jsse.enableFFDHE", true);
-
-    // Whether an ALPN extension was sent in the ClientHello
-    private boolean alpnActive = false;
-
-    private List<SNIServerName> requestedServerNames =
-            Collections.<SNIServerName>emptyList();
-
-    // maximum fragment length
-    private int requestedMFLength = -1;     // -1: no fragment length limit
-
-    private boolean serverNamesAccepted = false;
-
-    private ClientHello initialClientHelloMsg = null;   // DTLS only
-
-    /*
-     * the reserved server certificate chain in previous handshaking
-     *
-     * The server certificate chain is only reserved if the previous
-     * handshake is a session-resumption abbreviated initial handshake.
-     */
-    private X509Certificate[] reservedServerCerts = null;
-
-    /*
-     * Constructors
-     */
-    ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context,
-            ProtocolList enabledProtocols,
-            ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
-
-        super(socket, context, enabledProtocols, true, true,
-            activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-            clientVerifyData, serverVerifyData);
-    }
-
-    ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context,
-            ProtocolList enabledProtocols,
-            ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData,
-            boolean isDTLS) {
-
-        super(engine, context, enabledProtocols, true, true,
-            activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-            clientVerifyData, serverVerifyData, isDTLS);
-    }
-
-    /*
-     * This routine handles all the client side handshake messages, one at
-     * a time.  Given the message type (and in some cases the pending cipher
-     * spec) it parses the type-specific message.  Then it calls a function
-     * that handles that specific message.
-     *
-     * It updates the state machine (need to verify it) as each message
-     * is processed, and writes responses as needed using the connection
-     * in the constructor.
-     */
-    @Override
-    void processMessage(byte type, int messageLen) throws IOException {
-        // check the handshake state
-        List<Byte> ignoredOptStates = handshakeState.check(type);
-
-        // If the state machine has skipped over certificate status
-        // and stapling was enabled, we need to check the chain immediately
-        // because it was deferred, waiting for CertificateStatus.
-        if (staplingActive && ignoredOptStates.contains(
-                HandshakeMessage.ht_certificate_status)) {
-            checkServerCerts(deferredCerts);
-            serverKey = session.getPeerCertificates()[0].getPublicKey();
-        }
-
-        switch (type) {
-        case HandshakeMessage.ht_hello_request:
-            HelloRequest helloRequest = new HelloRequest(input);
-            handshakeState.update(helloRequest, resumingSession);
-            this.serverHelloRequest(helloRequest);
-            break;
-
-        case HandshakeMessage.ht_hello_verify_request:
-            if (!isDTLS) {
-                throw new SSLProtocolException(
-                    "hello_verify_request is not a SSL/TLS handshake message");
-            }
-
-            HelloVerifyRequest helloVerifyRequest =
-                        new HelloVerifyRequest(input, messageLen);
-            handshakeState.update(helloVerifyRequest, resumingSession);
-            this.helloVerifyRequest(helloVerifyRequest);
-            break;
-
-        case HandshakeMessage.ht_server_hello:
-            ServerHello serverHello = new ServerHello(input, messageLen);
-            this.serverHello(serverHello);
-
-            // This handshake state update needs the resumingSession value
-            // set by serverHello().
-            handshakeState.update(serverHello, resumingSession);
-            break;
-
-        case HandshakeMessage.ht_certificate:
-            if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
-                    || ClientKeyExchangeService.find(keyExchange.name) != null) {
-                // No external key exchange provider needs a cert now.
-                fatalSE(Alerts.alert_unexpected_message,
-                    "unexpected server cert chain");
-                // NOTREACHED
-            }
-            CertificateMsg certificateMsg = new CertificateMsg(input);
-            handshakeState.update(certificateMsg, resumingSession);
-            this.serverCertificate(certificateMsg);
-            if (!staplingActive) {
-                // If we are not doing stapling, we can set serverKey right
-                // away.  Otherwise, we will wait until verification of the
-                // chain has completed after CertificateStatus;
-                serverKey = session.getPeerCertificates()[0].getPublicKey();
-            }
-            break;
-
-        case HandshakeMessage.ht_certificate_status:
-            CertificateStatus certStatusMsg = new CertificateStatus(input);
-            handshakeState.update(certStatusMsg, resumingSession);
-            this.certificateStatus(certStatusMsg);
-            serverKey = session.getPeerCertificates()[0].getPublicKey();
-            break;
-
-        case HandshakeMessage.ht_server_key_exchange:
-            serverKeyExchangeReceived = true;
-            switch (keyExchange) {
-            case K_RSA_EXPORT:
-                /**
-                 * The server key exchange message is sent by the server only
-                 * when the server certificate message does not contain the
-                 * proper amount of data to allow the client to exchange a
-                 * premaster secret, such as when RSA_EXPORT is used and the
-                 * public key in the server certificate is longer than 512 bits.
-                 */
-                if (serverKey == null) {
-                    throw new SSLProtocolException
-                        ("Server did not send certificate message");
-                }
-
-                if (!(serverKey instanceof RSAPublicKey)) {
-                    throw new SSLProtocolException("Protocol violation:" +
-                        " the certificate type must be appropriate for the" +
-                        " selected cipher suite's key exchange algorithm");
-                }
-
-                if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
-                    throw new SSLProtocolException("Protocol violation:" +
-                        " server sent a server key exchange message for" +
-                        " key exchange " + keyExchange +
-                        " when the public key in the server certificate" +
-                        " is less than or equal to 512 bits in length");
-                }
-
-                try {
-                    RSA_ServerKeyExchange rsaSrvKeyExchange =
-                                    new RSA_ServerKeyExchange(input);
-                    handshakeState.update(rsaSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(rsaSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("Server key", e);
-                }
-                break;
-            case K_DH_ANON:
-                try {
-                    DH_ServerKeyExchange dhSrvKeyExchange =
-                            new DH_ServerKeyExchange(input, protocolVersion);
-                    handshakeState.update(dhSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(dhSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("Server key", e);
-                }
-                break;
-            case K_DHE_DSS:
-            case K_DHE_RSA:
-                try {
-                    DH_ServerKeyExchange dhSrvKeyExchange =
-                        new DH_ServerKeyExchange(
-                            input, serverKey,
-                            clnt_random.random_bytes, svr_random.random_bytes,
-                            messageLen,
-                            getLocalSupportedSignAlgs(), protocolVersion);
-                    handshakeState.update(dhSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(dhSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("Server key", e);
-                }
-                break;
-            case K_ECDHE_ECDSA:
-            case K_ECDHE_RSA:
-            case K_ECDH_ANON:
-                try {
-                    ECDH_ServerKeyExchange ecdhSrvKeyExchange =
-                        new ECDH_ServerKeyExchange
-                            (input, serverKey, clnt_random.random_bytes,
-                            svr_random.random_bytes,
-                            getLocalSupportedSignAlgs(), protocolVersion);
-                    handshakeState.update(ecdhSrvKeyExchange, resumingSession);
-                    this.serverKeyExchange(ecdhSrvKeyExchange);
-                } catch (GeneralSecurityException e) {
-                    throw new SSLException("Server key", e);
-                }
-                break;
-            case K_RSA:
-            case K_DH_RSA:
-            case K_DH_DSS:
-            case K_ECDH_ECDSA:
-            case K_ECDH_RSA:
-                throw new SSLProtocolException(
-                    "Protocol violation: server sent a server key exchange"
-                    + " message for key exchange " + keyExchange);
-            default:
-                throw new SSLProtocolException(
-                    "unsupported or unexpected key exchange algorithm = "
-                    + keyExchange);
-            }
-            break;
-
-        case HandshakeMessage.ht_certificate_request:
-            // save for later, it's handled by serverHelloDone
-            if ((keyExchange == K_DH_ANON) || (keyExchange == K_ECDH_ANON)) {
-                throw new SSLHandshakeException(
-                    "Client authentication requested for "+
-                    "anonymous cipher suite.");
-            } else if (ClientKeyExchangeService.find(keyExchange.name) != null) {
-                // No external key exchange provider needs a cert now.
-                throw new SSLHandshakeException(
-                    "Client certificate requested for "+
-                    "external cipher suite: " + keyExchange);
-            }
-            certRequest = new CertificateRequest(input, protocolVersion);
-            if (debug != null && Debug.isOn("handshake")) {
-                certRequest.print(System.out);
-            }
-            handshakeState.update(certRequest, resumingSession);
-
-            if (protocolVersion.useTLS12PlusSpec()) {
-                Collection<SignatureAndHashAlgorithm> peerSignAlgs =
-                                        certRequest.getSignAlgorithms();
-                if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
-                    throw new SSLHandshakeException(
-                        "No peer supported signature algorithms");
-                }
-
-                Collection<SignatureAndHashAlgorithm> supportedPeerSignAlgs =
-                    SignatureAndHashAlgorithm.getSupportedAlgorithms(
-                            algorithmConstraints, peerSignAlgs);
-                if (supportedPeerSignAlgs.isEmpty()) {
-                    throw new SSLHandshakeException(
-                        "No supported signature and hash algorithm in common");
-                }
-
-                setPeerSupportedSignAlgs(supportedPeerSignAlgs);
-                session.setPeerSupportedSignatureAlgorithms(
-                                                supportedPeerSignAlgs);
-            }
-
-            break;
-
-        case HandshakeMessage.ht_server_hello_done:
-            ServerHelloDone serverHelloDone = new ServerHelloDone(input);
-            handshakeState.update(serverHelloDone, resumingSession);
-            this.serverHelloDone(serverHelloDone);
-
-            break;
-
-        case HandshakeMessage.ht_finished:
-            Finished serverFinished =
-                    new Finished(protocolVersion, input, cipherSuite);
-            handshakeState.update(serverFinished, resumingSession);
-            this.serverFinished(serverFinished);
-
-            break;
-
-        default:
-            throw new SSLProtocolException(
-                "Illegal client handshake msg, " + type);
-        }
-    }
-
-    /*
-     * Used by the server to kickstart negotiations -- this requests a
-     * "client hello" to renegotiate current cipher specs (e.g. maybe lots
-     * of data has been encrypted with the same keys, or the server needs
-     * the client to present a certificate).
-     */
-    private void serverHelloRequest(HelloRequest mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        //
-        // Could be (e.g. at connection setup) that we already
-        // sent the "client hello" but the server's not seen it.
-        //
-        if (!clientHelloDelivered) {
-            if (!secureRenegotiation && !allowUnsafeRenegotiation) {
-                // renegotiation is not allowed.
-                if (activeProtocolVersion.useTLS10PlusSpec()) {
-                    // response with a no_renegotiation warning,
-                    warningSE(Alerts.alert_no_renegotiation);
-
-                    // invalidate the handshake so that the caller can
-                    // dispose this object.
-                    invalidated = true;
-
-                    // If there is still unread block in the handshake
-                    // input stream, it would be truncated with the disposal
-                    // and the next handshake message will become incomplete.
-                    //
-                    // However, according to SSL/TLS specifications, no more
-                    // handshake message should immediately follow ClientHello
-                    // or HelloRequest. So just let it be.
-                } else {
-                    // For SSLv3, send the handshake_failure fatal error.
-                    // Note that SSLv3 does not define a no_renegotiation
-                    // alert like TLSv1. However we cannot ignore the message
-                    // simply, otherwise the other side was waiting for a
-                    // response that would never come.
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Renegotiation is not allowed");
-                }
-            } else {
-                if (!secureRenegotiation) {
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println(
-                            "Warning: continue with insecure renegotiation");
-                    }
-                }
-                kickstart();
-            }
-        }
-    }
-
-    private void helloVerifyRequest(
-            HelloVerifyRequest mesg) throws IOException {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        //
-        // Note that HelloVerifyRequest.server_version is used solely to
-        // indicate packet formatting, and not as part of version negotiation.
-        // Need not to check version values match for HelloVerifyRequest
-        // message.
-        //
-        initialClientHelloMsg.cookie = mesg.cookie.clone();
-
-        if (debug != null && Debug.isOn("handshake")) {
-            initialClientHelloMsg.print(System.out);
-        }
-
-        // deliver the ClientHello message with cookie
-        initialClientHelloMsg.write(output);
-        handshakeState.update(initialClientHelloMsg, resumingSession);
-    }
-
-    /*
-     * Server chooses session parameters given options created by the
-     * client -- basically, cipher options, session id, and someday a
-     * set of compression options.
-     *
-     * There are two branches of the state machine, decided by the
-     * details of this message.  One is the "fast" handshake, where we
-     * can resume the pre-existing session we asked resume.  The other
-     * is a more expensive "full" handshake, with key exchange and
-     * probably authentication getting done.
-     */
-    private void serverHello(ServerHello mesg) throws IOException {
-        // Dispose the reserved ClientHello message (if exists).
-        initialClientHelloMsg = null;
-
-        serverKeyExchangeReceived = false;
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        // check if the server selected protocol version is OK for us
-        ProtocolVersion mesgVersion = mesg.protocolVersion;
-        if (!isNegotiable(mesgVersion)) {
-            throw new SSLHandshakeException(
-                "Server chose " + mesgVersion +
-                ", but that protocol version is not enabled or not supported " +
-                "by the client.");
-        }
-
-        handshakeHash.protocolDetermined(mesgVersion);
-
-        // Set protocolVersion and propagate to SSLSocket and the
-        // Handshake streams
-        setVersion(mesgVersion);
-
-        // check the "renegotiation_info" extension
-        RenegotiationInfoExtension serverHelloRI = (RenegotiationInfoExtension)
-                    mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO);
-        if (serverHelloRI != null) {
-            if (isInitialHandshake) {
-                // verify the length of the "renegotiated_connection" field
-                if (!serverHelloRI.isEmpty()) {
-                    // abort the handshake with a fatal handshake_failure alert
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "The renegotiation_info field is not empty");
-                }
-
-                secureRenegotiation = true;
-            } else {
-                // For a legacy renegotiation, the client MUST verify that
-                // it does not contain the "renegotiation_info" extension.
-                if (!secureRenegotiation) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Unexpected renegotiation indication extension");
-                }
-
-                // verify the client_verify_data and server_verify_data values
-                byte[] verifyData =
-                    new byte[clientVerifyData.length + serverVerifyData.length];
-                System.arraycopy(clientVerifyData, 0, verifyData,
-                        0, clientVerifyData.length);
-                System.arraycopy(serverVerifyData, 0, verifyData,
-                        clientVerifyData.length, serverVerifyData.length);
-                if (!MessageDigest.isEqual(verifyData,
-                                serverHelloRI.getRenegotiatedConnection())) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Incorrect verify data in ServerHello " +
-                        "renegotiation_info message");
-                }
-            }
-        } else {
-            // no renegotiation indication extension
-            if (isInitialHandshake) {
-                if (!allowLegacyHelloMessages) {
-                    // abort the handshake with a fatal handshake_failure alert
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Failed to negotiate the use of secure renegotiation");
-                }
-
-                secureRenegotiation = false;
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Warning: No renegotiation " +
-                                    "indication extension in ServerHello");
-                }
-            } else {
-                // For a secure renegotiation, the client must abort the
-                // handshake if no "renegotiation_info" extension is present.
-                if (secureRenegotiation) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "No renegotiation indication extension");
-                }
-
-                // we have already allowed unsafe renegotation before request
-                // the renegotiation.
-            }
-        }
-
-        //
-        // Save server nonce, we always use it to compute connection
-        // keys and it's also used to create the master secret if we're
-        // creating a new session (i.e. in the full handshake).
-        //
-        svr_random = mesg.svr_random;
-
-        if (isNegotiable(mesg.cipherSuite) == false) {
-            fatalSE(Alerts.alert_illegal_parameter,
-                "Server selected improper ciphersuite " + mesg.cipherSuite);
-        }
-
-        setCipherSuite(mesg.cipherSuite);
-        if (protocolVersion.useTLS12PlusSpec()) {
-            handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
-        }
-
-        if (mesg.compression_method != 0) {
-            fatalSE(Alerts.alert_illegal_parameter,
-                "compression type not supported, "
-                + mesg.compression_method);
-            // NOTREACHED
-        }
-
-        // so far so good, let's look at the session
-        if (session != null) {
-            // we tried to resume, let's see what the server decided
-            if (session.getSessionId().equals(mesg.sessionId)) {
-                // server resumed the session, let's make sure everything
-                // checks out
-
-                // Verify that the session ciphers are unchanged.
-                CipherSuite sessionSuite = session.getSuite();
-                if (cipherSuite != sessionSuite) {
-                    throw new SSLProtocolException
-                        ("Server returned wrong cipher suite for session");
-                }
-
-                // verify protocol version match
-                ProtocolVersion sessionVersion = session.getProtocolVersion();
-                if (protocolVersion != sessionVersion) {
-                    throw new SSLProtocolException
-                        ("Server resumed session with wrong protocol version");
-                }
-
-                // validate subject identity
-                ClientKeyExchangeService p =
-                        ClientKeyExchangeService.find(
-                                sessionSuite.keyExchange.name);
-                if (p != null) {
-                    Principal localPrincipal = session.getLocalPrincipal();
-
-                    if (p.isRelated(true, getAccSE(), localPrincipal)) {
-                        if (debug != null && Debug.isOn("session"))
-                            System.out.println("Subject identity is same");
-                    } else {
-                        throw new SSLProtocolException(
-                                "Server resumed session with " +
-                                "wrong subject identity or no subject");
-                    }
-                }
-
-                // looks fine; resume it.
-                resumingSession = true;
-                calculateConnectionKeys(session.getMasterSecret());
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% Server resumed " + session);
-                }
-            } else {
-                // we wanted to resume, but the server refused
-                //
-                // Invalidate the session for initial handshake in case
-                // of reusing next time.
-                if (isInitialHandshake) {
-                    session.invalidate();
-                }
-                session = null;
-                if (!enableNewSession) {
-                    throw new SSLException("New session creation is disabled");
-                }
-            }
-        }
-
-        // check the "max_fragment_length" extension
-        MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
-                mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
-        if (maxFragLenExt != null) {
-            if ((requestedMFLength == -1) ||
-                    maxFragLenExt.getMaxFragLen() != requestedMFLength) {
-                // If the client did not request this extension, or the
-                // response value is different from the length it requested,
-                // abort the handshake with a fatal illegal_parameter alert.
-                fatalSE(Alerts.alert_illegal_parameter,
-                        "Failed to negotiate the max_fragment_length");
-            }
-        } else if (!resumingSession) {
-            // no "max_fragment_length" extension
-            requestedMFLength = -1;
-        }   // Otherwise, using the value negotiated during the original
-            // session initiation
-
-        // check the "extended_master_secret" extension
-        ExtendedMasterSecretExtension extendedMasterSecretExt =
-                (ExtendedMasterSecretExtension)mesg.extensions.get(
-                        ExtensionType.EXT_EXTENDED_MASTER_SECRET);
-        if (extendedMasterSecretExt != null) {
-            // Is it the expected server extension?
-            if (!useExtendedMasterSecret ||
-                    !mesgVersion.useTLS10PlusSpec() || !requestedToUseEMS) {
-                fatalSE(Alerts.alert_unsupported_extension,
-                        "Server sent the extended_master_secret " +
-                        "extension improperly");
-            }
-
-            // For abbreviated handshake, if the original session did not use
-            // the "extended_master_secret" extension but the new ServerHello
-            // contains the extension, the client MUST abort the handshake.
-            if (resumingSession && (session != null) &&
-                    !session.getUseExtendedMasterSecret()) {
-                fatalSE(Alerts.alert_unsupported_extension,
-                        "Server sent an unexpected extended_master_secret " +
-                        "extension on session resumption");
-            }
-        } else {
-            if (useExtendedMasterSecret && !allowLegacyMasterSecret) {
-                // For full handshake, if a client receives a ServerHello
-                // without the extension, it SHOULD abort the handshake if
-                // it does not wish to interoperate with legacy servers.
-                fatalSE(Alerts.alert_handshake_failure,
-                    "Extended Master Secret extension is required");
-            }
-
-            if (resumingSession && (session != null)) {
-                if (session.getUseExtendedMasterSecret()) {
-                    // For abbreviated handshake, if the original session used
-                    // the "extended_master_secret" extension but the new
-                    // ServerHello does not contain the extension, the client
-                    // MUST abort the handshake.
-                    fatalSE(Alerts.alert_handshake_failure,
-                            "Missing Extended Master Secret extension " +
-                            "on session resumption");
-                } else if (useExtendedMasterSecret && !allowLegacyResumption) {
-                    // Unlikely, abbreviated handshake should be discarded.
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Extended Master Secret extension is required");
-                }
-            }
-        }
-
-        // check the ALPN extension
-        ALPNExtension serverHelloALPN =
-            (ALPNExtension) mesg.extensions.get(ExtensionType.EXT_ALPN);
-
-        if (serverHelloALPN != null) {
-            // Check whether an ALPN extension was sent in ClientHello message
-            if (!alpnActive) {
-                fatalSE(Alerts.alert_unsupported_extension,
-                    "Server sent " + ExtensionType.EXT_ALPN +
-                    " extension when not requested by client");
-            }
-
-            List<String> protocols = serverHelloALPN.getPeerAPs();
-            // Only one application protocol name should be present
-            String p;
-            if ((protocols.size() == 1) &&
-                    !((p = protocols.get(0)).isEmpty())) {
-                int i;
-                for (i = 0; i < localApl.length; i++) {
-                    if (localApl[i].equals(p)) {
-                        break;
-                    }
-                }
-                if (i == localApl.length) {
-                    fatalSE(Alerts.alert_handshake_failure,
-                        "Server has selected an application protocol name " +
-                        "which was not offered by the client: " + p);
-                }
-                applicationProtocol = p;
-            } else {
-                fatalSE(Alerts.alert_handshake_failure,
-                    "Incorrect data in ServerHello " + ExtensionType.EXT_ALPN +
-                    " message");
-            }
-        } else {
-            applicationProtocol = "";
-        }
-
-        if (resumingSession && session != null) {
-            setHandshakeSessionSE(session);
-            // Reserve the handshake state if this is a session-resumption
-            // abbreviated initial handshake.
-            if (isInitialHandshake) {
-                session.setAsSessionResumption(true);
-            }
-
-            return;
-        }
-
-        // check extensions
-        for (HelloExtension ext : mesg.extensions.list()) {
-            ExtensionType type = ext.type;
-            if (type == ExtensionType.EXT_SERVER_NAME) {
-                serverNamesAccepted = true;
-            } else if (type == ExtensionType.EXT_STATUS_REQUEST ||
-                    type == ExtensionType.EXT_STATUS_REQUEST_V2) {
-                // Only enable the stapling feature if the client asserted
-                // these extensions.
-                if (sslContext.isStaplingEnabled(true)) {
-                    staplingActive = true;
-                } else {
-                    fatalSE(Alerts.alert_unexpected_message, "Server set " +
-                            type + " extension when not requested by client");
-                }
-            } else if ((type != ExtensionType.EXT_SUPPORTED_GROUPS)
-                    && (type != ExtensionType.EXT_EC_POINT_FORMATS)
-                    && (type != ExtensionType.EXT_SERVER_NAME)
-                    && (type != ExtensionType.EXT_ALPN)
-                    && (type != ExtensionType.EXT_RENEGOTIATION_INFO)
-                    && (type != ExtensionType.EXT_STATUS_REQUEST)
-                    && (type != ExtensionType.EXT_STATUS_REQUEST_V2)
-                    && (type != ExtensionType.EXT_EXTENDED_MASTER_SECRET)) {
-                // Note: Better to check client requested extensions rather
-                // than all supported extensions.
-                fatalSE(Alerts.alert_unsupported_extension,
-                    "Server sent an unsupported extension: " + type);
-            }
-        }
-
-        // Create a new session, we need to do the full handshake
-        session = new SSLSessionImpl(protocolVersion, cipherSuite,
-                            getLocalSupportedSignAlgs(),
-                            mesg.sessionId, getHostSE(), getPortSE(),
-                            (extendedMasterSecretExt != null));
-        session.setRequestedServerNames(requestedServerNames);
-        session.setNegotiatedMaxFragSize(requestedMFLength);
-        session.setMaximumPacketSize(maximumPacketSize);
-        setHandshakeSessionSE(session);
-        if (debug != null && Debug.isOn("handshake")) {
-            System.out.println("** " + cipherSuite);
-        }
-    }
-
-    /*
-     * Server's own key was either a signing-only key, or was too
-     * large for export rules ... this message holds an ephemeral
-     * RSA key to use for key exchange.
-     */
-    private void serverKeyExchange(RSA_ServerKeyExchange mesg)
-            throws IOException, GeneralSecurityException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        if (!mesg.verify(serverKey, clnt_random, svr_random)) {
-            fatalSE(Alerts.alert_handshake_failure,
-                "server key exchange invalid");
-            // NOTREACHED
-        }
-        ephemeralServerKey = mesg.getPublicKey();
-
-        // check constraints of RSA PublicKey
-        if (!algorithmConstraints.permits(
-            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), ephemeralServerKey)) {
-
-            throw new SSLHandshakeException("RSA ServerKeyExchange " +
-                    "does not comply to algorithm constraints");
-        }
-    }
-
-    /*
-     * Diffie-Hellman key exchange.  We save the server public key and
-     * our own D-H algorithm object so we can defer key calculations
-     * until after we've sent the client key exchange message (which
-     * gives client and server some useful parallelism).
-     *
-     * Note per section 3 of RFC 7919, if the server is not compatible with
-     * FFDHE specification, the client MAY decide to continue the connection
-     * if the selected DHE group is acceptable under local policy, or it MAY
-     * decide to terminate the connection with a fatal insufficient_security
-     * (71) alert.  The algorithm constraints mechanism is JDK local policy
-     * used for additional DHE parameters checking.  So this implementation
-     * does not check the server compatibility and just pass to the local
-     * algorithm constraints checking.  The client will continue the
-     * connection if the server selected DHE group is acceptable by the
-     * specified algorithm constraints.
-     */
-    private void serverKeyExchange(DH_ServerKeyExchange mesg)
-            throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        dh = new DHCrypt(mesg.getModulus(), mesg.getBase(),
-                                            sslContext.getSecureRandom());
-        serverDH = mesg.getServerPublicKey();
-
-        // check algorithm constraints
-        dh.checkConstraints(algorithmConstraints, serverDH);
-    }
-
-    private void serverKeyExchange(ECDH_ServerKeyExchange mesg)
-            throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        ECPublicKey key = mesg.getPublicKey();
-        ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom());
-        ephemeralServerKey = key;
-
-        // check constraints of EC PublicKey
-        if (!algorithmConstraints.permits(
-            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), ephemeralServerKey)) {
-
-            throw new SSLHandshakeException("ECDH ServerKeyExchange " +
-                    "does not comply to algorithm constraints");
-        }
-    }
-
-    /*
-     * The server's "Hello Done" message is the client's sign that
-     * it's time to do all the hard work.
-     */
-    private void serverHelloDone(ServerHelloDone mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        /*
-         * FIRST ... if requested, send an appropriate Certificate chain
-         * to authenticate the client, and remember the associated private
-         * key to sign the CertificateVerify message.
-         */
-        PrivateKey signingKey = null;
-
-        if (certRequest != null) {
-            X509ExtendedKeyManager km = sslContext.getX509KeyManager();
-
-            ArrayList<String> keytypesTmp = new ArrayList<>(4);
-
-            for (int i = 0; i < certRequest.types.length; i++) {
-                String typeName;
-
-                switch (certRequest.types[i]) {
-                    case CertificateRequest.cct_rsa_sign:
-                        typeName = "RSA";
-                        break;
-
-                    case CertificateRequest.cct_dss_sign:
-                        typeName = "DSA";
-                            break;
-
-                    case CertificateRequest.cct_ecdsa_sign:
-                        // ignore if we do not have EC crypto available
-                        typeName = JsseJce.isEcAvailable() ? "EC" : null;
-                        break;
-
-                    // Fixed DH/ECDH client authentication not supported
-                    //
-                    // case CertificateRequest.cct_rsa_fixed_dh:
-                    // case CertificateRequest.cct_dss_fixed_dh:
-                    // case CertificateRequest.cct_rsa_fixed_ecdh:
-                    // case CertificateRequest.cct_ecdsa_fixed_ecdh:
-                    //
-                    // Any other values (currently not used in TLS)
-                    //
-                    // case CertificateRequest.cct_rsa_ephemeral_dh:
-                    // case CertificateRequest.cct_dss_ephemeral_dh:
-                    default:
-                        typeName = null;
-                        break;
-                }
-
-                if ((typeName != null) && (!keytypesTmp.contains(typeName))) {
-                    keytypesTmp.add(typeName);
-                }
-            }
-
-            String alias = null;
-            int keytypesTmpSize = keytypesTmp.size();
-            if (keytypesTmpSize != 0) {
-                String[] keytypes =
-                        keytypesTmp.toArray(new String[keytypesTmpSize]);
-
-                if (conn != null) {
-                    alias = km.chooseClientAlias(keytypes,
-                        certRequest.getAuthorities(), conn);
-                } else {
-                    alias = km.chooseEngineClientAlias(keytypes,
-                        certRequest.getAuthorities(), engine);
-                }
-            }
-
-            CertificateMsg m1 = null;
-            if (alias != null) {
-                X509Certificate[] certs = km.getCertificateChain(alias);
-                if ((certs != null) && (certs.length != 0)) {
-                    PublicKey publicKey = certs[0].getPublicKey();
-                    if (publicKey != null) {
-                        m1 = new CertificateMsg(certs);
-                        signingKey = km.getPrivateKey(alias);
-                        session.setLocalPrivateKey(signingKey);
-                        session.setLocalCertificates(certs);
-                    }
-                }
-            }
-            if (m1 == null) {
-                //
-                // No appropriate cert was found ... report this to the
-                // server.  For SSLv3, send the no_certificate alert;
-                // TLS uses an empty cert chain instead.
-                //
-                if (protocolVersion.useTLS10PlusSpec()) {
-                    m1 = new CertificateMsg(new X509Certificate [0]);
-                } else {
-                    warningSE(Alerts.alert_no_certificate);
-                }
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println(
-                        "Warning: no suitable certificate found - " +
-                        "continuing without client authentication");
-                }
-            }
-
-            //
-            // At last ... send any client certificate chain.
-            //
-            if (m1 != null) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    m1.print(System.out);
-                }
-                m1.write(output);
-                handshakeState.update(m1, resumingSession);
-            }
-        }
-
-        /*
-         * SECOND ... send the client key exchange message.  The
-         * procedure used is a function of the cipher suite selected;
-         * one is always needed.
-         */
-        HandshakeMessage m2;
-
-        switch (keyExchange) {
-
-        case K_RSA:
-        case K_RSA_EXPORT:
-            if (serverKey == null) {
-                throw new SSLProtocolException
-                        ("Server did not send certificate message");
-            }
-
-            if (!(serverKey instanceof RSAPublicKey)) {
-                throw new SSLProtocolException
-                        ("Server certificate does not include an RSA key");
-            }
-
-            /*
-             * For RSA key exchange, we randomly generate a new
-             * pre-master secret and encrypt it with the server's
-             * public key.  Then we save that pre-master secret
-             * so that we can calculate the keying data later;
-             * it's a performance speedup not to do that until
-             * the client's waiting for the server response, but
-             * more of a speedup for the D-H case.
-             *
-             * If the RSA_EXPORT scheme is active, when the public
-             * key in the server certificate is less than or equal
-             * to 512 bits in length, use the cert's public key,
-             * otherwise, the ephemeral one.
-             */
-            PublicKey key;
-            if (keyExchange == K_RSA) {
-                key = serverKey;
-            } else {    // K_RSA_EXPORT
-                if (JsseJce.getRSAKeyLength(serverKey) <= 512) {
-                    // extraneous ephemeralServerKey check done
-                    // above in processMessage()
-                    key = serverKey;
-                } else {
-                    if (ephemeralServerKey == null) {
-                        throw new SSLProtocolException("Server did not send" +
-                            " a RSA_EXPORT Server Key Exchange message");
-                    }
-                    key = ephemeralServerKey;
-                }
-            }
-
-            m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion,
-                                sslContext.getSecureRandom(), key);
-            break;
-        case K_DH_RSA:
-        case K_DH_DSS:
-            /*
-             * For DH Key exchange, we only need to make sure the server
-             * knows our public key, so we calculate the same pre-master
-             * secret.
-             *
-             * For certs that had DH keys in them, we send an empty
-             * handshake message (no key) ... we flag this case by
-             * passing a null "dhPublic" value.
-             *
-             * Otherwise we send ephemeral DH keys, unsigned.
-             */
-            // if (useDH_RSA || useDH_DSS)
-            m2 = new DHClientKeyExchange();
-            break;
-        case K_DHE_RSA:
-        case K_DHE_DSS:
-        case K_DH_ANON:
-            if (dh == null) {
-                throw new SSLProtocolException
-                    ("Server did not send a DH Server Key Exchange message");
-            }
-            m2 = new DHClientKeyExchange(dh.getPublicKey());
-            break;
-        case K_ECDHE_RSA:
-        case K_ECDHE_ECDSA:
-        case K_ECDH_ANON:
-            if (ecdh == null) {
-                throw new SSLProtocolException
-                    ("Server did not send a ECDH Server Key Exchange message");
-            }
-            m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
-            break;
-        case K_ECDH_RSA:
-        case K_ECDH_ECDSA:
-            if (serverKey == null) {
-                throw new SSLProtocolException
-                        ("Server did not send certificate message");
-            }
-            if (serverKey instanceof ECPublicKey == false) {
-                throw new SSLProtocolException
-                        ("Server certificate does not include an EC key");
-            }
-            ECParameterSpec params = ((ECPublicKey)serverKey).getParams();
-            ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
-            m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
-            break;
-        default:
-            ClientKeyExchangeService p =
-                    ClientKeyExchangeService.find(keyExchange.name);
-            if (p == null) {
-                // somethings very wrong
-                throw new RuntimeException
-                        ("Unsupported key exchange: " + keyExchange);
-            }
-            String sniHostname = null;
-            for (SNIServerName serverName : requestedServerNames) {
-                if (serverName instanceof SNIHostName) {
-                    sniHostname = ((SNIHostName) serverName).getAsciiName();
-                    break;
-                }
-            }
-
-            ClientKeyExchange exMsg = null;
-            if (sniHostname != null) {
-                // use first requested SNI hostname
-                try {
-                    exMsg = p.createClientExchange(
-                            sniHostname, getAccSE(), protocolVersion,
-                            sslContext.getSecureRandom());
-                } catch(IOException e) {
-                    if (serverNamesAccepted) {
-                        // server accepted requested SNI hostname,
-                        // so it must be used
-                        throw e;
-                    }
-                    // fallback to using hostname
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println(
-                            "Warning, cannot use Server Name Indication: "
-                                + e.getMessage());
-                    }
-                }
-            }
-
-            if (exMsg == null) {
-                String hostname = getHostSE();
-                if (hostname == null) {
-                    throw new IOException("Hostname is required" +
-                        " to use " + keyExchange + " key exchange");
-                }
-                exMsg = p.createClientExchange(
-                        hostname, getAccSE(), protocolVersion,
-                        sslContext.getSecureRandom());
-            }
-
-            // Record the principals involved in exchange
-            session.setPeerPrincipal(exMsg.getPeerPrincipal());
-            session.setLocalPrincipal(exMsg.getLocalPrincipal());
-            m2 = exMsg;
-            break;
-        }
-        if (debug != null && Debug.isOn("handshake")) {
-            m2.print(System.out);
-        }
-        m2.write(output);
-        handshakeState.update(m2, resumingSession);
-
-        /*
-         * THIRD, send a "change_cipher_spec" record followed by the
-         * "Finished" message.  We flush the messages we've queued up, to
-         * get concurrency between client and server.  The concurrency is
-         * useful as we calculate the master secret, which is needed both
-         * to compute the "Finished" message, and to compute the keys used
-         * to protect all records following the change_cipher_spec.
-         */
-        output.flush();
-
-        /*
-         * We deferred calculating the master secret and this connection's
-         * keying data; we do it now.  Deferring this calculation is good
-         * from a performance point of view, since it lets us do it during
-         * some time that network delays and the server's own calculations
-         * would otherwise cause to be "dead" in the critical path.
-         */
-        SecretKey preMasterSecret;
-        switch (keyExchange) {
-        case K_RSA:
-        case K_RSA_EXPORT:
-            preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
-            break;
-        case K_DHE_RSA:
-        case K_DHE_DSS:
-        case K_DH_ANON:
-            preMasterSecret = dh.getAgreedSecret(serverDH, true);
-            break;
-        case K_ECDHE_RSA:
-        case K_ECDHE_ECDSA:
-        case K_ECDH_ANON:
-            preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey);
-            break;
-        case K_ECDH_RSA:
-        case K_ECDH_ECDSA:
-            preMasterSecret = ecdh.getAgreedSecret(serverKey);
-            break;
-        default:
-            if (ClientKeyExchangeService.find(keyExchange.name) != null) {
-                preMasterSecret =
-                        ((ClientKeyExchange) m2).clientKeyExchange();
-            } else {
-                throw new IOException("Internal error: unknown key exchange "
-                        + keyExchange);
-            }
-        }
-
-        calculateKeys(preMasterSecret, null);
-
-        /*
-         * FOURTH, if we sent a Certificate, we need to send a signed
-         * CertificateVerify (unless the key in the client's certificate
-         * was a Diffie-Hellman key).
-         *
-         * This uses a hash of the previous handshake messages ... either
-         * a nonfinal one (if the particular implementation supports it)
-         * or else using the third element in the arrays of hashes being
-         * computed.
-         */
-        if (signingKey != null) {
-            CertificateVerify m3;
-            try {
-                SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
-                if (protocolVersion.useTLS12PlusSpec()) {
-                    preferableSignatureAlgorithm =
-                        SignatureAndHashAlgorithm.getPreferableAlgorithm(
-                            getPeerSupportedSignAlgs(),
-                            signingKey.getAlgorithm(), signingKey);
-
-                    if (preferableSignatureAlgorithm == null) {
-                        throw new SSLHandshakeException(
-                            "No supported signature algorithm");
-                    }
-
-                    String hashAlg =
-                        SignatureAndHashAlgorithm.getHashAlgorithmName(
-                                preferableSignatureAlgorithm);
-                    if (hashAlg == null || hashAlg.length() == 0) {
-                        throw new SSLHandshakeException(
-                                "No supported hash algorithm");
-                    }
-                }
-
-                m3 = new CertificateVerify(protocolVersion, handshakeHash,
-                    signingKey, session.getMasterSecret(),
-                    sslContext.getSecureRandom(),
-                    preferableSignatureAlgorithm);
-            } catch (GeneralSecurityException e) {
-                fatalSE(Alerts.alert_handshake_failure,
-                    "Error signing certificate verify", e);
-                // NOTREACHED, make compiler happy
-                m3 = null;
-            }
-            if (debug != null && Debug.isOn("handshake")) {
-                m3.print(System.out);
-            }
-            m3.write(output);
-            handshakeState.update(m3, resumingSession);
-            output.flush();
-        }
-
-        /*
-         * OK, that's that!
-         */
-        sendChangeCipherAndFinish(false);
-
-        // expecting the final ChangeCipherSpec and Finished messages
-        expectingFinishFlightSE();
-    }
-
-
-    /*
-     * "Finished" is the last handshake message sent.  If we got this
-     * far, the MAC has been validated post-decryption.  We validate
-     * the two hashes here as an additional sanity check, protecting
-     * the handshake against various active attacks.
-     */
-    private void serverFinished(Finished mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        boolean verified = mesg.verify(handshakeHash, Finished.SERVER,
-            session.getMasterSecret());
-
-        if (!verified) {
-            fatalSE(Alerts.alert_illegal_parameter,
-                       "server 'finished' message doesn't verify");
-            // NOTREACHED
-        }
-
-        /*
-         * save server verify data for secure renegotiation
-         */
-        if (secureRenegotiation) {
-            serverVerifyData = mesg.getVerifyData();
-        }
-
-        /*
-         * Reset the handshake state if this is not an initial handshake.
-         */
-        if (!isInitialHandshake) {
-            session.setAsSessionResumption(false);
-        }
-
-        /*
-         * OK, it verified.  If we're doing the fast handshake, add that
-         * "Finished" message to the hash of handshake messages, then send
-         * our own change_cipher_spec and Finished message for the server
-         * to verify in turn.  These are the last handshake messages.
-         *
-         * In any case, update the session cache.  We're done handshaking,
-         * so there are no threats any more associated with partially
-         * completed handshakes.
-         */
-        if (resumingSession) {
-            sendChangeCipherAndFinish(true);
-        } else {
-            handshakeFinished = true;
-        }
-        session.setLastAccessedTime(System.currentTimeMillis());
-
-        if (!resumingSession) {
-            if (session.isRejoinable()) {
-                ((SSLSessionContextImpl) sslContext
-                        .engineGetClientSessionContext())
-                        .put(session);
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% Cached client session: " + session);
-                }
-            } else if (debug != null && Debug.isOn("session")) {
-                System.out.println(
-                    "%% Didn't cache non-resumable client session: "
-                    + session);
-            }
-        }
-    }
-
-
-    /*
-     * Send my change-cipher-spec and Finished message ... done as the
-     * last handshake act in either the short or long sequences.  In
-     * the short one, we've already seen the server's Finished; in the
-     * long one, we wait for it now.
-     */
-    private void sendChangeCipherAndFinish(boolean finishedTag)
-            throws IOException {
-
-        // Reload if this message has been reserved.
-        handshakeHash.reload();
-
-        Finished mesg = new Finished(protocolVersion, handshakeHash,
-            Finished.CLIENT, session.getMasterSecret(), cipherSuite);
-
-        /*
-         * Send the change_cipher_spec message, then the Finished message
-         * which we just calculated (and protected using the keys we just
-         * calculated).  Server responds with its Finished message, except
-         * in the "fast handshake" (resume session) case.
-         */
-        sendChangeCipherSpec(mesg, finishedTag);
-
-        /*
-         * save client verify data for secure renegotiation
-         */
-        if (secureRenegotiation) {
-            clientVerifyData = mesg.getVerifyData();
-        }
-    }
-
-
-    /*
-     * Returns a ClientHello message to kickstart renegotiations
-     */
-    @Override
-    HandshakeMessage getKickstartMessage() throws SSLException {
-        // session ID of the ClientHello message
-        SessionId sessionId = SSLSessionImpl.nullSession.getSessionId();
-
-        // a list of cipher suites sent by the client
-        CipherSuiteList cipherSuites = getActiveCipherSuites();
-
-        // set the max protocol version this client is supporting.
-        maxProtocolVersion = protocolVersion;
-
-        //
-        // Try to resume an existing session.  This might be mandatory,
-        // given certain API options.
-        //
-        session = ((SSLSessionContextImpl)sslContext
-                        .engineGetClientSessionContext())
-                        .get(getHostSE(), getPortSE());
-        if (debug != null && Debug.isOn("session")) {
-            if (session != null) {
-                System.out.println("%% Client cached "
-                    + session
-                    + (session.isRejoinable() ? "" : " (not rejoinable)"));
-            } else {
-                System.out.println("%% No cached client session");
-            }
-        }
-        if (session != null) {
-            // If unsafe server certificate change is not allowed, reserve
-            // current server certificates if the previous handshake is a
-            // session-resumption abbreviated initial handshake.
-            if (!allowUnsafeServerCertChange && session.isSessionResumption()) {
-                try {
-                    // If existing, peer certificate chain cannot be null.
-                    reservedServerCerts =
-                        (X509Certificate[])session.getPeerCertificates();
-                } catch (SSLPeerUnverifiedException puve) {
-                    // Maybe not certificate-based, ignore the exception.
-                }
-            }
-
-            if (!session.isRejoinable()) {
-                session = null;
-            }
-        }
-
-        if (session != null) {
-            CipherSuite sessionSuite = session.getSuite();
-            ProtocolVersion sessionVersion = session.getProtocolVersion();
-            if (isNegotiable(sessionSuite) == false) {
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% can't resume, unavailable cipher");
-                }
-                session = null;
-            }
-
-            if ((session != null) && !isNegotiable(sessionVersion)) {
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("%% can't resume, protocol disabled");
-                }
-                session = null;
-            }
-
-            if ((session != null) && useExtendedMasterSecret) {
-                boolean isTLS10Plus = sessionVersion.useTLS10PlusSpec();
-                if (isTLS10Plus && !session.getUseExtendedMasterSecret()) {
-                    if (!allowLegacyResumption) {
-                        // perform full handshake instead
-                        //
-                        // The client SHOULD NOT offer an abbreviated handshake
-                        // to resume a session that does not use an extended
-                        // master secret.  Instead, it SHOULD offer a full
-                        // handshake.
-                        session = null;
-                    }
-                }
-
-                if ((session != null) && !allowUnsafeServerCertChange) {
-                    // It is fine to move on with abbreviate handshake if
-                    // endpoint identification is enabled.
-                    String identityAlg = getEndpointIdentificationAlgorithmSE();
-                    if ((identityAlg == null || identityAlg.length() == 0)) {
-                        if (isTLS10Plus) {
-                            if (!session.getUseExtendedMasterSecret()) {
-                                // perform full handshake instead
-                                session = null;
-                            }   // Otherwise, use extended master secret.
-                        } else {
-                            // The extended master secret extension does not
-                            // apply to SSL 3.0.  Perform a full handshake
-                            // instead.
-                            //
-                            // Note that the useExtendedMasterSecret is
-                            // extended to protect SSL 3.0 connections,
-                            // by discarding abbreviate handshake.
-                            session = null;
-                        }
-                    }
-                }
-            }
-
-            if (session != null) {
-                if (debug != null) {
-                    if (Debug.isOn("handshake") || Debug.isOn("session")) {
-                        System.out.println("%% Try resuming " + session
-                            + " from port " + getLocalPortSE());
-                    }
-                }
-
-                sessionId = session.getSessionId();
-                maxProtocolVersion = sessionVersion;
-
-                // Update SSL version number in underlying SSL socket and
-                // handshake output stream, so that the output records (at the
-                // record layer) have the correct version
-                setVersion(sessionVersion);
-            }
-
-            /*
-             * Force use of the previous session ciphersuite, and
-             * add the SCSV if enabled.
-             */
-            if (!enableNewSession) {
-                if (session == null) {
-                    throw new SSLHandshakeException(
-                        "Can't reuse existing SSL client session");
-                }
-
-                Collection<CipherSuite> cipherList = new ArrayList<>(2);
-                cipherList.add(sessionSuite);
-                if (!secureRenegotiation &&
-                        cipherSuites.contains(CipherSuite.C_SCSV)) {
-                    cipherList.add(CipherSuite.C_SCSV);
-                }   // otherwise, renegotiation_info extension will be used
-
-                cipherSuites = new CipherSuiteList(cipherList);
-            }
-        }
-
-        if (session == null && !enableNewSession) {
-            throw new SSLHandshakeException("No existing session to resume");
-        }
-
-        // exclude SCSV for secure renegotiation
-        if (secureRenegotiation && cipherSuites.contains(CipherSuite.C_SCSV)) {
-            Collection<CipherSuite> cipherList =
-                        new ArrayList<>(cipherSuites.size() - 1);
-            for (CipherSuite suite : cipherSuites.collection()) {
-                if (suite != CipherSuite.C_SCSV) {
-                    cipherList.add(suite);
-                }
-            }
-
-            cipherSuites = new CipherSuiteList(cipherList);
-        }
-
-        // make sure there is a negotiable cipher suite.
-        boolean negotiable = false;
-        for (CipherSuite suite : cipherSuites.collection()) {
-            if (isNegotiable(suite)) {
-                negotiable = true;
-                break;
-            }
-        }
-
-        if (!negotiable) {
-            throw new SSLHandshakeException("No negotiable cipher suite");
-        }
-
-        // Not a TLS1.2+ handshake
-        // For SSLv2Hello, HandshakeHash.reset() will be called, so we
-        // cannot call HandshakeHash.protocolDetermined() here. As it does
-        // not follow the spec that HandshakeHash.reset() can be only be
-        // called before protocolDetermined.
-        // if (maxProtocolVersion.v < ProtocolVersion.TLS12.v) {
-        //     handshakeHash.protocolDetermined(maxProtocolVersion);
-        // }
-
-        // create the ClientHello message
-        ClientHello clientHelloMessage = new ClientHello(
-                sslContext.getSecureRandom(), maxProtocolVersion,
-                sessionId, cipherSuites, isDTLS);
-
-        // Add named groups extension for ECDHE and FFDHE if necessary.
-        SupportedGroupsExtension sge =
-                SupportedGroupsExtension.createExtension(
-                        algorithmConstraints,
-                        cipherSuites, enableFFDHE);
-        if (sge != null) {
-            clientHelloMessage.extensions.add(sge);
-            // Add elliptic point format extensions
-            if (cipherSuites.contains(NamedGroupType.NAMED_GROUP_ECDHE)) {
-                clientHelloMessage.extensions.add(
-                    EllipticPointFormatsExtension.DEFAULT);
-            }
-        }
-
-        // add signature_algorithm extension
-        if (maxProtocolVersion.useTLS12PlusSpec()) {
-            // we will always send the signature_algorithm extension
-            Collection<SignatureAndHashAlgorithm> localSignAlgs =
-                                                getLocalSupportedSignAlgs();
-            if (localSignAlgs.isEmpty()) {
-                throw new SSLHandshakeException(
-                            "No supported signature algorithm");
-            }
-
-            clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs);
-        }
-
-        // add Extended Master Secret extension
-        if (useExtendedMasterSecret && maxProtocolVersion.useTLS10PlusSpec()) {
-            if ((session == null) || session.getUseExtendedMasterSecret()) {
-                clientHelloMessage.addExtendedMasterSecretExtension();
-                requestedToUseEMS = true;
-            }
-        }
-
-        // add server_name extension
-        if (enableSNIExtension) {
-            if (session != null) {
-                requestedServerNames = session.getRequestedServerNames();
-            } else {
-                requestedServerNames = serverNames;
-            }
-
-            if (!requestedServerNames.isEmpty()) {
-                clientHelloMessage.addSNIExtension(requestedServerNames);
-            }
-        }
-
-        // add max_fragment_length extension
-        if (enableMFLExtension) {
-            if (session != null) {
-                // The same extension should be sent for resumption.
-                requestedMFLength = session.getNegotiatedMaxFragSize();
-            } else if (maximumPacketSize != 0) {
-                // Maybe we can calculate the fragment size more accurate
-                // by condering the enabled cipher suites in the future.
-                requestedMFLength = maximumPacketSize;
-                if (isDTLS) {
-                    requestedMFLength -= DTLSRecord.maxPlaintextPlusSize;
-                } else {
-                    requestedMFLength -= SSLRecord.maxPlaintextPlusSize;
-                }
-            } else {
-                // Need no max_fragment_length extension.
-                requestedMFLength = -1;
-            }
-
-            if ((requestedMFLength > 0) &&
-                MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) {
-
-                requestedMFLength =
-                        MaxFragmentLengthExtension.getValidMaxFragLen(
-                                                        requestedMFLength);
-                clientHelloMessage.addMFLExtension(requestedMFLength);
-            } else {
-                requestedMFLength = -1;
-            }
-        }
-
-        // Add status_request and status_request_v2 extensions
-        if (sslContext.isStaplingEnabled(true)) {
-            clientHelloMessage.addCertStatusReqListV2Extension();
-            clientHelloMessage.addCertStatusRequestExtension();
-        }
-
-        // Add ALPN extension
-        if (localApl != null && localApl.length > 0) {
-            clientHelloMessage.addALPNExtension(localApl);
-            alpnActive = true;
-        }
-
-        // reset the client random cookie
-        clnt_random = clientHelloMessage.clnt_random;
-
-        /*
-         * need to set the renegotiation_info extension for:
-         * 1: secure renegotiation
-         * 2: initial handshake and no SCSV in the ClientHello
-         * 3: insecure renegotiation and no SCSV in the ClientHello
-         */
-        if (secureRenegotiation ||
-                !cipherSuites.contains(CipherSuite.C_SCSV)) {
-            clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData);
-        }
-
-        if (isDTLS) {
-            // Cookie exchange need to reserve the initial ClientHello message.
-            initialClientHelloMsg = clientHelloMessage;
-        }
-
-        return clientHelloMessage;
-    }
-
-    /*
-     * Fault detected during handshake.
-     */
-    @Override
-    void handshakeAlert(byte description) throws SSLProtocolException {
-        String message = Alerts.alertDescription(description);
-
-        if (debug != null && Debug.isOn("handshake")) {
-            System.out.println("SSL - handshake alert: " + message);
-        }
-        throw new SSLProtocolException("handshake alert:  " + message);
-    }
-
-    /*
-     * Unless we are using an anonymous ciphersuite, the server always
-     * sends a certificate message (for the CipherSuites we currently
-     * support). The trust manager verifies the chain for us.
-     */
-    private void serverCertificate(CertificateMsg mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-        X509Certificate[] peerCerts = mesg.getCertificateChain();
-        if (peerCerts.length == 0) {
-            fatalSE(Alerts.alert_bad_certificate, "empty certificate chain");
-        }
-
-        // Allow server certificate change in client side during renegotiation
-        // after a session-resumption abbreviated initial handshake?
-        //
-        // DO NOT need to check allowUnsafeServerCertChange here.  We only
-        // reserve server certificates when allowUnsafeServerCertChange is
-        // flase.
-        //
-        // Allow server certificate change if it is negotiated to use the
-        // extended master secret.
-        if ((reservedServerCerts != null) &&
-                !session.getUseExtendedMasterSecret()) {
-            // It is not necessary to check the certificate update if endpoint
-            // identification is enabled.
-            String identityAlg = getEndpointIdentificationAlgorithmSE();
-            if ((identityAlg == null || identityAlg.length() == 0) &&
-                !isIdentityEquivalent(peerCerts[0], reservedServerCerts[0])) {
-
-                fatalSE(Alerts.alert_bad_certificate,
-                        "server certificate change is restricted " +
-                        "during renegotiation");
-            }
-        }
-
-        // ask the trust manager to verify the chain
-        if (staplingActive) {
-            // Defer the certificate check until after we've received the
-            // CertificateStatus message.  If that message doesn't come in
-            // immediately following this message we will execute the check
-            // directly from processMessage before any other SSL/TLS processing.
-            deferredCerts = peerCerts;
-        } else {
-            // We're not doing stapling, so perform the check right now
-            checkServerCerts(peerCerts);
-        }
-    }
-
-    /**
-     * If certificate status stapling has been enabled, the server will send
-     * one or more status messages to the client.
-     *
-     * @param mesg a {@code CertificateStatus} object built from the data
-     *      sent by the server.
-     *
-     * @throws IOException if any parsing errors occur.
-     */
-    private void certificateStatus(CertificateStatus mesg) throws IOException {
-        if (debug != null && Debug.isOn("handshake")) {
-            mesg.print(System.out);
-        }
-
-        // Perform the certificate check using the deferred certificates
-        // and responses that we have obtained.
-        session.setStatusResponses(mesg.getResponses());
-        checkServerCerts(deferredCerts);
-    }
-
-    /*
-     * Whether the certificates can represent the same identity?
-     *
-     * The certificates can be used to represent the same identity:
-     *     1. If the subject alternative names of IP address are present in
-     *        both certificates, they should be identical; otherwise,
-     *     2. if the subject alternative names of DNS name are present in
-     *        both certificates, they should be identical; otherwise,
-     *     3. if the subject fields are present in both certificates, the
-     *        certificate subjects and issuers should be identical.
-     */
-    private static boolean isIdentityEquivalent(X509Certificate thisCert,
-            X509Certificate prevCert) {
-        if (thisCert.equals(prevCert)) {
-            return true;
-        }
-
-        // check subject alternative names
-        Collection<List<?>> thisSubjectAltNames = null;
-        try {
-            thisSubjectAltNames = thisCert.getSubjectAlternativeNames();
-        } catch (CertificateParsingException cpe) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println(
-                        "Attempt to obtain subjectAltNames extension failed!");
-            }
-        }
-
-        Collection<List<?>> prevSubjectAltNames = null;
-        try {
-            prevSubjectAltNames = prevCert.getSubjectAlternativeNames();
-        } catch (CertificateParsingException cpe) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println(
-                        "Attempt to obtain subjectAltNames extension failed!");
-            }
-        }
-
-        if ((thisSubjectAltNames != null) && (prevSubjectAltNames != null)) {
-            // check the iPAddress field in subjectAltName extension
-            Collection<String> thisSubAltIPAddrs =
-                        getSubjectAltNames(thisSubjectAltNames, ALTNAME_IP);
-            Collection<String> prevSubAltIPAddrs =
-                        getSubjectAltNames(prevSubjectAltNames, ALTNAME_IP);
-            if ((thisSubAltIPAddrs != null) && (prevSubAltIPAddrs != null) &&
-                (isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs))) {
-
-                return true;
-            }
-
-            // check the dNSName field in subjectAltName extension
-            Collection<String> thisSubAltDnsNames =
-                        getSubjectAltNames(thisSubjectAltNames, ALTNAME_DNS);
-            Collection<String> prevSubAltDnsNames =
-                        getSubjectAltNames(prevSubjectAltNames, ALTNAME_DNS);
-            if ((thisSubAltDnsNames != null) && (prevSubAltDnsNames != null) &&
-                (isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames))) {
-
-                return true;
-            }
-        }
-
-        // check the certificate subject and issuer
-        X500Principal thisSubject = thisCert.getSubjectX500Principal();
-        X500Principal prevSubject = prevCert.getSubjectX500Principal();
-        X500Principal thisIssuer = thisCert.getIssuerX500Principal();
-        X500Principal prevIssuer = prevCert.getIssuerX500Principal();
-        if (!thisSubject.getName().isEmpty() &&
-                !prevSubject.getName().isEmpty() &&
-                thisSubject.equals(prevSubject) &&
-                thisIssuer.equals(prevIssuer)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /*
-     * Returns the subject alternative name of the specified type in the
-     * subjectAltNames extension of a certificate.
-     *
-     * Note that only those subjectAltName types that use String data
-     * should be passed into this function.
-     */
-    private static Collection<String> getSubjectAltNames(
-            Collection<List<?>> subjectAltNames, int type) {
-
-        HashSet<String> subAltDnsNames = null;
-        for (List<?> subjectAltName : subjectAltNames) {
-            int subjectAltNameType = (Integer)subjectAltName.get(0);
-            if (subjectAltNameType == type) {
-                String subAltDnsName = (String)subjectAltName.get(1);
-                if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) {
-                    if (subAltDnsNames == null) {
-                        subAltDnsNames =
-                                new HashSet<>(subjectAltNames.size());
-                    }
-                    subAltDnsNames.add(subAltDnsName);
-                }
-            }
-        }
-
-        return subAltDnsNames;
-    }
-
-    private static boolean isEquivalent(Collection<String> thisSubAltNames,
-            Collection<String> prevSubAltNames) {
-
-        for (String thisSubAltName : thisSubAltNames) {
-            for (String prevSubAltName : prevSubAltNames) {
-                // Only allow the exactly match.  Check no wildcard character.
-                if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Perform client-side checking of server certificates.
-     *
-     * @param certs an array of {@code X509Certificate} objects presented
-     *      by the server in the ServerCertificate message.
-     *
-     * @throws IOException if a failure occurs during validation or
-     *      the trust manager associated with the {@code SSLContext} is not
-     *      an {@code X509ExtendedTrustManager}.
-     */
-    private void checkServerCerts(X509Certificate[] certs)
-            throws IOException {
-        X509TrustManager tm = sslContext.getX509TrustManager();
-
-        // find out the key exchange algorithm used
-        // use "RSA" for non-ephemeral "RSA_EXPORT"
-        String keyExchangeString;
-        if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) {
-            keyExchangeString = K_RSA.name;
-        } else {
-            keyExchangeString = keyExchange.name;
-        }
-
-        try {
-            if (tm instanceof X509ExtendedTrustManager) {
-                if (conn != null) {
-                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
-                        certs.clone(),
-                        keyExchangeString,
-                        conn);
-                } else {
-                    ((X509ExtendedTrustManager)tm).checkServerTrusted(
-                        certs.clone(),
-                        keyExchangeString,
-                        engine);
-                }
-            } else {
-                // Unlikely to happen, because we have wrapped the old
-                // X509TrustManager with the new X509ExtendedTrustManager.
-                throw new CertificateException(
-                        "Improper X509TrustManager implementation");
-            }
-
-            // Once the server certificate chain has been validated, set
-            // the certificate chain in the TLS session.
-            session.setPeerCertificates(certs);
-        } catch (CertificateException ce) {
-            fatalSE(getCertificateAlert(ce), ce);
-        }
-    }
-
-    /**
-     * When a failure happens during certificate checking from an
-     * {@link X509TrustManager}, determine what TLS alert description to use.
-     *
-     * @param cexc The exception thrown by the {@link X509TrustManager}
-     *
-     * @return A byte value corresponding to a TLS alert description number.
-     */
-    private byte getCertificateAlert(CertificateException cexc) {
-        // The specific reason for the failure will determine how to
-        // set the alert description value
-        byte alertDesc = Alerts.alert_certificate_unknown;
-
-        Throwable baseCause = cexc.getCause();
-        if (baseCause instanceof CertPathValidatorException) {
-            CertPathValidatorException cpve =
-                    (CertPathValidatorException)baseCause;
-            Reason reason = cpve.getReason();
-            if (reason == BasicReason.REVOKED) {
-                alertDesc = staplingActive ?
-                        Alerts.alert_bad_certificate_status_response :
-                        Alerts.alert_certificate_revoked;
-            } else if (reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
-                alertDesc = staplingActive ?
-                        Alerts.alert_bad_certificate_status_response :
-                        Alerts.alert_certificate_unknown;
-            }
-        }
-
-        return alertDesc;
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,1397 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLProtocolException;
+import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedVersionsExtension.CHSupportedVersionsSpec;
+
+/**
+ * Pack of the ClientHello handshake message.
+ */
+final class ClientHello {
+    static final SSLProducer kickstartProducer =
+        new ClientHelloKickstartProducer();
+    static final SSLConsumer handshakeConsumer =
+        new ClientHelloConsumer();
+    static final HandshakeProducer handshakeProducer =
+        new ClientHelloProducer();
+
+    private static final HandshakeConsumer t12HandshakeConsumer =
+            new T12ClientHelloConsumer();
+    private static final HandshakeConsumer t13HandshakeConsumer =
+            new T13ClientHelloConsumer();
+    private static final HandshakeConsumer d12HandshakeConsumer =
+            new D12ClientHelloConsumer();
+    private static final HandshakeConsumer d13HandshakeConsumer =
+            new D13ClientHelloConsumer();
+
+    /**
+     * The ClientHello handshake message.
+     *
+     * See RFC 5264/4346/2246/6347 for the specifications.
+     */
+    static final class ClientHelloMessage extends HandshakeMessage {
+        private final boolean       isDTLS;
+
+        final int                   clientVersion;
+        final RandomCookie          clientRandom;
+        final SessionId             sessionId;
+        private byte[]              cookie;         // DTLS only
+        final int[]                 cipherSuiteIds;
+        final List<CipherSuite>     cipherSuites;   // known cipher suites only
+        final byte[]                compressionMethod;
+        final SSLExtensions         extensions;
+
+        private static final byte[]  NULL_COMPRESSION = new byte[] {0};
+
+        ClientHelloMessage(HandshakeContext handshakeContext,
+                int clientVersion, SessionId sessionId,
+                List<CipherSuite> cipherSuites, SecureRandom generator) {
+            super(handshakeContext);
+            this.isDTLS = handshakeContext.sslContext.isDTLS();
+
+            this.clientVersion = clientVersion;
+            this.clientRandom = new RandomCookie(generator);
+            this.sessionId = sessionId;
+            if (isDTLS) {
+                this.cookie = new byte[0];
+            } else {
+                this.cookie = null;
+            }
+
+            this.cipherSuites = cipherSuites;
+            this.cipherSuiteIds = getCipherSuiteIds(cipherSuites);
+            this.extensions = new SSLExtensions(this);
+
+            // Don't support compression.
+            this.compressionMethod = NULL_COMPRESSION;
+        }
+
+        /* Read up to the binders in the PSK extension. After this method
+         * returns, the ByteBuffer position will be at end of the message
+         * fragment that should be hashed to produce the PSK binder values.
+         * The client of this method can use this position to determine the
+         * message fragment and produce the binder values.
+         */
+        static void readPartial(TransportContext tc,
+                ByteBuffer m) throws IOException {
+            boolean isDTLS = tc.sslContext.isDTLS();
+
+            // version
+            Record.getInt16(m);
+
+            new RandomCookie(m);
+
+            // session ID
+            Record.getBytes8(m);
+
+            // DTLS cookie
+            if (isDTLS) {
+                Record.getBytes8(m);
+            }
+
+            // cipher suite IDs
+            Record.getBytes16(m);
+            // compression method
+            Record.getBytes8(m);
+            // read extensions, if present
+            if (m.remaining() >= 2) {
+                int remaining = Record.getInt16(m);
+                while (remaining > 0) {
+                    int id = Record.getInt16(m);
+                    int extLen = Record.getInt16(m);
+                    remaining -= extLen + 4;
+
+                    if (id == SSLExtension.CH_PRE_SHARED_KEY.id) {
+                        // ensure pre_shared_key is the last extension
+                        if (remaining > 0) {
+                            tc.fatal(Alert.ILLEGAL_PARAMETER,
+                            "pre_shared_key extension is not last");
+                        }
+                        // read only up to the IDs
+                        Record.getBytes16(m);
+                        return;
+                    } else {
+                        m.position(m.position() + extLen);
+
+                    }
+                }
+            }   // Otherwise, ignore the remaining bytes.
+        }
+
+        ClientHelloMessage(HandshakeContext handshakeContext, ByteBuffer m,
+                SSLExtension[] supportedExtensions) throws IOException {
+            super(handshakeContext);
+            this.isDTLS = handshakeContext.sslContext.isDTLS();
+
+            this.clientVersion = ((m.get() & 0xFF) << 8) | (m.get() & 0xFF);
+            this.clientRandom = new RandomCookie(m);
+            this.sessionId = new SessionId(Record.getBytes8(m));
+            try {
+                sessionId.checkLength(clientVersion);
+            } catch (SSLProtocolException ex) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, ex);
+            }
+            if (isDTLS) {
+                this.cookie = Record.getBytes8(m);
+            } else {
+                this.cookie = null;
+            }
+
+            byte[] encodedIds = Record.getBytes16(m);
+            if (encodedIds.length == 0 || (encodedIds.length & 0x01) != 0) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid ClientHello message");
+            }
+
+            this.cipherSuiteIds = new int[encodedIds.length >> 1];
+            for (int i = 0, j = 0; i < encodedIds.length; i++, j++) {
+                cipherSuiteIds[j] =
+                    ((encodedIds[i++] & 0xFF) << 8) | (encodedIds[i] & 0xFF);
+            }
+            this.cipherSuites = getCipherSuites(cipherSuiteIds);
+
+            this.compressionMethod = Record.getBytes8(m);
+            // In TLS 1.3, use of certain extensions is mandatory.
+            if (m.hasRemaining()) {
+                this.extensions =
+                        new SSLExtensions(this, m, supportedExtensions);
+            } else {
+                this.extensions = new SSLExtensions(this);
+            }
+        }
+
+        void setHelloCookie(byte[] cookie) {
+            this.cookie = cookie;
+        }
+
+        // DTLS 1.0/1.2, for cookie generation.
+        byte[] getHelloCookieBytes() {
+            HandshakeOutStream hos = new HandshakeOutStream(null);
+            try {
+                // copied from send() method
+                hos.putInt8((byte)((clientVersion >>> 8) & 0xFF));
+                hos.putInt8((byte)(clientVersion & 0xFF));
+                hos.write(clientRandom.randomBytes, 0, 32);
+                hos.putBytes8(sessionId.getId());
+                // ignore cookie
+                hos.putBytes16(getEncodedCipherSuites());
+                hos.putBytes8(compressionMethod);
+                extensions.send(hos);       // In TLS 1.3, use of certain
+                                            // extensions is mandatory.
+            } catch (IOException ioe) {
+                // unlikely
+            }
+
+            return hos.toByteArray();
+        }
+
+        // (D)TLS 1.3, for cookie generation.
+        byte[] getHeaderBytes() {
+            HandshakeOutStream hos = new HandshakeOutStream(null);
+            try {
+                // copied from send() method
+                hos.putInt8((byte)((clientVersion >>> 8) & 0xFF));
+                hos.putInt8((byte)(clientVersion & 0xFF));
+                hos.write(clientRandom.randomBytes, 0, 32);
+                hos.putBytes8(sessionId.getId());
+                hos.putBytes16(getEncodedCipherSuites());
+                hos.putBytes8(compressionMethod);
+            } catch (IOException ioe) {
+                // unlikely
+            }
+
+            return hos.toByteArray();
+        }
+
+        private static int[] getCipherSuiteIds(
+                List<CipherSuite> cipherSuites) {
+            if (cipherSuites != null) {
+                int[] ids = new int[cipherSuites.size()];
+                int i = 0;
+                for (CipherSuite cipherSuite : cipherSuites) {
+                    ids[i++] = cipherSuite.id;
+                }
+
+                return ids;
+            }
+
+            return new int[0];
+        }
+
+        private static List<CipherSuite> getCipherSuites(int[] ids) {
+            List<CipherSuite> cipherSuites = new LinkedList<>();
+            for (int id : ids) {
+                CipherSuite cipherSuite = CipherSuite.valueOf(id);
+                if (cipherSuite != null) {
+                    cipherSuites.add(cipherSuite);
+                }
+            }
+
+            return Collections.unmodifiableList(cipherSuites);
+        }
+
+        private List<String> getCipherSuiteNames() {
+            List<String> names = new LinkedList<>();
+            for (int id : cipherSuiteIds) {
+                names.add(CipherSuite.nameOf(id) +
+                        "(" + Utilities.byte16HexString(id) + ")");            }
+
+            return names;
+        }
+
+        private byte[] getEncodedCipherSuites() {
+            byte[] encoded = new byte[cipherSuiteIds.length << 1];
+            int i = 0;
+            for (int id : cipherSuiteIds) {
+                encoded[i++] = (byte)(id >> 8);
+                encoded[i++] = (byte)id;
+            }
+            return encoded;
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CLIENT_HELLO;
+        }
+
+        @Override
+        public int messageLength() {
+            /*
+             * Add fixed size parts of each field...
+             * version + random + session + cipher + compress
+             */
+            return (2 + 32 + 1 + 2 + 1
+                + sessionId.length()        /* ... + variable parts */
+                + (isDTLS ? (1 + cookie.length) : 0)
+                + (cipherSuiteIds.length * 2)
+                + compressionMethod.length)
+                + extensions.length();      // In TLS 1.3, use of certain
+                                            // extensions is mandatory.
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            sendCore(hos);
+            extensions.send(hos);       // In TLS 1.3, use of certain
+                                        // extensions is mandatory.
+        }
+
+        void sendCore(HandshakeOutStream hos) throws IOException {
+            hos.putInt8((byte) (clientVersion >>> 8));
+            hos.putInt8((byte) clientVersion);
+            hos.write(clientRandom.randomBytes, 0, 32);
+            hos.putBytes8(sessionId.getId());
+            if (isDTLS) {
+                hos.putBytes8(cookie);
+            }
+            hos.putBytes16(getEncodedCipherSuites());
+            hos.putBytes8(compressionMethod);
+        }
+
+        @Override
+        public String toString() {
+            if (isDTLS) {
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"ClientHello\": '{'\n" +
+                    "  \"client version\"      : \"{0}\",\n" +
+                    "  \"random\"              : \"{1}\",\n" +
+                    "  \"session id\"          : \"{2}\",\n" +
+                    "  \"cookie\"              : \"{3}\",\n" +
+                    "  \"cipher suites\"       : \"{4}\",\n" +
+                    "  \"compression methods\" : \"{5}\",\n" +
+                    "  \"extensions\"          : [\n" +
+                    "{6}\n" +
+                    "  ]\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+                Object[] messageFields = {
+                    ProtocolVersion.nameOf(clientVersion),
+                    Utilities.toHexString(clientRandom.randomBytes),
+                    sessionId.toString(),
+                    Utilities.toHexString(cookie),
+                    getCipherSuiteNames().toString(),
+                    Utilities.toHexString(compressionMethod),
+                    Utilities.indent(Utilities.indent(extensions.toString()))
+                };
+
+                return messageFormat.format(messageFields);
+            } else {
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"ClientHello\": '{'\n" +
+                    "  \"client version\"      : \"{0}\",\n" +
+                    "  \"random\"              : \"{1}\",\n" +
+                    "  \"session id\"          : \"{2}\",\n" +
+                    "  \"cipher suites\"       : \"{3}\",\n" +
+                    "  \"compression methods\" : \"{4}\",\n" +
+                    "  \"extensions\"          : [\n" +
+                    "{5}\n" +
+                    "  ]\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+                Object[] messageFields = {
+                    ProtocolVersion.nameOf(clientVersion),
+                    Utilities.toHexString(clientRandom.randomBytes),
+                    sessionId.toString(),
+                    getCipherSuiteNames().toString(),
+                    Utilities.toHexString(compressionMethod),
+                    Utilities.indent(Utilities.indent(extensions.toString()))
+                };
+
+                return messageFormat.format(messageFields);
+            }
+        }
+    }
+
+    /**
+     * The "ClientHello" handshake message kick start producer.
+     */
+    private static final
+            class ClientHelloKickstartProducer implements SSLProducer {
+        // Prevent instantiation of this class.
+        private ClientHelloKickstartProducer() {
+            // blank
+        }
+
+        // Produce kickstart handshake message.
+        @Override
+        public byte[] produce(ConnectionContext context) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this producer
+            chc.handshakeProducers.remove(SSLHandshake.CLIENT_HELLO.id);
+
+            // the max protocol version this client is supporting.
+            ProtocolVersion maxProtocolVersion = chc.maximumActiveProtocol;
+
+            // session ID of the ClientHello message
+            SessionId sessionId = SSLSessionImpl.nullSession.getSessionId();
+
+            // a list of cipher suites sent by the client
+            List<CipherSuite> cipherSuites = chc.activeCipherSuites;
+
+            //
+            // Try to resume an existing session.
+            //
+            SSLSessionContextImpl ssci = (SSLSessionContextImpl)
+                    chc.sslContext.engineGetClientSessionContext();
+            SSLSessionImpl session = ssci.get(
+                    chc.conContext.transport.getPeerHost(),
+                    chc.conContext.transport.getPeerPort());
+            if (session != null) {
+                // If unsafe server certificate change is not allowed, reserve
+                // current server certificates if the previous handshake is a
+                // session-resumption abbreviated initial handshake.
+                if (!ClientHandshakeContext.allowUnsafeServerCertChange &&
+                        session.isSessionResumption()) {
+                    try {
+                        // If existing, peer certificate chain cannot be null.
+                        chc.reservedServerCerts =
+                            (X509Certificate[])session.getPeerCertificates();
+                    } catch (SSLPeerUnverifiedException puve) {
+                        // Maybe not certificate-based, ignore the exception.
+                    }
+                }
+
+                if (!session.isRejoinable()) {
+                    session = null;
+                    if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,handshake,verbose")) {
+                        SSLLogger.finest(
+                            "Can't resume, the session is not rejoinable");
+                    }
+                }
+            }
+
+            CipherSuite sessionSuite = null;
+            if (session != null) {
+                sessionSuite = session.getSuite();
+                if (!chc.isNegotiable(sessionSuite)) {
+                    session = null;
+                    if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,handshake,verbose")) {
+                        SSLLogger.finest(
+                            "Can't resume, unavailable session cipher suite");
+                    }
+                }
+            }
+
+            ProtocolVersion sessionVersion = null;
+            if (session != null) {
+                sessionVersion = session.getProtocolVersion();
+                if (!chc.isNegotiable(sessionVersion)) {
+                    session = null;
+                    if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,handshake,verbose")) {
+                        SSLLogger.finest(
+                            "Can't resume, unavailable protocol version");
+                    }
+                }
+            }
+
+            if (session != null &&
+                !sessionVersion.useTLS13PlusSpec() &&
+                SSLConfiguration.useExtendedMasterSecret) {
+
+                boolean isEmsAvailable = chc.sslConfig.isAvailable(
+                    SSLExtension.CH_EXTENDED_MASTER_SECRET, sessionVersion);
+                if (isEmsAvailable && !session.useExtendedMasterSecret &&
+                        !SSLConfiguration.allowLegacyResumption) {
+                    // perform full handshake instead
+                    //
+                    // The client SHOULD NOT offer an abbreviated handshake
+                    // to resume a session that does not use an extended
+                    // master secret.  Instead, it SHOULD offer a full
+                    // handshake.
+                     session = null;
+                }
+
+                if ((session != null) &&
+                        !ClientHandshakeContext.allowUnsafeServerCertChange) {
+                    // It is fine to move on with abbreviate handshake if
+                    // endpoint identification is enabled.
+                    String identityAlg = chc.sslConfig.identificationProtocol;
+                    if ((identityAlg == null || identityAlg.length() == 0)) {
+                        if (isEmsAvailable) {
+                            if (!session.useExtendedMasterSecret) {
+                                // perform full handshake instead
+                                session = null;
+                            }   // Otherwise, use extended master secret.
+                        } else {
+                            // The extended master secret extension does not
+                            // apply to SSL 3.0.  Perform a full handshake
+                            // instead.
+                            //
+                            // Note that the useExtendedMasterSecret is
+                            // extended to protect SSL 3.0 connections,
+                            // by discarding abbreviate handshake.
+                            session = null;
+                        }
+                    }
+                }
+            }
+
+            if (session != null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {
+                    SSLLogger.finest("Try resuming session", session);
+                }
+
+                // only set session id if session is 1.2 or earlier
+                if (!session.getProtocolVersion().useTLS13PlusSpec()) {
+                    sessionId = session.getSessionId();
+                }
+                if (!maxProtocolVersion.equals(sessionVersion)) {
+                    maxProtocolVersion = sessionVersion;
+
+                    // Update protocol version number in underlying socket and
+                    // handshake output stream, so that the output records
+                    // (at the record layer) have the correct version
+                    chc.setVersion(sessionVersion);
+                }
+
+                // If no new session is allowed, force use of the previous
+                // session ciphersuite, and add the renegotiation SCSV if
+                // necessary.
+                if (!chc.sslConfig.enableSessionCreation) {
+                    if (!chc.conContext.isNegotiated &&
+                        !sessionVersion.useTLS13PlusSpec() &&
+                        cipherSuites.contains(
+                            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
+                        cipherSuites = Arrays.asList(sessionSuite,
+                            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+                    } else {    // otherwise, use renegotiation_info extension
+                        cipherSuites = Arrays.asList(sessionSuite);
+                    }
+
+                    if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,handshake,verbose")) {
+                        SSLLogger.finest(
+                            "No new session is allowed, so try to resume " +
+                            "the session cipher suite only", sessionSuite);
+                    }
+                }
+
+                chc.isResumption = true;
+                chc.resumingSession = session;
+            }
+
+            if (session == null) {
+                if (!chc.sslConfig.enableSessionCreation) {
+                    throw new SSLHandshakeException(
+                            "No new session is allowed and " +
+                            "no existing session can be resumed");
+                }
+
+                if (maxProtocolVersion.useTLS13PlusSpec() &&
+                        SSLConfiguration.useCompatibilityMode) {
+                    // In compatibility mode, the TLS 1.3 legacy_session_id
+                    // field MUST be non-empty, so a client not offering a
+                    // pre-TLS 1.3 session MUST generate a new 32-byte value.
+                    sessionId =
+                        new SessionId(true, chc.sslContext.getSecureRandom());
+                }
+            }
+
+            ProtocolVersion minimumVersion = ProtocolVersion.NONE;
+            for (ProtocolVersion pv : chc.activeProtocols) {
+                if (minimumVersion == ProtocolVersion.NONE ||
+                        pv.compare(minimumVersion) < 0) {
+                    minimumVersion = pv;
+                }
+            }
+
+            // exclude SCSV for secure renegotiation
+            if (!minimumVersion.useTLS13PlusSpec()) {
+                if (chc.conContext.secureRenegotiation &&
+                        cipherSuites.contains(
+                            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
+                    // The cipherSuites may be unmodifiable
+                    cipherSuites = new LinkedList<>(cipherSuites);
+                    cipherSuites.remove(
+                            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+                }
+            }
+
+            // make sure there is a negotiable cipher suite.
+            boolean negotiable = false;
+            for (CipherSuite suite : cipherSuites) {
+                if (chc.isNegotiable(suite)) {
+                    negotiable = true;
+                    break;
+                }
+            }
+            if (!negotiable) {
+                throw new SSLHandshakeException("No negotiable cipher suite");
+            }
+
+            // Create the handshake message.
+            ProtocolVersion clientHelloVersion = maxProtocolVersion;
+            if (clientHelloVersion.useTLS13PlusSpec()) {
+                // In (D)TLS 1.3, the client indicates its version preferences
+                // in the "supported_versions" extension and the client_version
+                // (legacy_version) field MUST be set to (D)TLS 1.2.
+                if (clientHelloVersion.isDTLS) {
+                    clientHelloVersion = ProtocolVersion.DTLS12;
+                } else {
+                    clientHelloVersion = ProtocolVersion.TLS12;
+                }
+            }
+
+            ClientHelloMessage chm = new ClientHelloMessage(chc,
+                    clientHelloVersion.id, sessionId, cipherSuites,
+                    chc.sslContext.getSecureRandom());
+
+            // cache the client random number for further using
+            chc.clientHelloRandom = chm.clientRandom;
+            chc.clientHelloVersion = clientHelloVersion.id;
+
+            // Produce extensions for ClientHello handshake message.
+            SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.CLIENT_HELLO, chc.activeProtocols);
+            chm.extensions.produce(chc, extTypes);
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced ClientHello handshake message", chm);
+            }
+
+            // Output the handshake message.
+            chm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // Reserve the initial ClientHello message for the follow on
+            // cookie exchange if needed.
+            chc.initialClientHelloMsg = chm;
+
+            // What's the expected response?
+            chc.handshakeConsumers.put(
+                    SSLHandshake.SERVER_HELLO.id, SSLHandshake.SERVER_HELLO);
+            if (chc.sslContext.isDTLS() &&
+                    !minimumVersion.useTLS13PlusSpec()) {
+                chc.handshakeConsumers.put(
+                        SSLHandshake.HELLO_VERIFY_REQUEST.id,
+                        SSLHandshake.HELLO_VERIFY_REQUEST);
+            }
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    private static final
+            class ClientHelloProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private ClientHelloProducer() {
+            // blank
+        }
+
+        // Response to one of the following handshake message:
+        //     HelloRequest                     (SSL 3.0/TLS 1.0/1.1/1.2)
+        //     ServerHello(HelloRetryRequest)   (TLS 1.3)
+        //     HelloVerifyRequest               (DTLS 1.0/1.2)
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            SSLHandshake ht = message.handshakeType();
+            if (ht == null) {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            switch (ht) {
+                case HELLO_REQUEST:
+                    // SSL 3.0/TLS 1.0/1.1/1.2
+                    try {
+                        chc.kickstart();
+                    } catch (IOException ioe) {
+                        chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, ioe);
+                    }
+
+                    // The handshake message has been delivered.
+                    return null;
+                case HELLO_VERIFY_REQUEST:
+                    // DTLS 1.0/1.2
+                    //
+                    // The HelloVerifyRequest consumer should have updated the
+                    // ClientHello handshake message with cookie.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "Produced ClientHello(cookie) handshake message",
+                            chc.initialClientHelloMsg);
+                    }
+
+                    // Output the handshake message.
+                    chc.initialClientHelloMsg.write(chc.handshakeOutput);
+                    chc.handshakeOutput.flush();
+
+                    // What's the expected response?
+                    chc.handshakeConsumers.put(SSLHandshake.SERVER_HELLO.id,
+                            SSLHandshake.SERVER_HELLO);
+
+                    ProtocolVersion minimumVersion = ProtocolVersion.NONE;
+                    for (ProtocolVersion pv : chc.activeProtocols) {
+                        if (minimumVersion == ProtocolVersion.NONE ||
+                                pv.compare(minimumVersion) < 0) {
+                            minimumVersion = pv;
+                        }
+                    }
+                    if (chc.sslContext.isDTLS() &&
+                            !minimumVersion.useTLS13PlusSpec()) {
+                        chc.handshakeConsumers.put(
+                                SSLHandshake.HELLO_VERIFY_REQUEST.id,
+                                SSLHandshake.HELLO_VERIFY_REQUEST);
+                    }
+
+                    // The handshake message has been delivered.
+                    return null;
+                case HELLO_RETRY_REQUEST:
+                    // TLS 1.3
+                    // The HelloRetryRequest consumer should have updated the
+                    // ClientHello handshake message with cookie.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "Produced ClientHello(HRR) handshake message",
+                            chc.initialClientHelloMsg);
+                    }
+
+                    // Output the handshake message.
+                    chc.initialClientHelloMsg.write(chc.handshakeOutput);
+                    chc.handshakeOutput.flush();
+
+                    // What's the expected response?
+                    chc.conContext.consumers.putIfAbsent(
+                            ContentType.CHANGE_CIPHER_SPEC.id,
+                            ChangeCipherSpec.t13Consumer);
+                    chc.handshakeConsumers.put(SSLHandshake.SERVER_HELLO.id,
+                            SSLHandshake.SERVER_HELLO);
+
+                    // The handshake message has been delivered.
+                    return null;
+                default:
+                    throw new UnsupportedOperationException(
+                            "Not supported yet.");
+            }
+        }
+    }
+
+    /**
+     * The "ClientHello" handshake message consumer.
+     */
+    private static final class ClientHelloConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ClientHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // clean up this consumer
+            shc.handshakeConsumers.remove(SSLHandshake.CLIENT_HELLO.id);
+            if (!shc.handshakeConsumers.isEmpty()) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "No more handshake message allowed " +
+                        "in a ClientHello flight");
+            }
+
+            // Get enabled extension types in ClientHello handshake message.
+            SSLExtension[] enabledExtensions =
+                    shc.sslConfig.getEnabledExtensions(
+                            SSLHandshake.CLIENT_HELLO);
+
+            ClientHelloMessage chm =
+                    new ClientHelloMessage(shc, message, enabledExtensions);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Consuming ClientHello handshake message", chm);
+            }
+
+            shc.clientHelloVersion = chm.clientVersion;
+            onClientHello(shc, chm);
+        }
+
+        private void onClientHello(ServerHandshakeContext context,
+                ClientHelloMessage clientHello) throws IOException {
+            // Negotiate protocol version.
+            //
+            // Check and launch SupportedVersions.
+            SSLExtension[] extTypes = new SSLExtension[] {
+                    SSLExtension.CH_SUPPORTED_VERSIONS
+                };
+            clientHello.extensions.consumeOnLoad(context, extTypes);
+
+            ProtocolVersion negotiatedProtocol;
+            CHSupportedVersionsSpec svs =
+                    (CHSupportedVersionsSpec)context.handshakeExtensions.get(
+                            SSLExtension.CH_SUPPORTED_VERSIONS);
+            if (svs != null) {
+                negotiatedProtocol =
+                        negotiateProtocol(context, svs.requestedProtocols);
+            } else {
+                negotiatedProtocol =
+                        negotiateProtocol(context, clientHello.clientVersion);
+            }
+            context.negotiatedProtocol = negotiatedProtocol;
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Negotiated protocol version: " + negotiatedProtocol.name);
+            }
+
+            // Consume the handshake message for the specific protocol version.
+            if (negotiatedProtocol.isDTLS) {
+                if (negotiatedProtocol.useTLS13PlusSpec()) {
+                    d13HandshakeConsumer.consume(context, clientHello);
+                } else {
+                    d12HandshakeConsumer.consume(context, clientHello);
+                }
+            } else {
+                if (negotiatedProtocol.useTLS13PlusSpec()) {
+                    t13HandshakeConsumer.consume(context, clientHello);
+                } else {
+                    t12HandshakeConsumer.consume(context, clientHello);
+                }
+            }
+        }
+
+        // Select a protocol version according to the
+        // ClientHello.client_version.
+        private ProtocolVersion negotiateProtocol(
+                ServerHandshakeContext context,
+                int clientHelloVersion) throws SSLException {
+
+            // Per TLS 1.3 specification, server MUST negotiate TLS 1.2 or prior
+            // even if ClientHello.client_version is 0x0304 or later.
+            int chv = clientHelloVersion;
+            if (context.sslContext.isDTLS()) {
+                if (chv < ProtocolVersion.DTLS12.id) {
+                    chv = ProtocolVersion.DTLS12.id;
+                }
+            } else {
+                if (chv > ProtocolVersion.TLS12.id) {
+                    chv = ProtocolVersion.TLS12.id;
+                }
+            }
+
+            // Select a protocol version from the activated protocols.
+            ProtocolVersion pv = ProtocolVersion.selectedFrom(
+                    context.activeProtocols, chv);
+            if (pv == null || pv == ProtocolVersion.NONE ||
+                    pv == ProtocolVersion.SSL20Hello) {
+                context.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "Client requested protocol " +
+                    ProtocolVersion.nameOf(clientHelloVersion) +
+                    " is not enabled or supported in server context");
+            }
+
+            return pv;
+        }
+
+        // Select a protocol version according to the
+        // supported_versions extension.
+        private ProtocolVersion negotiateProtocol(
+                ServerHandshakeContext context,
+                int[] clientSupportedVersions) throws SSLException {
+
+            // The client supported protocol versions are present in client
+            // preference order.  This implementation chooses to use the server
+            // preference of protocol versions instead.
+            for (ProtocolVersion spv : context.activeProtocols) {
+                if (spv == ProtocolVersion.SSL20Hello) {
+                    continue;
+                }
+                for (int cpv : clientSupportedVersions) {
+                    if (cpv == ProtocolVersion.SSL20Hello.id) {
+                        continue;
+                    }
+                    if (spv.id == cpv) {
+                        return spv;
+                    }
+                }
+            }
+
+            // No protocol version can be negotiated.
+            context.conContext.fatal(Alert.PROTOCOL_VERSION,
+                "The client supported protocol versions " + Arrays.toString(
+                    ProtocolVersion.toStringArray(clientSupportedVersions)) +
+                " are not accepted by server preferences " +
+                context.activeProtocols);
+
+            return null;        // make the compiler happy
+        }
+    }
+
+    /**
+     * The "ClientHello" handshake message consumer for TLS 1.2 and
+     * prior SSL/TLS protocol versions.
+     */
+    private static final
+            class T12ClientHelloConsumer implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private T12ClientHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            //
+            // validate
+            //
+
+            // 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 (shc.conContext.isNegotiated) {
+                if (!shc.conContext.secureRenegotiation &&
+                        !HandshakeContext.allowUnsafeRenegotiation) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Unsafe renegotiation is not allowed");
+                }
+
+                if (ServerHandshakeContext.rejectClientInitiatedRenego &&
+                        !shc.kickstartMessageDelivered) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Client initiated renegotiation is not allowed");
+                }
+            }
+
+            // Is it an abbreviated handshake?
+            if (clientHello.sessionId.length() != 0) {
+                SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext
+                            .engineGetServerSessionContext())
+                            .get(clientHello.sessionId.getId());
+
+                boolean resumingSession =
+                        (previous != null) && previous.isRejoinable();
+                if (!resumingSession) {
+                    if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,handshake,verbose")) {
+                        SSLLogger.finest(
+                                "Can't resume, " +
+                                "the existing session is not rejoinable");
+                    }
+                }
+                // Validate the negotiated protocol version.
+                if (resumingSession) {
+                    ProtocolVersion sessionProtocol =
+                            previous.getProtocolVersion();
+                    if (sessionProtocol != shc.negotiatedProtocol) {
+                        resumingSession = false;
+                        if (SSLLogger.isOn &&
+                                SSLLogger.isOn("ssl,handshake,verbose")) {
+                            SSLLogger.finest(
+                                "Can't resume, not the same protocol version");
+                        }
+                    }
+                }
+
+                // Validate the required client authentication.
+                if (resumingSession &&
+                    (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) {
+                    try {
+                        previous.getPeerPrincipal();
+                    } catch (SSLPeerUnverifiedException e) {
+                        resumingSession = false;
+                        if (SSLLogger.isOn &&
+                                SSLLogger.isOn("ssl,handshake,verbose")) {
+                            SSLLogger.finest(
+                                "Can't resume, " +
+                                "client authentication is required");
+                        }
+                    }
+                }
+
+                // Validate that the cached cipher suite.
+                if (resumingSession) {
+                    CipherSuite suite = previous.getSuite();
+                    if ((!shc.isNegotiable(suite)) ||
+                            (!clientHello.cipherSuites.contains(suite))) {
+                        resumingSession = false;
+                        if (SSLLogger.isOn &&
+                                SSLLogger.isOn("ssl,handshake,verbose")) {
+                            SSLLogger.finest(
+                                "Can't resume, " +
+                                "the session cipher suite is absent");
+                        }
+                    }
+                }
+
+                // So far so good.  Note that the handshake extensions may reset
+                // the resuming options later.
+                shc.isResumption = resumingSession;
+                shc.resumingSession = resumingSession ? previous : null;
+            }
+
+            // cache the client random number for further using
+            shc.clientHelloRandom = clientHello.clientRandom;
+
+            // Check and launch ClientHello extensions.
+            SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.CLIENT_HELLO);
+            clientHello.extensions.consumeOnLoad(shc, extTypes);
+
+            //
+            // update
+            //
+            if (!shc.conContext.isNegotiated) {
+                shc.conContext.protocolVersion = shc.negotiatedProtocol;
+                shc.conContext.outputRecord.setVersion(shc.negotiatedProtocol);
+            }
+
+            // update the responders
+            //
+            // Only need to ServerHello, which may add more responders later.
+            // Note that ServerHello and HelloRetryRequest share the same
+            // handshake type/id.  The ServerHello producer may be replaced
+            // by HelloRetryRequest producer if needed.
+            shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id,
+                    SSLHandshake.SERVER_HELLO);
+
+            //
+            // produce
+            //
+            SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] {
+                SSLHandshake.SERVER_HELLO,
+
+                // full handshake messages
+                SSLHandshake.CERTIFICATE,
+                SSLHandshake.CERTIFICATE_STATUS,
+                SSLHandshake.SERVER_KEY_EXCHANGE,
+                SSLHandshake.CERTIFICATE_REQUEST,
+                SSLHandshake.SERVER_HELLO_DONE,
+
+                // abbreviated handshake messages
+                SSLHandshake.FINISHED
+            };
+
+            for (SSLHandshake hs : probableHandshakeMessages) {
+                HandshakeProducer handshakeProducer =
+                        shc.handshakeProducers.remove(hs.id);
+                if (handshakeProducer != null) {
+                    handshakeProducer.produce(context, clientHello);
+                }
+            }
+        }
+    }
+
+    /**
+     * The "ClientHello" handshake message consumer for TLS 1.3.
+     */
+    private static final
+            class T13ClientHelloConsumer implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private T13ClientHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            // The client may send a dummy change_cipher_spec record
+            // immediately after the first ClientHello.
+            shc.conContext.consumers.putIfAbsent(
+                    ContentType.CHANGE_CIPHER_SPEC.id,
+                    ChangeCipherSpec.t13Consumer);
+
+            // Is it a resumption?
+            //
+            // Check and launch the "psk_key_exchange_modes" and
+            // "pre_shared_key" extensions first, which will reset the
+            // resuming session, no matter the extensions present or not.
+            shc.isResumption = true;
+            SSLExtension[] extTypes = new SSLExtension[] {
+                    SSLExtension.PSK_KEY_EXCHANGE_MODES,
+                    SSLExtension.CH_PRE_SHARED_KEY
+                };
+            clientHello.extensions.consumeOnLoad(shc, extTypes);
+
+            // Check and launch ClientHello extensions other than
+            // "psk_key_exchange_modes", "pre_shared_key", "protocol_version"
+            // and "key_share" extensions.
+            //
+            // These extensions may discard session resumption, or ask for
+            // hello retry.
+            extTypes = shc.sslConfig.getExclusiveExtensions(
+                    SSLHandshake.CLIENT_HELLO,
+                    Arrays.asList(
+                            SSLExtension.PSK_KEY_EXCHANGE_MODES,
+                            SSLExtension.CH_PRE_SHARED_KEY,
+                            SSLExtension.CH_SUPPORTED_VERSIONS));
+            clientHello.extensions.consumeOnLoad(shc, extTypes);
+
+            if (!shc.handshakeProducers.isEmpty()) {
+                // Should be HelloRetryRequest producer.
+                goHelloRetryRequest(shc, clientHello);
+            } else {
+                goServerHello(shc, clientHello);
+            }
+        }
+
+        private void goHelloRetryRequest(ServerHandshakeContext shc,
+                ClientHelloMessage clientHello) throws IOException {
+            HandshakeProducer handshakeProducer =
+                    shc.handshakeProducers.remove(
+                            SSLHandshake.HELLO_RETRY_REQUEST.id);
+            if (handshakeProducer != null) {
+                    handshakeProducer.produce(shc, clientHello);
+            } else {
+                // unlikely
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No HelloRetryRequest producer: " + shc.handshakeProducers);
+            }
+
+            if (!shc.handshakeProducers.isEmpty()) {
+                // unlikely, but please double check.
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "unknown handshake producers: " + shc.handshakeProducers);
+            }
+        }
+
+        private void goServerHello(ServerHandshakeContext shc,
+                ClientHelloMessage clientHello) throws IOException {
+            //
+            // validate
+            //
+            shc.clientHelloRandom = clientHello.clientRandom;
+
+            //
+            // update
+            //
+            if (!shc.conContext.isNegotiated) {
+                shc.conContext.protocolVersion = shc.negotiatedProtocol;
+                shc.conContext.outputRecord.setVersion(shc.negotiatedProtocol);
+            }
+
+            // update the responders
+            //
+            // Only ServerHello/HelloRetryRequest producer, which adds
+            // more responders later.
+            shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id,
+                SSLHandshake.SERVER_HELLO);
+
+            SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] {
+                SSLHandshake.SERVER_HELLO,
+
+                // full handshake messages
+                SSLHandshake.ENCRYPTED_EXTENSIONS,
+                SSLHandshake.CERTIFICATE_REQUEST,
+                SSLHandshake.CERTIFICATE,
+                SSLHandshake.CERTIFICATE_VERIFY,
+                SSLHandshake.FINISHED
+            };
+
+            //
+            // produce
+            //
+            for (SSLHandshake hs : probableHandshakeMessages) {
+                HandshakeProducer handshakeProducer =
+                        shc.handshakeProducers.remove(hs.id);
+                if (handshakeProducer != null) {
+                    handshakeProducer.produce(shc, clientHello);
+                }
+            }
+        }
+    }
+
+    /**
+     * The "ClientHello" handshake message consumer for DTLS 1.2 and
+     * previous DTLS protocol versions.
+     */
+    private static final
+            class D12ClientHelloConsumer implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private D12ClientHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            //
+            // validate
+            //
+
+            // 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 (shc.conContext.isNegotiated) {
+                if (!shc.conContext.secureRenegotiation &&
+                        !HandshakeContext.allowUnsafeRenegotiation) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Unsafe renegotiation is not allowed");
+                }
+
+                if (ServerHandshakeContext.rejectClientInitiatedRenego &&
+                        !shc.kickstartMessageDelivered) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Client initiated renegotiation is not allowed");
+                }
+            }
+
+            // Is it an abbreviated handshake?
+            if (clientHello.sessionId.length() != 0) {
+                SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext
+                            .engineGetServerSessionContext())
+                            .get(clientHello.sessionId.getId());
+
+                boolean resumingSession =
+                        (previous != null) && previous.isRejoinable();
+                if (!resumingSession) {
+                    if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,handshake,verbose")) {
+                        SSLLogger.finest(
+                            "Can't resume, " +
+                            "the existing session is not rejoinable");
+                    }
+                }
+                // Validate the negotiated protocol version.
+                if (resumingSession) {
+                    ProtocolVersion sessionProtocol =
+                            previous.getProtocolVersion();
+                    if (sessionProtocol != shc.negotiatedProtocol) {
+                        resumingSession = false;
+                        if (SSLLogger.isOn &&
+                                SSLLogger.isOn("ssl,handshake,verbose")) {
+                            SSLLogger.finest(
+                                "Can't resume, not the same protocol version");
+                        }
+                    }
+                }
+
+                // Validate the required client authentication.
+                if (resumingSession &&
+                    (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) {
+
+                    try {
+                        previous.getPeerPrincipal();
+                    } catch (SSLPeerUnverifiedException e) {
+                        resumingSession = false;
+                        if (SSLLogger.isOn &&
+                                SSLLogger.isOn("ssl,handshake,verbose")) {
+                            SSLLogger.finest(
+                                "Can't resume, " +
+                                "client authentication is required");
+                        }
+                    }
+                }
+
+                // Validate that the cached cipher suite.
+                if (resumingSession) {
+                    CipherSuite suite = previous.getSuite();
+                    if ((!shc.isNegotiable(suite)) ||
+                            (!clientHello.cipherSuites.contains(suite))) {
+                        resumingSession = false;
+                        if (SSLLogger.isOn &&
+                                SSLLogger.isOn("ssl,handshake,verbose")) {
+                            SSLLogger.finest(
+                                "Can't resume, " +
+                                "the session cipher suite is absent");
+                        }
+                    }
+                }
+
+                // So far so good.  Note that the handshake extensions may reset
+                // the resuming options later.
+                shc.isResumption = resumingSession;
+                shc.resumingSession = resumingSession ? previous : null;
+            }
+
+            HelloCookieManager hcm =
+                shc.sslContext.getHelloCookieManager(ProtocolVersion.DTLS10);
+            if (!shc.isResumption &&
+                !hcm.isCookieValid(shc, clientHello, clientHello.cookie)) {
+                //
+                // Perform cookie exchange for DTLS handshaking if no cookie
+                // or the cookie is invalid in the ClientHello message.
+                //
+                // update the responders
+                shc.handshakeProducers.put(
+                        SSLHandshake.HELLO_VERIFY_REQUEST.id,
+                        SSLHandshake.HELLO_VERIFY_REQUEST);
+
+                //
+                // produce response handshake message
+                //
+                SSLHandshake.HELLO_VERIFY_REQUEST.produce(context, clientHello);
+
+                return;
+            }
+
+            // cache the client random number for further using
+            shc.clientHelloRandom = clientHello.clientRandom;
+
+            // Check and launch ClientHello extensions.
+            SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.CLIENT_HELLO);
+            clientHello.extensions.consumeOnLoad(shc, extTypes);
+
+            //
+            // update
+            //
+            if (!shc.conContext.isNegotiated) {
+                shc.conContext.protocolVersion = shc.negotiatedProtocol;
+                shc.conContext.outputRecord.setVersion(shc.negotiatedProtocol);
+            }
+
+            // update the responders
+            //
+            // Only need to ServerHello, which may add more responders later.
+            shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id,
+                    SSLHandshake.SERVER_HELLO);
+
+            //
+            // produce
+            //
+            SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] {
+                SSLHandshake.SERVER_HELLO,
+
+                // full handshake messages
+                SSLHandshake.CERTIFICATE,
+                SSLHandshake.CERTIFICATE_STATUS,
+                SSLHandshake.SERVER_KEY_EXCHANGE,
+                SSLHandshake.CERTIFICATE_REQUEST,
+                SSLHandshake.SERVER_HELLO_DONE,
+
+                // abbreviated handshake messages
+                SSLHandshake.FINISHED
+            };
+
+            for (SSLHandshake hs : probableHandshakeMessages) {
+                HandshakeProducer handshakeProducer =
+                        shc.handshakeProducers.remove(hs.id);
+                if (handshakeProducer != null) {
+                    handshakeProducer.produce(context, clientHello);
+                }
+            }
+        }
+    }
+
+    /**
+     * The "ClientHello" handshake message consumer for DTLS 1.3.
+     */
+    private static final
+            class D13ClientHelloConsumer implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private D13ClientHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,36 +25,89 @@
 
 package sun.security.ssl;
 
-import javax.crypto.SecretKey;
 import java.io.IOException;
-import java.io.PrintStream;
-import java.security.Principal;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
 
 /**
- * Models a non-certificate based ClientKeyExchange
+ * Pack of the "ClientKeyExchange" handshake message.
  */
-public abstract class ClientKeyExchange extends HandshakeMessage {
+final class ClientKeyExchange {
+    static final SSLConsumer handshakeConsumer =
+            new ClientKeyExchangeConsumer();
+    static final HandshakeProducer handshakeProducer =
+            new ClientKeyExchangeProducer();
+
+
+    /**
+     * The "ClientKeyExchange" handshake message producer.
+     */
+    private static final
+            class ClientKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private ClientKeyExchangeProducer() {
+            // blank
+        }
 
-    public ClientKeyExchange() {
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                        chc.negotiatedCipherSuite.keyExchange,
+                        chc.negotiatedProtocol);
+            if (ke != null) {
+                for (Map.Entry<Byte, HandshakeProducer> hp :
+                        ke.getHandshakeProducers(chc)) {
+                    if (hp.getKey() == SSLHandshake.CLIENT_KEY_EXCHANGE.id) {
+                        return hp.getValue().produce(context, message);
+                    }
+                }
+            }
+
+            // not consumer defined.
+            chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected ClientKeyExchange handshake message.");
+            return null;    // make the compiler happe
+        }
     }
 
-    @Override
-    int messageType() {
-        return ht_client_key_exchange;
-    }
-
-    @Override
-    public abstract int messageLength();
+    /**
+     * The "ClientKeyExchange" handshake message consumer.
+     */
+    private static final
+            class ClientKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ClientKeyExchangeConsumer() {
+            // blank
+        }
 
-    @Override
-    public abstract void send(HandshakeOutStream s) throws IOException;
-
-    @Override
-    public abstract void print(PrintStream s) throws IOException;
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            // clean up this consumer
+            shc.handshakeConsumers.remove(SSLHandshake.CLIENT_KEY_EXCHANGE.id);
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    shc.negotiatedCipherSuite.keyExchange,
+                    shc.negotiatedProtocol);
+            if (ke != null) {
+                for (Map.Entry<Byte, SSLConsumer> hc :
+                        ke.getHandshakeConsumers(shc)) {
+                    if (hc.getKey() == SSLHandshake.CLIENT_KEY_EXCHANGE.id) {
+                        hc.getValue().consume(context, message);
+                        return;
+                    }
+                }
+            }
 
-    public abstract SecretKey clientKeyExchange();
+            // not consumer defined.
+            shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected ClientKeyExchange handshake message.");
+        }
+    }
+}
 
-    public abstract Principal getPeerPrincipal();
-
-    public abstract Principal getLocalPrincipal();
-}
--- a/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2015, 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 sun.security.action.GetPropertyAction;
-
-import java.io.File;
-import java.io.FilePermission;
-import java.io.IOException;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.SecureRandom;
-import java.util.*;
-
-/**
- * Models a service that provides support for a particular client key exchange
- * mode. Currently used to implement Kerberos-related cipher suites.
- *
- * @since 9
- */
-public interface ClientKeyExchangeService {
-
-    static class Loader {
-        private static final Map<String,ClientKeyExchangeService>
-                providers = new HashMap<>();
-
-        static {
-            String path = GetPropertyAction.privilegedGetProperty("java.home");
-            ServiceLoader<ClientKeyExchangeService> sc =
-                    AccessController.doPrivileged(
-                            (PrivilegedAction<ServiceLoader<ClientKeyExchangeService>>)
-                                    () -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class),
-                            null,
-                            new FilePermission(new File(path, "-").toString(), "read"));
-            Iterator<ClientKeyExchangeService> iter = sc.iterator();
-            while (iter.hasNext()) {
-                ClientKeyExchangeService cs = iter.next();
-                for (String ex: cs.supported()) {
-                    providers.put(ex, cs);
-                }
-            }
-        }
-
-    }
-
-    public static ClientKeyExchangeService find(String ex) {
-        return Loader.providers.get(ex);
-    }
-
-
-    /**
-     * Returns the supported key exchange modes by this provider.
-     * @return the supported key exchange modes
-     */
-    String[] supported();
-
-    /**
-     * Returns a generalized credential object on the server side. The server
-     * side can use the info to determine if a cipher suite can be enabled.
-     * @param acc the AccessControlContext of the SSL session
-     * @return the credential object
-     */
-    Object getServiceCreds(AccessControlContext acc);
-
-    /**
-     * Returns the host name for a service principal. The info can be used in
-     * SNI or host name verifier.
-     * @param principal the principal of a service
-     * @return the string formed host name
-     */
-    String getServiceHostName(Principal principal);
-
-    /**
-     * Returns whether the specified principal is related to the current
-     * SSLSession. The info can be used to verify a SSL resume.
-     * @param isClient if true called from client side, otherwise from server
-     * @param acc the AccessControlContext of the SSL session
-     * @param p the specified principal
-     * @return true if related
-     */
-    boolean isRelated(boolean isClient, AccessControlContext acc, Principal p);
-
-    /**
-     * Creates the ClientKeyExchange object on the client side.
-     * @param serverName the intented peer name
-     * @param acc the AccessControlContext of the SSL session
-     * @param protocolVersion the TLS protocol version
-     * @param rand the SecureRandom that will used to generate the premaster
-     * @return the new Exchanger object
-     * @throws IOException if there is an error
-     */
-    ClientKeyExchange createClientExchange(String serverName, AccessControlContext acc,
-            ProtocolVersion protocolVersion, SecureRandom rand) throws IOException;
-
-    /**
-     * Create the ClientKeyExchange on the server side.
-     * @param protocolVersion the protocol version
-     * @param clientVersion the input protocol version
-     * @param rand a SecureRandom object used to generate premaster
-     *             (if the server has to create one)
-     * @param encodedTicket the ticket from client
-     * @param encrypted the encrypted premaster secret from client
-     * @param acc the AccessControlContext of the SSL session
-     * @param ServiceCreds the service side credentials object as retrived from
-     *                     {@link #getServiceCreds}
-     * @return the new Exchanger object
-     * @throws IOException if there is an error
-     */
-    ClientKeyExchange createServerExchange(
-            ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
-            SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
-            AccessControlContext acc, Object ServiceCreds) throws IOException;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ConnectionContext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+/**
+ * SSL/(D)TLS connection context.
+ */
+interface ConnectionContext {
+    // blank
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ContentType.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+/**
+ * Enum for SSL/(D)TLS content types.
+ */
+enum ContentType {
+    INVALID             ((byte)0,   "invalid",
+                            ProtocolVersion.PROTOCOLS_OF_13),
+    CHANGE_CIPHER_SPEC  ((byte)20,  "change_cipher_spec",
+                            ProtocolVersion.PROTOCOLS_TO_12),
+    ALERT               ((byte)21,  "alert",
+                            ProtocolVersion.PROTOCOLS_TO_13),
+    HANDSHAKE           ((byte)22,  "handshake",
+                            ProtocolVersion.PROTOCOLS_TO_13),
+    APPLICATION_DATA    ((byte)23,  "application_data",
+                            ProtocolVersion.PROTOCOLS_TO_13);
+
+    final byte id;
+    final String name;
+    final ProtocolVersion[] supportedProtocols;
+
+    private ContentType(byte id, String name,
+            ProtocolVersion[] supportedProtocols) {
+        this.id = id;
+        this.name = name;
+        this.supportedProtocols = supportedProtocols;
+    }
+
+    static ContentType valueOf(byte id) {
+        for (ContentType ct : ContentType.values()) {
+            if (ct.id == id) {
+                return ct;
+            }
+        }
+
+        return null;
+    }
+
+    static String nameOf(byte id) {
+        for (ContentType ct : ContentType.values()) {
+            if (ct.id == id) {
+                return ct.name;
+            }
+        }
+
+        return "<UNKNOWN CONTENT TYPE: " + (id & 0x0FF) + ">";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/CookieExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+
+import sun.security.ssl.ClientHello.ClientHelloMessage;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.ServerHello.ServerHelloMessage;
+import sun.security.util.HexDumpEncoder;
+
+public class CookieExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHCookieProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHCookieConsumer();
+    static final HandshakeConsumer chOnTradeConsumer =
+            new CHCookieUpdate();
+
+    static final HandshakeProducer hrrNetworkProducer =
+            new HRRCookieProducer();
+    static final ExtensionConsumer hrrOnLoadConsumer =
+            new HRRCookieConsumer();
+
+    static final HandshakeProducer hrrNetworkReproducer =
+            new HRRCookieReproducer();
+
+    static final CookieStringizer cookieStringizer =
+            new CookieStringizer();
+
+    /**
+     * The "cookie" extension.
+     */
+    static class CookieSpec implements SSLExtensionSpec {
+        final byte[] cookie;
+
+        private CookieSpec(ByteBuffer m) throws IOException {
+            // opaque cookie<1..2^16-1>;
+            if (m.remaining() < 3) {
+                throw new SSLProtocolException(
+                    "Invalid cookie extension: insufficient data");
+            }
+
+            this.cookie = Record.getBytes16(m);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"cookie\": '{'\n" +
+                    "{0}\n" +
+                    "'}',", Locale.ENGLISH);
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                Utilities.indent(hexEncoder.encode(cookie))
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    private static final class CookieStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new CookieSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    private static final
+            class CHCookieProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHCookieProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ClientHandshakeContext chc = (ClientHandshakeContext) context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable cookie extension");
+                }
+                return null;
+            }
+
+            // response to an HelloRetryRequest cookie
+            CookieSpec spec = (CookieSpec)chc.handshakeExtensions.get(
+                    SSLExtension.HRR_COOKIE);
+
+            if (spec != null &&
+                    spec.cookie != null && spec.cookie.length != 0) {
+                byte[] extData = new byte[spec.cookie.length + 2];
+                ByteBuffer m = ByteBuffer.wrap(extData);
+                Record.putBytes16(m, spec.cookie);
+                return extData;
+            }
+
+            return null;
+        }
+    }
+
+    private static final
+            class CHCookieConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHCookieConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable cookie extension");
+                }
+                return;     // ignore the extension
+            }
+
+            CookieSpec spec;
+            try {
+                spec = new CookieSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            shc.handshakeExtensions.put(SSLExtension.CH_COOKIE, spec);
+
+            // No impact on session resumption.
+            //
+            // Note that the protocol version negotiation happens before the
+            // session resumption negotiation.  And the session resumption
+            // negotiation depends on the negotiated protocol version.
+        }
+    }
+
+    private static final
+            class CHCookieUpdate implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CHCookieUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            CookieSpec spec = (CookieSpec)
+                    shc.handshakeExtensions.get(SSLExtension.CH_COOKIE);
+            if (spec == null) {
+                // Ignore, no "cookie" extension requested.
+                return;
+            }
+
+            HelloCookieManager hcm =
+                shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
+            if (!hcm.isCookieValid(shc, clientHello, spec.cookie)) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "unrecognized cookie");
+                return;     // fatal() always throws, make the compiler happy.
+            }
+        }
+    }
+
+    private static final
+            class HRRCookieProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private HRRCookieProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ServerHelloMessage hrrm = (ServerHelloMessage)message;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable cookie extension");
+                }
+                return null;
+            }
+
+            HelloCookieManager hcm =
+                shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
+
+            byte[] cookie = hcm.createCookie(shc, hrrm.clientHello);
+
+            byte[] extData = new byte[cookie.length + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putBytes16(m, cookie);
+
+            return extData;
+        }
+    }
+
+    private static final
+            class HRRCookieConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private HRRCookieConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable cookie extension");
+                }
+                return;     // ignore the extension
+            }
+
+            CookieSpec spec;
+            try {
+                spec = new CookieSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            chc.handshakeExtensions.put(SSLExtension.HRR_COOKIE, spec);
+        }
+    }
+
+    private static final
+            class HRRCookieReproducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private HRRCookieReproducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext) context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable cookie extension");
+                }
+                return null;
+            }
+
+            // copy of the ClientHello cookie
+            CookieSpec spec = (CookieSpec)shc.handshakeExtensions.get(
+                    SSLExtension.CH_COOKIE);
+
+            if (spec != null &&
+                    spec.cookie != null && spec.cookie.length != 0) {
+                byte[] extData = new byte[spec.cookie.length + 2];
+                ByteBuffer m = ByteBuffer.wrap(extData);
+                Record.putBytes16(m, spec.cookie);
+                return extData;
+            }
+
+            return null;
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,88 +23,299 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.IOException;
-import java.io.PrintStream;
 import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Locale;
+import javax.crypto.SecretKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
 import javax.net.ssl.SSLHandshakeException;
+import sun.security.ssl.DHKeyExchange.DHECredentials;
+import sun.security.ssl.DHKeyExchange.DHEPossession;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.util.HexDumpEncoder;
 
-/*
- * Message used by clients to send their Diffie-Hellman public
- * keys to servers.
- *
- * @author David Brownell
+/**
+ * Pack of the "ClientKeyExchange" handshake message.
  */
-final class DHClientKeyExchange extends HandshakeMessage {
+final class DHClientKeyExchange {
+    static final DHClientKeyExchangeConsumer dhHandshakeConsumer =
+            new DHClientKeyExchangeConsumer();
+    static final DHClientKeyExchangeProducer dhHandshakeProducer =
+            new DHClientKeyExchangeProducer();
 
-    @Override
-    int messageType() {
-        return ht_client_key_exchange;
-    }
+    /**
+     * The DiffieHellman ClientKeyExchange handshake message.
+     *
+     * If the client has sent a certificate which contains a suitable
+     * DiffieHellman key (for fixed_dh client authentication), then the
+     * client public value is implicit and does not need to be sent again.
+     * In this case, the client key exchange message will be sent, but it
+     * MUST be empty.
+     *
+     * Currently, we don't support cipher suite that requires implicit public
+     * key of client.
+     */
+    private static final
+            class DHClientKeyExchangeMessage extends HandshakeMessage {
+        private byte[] y;        // 1 to 2^16 - 1 bytes
 
-    /*
-     * This value may be empty if it was included in the
-     * client's certificate ...
-     */
-    private byte[] dh_Yc;               // 1 to 2^16 -1 bytes
+        DHClientKeyExchangeMessage(
+                HandshakeContext handshakeContext) throws IOException {
+            super(handshakeContext);
+            // This happens in client side only.
+            ClientHandshakeContext chc =
+                    (ClientHandshakeContext)handshakeContext;
 
-    BigInteger getClientPublicKey() {
-        return dh_Yc == null ? null : new BigInteger(1, dh_Yc);
-    }
+            DHEPossession dhePossession = null;
+            for (SSLPossession possession : chc.handshakePossessions) {
+                if (possession instanceof DHEPossession) {
+                    dhePossession = (DHEPossession)possession;
+                    break;
+                }
+            }
 
-    /*
-     * Either pass the client's public key explicitly (because it's
-     * using DHE or DH_anon), or implicitly (the public key was in the
-     * certificate).
-     */
-    DHClientKeyExchange(BigInteger publicKey) {
-        dh_Yc = toByteArray(publicKey);
-    }
+            if (dhePossession == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No DHE credentials negotiated for client key exchange");
+            }
+
+            DHPublicKey publicKey = dhePossession.publicKey;
+            DHParameterSpec params = publicKey.getParams();
+            this.y = Utilities.toByteArray(publicKey.getY());
+        }
 
-    DHClientKeyExchange() {
-        dh_Yc = null;
-    }
+        DHClientKeyExchangeMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+            // This happens in server side only.
+            ServerHandshakeContext shc =
+                    (ServerHandshakeContext)handshakeContext;
+
+            if (m.remaining() < 3) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "Invalid DH ClientKeyExchange message: insufficient data");
+            }
+
+            this.y = Record.getBytes16(m);
+
+            if (m.hasRemaining()) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "Invalid DH ClientKeyExchange message: unknown extra data");
+            }
+        }
 
-    /*
-     * Get the client's public key either explicitly or implicitly.
-     * (It's ugly to have an empty record be sent in the latter case,
-     * but that's what the protocol spec requires.)
-     */
-    DHClientKeyExchange(HandshakeInStream input) throws IOException {
-        if (input.available() >= 2) {
-            dh_Yc = input.getBytes16();
-        } else {
-            // currently, we don't support cipher suites that requires
-            // implicit public key of client.
-            throw new SSLHandshakeException(
-                    "Unsupported implicit client DiffieHellman public key");
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CLIENT_KEY_EXCHANGE;
+        }
+
+        @Override
+        public int messageLength() {
+            return y.length + 2;    // 2: length filed
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes16(y);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"DH ClientKeyExchange\": '{'\n" +
+                "  \"parameters\": '{'\n" +
+                "    \"dh_Yc\": '{'\n" +
+                "{0}\n" +
+                "    '}',\n" +
+                "  '}'\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(y), "      "),
+            };
+            return messageFormat.format(messageFields);
         }
     }
 
-    @Override
-    int messageLength() {
-        if (dh_Yc == null) {
-            return 0;
-        } else {
-            return dh_Yc.length + 2;
+    /**
+     * The DiffieHellman "ClientKeyExchange" handshake message producer.
+     */
+    private static final
+            class DHClientKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private DHClientKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            DHECredentials dheCredentials = null;
+            for (SSLCredentials cd : chc.handshakeCredentials) {
+                if (cd instanceof DHECredentials) {
+                    dheCredentials = (DHECredentials)cd;
+                    break;
+                }
+            }
+
+            if (dheCredentials == null) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No DHE credentials negotiated for client key exchange");
+            }
+
+
+            DHEPossession dhePossession = new DHEPossession(
+                    dheCredentials, chc.sslContext.getSecureRandom());
+            chc.handshakePossessions.add(dhePossession);
+            DHClientKeyExchangeMessage ckem =
+                    new DHClientKeyExchangeMessage(chc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced DH ClientKeyExchange handshake message", ckem);
+            }
+
+            // Output the handshake message.
+            ckem.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // update the states
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    chc.negotiatedCipherSuite.keyExchange,
+                    chc.negotiatedProtocol);
+            if (ke == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            } else {
+                SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
+                SecretKey masterSecret =
+                        masterKD.deriveKey("MasterSecret", null);
+                chc.handshakeSession.setMasterSecret(masterSecret);
+
+                SSLTrafficKeyDerivation kd =
+                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+                if (kd == null) {
+                    // unlikely
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                            chc.negotiatedProtocol);
+                } else {
+                    chc.handshakeKeyDerivation =
+                        kd.createKeyDerivation(chc, masterSecret);
+                }
+            }
+
+            // The handshake message has been delivered.
+            return null;
         }
     }
 
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        if (dh_Yc != null && dh_Yc.length != 0) {
-            s.putBytes16(dh_Yc);
+    /**
+     * The DiffieHellman "ClientKeyExchange" handshake message consumer.
+     */
+    private static final
+            class DHClientKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private DHClientKeyExchangeConsumer() {
+            // blank
         }
-    }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            DHEPossession dhePossession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof DHEPossession) {
+                    dhePossession = (DHEPossession)possession;
+                    break;
+                }
+            }
+
+            if (dhePossession == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No expected DHE possessions for client key exchange");
+            }
+
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    shc.negotiatedCipherSuite.keyExchange,
+                    shc.negotiatedProtocol);
+            if (ke == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            }
 
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** ClientKeyExchange, DH");
+            DHClientKeyExchangeMessage ckem =
+                    new DHClientKeyExchangeMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming DH ClientKeyExchange handshake message", ckem);
+            }
+
+            // create the credentials
+            try {
+                DHParameterSpec params = dhePossession.publicKey.getParams();
+                DHPublicKeySpec spec = new DHPublicKeySpec(
+                        new BigInteger(1, ckem.y),
+                        params.getP(), params.getG());
+                KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
+                DHPublicKey peerPublicKey =
+                        (DHPublicKey)kf.generatePublic(spec);
+
+                // check constraints of peer DHPublicKey
+                if (!shc.algorithmConstraints.permits(
+                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                        peerPublicKey)) {
+                    throw new SSLHandshakeException(
+                        "DHPublicKey does not comply to algorithm constraints");
+                }
 
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(s, "DH Public key", dh_Yc);
+                NamedGroup namedGroup = NamedGroup.valueOf(params);
+                shc.handshakeCredentials.add(
+                        new DHECredentials(peerPublicKey, namedGroup));
+            } catch (GeneralSecurityException | java.io.IOException e) {
+                throw (SSLHandshakeException)(new SSLHandshakeException(
+                        "Could not generate DHPublicKey").initCause(e));
+            }
+
+            // update the states
+            SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
+            SecretKey masterSecret =
+                    masterKD.deriveKey("MasterSecret", null);
+            shc.handshakeSession.setMasterSecret(masterSecret);
+
+            SSLTrafficKeyDerivation kd =
+                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+            if (kd == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "Not supported key derivation: " + shc.negotiatedProtocol);
+            } else {
+                shc.handshakeKeyDerivation =
+                    kd.createKeyDerivation(shc, masterSecret);
+            }
         }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/DHCrypt.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,278 +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.math.BigInteger;
-import java.security.*;
-import javax.net.ssl.SSLHandshakeException;
-import javax.crypto.SecretKey;
-import javax.crypto.KeyAgreement;
-import javax.crypto.interfaces.DHPublicKey;
-import javax.crypto.spec.*;
-import java.util.EnumSet;
-
-import sun.security.util.KeyUtil;
-
-/**
- * This class implements the Diffie-Hellman key exchange algorithm.
- * D-H means combining your private key with your partners public key to
- * generate a number. The peer does the same with its private key and our
- * public key. Through the magic of Diffie-Hellman we both come up with the
- * same number. This number is secret (discounting MITM attacks) and hence
- * called the shared secret. It has the same length as the modulus, e.g. 512
- * or 1024 bit. Man-in-the-middle attacks are typically countered by an
- * independent authentication step using certificates (RSA, DSA, etc.).
- *
- * The thing to note is that the shared secret is constant for two partners
- * with constant private keys. This is often not what we want, which is why
- * it is generally a good idea to create a new private key for each session.
- * Generating a private key involves one modular exponentiation assuming
- * suitable D-H parameters are available.
- *
- * General usage of this class (TLS DHE case):
- *  . if we are server, call DHCrypt(keyLength,random). This generates
- *    an ephemeral keypair of the request length.
- *  . if we are client, call DHCrypt(modulus, base, random). This
- *    generates an ephemeral keypair using the parameters specified by
- *    the server.
- *  . send parameters and public value to remote peer
- *  . receive peers ephemeral public key
- *  . call getAgreedSecret() to calculate the shared secret
- *
- * In TLS the server chooses the parameter values itself, the client must use
- * those sent to it by the server.
- *
- * The use of ephemeral keys as described above also achieves what is called
- * "forward secrecy". This means that even if the authentication keys are
- * broken at a later date, the shared secret remains secure. The session is
- * compromised only if the authentication keys are already broken at the
- * time the key exchange takes place and an active MITM attack is used.
- * This is in contrast to straightforward encrypting RSA key exchanges.
- *
- * @author David Brownell
- */
-final class DHCrypt {
-
-    // group parameters (prime modulus and generator)
-    private BigInteger modulus;                 // P (aka N)
-    private BigInteger base;                    // G (aka alpha)
-
-    // our private key (including private component x)
-    private PrivateKey privateKey;
-
-    // public component of our key, X = (g ^ x) mod p
-    private BigInteger publicValue;             // X (aka y)
-
-    // the times to recove from failure if public key validation
-    private static int MAX_FAILOVER_TIMES = 2;
-
-    /**
-     * Generate a Diffie-Hellman keypair of the specified size.
-     */
-    DHCrypt(int keyLength, SecureRandom random) {
-        this(keyLength,
-            PredefinedDHParameterSpecs.definedParams.get(keyLength), random);
-    }
-
-    /**
-     * Generate a Diffie-Hellman keypair using the specified parameters.
-     *
-     * @param modulus the Diffie-Hellman modulus P
-     * @param base the Diffie-Hellman base G
-     */
-    DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
-        this(modulus.bitLength(),
-                new DHParameterSpec(modulus, base), random);
-    }
-
-    /**
-     * Generate a Diffie-Hellman keypair using the named group.
-     */
-    DHCrypt(NamedGroup namedGroup, SecureRandom random) {
-        this(-1,        // The length (-1) is not used in the implementation.
-            SupportedGroupsExtension.getDHParameterSpec(namedGroup), random);
-    }
-
-    /**
-     * Generate a Diffie-Hellman keypair using the specified size and
-     * parameters.
-     */
-    private DHCrypt(int keyLength,
-            DHParameterSpec params, SecureRandom random) {
-
-        try {
-            KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
-            if (params != null) {
-                kpg.initialize(params, random);
-            } else {
-                kpg.initialize(keyLength, random);
-            }
-
-            DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
-            if (spec == null) {
-                throw new RuntimeException("Could not generate DH keypair");
-            }
-
-            publicValue = spec.getY();
-            modulus = spec.getP();
-            base = spec.getG();
-        } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Could not generate DH keypair", e);
-        }
-    }
-
-    static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
-        if (key instanceof DHPublicKey) {
-            DHPublicKey dhKey = (DHPublicKey)key;
-            DHParameterSpec params = dhKey.getParams();
-            return new DHPublicKeySpec(dhKey.getY(),
-                                    params.getP(), params.getG());
-        }
-        try {
-            KeyFactory factory = JsseJce.getKeyFactory("DiffieHellman");
-            return factory.getKeySpec(key, DHPublicKeySpec.class);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-
-    /** Returns the Diffie-Hellman modulus. */
-    BigInteger getModulus() {
-        return modulus;
-    }
-
-    /** Returns the Diffie-Hellman base (generator).  */
-    BigInteger getBase() {
-        return base;
-    }
-
-    /**
-     * Gets the public key of this end of the key exchange.
-     */
-    BigInteger getPublicKey() {
-        return publicValue;
-    }
-
-    /**
-     * Get the secret data that has been agreed on through Diffie-Hellman
-     * key agreement protocol.  Note that in the two party protocol, if
-     * the peer keys are already known, no other data needs to be sent in
-     * order to agree on a secret.  That is, a secured message may be
-     * sent without any mandatory round-trip overheads.
-     *
-     * <P>It is illegal to call this member function if the private key
-     * has not been set (or generated).
-     *
-     * @param  peerPublicKey the peer's public key.
-     * @param  keyIsValidated whether the {@code peerPublicKey} has beed
-     *         validated
-     * @return the secret, which is an unsigned big-endian integer
-     *         the same size as the Diffie-Hellman modulus.
-     */
-    SecretKey getAgreedSecret(BigInteger peerPublicValue,
-            boolean keyIsValidated) throws SSLHandshakeException {
-        try {
-            KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
-            DHPublicKeySpec spec =
-                        new DHPublicKeySpec(peerPublicValue, modulus, base);
-            PublicKey publicKey = kf.generatePublic(spec);
-            KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
-
-            // validate the Diffie-Hellman public key
-            if (!keyIsValidated &&
-                    !KeyUtil.isOracleJCEProvider(ka.getProvider().getName())) {
-                try {
-                    KeyUtil.validate(spec);
-                } catch (InvalidKeyException ike) {
-                    // prefer handshake_failure alert to internal_error alert
-                    throw new SSLHandshakeException(ike.getMessage());
-                }
-            }
-
-            ka.init(privateKey);
-            ka.doPhase(publicKey, true);
-            return ka.generateSecret("TlsPremasterSecret");
-        } catch (GeneralSecurityException e) {
-            throw (SSLHandshakeException) new SSLHandshakeException(
-                "Could not generate secret").initCause(e);
-        }
-    }
-
-    // Check constraints of the specified DH public key.
-    void checkConstraints(AlgorithmConstraints constraints,
-            BigInteger peerPublicValue) throws SSLHandshakeException {
-
-        try {
-            KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
-            DHPublicKeySpec spec =
-                        new DHPublicKeySpec(peerPublicValue, modulus, base);
-            DHPublicKey publicKey = (DHPublicKey)kf.generatePublic(spec);
-
-            // check constraints of DHPublicKey
-            if (!constraints.permits(
-                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
-                throw new SSLHandshakeException(
-                    "DHPublicKey does not comply to algorithm constraints");
-            }
-        } catch (GeneralSecurityException gse) {
-            throw (SSLHandshakeException) new SSLHandshakeException(
-                    "Could not generate DHPublicKey").initCause(gse);
-        }
-    }
-
-    // Generate and validate DHPublicKeySpec
-    private DHPublicKeySpec generateDHPublicKeySpec(KeyPairGenerator kpg)
-            throws GeneralSecurityException {
-
-        boolean doExtraValiadtion =
-                    (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
-        for (int i = 0; i <= MAX_FAILOVER_TIMES; i++) {
-            KeyPair kp = kpg.generateKeyPair();
-            privateKey = kp.getPrivate();
-            DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
-
-            // validate the Diffie-Hellman public key
-            if (doExtraValiadtion) {
-                try {
-                    KeyUtil.validate(spec);
-                } catch (InvalidKeyException ivke) {
-                    if (i == MAX_FAILOVER_TIMES) {
-                        throw ivke;
-                    }
-                    // otherwise, ignore the exception and try the next one
-                    continue;
-                }
-            }
-
-            return spec;
-        }
-
-        return null;
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.action.GetPropertyAction;
+import sun.security.ssl.CipherSuite.HashAlg;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.KeyUtil;
+
+final class DHKeyExchange {
+    static final SSLPossessionGenerator poGenerator =
+            new DHEPossessionGenerator(false);
+    static final SSLPossessionGenerator poExportableGenerator =
+            new DHEPossessionGenerator(true);
+    static final SSLKeyAgreementGenerator kaGenerator =
+            new DHEKAGenerator();
+
+    static final class DHECredentials implements SSLCredentials {
+        final DHPublicKey popPublicKey;
+        final NamedGroup namedGroup;
+
+        DHECredentials(DHPublicKey popPublicKey, NamedGroup namedGroup) {
+            this.popPublicKey = popPublicKey;
+            this.namedGroup = namedGroup;
+        }
+
+        static DHECredentials valueOf(NamedGroup ng,
+            byte[] encodedPublic) throws IOException, GeneralSecurityException {
+
+            if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
+                throw new RuntimeException(
+                        "Credentials decoding:  Not FFDHE named group");
+            }
+
+            if (encodedPublic == null || encodedPublic.length == 0) {
+                return null;
+            }
+
+            DHParameterSpec params = (DHParameterSpec)ng.getParameterSpec();
+            if (params == null) {
+                return null;
+            }
+
+            KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
+            DHPublicKeySpec spec = new DHPublicKeySpec(
+                    new BigInteger(1, encodedPublic),
+                    params.getP(), params.getG());
+            DHPublicKey publicKey =
+                    (DHPublicKey)kf.generatePublic(spec);
+
+            return new DHECredentials(publicKey, ng);
+        }
+    }
+
+    static final class DHEPossession implements SSLPossession {
+        final PrivateKey privateKey;
+        final DHPublicKey publicKey;
+        final NamedGroup namedGroup;
+
+        DHEPossession(NamedGroup namedGroup, SecureRandom random) {
+            try {
+                KeyPairGenerator kpg =
+                        JsseJce.getKeyPairGenerator("DiffieHellman");
+                DHParameterSpec params =
+                        (DHParameterSpec)namedGroup.getParameterSpec();
+                kpg.initialize(params, random);
+                KeyPair kp = generateDHKeyPair(kpg);
+                if (kp == null) {
+                    throw new RuntimeException("Could not generate DH keypair");
+                }
+                privateKey = kp.getPrivate();
+                publicKey = (DHPublicKey)kp.getPublic();
+            } catch (GeneralSecurityException gse) {
+                throw new RuntimeException(
+                        "Could not generate DH keypair", gse);
+            }
+
+            this.namedGroup = namedGroup;
+        }
+
+        DHEPossession(int keyLength, SecureRandom random) {
+            DHParameterSpec params =
+                    PredefinedDHParameterSpecs.definedParams.get(keyLength);
+            try {
+                KeyPairGenerator kpg =
+                    JsseJce.getKeyPairGenerator("DiffieHellman");
+                if (params != null) {
+                    kpg.initialize(params, random);
+                } else {
+                    kpg.initialize(keyLength, random);
+                }
+
+                KeyPair kp = generateDHKeyPair(kpg);
+                if (kp == null) {
+                    throw new RuntimeException(
+                            "Could not generate DH keypair of " +
+                            keyLength + " bits");
+                }
+                privateKey = kp.getPrivate();
+                publicKey = (DHPublicKey)kp.getPublic();
+            } catch (GeneralSecurityException gse) {
+                throw new RuntimeException(
+                        "Could not generate DH keypair", gse);
+            }
+
+            this.namedGroup = NamedGroup.valueOf(publicKey.getParams());
+        }
+
+        DHEPossession(DHECredentials credentials, SecureRandom random) {
+            try {
+                KeyPairGenerator kpg =
+                        JsseJce.getKeyPairGenerator("DiffieHellman");
+                kpg.initialize(credentials.popPublicKey.getParams(), random);
+                KeyPair kp = generateDHKeyPair(kpg);
+                if (kp == null) {
+                    throw new RuntimeException("Could not generate DH keypair");
+                }
+                privateKey = kp.getPrivate();
+                publicKey = (DHPublicKey)kp.getPublic();
+            } catch (GeneralSecurityException gse) {
+                throw new RuntimeException(
+                        "Could not generate DH keypair", gse);
+            }
+
+            this.namedGroup = credentials.namedGroup;
+        }
+
+        // Generate and validate DHPublicKeySpec
+        private KeyPair generateDHKeyPair(
+                KeyPairGenerator kpg) throws GeneralSecurityException {
+            boolean doExtraValiadtion =
+                    (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
+            boolean isRecovering = false;
+            for (int i = 0; i <= 2; i++) {      // Try to recover from failure.
+                KeyPair kp = kpg.generateKeyPair();
+                // validate the Diffie-Hellman public key
+                if (doExtraValiadtion) {
+                    DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
+                    try {
+                        KeyUtil.validate(spec);
+                    } catch (InvalidKeyException ivke) {
+                        if (isRecovering) {
+                            throw ivke;
+                        }
+                        // otherwise, ignore the exception and try again
+                        isRecovering = true;
+                        continue;
+                    }
+                }
+
+                return kp;
+            }
+
+            return null;
+        }
+
+        private static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
+            if (key instanceof DHPublicKey) {
+                DHPublicKey dhKey = (DHPublicKey)key;
+                DHParameterSpec params = dhKey.getParams();
+                return new DHPublicKeySpec(dhKey.getY(),
+                                        params.getP(), params.getG());
+            }
+            try {
+                KeyFactory factory = JsseJce.getKeyFactory("DiffieHellman");
+                return factory.getKeySpec(key, DHPublicKeySpec.class);
+            } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                // unlikely
+                throw new RuntimeException("Unable to get DHPublicKeySpec", e);
+            }
+        }
+
+        @Override
+        public byte[] encode() {
+            // Note: the DH public value is encoded as a big-endian integer
+            // and padded to the left with zeros to the size of p in bytes.
+            byte[] encoded = publicKey.getY().toByteArray();
+            int pSize = KeyUtil.getKeySize(publicKey);
+            if (pSize > 0 && encoded.length < pSize) {
+                byte[] buffer = new byte[pSize];
+                System.arraycopy(encoded, 0,
+                        buffer, pSize - encoded.length, encoded.length);
+                encoded = buffer;
+            }
+
+            return encoded;
+        }
+    }
+
+    private static final class
+            DHEPossessionGenerator implements SSLPossessionGenerator {
+        // 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;
+
+        // Is it for exportable cipher suite?
+        private final boolean exportable;
+
+        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 range from 1024 to 8192 (inclusive)");
+                    }
+                } catch (NumberFormatException nfe) {
+                    throw new IllegalArgumentException(
+                        "Invalid system property jdk.tls.ephemeralDHKeySize");
+                }
+            }
+        }
+
+        // Prevent instantiation of this class.
+        private DHEPossessionGenerator(boolean exportable) {
+            this.exportable = exportable;
+        }
+
+        // Used for ServerKeyExchange, TLS 1.2 and prior versions.
+        @Override
+        public SSLPossession createPossession(HandshakeContext context) {
+            NamedGroup preferableNamedGroup = null;
+            if (!useLegacyEphemeralDHKeys &&
+                    (context.clientRequestedNamedGroups != null) &&
+                    (!context.clientRequestedNamedGroups.isEmpty())) {
+                preferableNamedGroup =
+                        SupportedGroups.getPreferredGroup(
+                                context.negotiatedProtocol,
+                                context.algorithmConstraints,
+                                NamedGroupType.NAMED_GROUP_FFDHE,
+                                context.clientRequestedNamedGroups);
+                if (preferableNamedGroup != null) {
+                    return new DHEPossession(preferableNamedGroup,
+                                context.sslContext.getSecureRandom());
+                }
+            }
+
+            /*
+             * 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 = exportable ? 512 : 1024;           // default mode
+            if (!exportable) {
+                if (useLegacyEphemeralDHKeys) {          // legacy mode
+                    keySize = 768;
+                } else if (useSmartEphemeralDHKeys) {    // matched mode
+                    PrivateKey key = null;
+                    ServerHandshakeContext shc =
+                            (ServerHandshakeContext)context;
+                    if (shc.interimAuthn instanceof X509Possession) {
+                        key = ((X509Possession)shc.interimAuthn).popPrivateKey;
+                    }
+
+                    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.
+                        //
+                        // 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 : 2048;
+                    } // Otherwise, anonymous cipher suites, 1024-bit is used.
+                } else if (customizedDHKeySize > 0) {    // customized mode
+                    keySize = customizedDHKeySize;
+                }
+            }
+
+            return new DHEPossession(
+                    keySize, context.sslContext.getSecureRandom());
+        }
+    }
+
+    private static final
+            class DHEKAGenerator implements SSLKeyAgreementGenerator {
+        static private DHEKAGenerator instance = new DHEKAGenerator();
+
+        // Prevent instantiation of this class.
+        private DHEKAGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+            DHEPossession dhePossession = null;
+            DHECredentials dheCredentials = null;
+            for (SSLPossession poss : context.handshakePossessions) {
+                if (!(poss instanceof DHEPossession)) {
+                    continue;
+                }
+
+                DHEPossession dhep = (DHEPossession)poss;
+                for (SSLCredentials cred : context.handshakeCredentials) {
+                    if (!(cred instanceof DHECredentials)) {
+                        continue;
+                    }
+                    DHECredentials dhec = (DHECredentials)cred;
+                    if (dhep.namedGroup != null && dhec.namedGroup != null) {
+                        if (dhep.namedGroup.equals(dhec.namedGroup)) {
+                            dheCredentials = (DHECredentials)cred;
+                            break;
+                        }
+                    } else {
+                        DHParameterSpec pps = dhep.publicKey.getParams();
+                        DHParameterSpec cps = dhec.popPublicKey.getParams();
+                        if (pps.getP().equals(cps.getP()) &&
+                                pps.getG().equals(cps.getG())) {
+                            dheCredentials = (DHECredentials)cred;
+                            break;
+                        }
+                    }
+                }
+
+                if (dheCredentials != null) {
+                    dhePossession = (DHEPossession)poss;
+                    break;
+                }
+            }
+
+            if (dhePossession == null || dheCredentials == null) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No sufficient DHE key agreement parameters negotiated");
+            }
+
+            return new DHEKAKeyDerivation(context,
+                    dhePossession.privateKey, dheCredentials.popPublicKey);
+        }
+
+        private static final
+                class DHEKAKeyDerivation implements SSLKeyDerivation {
+            private final HandshakeContext context;
+            private final PrivateKey localPrivateKey;
+            private final PublicKey peerPublicKey;
+
+            DHEKAKeyDerivation(HandshakeContext context,
+                    PrivateKey localPrivateKey,
+                    PublicKey peerPublicKey) {
+                this.context = context;
+                this.localPrivateKey = localPrivateKey;
+                this.peerPublicKey = peerPublicKey;
+            }
+
+            @Override
+            public SecretKey deriveKey(String algorithm,
+                    AlgorithmParameterSpec params) throws IOException {
+                if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
+                    return t12DeriveKey(algorithm, params);
+                } else {
+                    return t13DeriveKey(algorithm, params);
+                }
+            }
+
+            private SecretKey t12DeriveKey(String algorithm,
+                    AlgorithmParameterSpec params) throws IOException {
+                try {
+                    KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
+                    ka.init(localPrivateKey);
+                    ka.doPhase(peerPublicKey, true);
+                    SecretKey preMasterSecret =
+                            ka.generateSecret("TlsPremasterSecret");
+                    SSLMasterKeyDerivation mskd =
+                            SSLMasterKeyDerivation.valueOf(
+                                    context.negotiatedProtocol);
+                    if (mskd == null) {
+                        // unlikely
+                        throw new SSLHandshakeException(
+                            "No expected master key derivation for protocol: " +
+                            context.negotiatedProtocol.name);
+                    }
+                    SSLKeyDerivation kd = mskd.createKeyDerivation(
+                            context, preMasterSecret);
+                    return kd.deriveKey("MasterSecret", params);
+                } catch (GeneralSecurityException gse) {
+                    throw (SSLHandshakeException) new SSLHandshakeException(
+                        "Could not generate secret").initCause(gse);
+                }
+            }
+
+            private SecretKey t13DeriveKey(String algorithm,
+                    AlgorithmParameterSpec params) throws IOException {
+                try {
+                    KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
+                    ka.init(localPrivateKey);
+                    ka.doPhase(peerPublicKey, true);
+                    SecretKey sharedSecret =
+                            ka.generateSecret("TlsPremasterSecret");
+
+                    HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
+                    SSLKeyDerivation kd = context.handshakeKeyDerivation;
+                    HKDF hkdf = new HKDF(hashAlg.name);
+                    if (kd == null) {   // No PSK is in use.
+                        // If PSK is not in use Early Secret will still be
+                        // HKDF-Extract(0, 0).
+                        byte[] zeros = new byte[hashAlg.hashLength];
+                        SecretKeySpec ikm =
+                                new SecretKeySpec(zeros, "TlsPreSharedSecret");
+                        SecretKey earlySecret =
+                                hkdf.extract(zeros, ikm, "TlsEarlySecret");
+                        kd = new SSLSecretDerivation(context, earlySecret);
+                    }
+
+                    // derive salt secret
+                    SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
+
+                    // derive handshake secret
+                    return hkdf.extract(saltSecret, sharedSecret, algorithm);
+                } catch (GeneralSecurityException gse) {
+                    throw (SSLHandshakeException) new SSLHandshakeException(
+                        "Could not generate secret").initCause(gse);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Locale;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import sun.security.ssl.DHKeyExchange.DHECredentials;
+import sun.security.ssl.DHKeyExchange.DHEPossession;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.HexDumpEncoder;
+import sun.security.util.KeyUtil;
+
+/**
+ * Pack of the ServerKeyExchange handshake message.
+ */
+final class DHServerKeyExchange {
+    static final SSLConsumer dhHandshakeConsumer =
+            new DHServerKeyExchangeConsumer();
+    static final HandshakeProducer dhHandshakeProducer =
+            new DHServerKeyExchangeProducer();
+
+    /**
+     * The DiffieHellman ServerKeyExchange handshake message.
+     */
+    private static final
+            class DHServerKeyExchangeMessage extends HandshakeMessage {
+        // public key encapsulated in this message
+        private final byte[] p;        // 1 to 2^16 - 1 bytes
+        private final byte[] g;        // 1 to 2^16 - 1 bytes
+        private final byte[] y;        // 1 to 2^16 - 1 bytes
+
+        // the signature algorithm used by this ServerKeyExchange message
+        private final boolean useExplicitSigAlgorithm;
+        private final SignatureScheme signatureScheme;
+
+        // signature bytes, or null if anonymous
+        private final byte[] paramsSignature;
+
+        DHServerKeyExchangeMessage(
+                HandshakeContext handshakeContext) throws IOException {
+            super(handshakeContext);
+
+            // This happens in server side only.
+            ServerHandshakeContext shc =
+                    (ServerHandshakeContext)handshakeContext;
+
+            DHEPossession dhePossession = null;
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof DHEPossession) {
+                    dhePossession = (DHEPossession)possession;
+                    if (x509Possession != null) {
+                        break;
+                    }
+                } else if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    if (dhePossession != null) {
+                        break;
+                    }
+                }
+            }
+
+            if (dhePossession == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No DHE credentials negotiated for server key exchange");
+            }
+            DHPublicKey publicKey = dhePossession.publicKey;
+            DHParameterSpec params = publicKey.getParams();
+            this.p = Utilities.toByteArray(params.getP());
+            this.g = Utilities.toByteArray(params.getG());
+            this.y = Utilities.toByteArray(publicKey.getY());
+
+            if (x509Possession == null) {
+                // anonymous, no authentication, no signature
+                paramsSignature = null;
+                signatureScheme = null;
+                useExplicitSigAlgorithm = false;
+            } else {
+                useExplicitSigAlgorithm =
+                        shc.negotiatedProtocol.useTLS12PlusSpec();
+                Signature signer = null;
+                if (useExplicitSigAlgorithm) {
+                    signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                            shc.peerRequestedSignatureSchemes,
+                            x509Possession.popPrivateKey,
+                            shc.negotiatedProtocol);
+                    if (signatureScheme == null) {
+                        // Unlikely, the credentials generator should have
+                        // selected the preferable signature algorithm properly.
+                        shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "No preferred signature algorithm");
+                    }
+                    try {
+                        signer = signatureScheme.getSignature(
+                                x509Possession.popPrivateKey);
+                    } catch (NoSuchAlgorithmException | InvalidKeyException |
+                            InvalidAlgorithmParameterException nsae) {
+                        shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Unsupported signature algorithm: " +
+                            signatureScheme.name, nsae);
+                    }
+                } else {
+                    signatureScheme = null;
+                    try {
+                        signer = getSignature(
+                                x509Possession.popPrivateKey.getAlgorithm(),
+                                x509Possession.popPrivateKey);
+                    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+                        shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Unsupported signature algorithm: " +
+                            x509Possession.popPrivateKey.getAlgorithm(), e);
+                    }
+                }
+
+                byte[] signature = null;
+                try {
+                    updateSignature(signer, shc.clientHelloRandom.randomBytes,
+                            shc.serverHelloRandom.randomBytes);
+                    signature = signer.sign();
+                } catch (SignatureException ex) {
+                    shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failed to sign dhe parameters: " +
+                        x509Possession.popPrivateKey.getAlgorithm(), ex);
+                }
+                paramsSignature = signature;
+            }
+        }
+
+        DHServerKeyExchangeMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // This happens in client side only.
+            ClientHandshakeContext chc =
+                    (ClientHandshakeContext)handshakeContext;
+
+            this.p = Record.getBytes16(m);
+            this.g = Record.getBytes16(m);
+            this.y = Record.getBytes16(m);
+
+            try {
+                KeyUtil.validate(new DHPublicKeySpec(
+                        new BigInteger(1, y),
+                        new BigInteger(1, p),
+                        new BigInteger(1, p)));
+            } catch (InvalidKeyException ike) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "Invalid DH ServerKeyExchange: invalid parameters", ike);
+            }
+
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials cd : chc.handshakeCredentials) {
+                if (cd instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)cd;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null) {
+                // anonymous, no authentication, no signature
+                if (m.hasRemaining()) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid DH ServerKeyExchange: unknown extra data");
+                }
+
+                this.signatureScheme = null;
+                this.paramsSignature = null;
+                this.useExplicitSigAlgorithm = false;
+
+                return;
+            }
+
+            this.useExplicitSigAlgorithm =
+                    chc.negotiatedProtocol.useTLS12PlusSpec();
+            if (useExplicitSigAlgorithm) {
+                int ssid = Record.getInt16(m);
+                signatureScheme = SignatureScheme.valueOf(ssid);
+                if (signatureScheme == null) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Invalid signature algorithm (" + ssid +
+                            ") used in DH ServerKeyExchange handshake message");
+                }
+
+                if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Unsupported signature algorithm (" +
+                            signatureScheme.name +
+                            ") used in DH ServerKeyExchange handshake message");
+                }
+            } else {
+                this.signatureScheme = null;
+            }
+
+            // read and verify the signature
+            this.paramsSignature = Record.getBytes16(m);
+            Signature signer;
+            if (useExplicitSigAlgorithm) {
+                try {
+                    signer = signatureScheme.getSignature(
+                            x509Credentials.popPublicKey);
+                } catch (NoSuchAlgorithmException | InvalidKeyException |
+                        InvalidAlgorithmParameterException nsae) {
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Unsupported signature algorithm: " +
+                            signatureScheme.name, nsae);
+
+                    return;     // make the compiler happe
+                }
+            } else {
+                try {
+                    signer = getSignature(
+                            x509Credentials.popPublicKey.getAlgorithm(),
+                            x509Credentials.popPublicKey);
+                } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Unsupported signature algorithm: " +
+                            x509Credentials.popPublicKey.getAlgorithm(), e);
+
+                    return;     // make the compiler happe
+                }
+            }
+
+            try {
+                updateSignature(signer,
+                        chc.clientHelloRandom.randomBytes,
+                        chc.serverHelloRandom.randomBytes);
+
+                if (!signer.verify(paramsSignature)) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid signature on DH ServerKeyExchange message");
+                }
+            } catch (SignatureException ex) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot verify DH ServerKeyExchange signature", ex);
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.SERVER_KEY_EXCHANGE;
+        }
+
+        @Override
+        public int messageLength() {
+            int sigLen = 0;
+            if (paramsSignature != null) {
+                sigLen = 2 + paramsSignature.length;
+                if (useExplicitSigAlgorithm) {
+                    sigLen += SignatureScheme.sizeInRecord();
+                }
+            }
+
+            return 6 + p.length + g.length + y.length + sigLen;
+                    // 6: overhead for p, g, y values
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes16(p);
+            hos.putBytes16(g);
+            hos.putBytes16(y);
+
+            if (paramsSignature != null) {
+                if (useExplicitSigAlgorithm) {
+                    hos.putInt16(signatureScheme.id);
+                }
+
+                hos.putBytes16(paramsSignature);
+            }
+        }
+
+        @Override
+        public String toString() {
+            if (paramsSignature == null) {    // anonymous
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"DH ServerKeyExchange\": '{'\n" +
+                    "  \"parameters\": '{'\n" +
+                    "    \"dh_p\": '{'\n" +
+                    "{0}\n" +
+                    "    '}',\n" +
+                    "    \"dh_g\": '{'\n" +
+                    "{1}\n" +
+                    "    '}',\n" +
+                    "    \"dh_Ys\": '{'\n" +
+                    "{2}\n" +
+                    "    '}',\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                Object[] messageFields = {
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(p), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(g), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(y), "      "),
+                };
+
+                return messageFormat.format(messageFields);
+            }
+
+            if (useExplicitSigAlgorithm) {
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"DH ServerKeyExchange\": '{'\n" +
+                    "  \"parameters\": '{'\n" +
+                    "    \"dh_p\": '{'\n" +
+                    "{0}\n" +
+                    "    '}',\n" +
+                    "    \"dh_g\": '{'\n" +
+                    "{1}\n" +
+                    "    '}',\n" +
+                    "    \"dh_Ys\": '{'\n" +
+                    "{2}\n" +
+                    "    '}',\n" +
+                    "  '}',\n" +
+                    "  \"digital signature\":  '{'\n" +
+                    "    \"signature algorithm\": \"{3}\"\n" +
+                    "    \"signature\": '{'\n" +
+                    "{4}\n" +
+                    "    '}',\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                Object[] messageFields = {
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(p), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(g), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(y), "      "),
+                    signatureScheme.name,
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(paramsSignature), "      ")
+                };
+
+                return messageFormat.format(messageFields);
+            } else {
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"DH ServerKeyExchange\": '{'\n" +
+                    "  \"parameters\": '{'\n" +
+                    "    \"dh_p\": '{'\n" +
+                    "{0}\n" +
+                    "    '}',\n" +
+                    "    \"dh_g\": '{'\n" +
+                    "{1}\n" +
+                    "    '}',\n" +
+                    "    \"dh_Ys\": '{'\n" +
+                    "{2}\n" +
+                    "    '}',\n" +
+                    "  '}',\n" +
+                    "  \"signature\": '{'\n" +
+                    "{3}\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                Object[] messageFields = {
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(p), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(g), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(y), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(paramsSignature), "    ")
+                };
+
+                return messageFormat.format(messageFields);
+            }
+        }
+
+        private static Signature getSignature(String keyAlgorithm,
+                Key key) throws NoSuchAlgorithmException, InvalidKeyException {
+            Signature signer = null;
+            switch (keyAlgorithm) {
+                case "DSA":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_DSA);
+                    break;
+                case "RSA":
+                    signer = RSASignature.getInstance();
+                    break;
+                default:
+                    throw new NoSuchAlgorithmException(
+                        "neither an RSA or a DSA key : " + keyAlgorithm);
+            }
+
+            if (signer != null) {
+                if (key instanceof PublicKey) {
+                    signer.initVerify((PublicKey)(key));
+                } else {
+                    signer.initSign((PrivateKey)key);
+                }
+            }
+
+            return signer;
+        }
+
+        /*
+         * Update sig with nonces and Diffie-Hellman public key.
+         */
+        private void updateSignature(Signature sig, byte[] clntNonce,
+                byte[] svrNonce) throws SignatureException {
+            int tmp;
+
+            sig.update(clntNonce);
+            sig.update(svrNonce);
+
+            sig.update((byte)(p.length >> 8));
+            sig.update((byte)(p.length & 0x0ff));
+            sig.update(p);
+
+            sig.update((byte)(g.length >> 8));
+            sig.update((byte)(g.length & 0x0ff));
+            sig.update(g);
+
+            sig.update((byte)(y.length >> 8));
+            sig.update((byte)(y.length & 0x0ff));
+            sig.update(y);
+        }
+    }
+
+    /**
+     * The DiffieHellman "ServerKeyExchange" handshake message producer.
+     */
+    static final class DHServerKeyExchangeProducer
+            implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private DHServerKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            DHServerKeyExchangeMessage skem =
+                    new DHServerKeyExchangeMessage(shc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced DH ServerKeyExchange handshake message", skem);
+            }
+
+            // Output the handshake message.
+            skem.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The DiffieHellman "ServerKeyExchange" handshake message consumer.
+     */
+    static final class DHServerKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private DHServerKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            DHServerKeyExchangeMessage skem =
+                    new DHServerKeyExchangeMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming DH ServerKeyExchange handshake message", skem);
+            }
+
+            //
+            // validate
+            //
+            // check constraints of EC PublicKey
+            DHPublicKey publicKey;
+            try {
+                KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
+                DHPublicKeySpec spec = new DHPublicKeySpec(
+                        new BigInteger(1, skem.y),
+                        new BigInteger(1, skem.p),
+                        new BigInteger(1, skem.g));
+                publicKey = (DHPublicKey)kf.generatePublic(spec);
+            } catch (GeneralSecurityException gse) {
+                chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                    "Could not generate DHPublicKey", gse);
+
+                return;     // make the compiler happy
+            }
+
+            if (!chc.algorithmConstraints.permits(
+                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
+                chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                        "DH ServerKeyExchange does not comply to " +
+                        "algorithm constraints");
+            }
+
+            //
+            // update
+            //
+            NamedGroup namedGroup = NamedGroup.valueOf(publicKey.getParams());
+            chc.handshakeCredentials.add(
+                    new DHECredentials(publicKey, namedGroup));
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -18,56 +18,41 @@
  * 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
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 9406+5 USA
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.util.*;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
 import javax.crypto.BadPaddingException;
-
-import javax.net.ssl.*;
-
-import sun.security.util.HexDumpEncoder;
-import static sun.security.ssl.HandshakeMessage.*;
+import javax.net.ssl.SSLException;
+import sun.security.ssl.SSLCipher.SSLReadCipher;
 
 /**
  * DTLS {@code InputRecord} implementation for {@code SSLEngine}.
  */
 final class DTLSInputRecord extends InputRecord implements DTLSRecord {
-
     private DTLSReassembler reassembler = null;
-
-    int                 readEpoch;
+    private int             readEpoch;
 
-    int                 prevReadEpoch;
-    Authenticator       prevReadAuthenticator;
-    CipherBox           prevReadCipher;
-
-    DTLSInputRecord() {
+    DTLSInputRecord(HandshakeHash handshakeHash) {
+        super(handshakeHash, SSLReadCipher.nullDTlsReadCipher());
         this.readEpoch = 0;
-        this.readAuthenticator = new MAC(true);
-
-        this.prevReadEpoch = 0;
-        this.prevReadCipher = CipherBox.NULL;
-        this.prevReadAuthenticator = new MAC(true);
     }
 
     @Override
-    void changeReadCiphers(Authenticator readAuthenticator,
-            CipherBox readCipher) {
-
-        prevReadCipher.dispose();
-
-        this.prevReadAuthenticator = this.readAuthenticator;
-        this.prevReadCipher = this.readCipher;
-        this.prevReadEpoch = this.readEpoch;
-
-        this.readAuthenticator = readAuthenticator;
+    void changeReadCiphers(SSLReadCipher readCipher) {
         this.readCipher = readCipher;
         this.readEpoch++;
     }
@@ -75,7 +60,6 @@
     @Override
     public synchronized void close() throws IOException {
         if (!isClosed) {
-            prevReadCipher.dispose();
             super.close();
         }
     }
@@ -87,14 +71,8 @@
 
     @Override
     int estimateFragmentSize(int packetSize) {
-        int macLen = 0;
-        if (readAuthenticator instanceof MAC) {
-            macLen = ((MAC)readAuthenticator).MAClen();
-        }
-
         if (packetSize > 0) {
-            return readCipher.estimateFragmentSize(
-                    packetSize, macLen, headerSize);
+            return readCipher.estimateFragmentSize(packetSize, headerSize);
         } else {
             return Record.maxDataSize;
         }
@@ -108,6 +86,11 @@
     }
 
     @Override
+    void finishHandshake() {
+        reassembler = null;
+    }
+
+    @Override
     Plaintext acquirePlaintext() {
         if (reassembler != null) {
             return reassembler.acquirePlaintext();
@@ -116,16 +99,28 @@
         return null;
     }
 
-    @Override
-    Plaintext decode(ByteBuffer packet) {
+     @Override
+    Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
+            int srcsLength) throws IOException, BadPaddingException {
+        if (srcs == null || srcs.length == 0 || srcsLength == 0) {
+            Plaintext pt = acquirePlaintext();
+            return pt == null ? new Plaintext[0] : new Plaintext[] { pt };
+        } else if (srcsLength == 1) {
+            return decode(srcs[srcsOffset]);
+        } else {
+            ByteBuffer packet = extract(srcs,
+                    srcsOffset, srcsLength, DTLSRecord.headerSize);
+            return decode(packet);
+        }
+    }
 
+    Plaintext[] decode(ByteBuffer packet) {
         if (isClosed) {
             return null;
         }
 
-        if (debug != null && Debug.isOn("packet")) {
-             Debug.printHex(
-                    "[Raw read]: length = " + packet.remaining(), packet);
+        if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+            SSLLogger.fine("Raw read", packet);
         }
 
         // The caller should have validated the record.
@@ -149,20 +144,20 @@
         int contentLen = ((packet.get() & 0xFF) << 8) |
                           (packet.get() & 0xFF);           // pos: 11, 12
 
-        if (debug != null && Debug.isOn("record")) {
-            Debug.log("READ: " +
-                    ProtocolVersion.valueOf(majorVersion, minorVersion) +
-                    " " + Record.contentName(contentType) + ", length = " +
+        if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+            SSLLogger.fine("READ: " +
+                    ProtocolVersion.nameOf(majorVersion, minorVersion) +
+                    " " + ContentType.nameOf(contentType) + ", length = " +
                     contentLen);
         }
 
-        int recLim = srcPos + DTLSRecord.headerSize + contentLen;
+        int recLim = Math.addExact(srcPos, DTLSRecord.headerSize + contentLen);
 
-        if (this.prevReadEpoch > recordEpoch) {
+        if (this.readEpoch > recordEpoch) {
             // Reset the position of the packet buffer.
             packet.position(recLim);
-            if (debug != null && Debug.isOn("record")) {
-                Debug.printHex("READ: discard this old record", recordEnS);
+            if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                SSLLogger.fine("READ: discard this old record", recordEnS);
             }
             return null;
         }
@@ -171,20 +166,23 @@
         if (this.readEpoch < recordEpoch) {
             // Discard the record younger than the current epcoh if:
             // 1. it is not a handshake message, or
-            // 2. it is not of next epoch.
-            if (((contentType != Record.ct_handshake) &&
-                    (contentType != Record.ct_change_cipher_spec)) ||
+            // 3. it is not of next epoch.
+            if ((contentType != ContentType.HANDSHAKE.id &&
+                    contentType != ContentType.CHANGE_CIPHER_SPEC.id) ||
+                (reassembler == null &&
+                    contentType != ContentType.HANDSHAKE.id) ||
                 (this.readEpoch < (recordEpoch - 1))) {
 
                 packet.position(recLim);
 
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Premature record (epoch), discard it.");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine("Premature record (epoch), discard it.");
                 }
 
                 return null;
             }
 
+
             // Not ready to decrypt this record, may be an encrypted Finished
             // message, need to buffer it.
             byte[] fragment = new byte[contentLen];
@@ -193,80 +191,65 @@
                     majorVersion, minorVersion,
                     recordEnS, recordEpoch, recordSeq, true);
 
+            if (reassembler == null) {
+                reassembler = new DTLSReassembler(recordEpoch);
+            }
             reassembler.queueUpFragment(buffered);
 
             // consume the full record in the packet buffer.
             packet.position(recLim);
 
-            return reassembler.acquirePlaintext();
+            Plaintext pt = reassembler.acquirePlaintext();
+            return pt == null ? null : new Plaintext[] { pt };
         }
 
         //
-        // Now, the message is of this epoch or the previous epoch.
+        // Now, the message is of this epoch.
         //
-        Authenticator decodeAuthenticator;
-        CipherBox decodeCipher;
-        if (this.readEpoch == recordEpoch) {
-            decodeAuthenticator = readAuthenticator;
-            decodeCipher = readCipher;
-        } else {                        // prevReadEpoch == recordEpoch
-            decodeAuthenticator = prevReadAuthenticator;
-            decodeCipher = prevReadCipher;
-        }
-
         // decrypt the fragment
         packet.limit(recLim);
         packet.position(srcPos + DTLSRecord.headerSize);
 
         ByteBuffer plaintextFragment;
         try {
-            plaintextFragment = decrypt(decodeAuthenticator,
-                    decodeCipher, contentType, packet, recordEnS);
-        } catch (BadPaddingException bpe) {
-            if (debug != null && Debug.isOn("ssl")) {
-                Debug.log("Discard invalid record: " + bpe);
+            Plaintext plaintext =
+                    readCipher.decrypt(contentType, packet, recordEnS);
+            plaintextFragment = plaintext.fragment;
+            contentType = plaintext.contentType;
+        } catch (GeneralSecurityException gse) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("Discard invalid record: " + gse);
             }
 
             // invalid, discard this record [section 4.1.2.7, RFC 6347]
             return null;
         } finally {
-            // comsume a complete record
+            // consume a complete record
             packet.limit(srcLim);
             packet.position(recLim);
         }
 
-        if (contentType != Record.ct_change_cipher_spec &&
-            contentType != Record.ct_handshake) {   // app data or alert
+        if (contentType != ContentType.CHANGE_CIPHER_SPEC.id &&
+            contentType != ContentType.HANDSHAKE.id) {   // app data or alert
                                                     // no retransmission
             // Cleanup the handshake reassembler if necessary.
             if ((reassembler != null) &&
                     (reassembler.handshakeEpoch < recordEpoch)) {
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Cleanup the handshake reassembler");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine("Cleanup the handshake reassembler");
                 }
 
                 reassembler = null;
             }
 
-            return new Plaintext(contentType, majorVersion, minorVersion,
-                    recordEpoch, Authenticator.toLong(recordEnS),
-                    plaintextFragment);
+            return new Plaintext[] {
+                    new Plaintext(contentType, majorVersion, minorVersion,
+                            recordEpoch, Authenticator.toLong(recordEnS),
+                            plaintextFragment)};
         }
 
-        if (contentType == Record.ct_change_cipher_spec) {
+        if (contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
             if (reassembler == null) {
-                if (this.readEpoch != recordEpoch) {
-                    // handshake has not started, should be an
-                    // old handshake message, discard it.
-
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log(
-                                "Lagging behind ChangeCipherSpec, discard it.");
-                    }
-
-                    return null;
-                }
-
                 reassembler = new DTLSReassembler(recordEpoch);
             }
 
@@ -284,26 +267,15 @@
 
                 if (hsFrag == null) {
                     // invalid, discard this record
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log("Invalid handshake message, discard it.");
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
+                                "Invalid handshake message, discard it.");
                     }
 
                     return null;
                 }
 
                 if (reassembler == null) {
-                    if (this.readEpoch != recordEpoch) {
-                        // handshake has not started, should be an
-                        // old handshake message, discard it.
-
-                        if (debug != null && Debug.isOn("verbose")) {
-                            Debug.log(
-                                "Lagging behind handshake record, discard it.");
-                        }
-
-                        return null;
-                    }
-
                     reassembler = new DTLSReassembler(recordEpoch);
                 }
 
@@ -314,18 +286,25 @@
         // Completed the read of the full record.  Acquire the reassembled
         // messages.
         if (reassembler != null) {
-            return reassembler.acquirePlaintext();
+            Plaintext pt = reassembler.acquirePlaintext();
+            return pt == null ? null : new Plaintext[] { pt };
         }
 
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.log("The reassembler is not initialized yet.");
+        if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+             SSLLogger.fine("The reassembler is not initialized yet.");
         }
 
         return null;
     }
 
     @Override
-    int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
+    int bytesInCompletePacket(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
+
+        return bytesInCompletePacket(srcs[srcsOffset]);
+    }
+
+    private int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
 
         // DTLS length field is in bytes 11/12
         if (packet.remaining() < headerSize) {
@@ -337,15 +316,20 @@
 
         // Check the content type of the record.
         byte contentType = packet.get(pos);
-        if (!Record.isValidContentType(contentType)) {
+        if (ContentType.valueOf(contentType) == null) {
             throw new SSLException(
                     "Unrecognized SSL message, plaintext connection?");
         }
 
         // Check the protocol version of the record.
-        ProtocolVersion recordVersion =
-            ProtocolVersion.valueOf(packet.get(pos + 1), packet.get(pos + 2));
-        checkRecordVersion(recordVersion, false);
+        byte majorVersion = packet.get(pos + 1);
+        byte minorVersion = packet.get(pos + 2);
+        if (!ProtocolVersion.isNegotiable(
+                majorVersion, minorVersion, true, false)) {
+            throw new SSLException("Unrecognized record version " +
+                    ProtocolVersion.nameOf(majorVersion, minorVersion) +
+                    " , plaintext connection?");
+        }
 
         // Get the fragment length of the record.
         int fragLen = ((packet.get(pos + 11) & 0xFF) << 8) +
@@ -359,17 +343,6 @@
         return fragLen;
     }
 
-    @Override
-    void checkRecordVersion(ProtocolVersion recordVersion,
-            boolean allowSSL20Hello) throws SSLException {
-
-        if (!recordVersion.maybeDTLSProtocol()) {
-            throw new SSLException(
-                    "Unrecognized record version " + recordVersion +
-                    " , plaintext connection?");
-        }
-    }
-
     private static HandshakeFragment parseHandshakeMessage(
             byte contentType, byte majorVersion, byte minorVersion,
             byte[] recordEnS, int recordEpoch, long recordSeq,
@@ -377,8 +350,8 @@
 
         int remaining = plaintextFragment.remaining();
         if (remaining < handshakeHeaderSize) {
-            if (debug != null && Debug.isOn("ssl")) {
-                Debug.log("Discard invalid record: " +
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("Discard invalid record: " +
                         "too small record to hold a handshake fragment");
             }
 
@@ -403,8 +376,8 @@
                 ((plaintextFragment.get() & 0xFF) << 8) |
                  (plaintextFragment.get() & 0xFF);          // pos: 9-11
         if ((remaining - handshakeHeaderSize) < fragmentLength) {
-            if (debug != null && Debug.isOn("ssl")) {
-                Debug.log("Discard invalid record: " +
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("Discard invalid record: " +
                         "not a complete handshake fragment in the record");
             }
 
@@ -461,20 +434,20 @@
 
         @Override
         public int compareTo(RecordFragment o) {
-            if (this.contentType == Record.ct_change_cipher_spec) {
-                if (o.contentType == Record.ct_change_cipher_spec) {
+            if (this.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
+                if (o.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
                     // Only one incoming ChangeCipherSpec message for an epoch.
                     //
                     // Ignore duplicated ChangeCipherSpec messages.
                     return Integer.compare(this.recordEpoch, o.recordEpoch);
                 } else if ((this.recordEpoch == o.recordEpoch) &&
-                        (o.contentType == Record.ct_handshake)) {
+                        (o.contentType == ContentType.HANDSHAKE.id)) {
                     // ChangeCipherSpec is the latest message of an epoch.
                     return 1;
                 }
-            } else if (o.contentType == Record.ct_change_cipher_spec) {
+            } else if (o.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
                 if ((this.recordEpoch == o.recordEpoch) &&
-                        (this.contentType == Record.ct_handshake)) {
+                        (this.contentType == ContentType.HANDSHAKE.id)) {
                     // ChangeCipherSpec is the latest message of an epoch.
                     return -1;
                 } else {
@@ -559,7 +532,7 @@
     }
 
     private static final class HandshakeFlight implements Cloneable {
-        static final byte HF_UNKNOWN = HandshakeMessage.ht_not_applicable;
+        static final byte HF_UNKNOWN = SSLHandshake.NOT_APPLICABLE.id;
 
         byte        handshakeType;      // handshake type
         int         flightEpoch;        // the epoch of the first message
@@ -665,7 +638,7 @@
             }
 
             if (isMinimalFlightMessage && (hsf.fragmentOffset == 0) &&
-                    (hsf.handshakeType != HandshakeMessage.ht_finished)) {
+                    (hsf.handshakeType != SSLHandshake.FINISHED.id)) {
 
                 // reset the handshake flight
                 handshakeFlight.handshakeType = hsf.handshakeType;
@@ -673,7 +646,7 @@
                 handshakeFlight.minMessageSeq = hsf.messageSeq;
             }
 
-            if (hsf.handshakeType == HandshakeMessage.ht_finished) {
+            if (hsf.handshakeType == SSLHandshake.FINISHED.id) {
                 handshakeFlight.maxMessageSeq = hsf.messageSeq;
                 handshakeFlight.maxRecordEpoch = hsf.recordEpoch;
                 handshakeFlight.maxRecordSeq = hsf.recordSeq;
@@ -718,8 +691,8 @@
                 // It's OK to discard retransmission as the handshake hash
                 // is computed as if each handshake message had been sent
                 // as a single fragment.
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Have got the full message, discard it.");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine("Have got the full message, discard it.");
                 }
 
                 return;
@@ -742,8 +715,8 @@
                         ((hole.limit > hsf.fragmentOffset) &&
                          (hole.limit < fragmentLimit))) {
 
-                        if (debug != null && Debug.isOn("ssl")) {
-                            Debug.log("Discard invalid record: " +
+                        if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                            SSLLogger.fine("Discard invalid record: " +
                                 "handshake fragment ranges are overlapping");
                         }
 
@@ -773,7 +746,7 @@
             }
 
             // buffer this fragment
-            if (hsf.handshakeType == HandshakeMessage.ht_finished) {
+            if (hsf.handshakeType == SSLHandshake.FINISHED.id) {
                 // Need no status update.
                 bufferedFragments.add(hsf);
             } else {
@@ -849,7 +822,9 @@
                         if (precedingFlight.maxMessageSeq  < hsf.messageSeq) {
                             isNewFlight = true;
                         }
-                    } else if (rf.contentType != Record.ct_change_cipher_spec) {
+                    } else if (
+                        rf.contentType != ContentType.CHANGE_CIPHER_SPEC.id) {
+
                         // ciphertext
                         if (precedingFlight.maxRecordEpoch < rf.recordEpoch) {
                             isNewFlight = true;
@@ -904,8 +879,9 @@
             int previousEpoch = nextRecordEpoch - 1;
             if (rf.recordEpoch < previousEpoch) {
                 // Too old to use, discard this record.
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Too old epoch to use this record, discard it.");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                            "Too old epoch to use this record, discard it.");
                 }
 
                 return false;
@@ -932,7 +908,9 @@
                         if (precedingFlight.minMessageSeq > hsf.messageSeq) {
                             isDesired = false;
                         }
-                    } else if (rf.contentType == Record.ct_change_cipher_spec) {
+                    } else if (
+                        rf.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
+
                         // ChangeCipherSpec
                         if (precedingFlight.flightEpoch != rf.recordEpoch) {
                             isDesired = false;
@@ -948,8 +926,9 @@
 
                 if (!isDesired) {
                     // Too old to use, discard this retransmitted record
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log("Too old retransmission to use, discard it.");
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
+                                "Too old retransmission to use, discard it.");
                     }
 
                     return false;
@@ -960,8 +939,9 @@
                 // Previously disordered record for the current epoch.
                 //
                 // Should has been retransmitted. Discard this record.
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Lagging behind record (sequence), discard it.");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                            "Lagging behind record (sequence), discard it.");
                 }
 
                 return false;
@@ -978,8 +958,8 @@
 
         Plaintext acquirePlaintext() {
             if (bufferedFragments.isEmpty()) {
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("No received handshake messages");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine("No received handshake messages");
                 }
                 return null;
             }
@@ -999,8 +979,8 @@
                         // Reset the next handshake flight.
                         resetHandshakeFlight(precedingFlight);
 
-                        if (debug != null && Debug.isOn("verbose")) {
-                            Debug.log("Received a retransmission flight.");
+                        if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                            SSLLogger.fine("Received a retransmission flight.");
                         }
 
                         return Plaintext.PLAINTEXT_NULL;
@@ -1011,9 +991,10 @@
             }
 
             if (!flightIsReady) {
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("The handshake flight is not ready to use: " +
-                                handshakeFlight.handshakeType);
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                            "The handshake flight is not ready to use: " +
+                            handshakeFlight.handshakeType);
                 }
                 return null;
             }
@@ -1036,7 +1017,7 @@
                     resetHandshakeFlight(precedingFlight);
 
                     if (expectCCSFlight &&
-                            (precedingFlight.flightEpoch ==
+                            (precedingFlight.handshakeType ==
                                     HandshakeFlight.HF_UNKNOWN)) {
                         expectCCSFlight = false;
                     }
@@ -1088,13 +1069,13 @@
         }
 
         private Plaintext acquireCachedMessage() {
-
             RecordFragment rFrag = bufferedFragments.first();
             if (readEpoch != rFrag.recordEpoch) {
                 if (readEpoch > rFrag.recordEpoch) {
                     // discard old records
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log("Discard old buffered ciphertext fragments.");
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
+                                "Discard old buffered ciphertext fragments.");
                     }
                     bufferedFragments.remove(rFrag);    // popup the fragment
                 }
@@ -1104,8 +1085,9 @@
                     flightIsReady = false;
                 }
 
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Not yet ready to decrypt the cached fragments.");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                            "Not yet ready to decrypt the cached fragments.");
                 }
                 return null;
             }
@@ -1115,11 +1097,13 @@
             ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment);
             ByteBuffer plaintextFragment = null;
             try {
-                plaintextFragment = decrypt(readAuthenticator, readCipher,
+                Plaintext plaintext = readCipher.decrypt(
                         rFrag.contentType, fragment, rFrag.recordEnS);
-            } catch (BadPaddingException bpe) {
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Discard invalid record: " + bpe);
+                plaintextFragment = plaintext.fragment;
+                rFrag.contentType = plaintext.contentType;
+            } catch (GeneralSecurityException gse) {
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine("Discard invalid record: ", gse);
                 }
 
                 // invalid, discard this record [section 4.1.2.7, RFC 6347]
@@ -1130,7 +1114,7 @@
             // end of this flight), ClinetHello or HelloRequest (the
             // beginning of the next flight) message.  Need not to check
             // any ChangeCipherSpec message.
-            if (rFrag.contentType == Record.ct_handshake) {
+            if (rFrag.contentType == ContentType.HANDSHAKE.id) {
                 while (plaintextFragment.remaining() > 0) {
                     HandshakeFragment hsFrag = parseHandshakeMessage(
                             rFrag.contentType,
@@ -1140,8 +1124,8 @@
 
                     if (hsFrag == null) {
                         // invalid, discard this record
-                        if (debug != null && Debug.isOn("verbose")) {
-                            Debug.printHex(
+                        if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                            SSLLogger.fine(
                                     "Invalid handshake fragment, discard it",
                                     plaintextFragment);
                         }
@@ -1153,7 +1137,7 @@
                     // been checked and updated for the Finished handshake
                     // message before the decryption.  Please don't update
                     // flightIsReady for Finished messages.
-                    if (hsFrag.handshakeType != HandshakeMessage.ht_finished) {
+                    if (hsFrag.handshakeType != SSLHandshake.FINISHED.id) {
                         flightIsReady = false;
                         needToCheckFlight = true;
                     }
@@ -1172,7 +1156,7 @@
         private Plaintext acquireHandshakeMessage() {
 
             RecordFragment rFrag = bufferedFragments.first();
-            if (rFrag.contentType == Record.ct_change_cipher_spec) {
+            if (rFrag.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
                 this.nextRecordEpoch = rFrag.recordEpoch + 1;
 
                 // For retransmissions, the next record sequence number is a
@@ -1183,16 +1167,12 @@
 
                 // Popup the fragment.
                 bufferedFragments.remove(rFrag);
-
-                // Reload if this message has been reserved for handshake hash.
-                handshakeHash.reload();
-
                 return new Plaintext(rFrag.contentType,
                         rFrag.majorVersion, rFrag.minorVersion,
                         rFrag.recordEpoch,
                         Authenticator.toLong(rFrag.recordEnS),
                         ByteBuffer.wrap(rFrag.fragment));
-            } else {    // rFrag.contentType == Record.ct_handshake
+            } else {    // rFrag.contentType == ContentType.HANDSHAKE.id
                 HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
                 if ((hsFrag.messageLength == hsFrag.fragmentLength) &&
                     (hsFrag.fragmentOffset == 0)) {     // no fragmentation
@@ -1204,7 +1184,8 @@
 
                     // Note: may try to avoid byte array copy in the future.
                     byte[] recordFrag = new byte[hsFrag.messageLength + 4];
-                    Plaintext plaintext = new Plaintext(hsFrag.contentType,
+                    Plaintext plaintext = new Plaintext(
+                            hsFrag.contentType,
                             hsFrag.majorVersion, hsFrag.minorVersion,
                             hsFrag.recordEpoch,
                             Authenticator.toLong(hsFrag.recordEnS),
@@ -1230,7 +1211,8 @@
                     //
                     // Note: may try to avoid byte array copy in the future.
                     byte[] recordFrag = new byte[hsFrag.messageLength + 4];
-                    Plaintext plaintext = new Plaintext(hsFrag.contentType,
+                    Plaintext plaintext = new Plaintext(
+                            hsFrag.contentType,
                             hsFrag.majorVersion, hsFrag.minorVersion,
                             hsFrag.recordEpoch,
                             Authenticator.toLong(hsFrag.recordEnS),
@@ -1264,7 +1246,7 @@
                         // read the next buffered record
                         if (!bufferedFragments.isEmpty()) {
                             rFrag = bufferedFragments.first();
-                            if (rFrag.contentType != Record.ct_handshake) {
+                            if (rFrag.contentType != ContentType.HANDSHAKE.id) {
                                 break;
                             } else {
                                 hmFrag = (HandshakeFragment)rFrag;
@@ -1293,29 +1275,30 @@
                 if (expectCCSFlight) {
                     // Have the ChangeCipherSpec/Finished flight been received?
                     boolean isReady = hasFinishedMessage(bufferedFragments);
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log(
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
                             "Has the final flight been received? " + isReady);
                     }
 
                     return isReady;
                 }
 
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("No flight is received yet.");
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine("No flight is received yet.");
                 }
 
                 return false;
             }
 
-            if ((flightType == HandshakeMessage.ht_client_hello) ||
-                (flightType == HandshakeMessage.ht_hello_request) ||
-                (flightType == HandshakeMessage.ht_hello_verify_request)) {
+            if ((flightType == SSLHandshake.CLIENT_HELLO.id) ||
+                (flightType == SSLHandshake.HELLO_REQUEST.id) ||
+                (flightType == SSLHandshake.HELLO_VERIFY_REQUEST.id)) {
 
                 // single handshake message flight
                 boolean isReady = hasCompleted(flightType);
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Is the handshake message completed? " + isReady);
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                            "Is the handshake message completed? " + isReady);
                 }
 
                 return isReady;
@@ -1324,11 +1307,11 @@
             //
             // the ServerHello flight
             //
-            if (flightType == HandshakeMessage.ht_server_hello) {
+            if (flightType == SSLHandshake.SERVER_HELLO.id) {
                 // Firstly, check the first flight handshake message.
                 if (!hasCompleted(flightType)) {
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log(
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
                             "The ServerHello message is not completed yet.");
                     }
 
@@ -1339,8 +1322,8 @@
                 // an abbreviated handshake
                 //
                 if (hasFinishedMessage(bufferedFragments)) {
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log("It's an abbreviated handshake.");
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine("It's an abbreviated handshake.");
                     }
 
                     return true;
@@ -1350,11 +1333,12 @@
                 // a full handshake
                 //
                 List<HoleDescriptor> holes = handshakeFlight.holesMap.get(
-                        HandshakeMessage.ht_server_hello_done);
+                        SSLHandshake.SERVER_HELLO_DONE.id);
                 if ((holes == null) || !holes.isEmpty()) {
                     // Not yet got the final message of the flight.
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log("Not yet got the ServerHelloDone message");
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
+                                "Not yet got the ServerHelloDone message");
                     }
 
                     return false;
@@ -1364,8 +1348,9 @@
                 boolean isReady = hasCompleted(bufferedFragments,
                             handshakeFlight.minMessageSeq,
                             handshakeFlight.maxMessageSeq);
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Is the ServerHello flight (message " +
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                            "Is the ServerHello flight (message " +
                             handshakeFlight.minMessageSeq + "-" +
                             handshakeFlight.maxMessageSeq +
                             ") completed? " + isReady);
@@ -1381,13 +1366,13 @@
             //       ht_supplemental_data and ht_certificate_url are
             //       suppported in the future.
             //
-            if ((flightType == HandshakeMessage.ht_certificate) ||
-                (flightType == HandshakeMessage.ht_client_key_exchange)) {
+            if ((flightType == SSLHandshake.CERTIFICATE.id) ||
+                (flightType == SSLHandshake.CLIENT_KEY_EXCHANGE.id)) {
 
                 // Firstly, check the first flight handshake message.
                 if (!hasCompleted(flightType)) {
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log(
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
                             "The ClientKeyExchange or client Certificate " +
                             "message is not completed yet.");
                     }
@@ -1396,12 +1381,12 @@
                 }
 
                 // Is client CertificateVerify a mandatory message?
-                if (flightType == HandshakeMessage.ht_certificate) {
+                if (flightType == SSLHandshake.CERTIFICATE.id) {
                     if (needClientVerify(bufferedFragments) &&
-                        !hasCompleted(ht_certificate_verify)) {
+                        !hasCompleted(SSLHandshake.CERTIFICATE_VERIFY.id)) {
 
-                        if (debug != null && Debug.isOn("verbose")) {
-                            Debug.log(
+                        if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                            SSLLogger.fine(
                                 "Not yet have the CertificateVerify message");
                         }
 
@@ -1411,8 +1396,8 @@
 
                 if (!hasFinishedMessage(bufferedFragments)) {
                     // not yet have the ChangeCipherSpec/Finished messages
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log(
+                    if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine(
                             "Not yet have the ChangeCipherSpec and " +
                             "Finished messages");
                     }
@@ -1424,8 +1409,9 @@
                 boolean isReady = hasCompleted(bufferedFragments,
                             handshakeFlight.minMessageSeq,
                             handshakeFlight.maxMessageSeq);
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log("Is the ClientKeyExchange flight (message " +
+                if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                            "Is the ClientKeyExchange flight (message " +
                             handshakeFlight.minMessageSeq + "-" +
                             handshakeFlight.maxMessageSeq +
                             ") completed? " + isReady);
@@ -1437,8 +1423,8 @@
             //
             // Otherwise, need to receive more handshake messages.
             //
-            if (debug != null && Debug.isOn("verbose")) {
-                Debug.log("Need to receive more handshake messages");
+            if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                SSLLogger.fine("Need to receive more handshake messages");
             }
 
             return false;
@@ -1456,12 +1442,12 @@
             boolean hasCCS = false;
             boolean hasFin = false;
             for (RecordFragment fragment : fragments) {
-                if (fragment.contentType == Record.ct_change_cipher_spec) {
+                if (fragment.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
                     if (hasFin) {
                         return true;
                     }
                     hasCCS = true;
-                } else if (fragment.contentType == Record.ct_handshake) {
+                } else if (fragment.contentType == ContentType.HANDSHAKE.id) {
                     // Finished is the first expected message of a new epoch.
                     if (fragment.isCiphertext) {
                         if (hasCCS) {
@@ -1484,13 +1470,13 @@
             // The caller should have checked the completion of the first
             // present handshake message.  Need not to check it again.
             for (RecordFragment rFrag : fragments) {
-                if ((rFrag.contentType != Record.ct_handshake) ||
+                if ((rFrag.contentType != ContentType.HANDSHAKE.id) ||
                         rFrag.isCiphertext) {
                     break;
                 }
 
                 HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
-                if (hsFrag.handshakeType != HandshakeMessage.ht_certificate) {
+                if (hsFrag.handshakeType != SSLHandshake.CERTIFICATE.id) {
                     continue;
                 }
 
@@ -1519,7 +1505,7 @@
             // The caller should have checked the completion of the first
             // present handshake message.  Need not to check it again.
             for (RecordFragment rFrag : fragments) {
-                if ((rFrag.contentType != Record.ct_handshake) ||
+                if ((rFrag.contentType != ContentType.HANDSHAKE.id) ||
                         rFrag.isCiphertext) {
                     break;
                 }
@@ -1546,32 +1532,16 @@
 
         private void handshakeHashing(
                 HandshakeFragment hsFrag, Plaintext plaintext) {
-
             byte hsType = hsFrag.handshakeType;
-            if ((hsType == HandshakeMessage.ht_hello_request) ||
-                (hsType == HandshakeMessage.ht_hello_verify_request)) {
-
+            if (!handshakeHash.isHashable(hsType)) {
                 // omitted from handshake hash computation
                 return;
             }
 
-            if ((hsFrag.messageSeq == 0) &&
-                (hsType == HandshakeMessage.ht_client_hello)) {
-
-                // omit initial ClientHello message
-                //
-                //  4: handshake header
-                //  2: ClientHello.client_version
-                // 32: ClientHello.random
-                int sidLen = plaintext.fragment.get(38);
-
-                if (sidLen == 0) {      // empty session_id, initial handshake
-                    return;
-                }
-            }
-
-            // calculate the DTLS header
-            byte[] temporary = new byte[12];    // 12: handshake header size
+            // calculate the DTLS header and reserve the handshake message
+            plaintext.fragment.position(4);     // ignore the TLS header
+            byte[] temporary = new byte[plaintext.fragment.remaining() + 12];
+                                                // 12: handshake header size
 
             // Handshake.msg_type
             temporary[0] = hsFrag.handshakeType;
@@ -1595,25 +1565,9 @@
             temporary[10] = temporary[2];
             temporary[11] = temporary[3];
 
-            plaintext.fragment.position(4);     // ignore the TLS header
-            if ((hsType != HandshakeMessage.ht_finished) &&
-                (hsType != HandshakeMessage.ht_certificate_verify)) {
-
-                if (handshakeHash == null) {
-                    // used for cache only
-                    handshakeHash = new HandshakeHash(false);
-                }
-                handshakeHash.update(temporary, 0, 12);
-                handshakeHash.update(plaintext.fragment);
-            } else {
-                // Reserve until this handshake message has been processed.
-                if (handshakeHash == null) {
-                    // used for cache only
-                    handshakeHash = new HandshakeHash(false);
-                }
-                handshakeHash.reserve(temporary, 0, 12);
-                handshakeHash.reserve(plaintext.fragment);
-            }
+            plaintext.fragment.get(temporary,
+                    12, plaintext.fragment.remaining());
+            handshakeHash.receive(temporary);
             plaintext.fragment.position(0);     // restore the position
         }
     }
--- a/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,13 +28,8 @@
 import java.io.*;
 import java.nio.*;
 import java.util.*;
-
-import javax.crypto.BadPaddingException;
-
 import javax.net.ssl.*;
-
-import sun.security.util.HexDumpEncoder;
-import static sun.security.ssl.Ciphertext.RecordType;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
 
 /**
  * DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
@@ -47,54 +42,62 @@
 
     int                 prevWriteEpoch;
     Authenticator       prevWriteAuthenticator;
-    CipherBox           prevWriteCipher;
+    SSLWriteCipher      prevWriteCipher;
 
-    private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
+    private final LinkedList<RecordMemo> alertMemos = new LinkedList<>();
 
-    DTLSOutputRecord() {
-        this.writeAuthenticator = new MAC(true);
+    DTLSOutputRecord(HandshakeHash handshakeHash) {
+        super(handshakeHash, SSLWriteCipher.nullDTlsWriteCipher());
 
         this.writeEpoch = 0;
         this.prevWriteEpoch = 0;
-        this.prevWriteCipher = CipherBox.NULL;
-        this.prevWriteAuthenticator = new MAC(true);
+        this.prevWriteCipher = SSLWriteCipher.nullDTlsWriteCipher();
 
         this.packetSize = DTLSRecord.maxRecordSize;
-        this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
+        this.protocolVersion = ProtocolVersion.NONE;
+    }
+
+    @Override
+    void initHandshaker() {
+        // clean up
+        fragmenter = null;
     }
 
     @Override
-    void changeWriteCiphers(Authenticator writeAuthenticator,
-            CipherBox writeCipher) throws IOException {
+    void finishHandshake() {
+        // Nothing to do here currently.
+    }
 
-        encodeChangeCipherSpec();
+    @Override
+    void changeWriteCiphers(SSLWriteCipher writeCipher,
+            boolean useChangeCipherSpec) throws IOException {
+        if (useChangeCipherSpec) {
+            encodeChangeCipherSpec();
+        }
 
         prevWriteCipher.dispose();
 
-        this.prevWriteAuthenticator = this.writeAuthenticator;
         this.prevWriteCipher = this.writeCipher;
         this.prevWriteEpoch = this.writeEpoch;
 
-        this.writeAuthenticator = writeAuthenticator;
         this.writeCipher = writeCipher;
         this.writeEpoch++;
 
         this.isFirstAppOutputRecord = true;
 
         // set the epoch number
-        this.writeAuthenticator.setEpochNumber(this.writeEpoch);
+        this.writeCipher.authenticator.setEpochNumber(this.writeEpoch);
     }
 
     @Override
     void encodeAlert(byte level, byte description) throws IOException {
         RecordMemo memo = new RecordMemo();
 
-        memo.contentType = Record.ct_alert;
+        memo.contentType = ContentType.ALERT.id;
         memo.majorVersion = protocolVersion.major;
         memo.minorVersion = protocolVersion.minor;
         memo.encodeEpoch = writeEpoch;
         memo.encodeCipher = writeCipher;
-        memo.encodeAuthenticator = writeAuthenticator;
 
         memo.fragment = new byte[2];
         memo.fragment[0] = level;
@@ -114,7 +117,6 @@
     @Override
     void encodeHandshake(byte[] source,
             int offset, int length) throws IOException {
-
         if (firstMessage) {
             firstMessage = false;
         }
@@ -127,30 +129,53 @@
     }
 
     @Override
-    Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+    Ciphertext encode(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
+        return encode(srcs, srcsOffset, srcsLength, dsts[0]);
+    }
+
+    private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
             ByteBuffer destination) throws IOException {
 
-        if (writeAuthenticator.seqNumOverflow()) {
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", sequence number extremely close to overflow " +
+        if (writeCipher.authenticator.seqNumOverflow()) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine(
+                    "sequence number extremely close to overflow " +
                     "(2^64-1 packets). Closing connection.");
             }
 
             throw new SSLHandshakeException("sequence number overflow");
         }
 
-        // not apply to handshake message
-        int macLen = 0;
-        if (writeAuthenticator instanceof MAC) {
-            macLen = ((MAC)writeAuthenticator).MAClen();
+        // Don't process the incoming record until all of the buffered records
+        // get handled.  May need retransmission if no sources specified.
+        if (!isEmpty() || sources == null || sources.length == 0) {
+            Ciphertext ct = acquireCiphertext(destination);
+            if (ct != null) {
+                return ct;
+            }
         }
 
+        if (sources == null || sources.length == 0) {
+            return null;
+        }
+
+        int srcsRemains = 0;
+        for (int i = offset; i < offset + length; i++) {
+            srcsRemains += sources[i].remaining();
+        }
+
+        if (srcsRemains == 0) {
+            return null;
+        }
+
+        // not apply to handshake message
         int fragLen;
         if (packetSize > 0) {
             fragLen = Math.min(maxRecordSize, packetSize);
             fragLen = writeCipher.calculateFragmentSize(
-                    fragLen, macLen, headerSize);
+                    fragLen, headerSize);
 
             fragLen = Math.min(fragLen, Record.maxDataSize);
         } else {
@@ -183,44 +208,38 @@
         destination.limit(destination.position());
         destination.position(dstContent);
 
-        if ((debug != null) && Debug.isOn("record")) {
-            System.out.println(Thread.currentThread().getName() +
-                    ", WRITE: " + protocolVersion + " " +
-                    Record.contentName(Record.ct_application_data) +
+        if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+            SSLLogger.fine(
+                    "WRITE: " + protocolVersion + " " +
+                    ContentType.APPLICATION_DATA.name +
                     ", length = " + destination.remaining());
         }
 
         // Encrypt the fragment and wrap up a record.
-        long recordSN = encrypt(writeAuthenticator, writeCipher,
-                Record.ct_application_data, destination,
+        long recordSN = encrypt(writeCipher,
+                ContentType.APPLICATION_DATA.id, destination,
                 dstPos, dstLim, headerSize,
-                protocolVersion, true);
+                protocolVersion);
 
-        if ((debug != null) && Debug.isOn("packet")) {
+        if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
             ByteBuffer temporary = destination.duplicate();
             temporary.limit(temporary.position());
             temporary.position(dstPos);
-            Debug.printHex(
-                    "[Raw write]: length = " + temporary.remaining(),
-                    temporary);
+            SSLLogger.fine("Raw write", temporary);
         }
 
         // remain the limit unchanged
         destination.limit(dstLim);
 
-        return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
+        return new Ciphertext(ContentType.APPLICATION_DATA.id,
+                SSLHandshake.NOT_APPLICABLE.id, recordSN);
     }
 
-    @Override
-    Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+    private Ciphertext acquireCiphertext(
+            ByteBuffer destination) throws IOException {
         if (alertMemos != null && !alertMemos.isEmpty()) {
             RecordMemo memo = alertMemos.pop();
 
-            int macLen = 0;
-            if (memo.encodeAuthenticator instanceof MAC) {
-                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
-            }
-
             int dstPos = destination.position();
             int dstLim = destination.limit();
             int dstContent = dstPos + headerSize +
@@ -232,32 +251,32 @@
             destination.limit(destination.position());
             destination.position(dstContent);
 
-            if ((debug != null) && Debug.isOn("record")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: " + protocolVersion + " " +
-                        Record.contentName(Record.ct_alert) +
+            if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                SSLLogger.fine(
+                        "WRITE: " + protocolVersion + " " +
+                        ContentType.ALERT.name +
                         ", length = " + destination.remaining());
             }
 
             // Encrypt the fragment and wrap up a record.
-            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
-                    Record.ct_alert, destination, dstPos, dstLim, headerSize,
+            long recordSN = encrypt(memo.encodeCipher,
+                    ContentType.ALERT.id,
+                    destination, dstPos, dstLim, headerSize,
                     ProtocolVersion.valueOf(memo.majorVersion,
-                            memo.minorVersion), true);
+                            memo.minorVersion));
 
-            if ((debug != null) && Debug.isOn("packet")) {
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
                 ByteBuffer temporary = destination.duplicate();
                 temporary.limit(temporary.position());
                 temporary.position(dstPos);
-                Debug.printHex(
-                        "[Raw write]: length = " + temporary.remaining(),
-                        temporary);
+                SSLLogger.fine("Raw write", temporary);
             }
 
             // remain the limit unchanged
             destination.limit(dstLim);
 
-            return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
+            return new Ciphertext(ContentType.ALERT.id,
+                    SSLHandshake.NOT_APPLICABLE.id, recordSN);
         }
 
         if (fragmenter != null) {
@@ -274,12 +293,6 @@
     }
 
     @Override
-    void initHandshaker() {
-        // clean up
-        fragmenter = null;
-    }
-
-    @Override
     void launchRetransmission() {
         // Note: Please don't retransmit if there are handshake messages
         // or alerts waiting in the queue.
@@ -295,8 +308,7 @@
         byte            majorVersion;
         byte            minorVersion;
         int             encodeEpoch;
-        CipherBox       encodeCipher;
-        Authenticator   encodeAuthenticator;
+        SSLWriteCipher  encodeCipher;
 
         byte[]          fragment;
     }
@@ -308,7 +320,8 @@
     }
 
     private final class DTLSFragmenter {
-        private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
+        private final LinkedList<RecordMemo> handshakeMemos =
+                new LinkedList<>();
         private int acquireIndex = 0;
         private int messageSequence = 0;
         private boolean flightIsReady = false;
@@ -336,12 +349,11 @@
 
             RecordMemo memo = new RecordMemo();
 
-            memo.contentType = Record.ct_change_cipher_spec;
+            memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
             memo.majorVersion = protocolVersion.major;
             memo.minorVersion = protocolVersion.minor;
             memo.encodeEpoch = writeEpoch;
             memo.encodeCipher = writeCipher;
-            memo.encodeAuthenticator = writeAuthenticator;
 
             memo.fragment = new byte[1];
             memo.fragment[0] = 1;
@@ -361,12 +373,11 @@
 
             HandshakeMemo memo = new HandshakeMemo();
 
-            memo.contentType = Record.ct_handshake;
+            memo.contentType = ContentType.HANDSHAKE.id;
             memo.majorVersion = protocolVersion.major;
             memo.minorVersion = protocolVersion.minor;
             memo.encodeEpoch = writeEpoch;
             memo.encodeCipher = writeCipher;
-            memo.encodeAuthenticator = writeAuthenticator;
 
             memo.handshakeType = buf[offset];
             memo.messageSequence = messageSequence++;
@@ -379,12 +390,12 @@
             handshakeHashing(memo, memo.fragment);
             handshakeMemos.add(memo);
 
-            if ((memo.handshakeType == HandshakeMessage.ht_client_hello) ||
-                (memo.handshakeType == HandshakeMessage.ht_hello_request) ||
+            if ((memo.handshakeType == SSLHandshake.CLIENT_HELLO.id) ||
+                (memo.handshakeType == SSLHandshake.HELLO_REQUEST.id) ||
                 (memo.handshakeType ==
-                        HandshakeMessage.ht_hello_verify_request) ||
-                (memo.handshakeType == HandshakeMessage.ht_server_hello_done) ||
-                (memo.handshakeType == HandshakeMessage.ht_finished)) {
+                        SSLHandshake.HELLO_VERIFY_REQUEST.id) ||
+                (memo.handshakeType == SSLHandshake.SERVER_HELLO_DONE.id) ||
+                (memo.handshakeType == SSLHandshake.FINISHED.id)) {
 
                 flightIsReady = true;
             }
@@ -401,22 +412,17 @@
 
             RecordMemo memo = handshakeMemos.get(acquireIndex);
             HandshakeMemo hsMemo = null;
-            if (memo.contentType == Record.ct_handshake) {
+            if (memo.contentType == ContentType.HANDSHAKE.id) {
                 hsMemo = (HandshakeMemo)memo;
             }
 
-            int macLen = 0;
-            if (memo.encodeAuthenticator instanceof MAC) {
-                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
-            }
-
             // ChangeCipherSpec message is pretty small.  Don't worry about
             // the fragmentation of ChangeCipherSpec record.
             int fragLen;
             if (packetSize > 0) {
                 fragLen = Math.min(maxRecordSize, packetSize);
                 fragLen = memo.encodeCipher.calculateFragmentSize(
-                        fragLen, macLen, 25);   // 25: header size
+                        fragLen, 25);   // 25: header size
                                                 //   13: DTLS record
                                                 //   12: DTLS handshake message
                 fragLen = Math.min(fragLen, Record.maxDataSize);
@@ -459,27 +465,26 @@
             dstBuf.limit(dstBuf.position());
             dstBuf.position(dstContent);
 
-            if ((debug != null) && Debug.isOn("record")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: " + protocolVersion + " " +
-                        Record.contentName(memo.contentType) +
+            if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                SSLLogger.fine(
+                        "WRITE: " + protocolVersion + " " +
+                        ContentType.nameOf(memo.contentType) +
                         ", length = " + dstBuf.remaining());
             }
 
             // Encrypt the fragment and wrap up a record.
-            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+            long recordSN = encrypt(memo.encodeCipher,
                     memo.contentType, dstBuf,
                     dstPos, dstLim, headerSize,
                     ProtocolVersion.valueOf(memo.majorVersion,
-                            memo.minorVersion), true);
+                            memo.minorVersion));
 
-            if ((debug != null) && Debug.isOn("packet")) {
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
                 ByteBuffer temporary = dstBuf.duplicate();
                 temporary.limit(temporary.position());
                 temporary.position(dstPos);
-                Debug.printHex(
-                        "[Raw write]: length = " + temporary.remaining(),
-                        temporary);
+                SSLLogger.fine(
+                        "Raw write (" + temporary.remaining() + ")", temporary);
             }
 
             // remain the limit unchanged
@@ -492,39 +497,23 @@
                     acquireIndex++;
                 }
 
-                return new Ciphertext(RecordType.valueOf(
-                        hsMemo.contentType, hsMemo.handshakeType), recordSN);
+                return new Ciphertext(hsMemo.contentType,
+                        hsMemo.handshakeType, recordSN);
             } else {
                 acquireIndex++;
-                return new Ciphertext(
-                        RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
+                return new Ciphertext(ContentType.CHANGE_CIPHER_SPEC.id,
+                        SSLHandshake.NOT_APPLICABLE.id, recordSN);
             }
         }
 
         private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
 
             byte hsType = hsFrag.handshakeType;
-            if ((hsType == HandshakeMessage.ht_hello_request) ||
-                (hsType == HandshakeMessage.ht_hello_verify_request)) {
-
+            if (!handshakeHash.isHashable(hsType)) {
                 // omitted from handshake hash computation
                 return;
             }
 
-            if ((hsFrag.messageSequence == 0) &&
-                (hsType == HandshakeMessage.ht_client_hello)) {
-
-                // omit initial ClientHello message
-                //
-                //  2: ClientHello.client_version
-                // 32: ClientHello.random
-                int sidLen = hsBody[34];
-
-                if (sidLen == 0) {      // empty session_id, initial handshake
-                    return;
-                }
-            }
-
             // calculate the DTLS header
             byte[] temporary = new byte[12];    // 12: handshake header size
 
@@ -550,17 +539,8 @@
             temporary[10] = temporary[2];
             temporary[11] = temporary[3];
 
-            if ((hsType != HandshakeMessage.ht_finished) &&
-                (hsType != HandshakeMessage.ht_certificate_verify)) {
-
-                handshakeHash.update(temporary, 0, 12);
-                handshakeHash.update(hsBody, 0, hsBody.length);
-            } else {
-                // Reserve until this handshake message has been processed.
-                handshakeHash.reserve(temporary, 0, 12);
-                handshakeHash.reserve(hsBody, 0, hsBody.length);
-            }
-
+            handshakeHash.deliver(temporary, 0, 12);
+            handshakeHash.deliver(hsBody, 0, hsBody.length);
         }
 
         boolean isEmpty() {
--- a/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -61,30 +61,6 @@
                                     + maxMacSize;           // MAC or AEAD tag
 
     /*
-     * For CBC protection in SSL3/TLS1, we break some plaintext into two
-     * packets.  Max application data size for the second packet.
-     */
-    static final int    maxDataSizeMinusOneByteRecord =
-                                  maxDataSize       // max data size
-                                - (                 // max one byte record size
-                                      headerPlusMaxIVSize   // header + iv
-                                    + 1             // one byte data
-                                    + maxPadding    // padding
-                                    + maxMacSize    // MAC
-                                  );
-
-    /*
-     * Maximum record size for alert and change cipher spec records.
-     * They only contain 2 and 1 bytes of data, respectively.
-     * Allocate a smaller array.
-     */
-    static final int    maxAlertRecordSize =
-                                      headerPlusMaxIVSize   // header + iv
-                                    + 2                     // alert
-                                    + maxPadding            // padding
-                                    + maxMacSize;           // MAC
-
-    /*
      * Minimum record size of Certificate handshake message.
      * Client sends a certificate message containing no certificates if no
      * suitable certificate is available.  That is, the certificate_list
--- a/src/java.base/share/classes/sun/security/ssl/Debug.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-/*
- * Copyright (c) 1999, 2016, 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.PrintStream;
-import java.util.Locale;
-
-import sun.security.util.HexDumpEncoder;
-import java.nio.ByteBuffer;
-
-import sun.security.action.GetPropertyAction;
-
-/**
- * This class has be shamefully lifted from sun.security.util.Debug
- *
- * @author Gary Ellison
- */
-public class Debug {
-
-    private String prefix;
-
-    private static String args;
-
-    static {
-        args = GetPropertyAction.privilegedGetProperty("javax.net.debug", "");
-        args = args.toLowerCase(Locale.ENGLISH);
-        if (args.equals("help")) {
-            Help();
-        }
-    }
-
-    public static void Help()
-    {
-        System.err.println();
-        System.err.println("all            turn on all debugging");
-        System.err.println("ssl            turn on ssl debugging");
-        System.err.println();
-        System.err.println("The following can be used with ssl:");
-        System.err.println("\trecord       enable per-record tracing");
-        System.err.println("\thandshake    print each handshake message");
-        System.err.println("\tkeygen       print key generation data");
-        System.err.println("\tsession      print session activity");
-        System.err.println("\tdefaultctx   print default SSL initialization");
-        System.err.println("\tsslctx       print SSLContext tracing");
-        System.err.println("\tsessioncache print session cache tracing");
-        System.err.println("\tkeymanager   print key manager tracing");
-        System.err.println("\ttrustmanager print trust manager tracing");
-        System.err.println("\tpluggability print pluggability tracing");
-        System.err.println();
-        System.err.println("\thandshake debugging can be widened with:");
-        System.err.println("\tdata         hex dump of each handshake message");
-        System.err.println("\tverbose      verbose handshake message printing");
-        System.err.println();
-        System.err.println("\trecord debugging can be widened with:");
-        System.err.println("\tplaintext    hex dump of record plaintext");
-        System.err.println("\tpacket       print raw SSL/TLS packets");
-        System.err.println();
-        System.exit(0);
-    }
-
-    /**
-     * Get a Debug object corresponding to whether or not the given
-     * option is set. Set the prefix to be the same as option.
-     */
-
-    public static Debug getInstance(String option)
-    {
-        return getInstance(option, option);
-    }
-
-    /**
-     * Get a Debug object corresponding to whether or not the given
-     * option is set. Set the prefix to be prefix.
-     */
-    public static Debug getInstance(String option, String prefix)
-    {
-        if (isOn(option)) {
-            Debug d = new Debug();
-            d.prefix = prefix;
-            return d;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * True if the property "javax.net.debug" contains the
-     * string "option".
-     */
-    public static boolean isOn(String option)
-    {
-        if (args == null) {
-            return false;
-        } else {
-            int n = 0;
-            option = option.toLowerCase(Locale.ENGLISH);
-
-            if (args.indexOf("all") != -1) {
-                return true;
-            } else if ((n = args.indexOf("ssl")) != -1) {
-                if (args.indexOf("sslctx", n) == -1) {
-                    // don't enable data and plaintext options by default
-                    if (!(option.equals("data")
-                        || option.equals("packet")
-                        || option.equals("plaintext"))) {
-                        return true;
-                    }
-                }
-            }
-            return (args.indexOf(option) != -1);
-        }
-    }
-
-    /**
-     * print a message to stderr that is prefixed with the prefix
-     * created from the call to getInstance.
-     */
-
-    public void println(String message)
-    {
-        System.err.println(prefix + ": "+message);
-    }
-
-    /**
-     * Print a message to stdout.
-     */
-    static void log(String message) {
-        System.out.println(Thread.currentThread().getName() + ": " + message);
-    }
-
-    /**
-     * print a blank line to stderr that is prefixed with the prefix.
-     */
-
-    public void println()
-    {
-        System.err.println(prefix + ":");
-    }
-
-    /**
-     * print a message to stderr that is prefixed with the prefix.
-     */
-    public static void println(String prefix, String message)
-    {
-        System.err.println(prefix + ": "+message);
-    }
-
-    public static void println(PrintStream s, String name, byte[] data) {
-        s.print(name + ":  { ");
-        if (data == null) {
-            s.print("null");
-        } else {
-            for (int i = 0; i < data.length; i++) {
-                if (i != 0) s.print(", ");
-                s.print(data[i] & 0x0ff);
-            }
-        }
-        s.println(" }");
-    }
-
-    /**
-     * Return the value of the boolean System property propName.
-     *
-     * Note use of privileged action. Do NOT make accessible to applications.
-     */
-    static boolean getBooleanProperty(String propName, boolean defaultValue) {
-        // if set, require value of either true or false
-        String b = GetPropertyAction.privilegedGetProperty(propName);
-        if (b == null) {
-            return defaultValue;
-        } else if (b.equalsIgnoreCase("false")) {
-            return false;
-        } else if (b.equalsIgnoreCase("true")) {
-            return true;
-        } else {
-            throw new RuntimeException("Value of " + propName
-                + " must either be 'true' or 'false'");
-        }
-    }
-
-    static String toString(byte[] b) {
-        return sun.security.util.Debug.toString(b);
-    }
-
-    static void printHex(String prefix, byte[] bytes) {
-        HexDumpEncoder dump = new HexDumpEncoder();
-
-        synchronized (System.out) {
-            System.out.println(prefix);
-            try {
-                dump.encodeBuffer(bytes, System.out);
-            } catch (Exception e) {
-                // ignore
-            }
-            System.out.flush();
-        }
-    }
-
-    static void printHex(String prefix, ByteBuffer bb) {
-        HexDumpEncoder dump = new HexDumpEncoder();
-
-        synchronized (System.out) {
-            System.out.println(prefix);
-            try {
-                dump.encodeBuffer(bb.slice(), System.out);
-            } catch (Exception e) {
-                // ignore
-            }
-            System.out.flush();
-        }
-    }
-
-    static void printHex(String prefix, byte[] bytes, int offset, int length) {
-        HexDumpEncoder dump = new HexDumpEncoder();
-
-        synchronized (System.out) {
-            System.out.println(prefix);
-            try {
-                ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
-                dump.encodeBuffer(bb, System.out);
-            } catch (Exception e) {
-                // ignore
-            }
-            System.out.flush();
-        }
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,60 +26,515 @@
 package sun.security.ssl;
 
 import java.io.IOException;
-import java.io.PrintStream;
-
+import java.nio.ByteBuffer;
+import java.security.AlgorithmConstraints;
+import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
 import java.security.interfaces.ECPublicKey;
-import java.security.spec.*;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Locale;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
+import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.HexDumpEncoder;
 
 /**
- * ClientKeyExchange message for all ECDH based key exchange methods. It
- * contains the client's ephemeral public value.
- *
- * @since   1.6
- * @author  Andreas Sterbenz
+ * Pack of the "ClientKeyExchange" handshake message.
  */
-final class ECDHClientKeyExchange extends HandshakeMessage {
+final class ECDHClientKeyExchange {
+    static final SSLConsumer ecdhHandshakeConsumer =
+            new ECDHClientKeyExchangeConsumer();
+    static final HandshakeProducer ecdhHandshakeProducer =
+            new ECDHClientKeyExchangeProducer();
+
+    static final SSLConsumer ecdheHandshakeConsumer =
+            new ECDHEClientKeyExchangeConsumer();
+    static final HandshakeProducer ecdheHandshakeProducer =
+            new ECDHEClientKeyExchangeProducer();
+
+    /**
+     * The ECDH/ECDHE ClientKeyExchange handshake message.
+     */
+    private static final
+            class ECDHClientKeyExchangeMessage extends HandshakeMessage {
+        private final byte[] encodedPoint;
+
+        ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
+                ECPublicKey publicKey) {
+            super(handshakeContext);
+
+            ECPoint point = publicKey.getW();
+            ECParameterSpec params = publicKey.getParams();
+            encodedPoint = JsseJce.encodePoint(point, params.getCurve());
+        }
+
+        ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+            if (m.remaining() != 0) {       // explicit PublicValueEncoding
+                this.encodedPoint = Record.getBytes8(m);
+            } else {
+                this.encodedPoint = new byte[0];
+            }
+        }
+
+        // Check constraints of the specified EC public key.
+        static void checkConstraints(AlgorithmConstraints constraints,
+                ECPublicKey publicKey,
+                byte[] encodedPoint) throws SSLHandshakeException {
+
+            try {
+                ECParameterSpec params = publicKey.getParams();
+                ECPoint point =
+                        JsseJce.decodePoint(encodedPoint, params.getCurve());
+                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
+
+                KeyFactory kf = JsseJce.getKeyFactory("EC");
+                ECPublicKey peerPublicKey =
+                        (ECPublicKey)kf.generatePublic(spec);
 
-    @Override
-    int messageType() {
-        return ht_client_key_exchange;
+                // check constraints of ECPublicKey
+                if (!constraints.permits(
+                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                        peerPublicKey)) {
+                    throw new SSLHandshakeException(
+                        "ECPublicKey does not comply to algorithm constraints");
+                }
+            } catch (GeneralSecurityException | java.io.IOException e) {
+                throw (SSLHandshakeException) new SSLHandshakeException(
+                        "Could not generate ECPublicKey").initCause(e);
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CLIENT_KEY_EXCHANGE;
+        }
+
+        @Override
+        public int messageLength() {
+            if (encodedPoint == null || encodedPoint.length == 0) {
+                return 0;
+            } else {
+                return 1 + encodedPoint.length;
+            }
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            if (encodedPoint != null && encodedPoint.length != 0) {
+                hos.putBytes8(encodedPoint);
+            }
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"ECDH ClientKeyExchange\": '{'\n" +
+                "  \"ecdh public\": '{'\n" +
+                "{0}\n" +
+                "  '}',\n" +
+                "'}'",
+                Locale.ENGLISH);
+            if (encodedPoint == null || encodedPoint.length == 0) {
+                Object[] messageFields = {
+                    "    <implicit>"
+                };
+                return messageFormat.format(messageFields);
+            } else {
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                Object[] messageFields = {
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(encodedPoint), "    "),
+                };
+                return messageFormat.format(messageFields);
+            }
+        }
     }
 
-    private byte[] encodedPoint;
+    /**
+     * The ECDH "ClientKeyExchange" handshake message producer.
+     */
+    private static final
+            class ECDHClientKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private ECDHClientKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials credential : chc.handshakeCredentials) {
+                if (credential instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)credential;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No server certificate for ECDH client key exchange");
+            }
+
+            PublicKey publicKey = x509Credentials.popPublicKey;
+            if (!publicKey.getAlgorithm().equals("EC")) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Not EC server certificate for ECDH client key exchange");
+            }
+
+            ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
+            NamedGroup namedGroup = NamedGroup.valueOf(params);
+            if (namedGroup == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unsupported EC server cert for ECDH client key exchange");
+            }
 
-    byte[] getEncodedPoint() {
-        return encodedPoint;
+            ECDHEPossession ecdhePossession = new ECDHEPossession(
+                    namedGroup, chc.sslContext.getSecureRandom());
+            chc.handshakePossessions.add(ecdhePossession);
+            ECDHClientKeyExchangeMessage cke =
+                    new ECDHClientKeyExchangeMessage(
+                            chc, ecdhePossession.publicKey);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced ECDH ClientKeyExchange handshake message", cke);
+            }
+
+            // Output the handshake message.
+            cke.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // update the states
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    chc.negotiatedCipherSuite.keyExchange,
+                    chc.negotiatedProtocol);
+            if (ke == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            } else {
+                SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
+                SecretKey masterSecret =
+                        masterKD.deriveKey("MasterSecret", null);
+                chc.handshakeSession.setMasterSecret(masterSecret);
+
+                SSLTrafficKeyDerivation kd =
+                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+                if (kd == null) {
+                    // unlikely
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                            chc.negotiatedProtocol);
+                } else {
+                    chc.handshakeKeyDerivation =
+                        kd.createKeyDerivation(chc, masterSecret);
+                }
+            }
+
+            // The handshake message has been delivered.
+            return null;
+        }
     }
 
-    // Called by the client with its ephemeral public key.
-    ECDHClientKeyExchange(PublicKey publicKey) {
-        ECPublicKey ecKey = (ECPublicKey)publicKey;
-        ECPoint point = ecKey.getW();
-        ECParameterSpec params = ecKey.getParams();
-        encodedPoint = JsseJce.encodePoint(point, params.getCurve());
-    }
+    /**
+     * The ECDH "ClientKeyExchange" handshake message consumer.
+     */
+    private static final
+            class ECDHClientKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ECDHClientKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    break;
+                }
+            }
+
+            if (x509Possession == null) {
+                // unlikely, have been checked during cipher suite negotiation.
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No expected EC server cert for ECDH client key exchange");
+                return;     // make the compiler happy
+            }
+
+            PrivateKey privateKey = x509Possession.popPrivateKey;
+            if (!privateKey.getAlgorithm().equals("EC")) {
+                // unlikely, have been checked during cipher suite negotiation.
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Not EC server cert for ECDH client key exchange");
+            }
+
+            ECParameterSpec params = ((ECPrivateKey)privateKey).getParams();
+            NamedGroup namedGroup = NamedGroup.valueOf(params);
+            if (namedGroup == null) {
+                // unlikely, have been checked during cipher suite negotiation.
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unsupported EC server cert for ECDH client key exchange");
+            }
 
-    ECDHClientKeyExchange(HandshakeInStream input) throws IOException {
-        encodedPoint = input.getBytes8();
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    shc.negotiatedCipherSuite.keyExchange,
+                    shc.negotiatedProtocol);
+            if (ke == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+                return;     // make the compiler happy
+            }
+
+            // parse the handshake message
+            ECDHClientKeyExchangeMessage cke =
+                    new ECDHClientKeyExchangeMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming ECDH ClientKeyExchange handshake message", cke);
+            }
+
+            // create the credentials
+            try {
+                ECPoint point =
+                    JsseJce.decodePoint(cke.encodedPoint, params.getCurve());
+                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
+
+                KeyFactory kf = JsseJce.getKeyFactory("EC");
+                ECPublicKey peerPublicKey =
+                        (ECPublicKey)kf.generatePublic(spec);
+
+                // check constraints of peer ECPublicKey
+                if (!shc.algorithmConstraints.permits(
+                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                        peerPublicKey)) {
+                    throw new SSLHandshakeException(
+                        "ECPublicKey does not comply to algorithm constraints");
+                }
+
+                shc.handshakeCredentials.add(new ECDHECredentials(
+                        peerPublicKey, namedGroup));
+            } catch (GeneralSecurityException | java.io.IOException e) {
+                throw (SSLHandshakeException)(new SSLHandshakeException(
+                        "Could not generate ECPublicKey").initCause(e));
+            }
+
+            // update the states
+            SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
+            SecretKey masterSecret =
+                    masterKD.deriveKey("MasterSecret", null);
+            shc.handshakeSession.setMasterSecret(masterSecret);
+
+            SSLTrafficKeyDerivation kd =
+                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+            if (kd == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "Not supported key derivation: " + shc.negotiatedProtocol);
+            } else {
+                shc.handshakeKeyDerivation =
+                    kd.createKeyDerivation(shc, masterSecret);
+            }
+        }
     }
 
-    @Override
-    int messageLength() {
-        return encodedPoint.length + 1;
+    /**
+     * The ECDHE "ClientKeyExchange" handshake message producer.
+     */
+    private static final
+            class ECDHEClientKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private ECDHEClientKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            ECDHECredentials ecdheCredentials = null;
+            for (SSLCredentials cd : chc.handshakeCredentials) {
+                if (cd instanceof ECDHECredentials) {
+                    ecdheCredentials = (ECDHECredentials)cd;
+                    break;
+                }
+            }
+
+            if (ecdheCredentials == null) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No ECDHE credentials negotiated for client key exchange");
+            }
+
+            ECDHEPossession ecdhePossession = new ECDHEPossession(
+                    ecdheCredentials, chc.sslContext.getSecureRandom());
+            chc.handshakePossessions.add(ecdhePossession);
+            ECDHClientKeyExchangeMessage cke =
+                    new ECDHClientKeyExchangeMessage(
+                            chc, ecdhePossession.publicKey);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced ECDHE ClientKeyExchange handshake message", cke);
+            }
+
+            // Output the handshake message.
+            cke.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // update the states
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    chc.negotiatedCipherSuite.keyExchange,
+                    chc.negotiatedProtocol);
+            if (ke == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            } else {
+                SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
+                SecretKey masterSecret =
+                        masterKD.deriveKey("MasterSecret", null);
+                chc.handshakeSession.setMasterSecret(masterSecret);
+
+                SSLTrafficKeyDerivation kd =
+                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+                if (kd == null) {
+                    // unlikely
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                            chc.negotiatedProtocol);
+                } else {
+                    chc.handshakeKeyDerivation =
+                        kd.createKeyDerivation(chc, masterSecret);
+                }
+            }
+
+            // The handshake message has been delivered.
+            return null;
+        }
     }
 
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putBytes8(encodedPoint);
-    }
+    /**
+     * The ECDHE "ClientKeyExchange" handshake message consumer.
+     */
+    private static final
+            class ECDHEClientKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ECDHEClientKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            ECDHEPossession ecdhePossession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof ECDHEPossession) {
+                    ecdhePossession = (ECDHEPossession)possession;
+                    break;
+                }
+            }
+            if (ecdhePossession == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No expected ECDHE possessions for client key exchange");
+                return;     // make the compiler happy
+            }
+
+            ECParameterSpec params = ecdhePossession.publicKey.getParams();
+            NamedGroup namedGroup = NamedGroup.valueOf(params);
+            if (namedGroup == null) {
+                // unlikely, have been checked during cipher suite negotiation.
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unsupported EC server cert for ECDHE client key exchange");
+            }
+
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    shc.negotiatedCipherSuite.keyExchange,
+                    shc.negotiatedProtocol);
+            if (ke == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+                return;     // make the compiler happy
+            }
 
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** ECDHClientKeyExchange");
+            // parse the handshake message
+            ECDHClientKeyExchangeMessage cke =
+                    new ECDHClientKeyExchangeMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming ECDHE ClientKeyExchange handshake message", cke);
+            }
+
+            // create the credentials
+            try {
+                ECPoint point =
+                    JsseJce.decodePoint(cke.encodedPoint, params.getCurve());
+                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
+
+                KeyFactory kf = JsseJce.getKeyFactory("EC");
+                ECPublicKey peerPublicKey =
+                        (ECPublicKey)kf.generatePublic(spec);
 
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(s, "ECDH Public value", encodedPoint);
+                // check constraints of peer ECPublicKey
+                if (!shc.algorithmConstraints.permits(
+                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                        peerPublicKey)) {
+                    throw new SSLHandshakeException(
+                        "ECPublicKey does not comply to algorithm constraints");
+                }
+
+                shc.handshakeCredentials.add(new ECDHECredentials(
+                        peerPublicKey, namedGroup));
+            } catch (GeneralSecurityException | java.io.IOException e) {
+                throw (SSLHandshakeException)(new SSLHandshakeException(
+                        "Could not generate ECPublicKey").initCause(e));
+            }
+
+            // update the states
+            SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
+            SecretKey masterSecret =
+                    masterKD.deriveKey("MasterSecret", null);
+            shc.handshakeSession.setMasterSecret(masterSecret);
+
+            SSLTrafficKeyDerivation kd =
+                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+            if (kd == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "Not supported key derivation: " + shc.negotiatedProtocol);
+            } else {
+                shc.handshakeKeyDerivation =
+                    kd.createKeyDerivation(shc, masterSecret);
+            }
         }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/ECDHCrypt.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-/*
- * Copyright (c) 2006, 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.security.*;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.*;
-
-import java.util.EnumSet;
-import javax.crypto.SecretKey;
-import javax.crypto.KeyAgreement;
-import javax.net.ssl.SSLHandshakeException;
-
-/**
- * Helper class for the ECDH key exchange. It generates the appropriate
- * ephemeral keys as necessary and performs the actual shared secret derivation.
- *
- * @since   1.6
- * @author  Andreas Sterbenz
- */
-final class ECDHCrypt {
-
-    // our private key
-    private PrivateKey privateKey;
-
-    // our public key
-    private ECPublicKey publicKey;
-
-    // Called by ServerHandshaker for static ECDH
-    ECDHCrypt(PrivateKey privateKey, PublicKey publicKey) {
-        this.privateKey = privateKey;
-        this.publicKey = (ECPublicKey)publicKey;
-    }
-
-    // Called by ServerHandshaker for ephemeral ECDH
-    ECDHCrypt(NamedGroup namedGroup, SecureRandom random) {
-        try {
-            KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
-            ECGenParameterSpec params =
-                    SupportedGroupsExtension.getECGenParamSpec(namedGroup);
-            kpg.initialize(params, random);
-            KeyPair kp = kpg.generateKeyPair();
-            privateKey = kp.getPrivate();
-            publicKey = (ECPublicKey)kp.getPublic();
-        } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Could not generate ECDH keypair", e);
-        }
-    }
-
-    // Called by ClientHandshaker with params it received from the server
-    ECDHCrypt(ECParameterSpec params, SecureRandom random) {
-        try {
-            KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
-            kpg.initialize(params, random);
-            KeyPair kp = kpg.generateKeyPair();
-            privateKey = kp.getPrivate();
-            publicKey = (ECPublicKey)kp.getPublic();
-        } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Could not generate ECDH keypair", e);
-        }
-    }
-
-    /**
-     * Gets the public key of this end of the key exchange.
-     */
-    PublicKey getPublicKey() {
-        return publicKey;
-    }
-
-    // called by ClientHandshaker with either the server's static or
-    // ephemeral public key
-    SecretKey getAgreedSecret(
-            PublicKey peerPublicKey) throws SSLHandshakeException {
-
-        try {
-            KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
-            ka.init(privateKey);
-            ka.doPhase(peerPublicKey, true);
-            return ka.generateSecret("TlsPremasterSecret");
-        } catch (GeneralSecurityException e) {
-            throw (SSLHandshakeException) new SSLHandshakeException(
-                "Could not generate secret").initCause(e);
-        }
-    }
-
-    // called by ServerHandshaker
-    SecretKey getAgreedSecret(
-            byte[] encodedPoint) throws SSLHandshakeException {
-
-        try {
-            ECParameterSpec params = publicKey.getParams();
-            ECPoint point =
-                    JsseJce.decodePoint(encodedPoint, params.getCurve());
-            KeyFactory kf = JsseJce.getKeyFactory("EC");
-            ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
-            PublicKey peerPublicKey = kf.generatePublic(spec);
-            return getAgreedSecret(peerPublicKey);
-        } catch (GeneralSecurityException | java.io.IOException e) {
-            throw (SSLHandshakeException) new SSLHandshakeException(
-                "Could not generate secret").initCause(e);
-        }
-    }
-
-    // Check constraints of the specified EC public key.
-    void checkConstraints(AlgorithmConstraints constraints,
-            byte[] encodedPoint) throws SSLHandshakeException {
-
-        try {
-
-            ECParameterSpec params = publicKey.getParams();
-            ECPoint point =
-                    JsseJce.decodePoint(encodedPoint, params.getCurve());
-            ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
-
-            KeyFactory kf = JsseJce.getKeyFactory("EC");
-            ECPublicKey publicKey = (ECPublicKey)kf.generatePublic(spec);
-
-            // check constraints of ECPublicKey
-            if (!constraints.permits(
-                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
-                throw new SSLHandshakeException(
-                    "ECPublicKey does not comply to algorithm constraints");
-            }
-        } catch (GeneralSecurityException | java.io.IOException e) {
-            throw (SSLHandshakeException) new SSLHandshakeException(
-                    "Could not generate ECPublicKey").initCause(e);
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.security.AlgorithmConstraints;
+import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.util.EnumSet;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.ssl.CipherSuite.HashAlg;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.ECUtil;
+
+final class ECDHKeyExchange {
+    static final SSLPossessionGenerator poGenerator =
+            new ECDHEPossessionGenerator();
+    static final SSLKeyAgreementGenerator ecdheKAGenerator =
+            new ECDHEKAGenerator();
+    static final SSLKeyAgreementGenerator ecdhKAGenerator =
+            new ECDHKAGenerator();
+
+    static final class ECDHECredentials implements SSLCredentials {
+        final ECPublicKey popPublicKey;
+        final NamedGroup namedGroup;
+
+        ECDHECredentials(ECPublicKey popPublicKey, NamedGroup namedGroup) {
+            this.popPublicKey = popPublicKey;
+            this.namedGroup = namedGroup;
+        }
+
+        static ECDHECredentials valueOf(NamedGroup namedGroup,
+            byte[] encodedPoint) throws IOException, GeneralSecurityException {
+
+            if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
+                throw new RuntimeException(
+                    "Credentials decoding:  Not ECDHE named group");
+            }
+
+            if (encodedPoint == null || encodedPoint.length == 0) {
+                return null;
+            }
+
+            ECParameterSpec parameters =
+                    JsseJce.getECParameterSpec(namedGroup.oid);
+            if (parameters == null) {
+                return null;
+            }
+
+            ECPoint point = JsseJce.decodePoint(
+                    encodedPoint, parameters.getCurve());
+            KeyFactory factory = JsseJce.getKeyFactory("EC");
+            ECPublicKey publicKey = (ECPublicKey)factory.generatePublic(
+                    new ECPublicKeySpec(point, parameters));
+            return new ECDHECredentials(publicKey, namedGroup);
+        }
+    }
+
+    static final class ECDHEPossession implements SSLPossession {
+        final PrivateKey privateKey;
+        final ECPublicKey publicKey;
+        final NamedGroup namedGroup;
+
+        ECDHEPossession(NamedGroup namedGroup, SecureRandom random) {
+            try {
+                KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
+                ECGenParameterSpec params =
+                        (ECGenParameterSpec)namedGroup.getParameterSpec();
+                kpg.initialize(params, random);
+                KeyPair kp = kpg.generateKeyPair();
+                privateKey = kp.getPrivate();
+                publicKey = (ECPublicKey)kp.getPublic();
+            } catch (GeneralSecurityException e) {
+                throw new RuntimeException(
+                    "Could not generate ECDH keypair", e);
+            }
+
+            this.namedGroup = namedGroup;
+        }
+
+        ECDHEPossession(ECDHECredentials credentials, SecureRandom random) {
+            ECParameterSpec params = credentials.popPublicKey.getParams();
+            try {
+                KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
+                kpg.initialize(params, random);
+                KeyPair kp = kpg.generateKeyPair();
+                privateKey = kp.getPrivate();
+                publicKey = (ECPublicKey)kp.getPublic();
+            } catch (GeneralSecurityException e) {
+                throw new RuntimeException(
+                    "Could not generate ECDH keypair", e);
+            }
+
+            this.namedGroup = credentials.namedGroup;
+        }
+
+        @Override
+        public byte[] encode() {
+            return ECUtil.encodePoint(
+                    publicKey.getW(), publicKey.getParams().getCurve());
+        }
+
+        // called by ClientHandshaker with either the server's static or
+        // ephemeral public key
+        SecretKey getAgreedSecret(
+                PublicKey peerPublicKey) throws SSLHandshakeException {
+
+            try {
+                KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
+                ka.init(privateKey);
+                ka.doPhase(peerPublicKey, true);
+                return ka.generateSecret("TlsPremasterSecret");
+            } catch (GeneralSecurityException e) {
+                throw (SSLHandshakeException) new SSLHandshakeException(
+                    "Could not generate secret").initCause(e);
+            }
+        }
+
+        // called by ServerHandshaker
+        SecretKey getAgreedSecret(
+                byte[] encodedPoint) throws SSLHandshakeException {
+            try {
+                ECParameterSpec params = publicKey.getParams();
+                ECPoint point =
+                        JsseJce.decodePoint(encodedPoint, params.getCurve());
+                KeyFactory kf = JsseJce.getKeyFactory("EC");
+                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
+                PublicKey peerPublicKey = kf.generatePublic(spec);
+                return getAgreedSecret(peerPublicKey);
+            } catch (GeneralSecurityException | java.io.IOException e) {
+                throw (SSLHandshakeException) new SSLHandshakeException(
+                    "Could not generate secret").initCause(e);
+            }
+        }
+
+        // Check constraints of the specified EC public key.
+        void checkConstraints(AlgorithmConstraints constraints,
+                byte[] encodedPoint) throws SSLHandshakeException {
+            try {
+
+                ECParameterSpec params = publicKey.getParams();
+                ECPoint point =
+                        JsseJce.decodePoint(encodedPoint, params.getCurve());
+                ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
+
+                KeyFactory kf = JsseJce.getKeyFactory("EC");
+                ECPublicKey pubKey = (ECPublicKey)kf.generatePublic(spec);
+
+                // check constraints of ECPublicKey
+                if (!constraints.permits(
+                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), pubKey)) {
+                    throw new SSLHandshakeException(
+                        "ECPublicKey does not comply to algorithm constraints");
+                }
+            } catch (GeneralSecurityException | java.io.IOException e) {
+                throw (SSLHandshakeException) new SSLHandshakeException(
+                        "Could not generate ECPublicKey").initCause(e);
+            }
+        }
+    }
+
+    private static final
+            class ECDHEPossessionGenerator implements SSLPossessionGenerator {
+        // Prevent instantiation of this class.
+        private ECDHEPossessionGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLPossession createPossession(HandshakeContext context) {
+            NamedGroup preferableNamedGroup = null;
+            if ((context.clientRequestedNamedGroups != null) &&
+                    (!context.clientRequestedNamedGroups.isEmpty())) {
+                preferableNamedGroup = SupportedGroups.getPreferredGroup(
+                        context.negotiatedProtocol,
+                        context.algorithmConstraints,
+                        NamedGroupType.NAMED_GROUP_ECDHE,
+                        context.clientRequestedNamedGroups);
+            } else {
+                preferableNamedGroup = SupportedGroups.getPreferredGroup(
+                        context.negotiatedProtocol,
+                        context.algorithmConstraints,
+                        NamedGroupType.NAMED_GROUP_ECDHE);
+            }
+
+            if (preferableNamedGroup != null) {
+                return new ECDHEPossession(preferableNamedGroup,
+                            context.sslContext.getSecureRandom());
+            }
+
+            // no match found, cannot use this cipher suite.
+            //
+            return null;
+        }
+    }
+
+    private static final
+            class ECDHKAGenerator implements SSLKeyAgreementGenerator {
+        // Prevent instantiation of this class.
+        private ECDHKAGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+            if (context instanceof ServerHandshakeContext) {
+                return createServerKeyDerivation(
+                        (ServerHandshakeContext)context);
+            } else {
+                return createClientKeyDerivation(
+                        (ClientHandshakeContext)context);
+            }
+        }
+
+        private SSLKeyDerivation createServerKeyDerivation(
+                ServerHandshakeContext shc) throws IOException {
+            X509Possession x509Possession = null;
+            ECDHECredentials ecdheCredentials = null;
+            for (SSLPossession poss : shc.handshakePossessions) {
+                if (!(poss instanceof X509Possession)) {
+                    continue;
+                }
+
+                PrivateKey privateKey = ((X509Possession)poss).popPrivateKey;
+                if (!privateKey.getAlgorithm().equals("EC")) {
+                    continue;
+                }
+
+                ECParameterSpec params = ((ECPrivateKey)privateKey).getParams();
+                NamedGroup ng = NamedGroup.valueOf(params);
+                if (ng == null) {
+                    // unlikely, have been checked during cipher suite negotiation.
+                    shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Unsupported EC server cert for ECDH key exchange");
+                }
+
+                for (SSLCredentials cred : shc.handshakeCredentials) {
+                    if (!(cred instanceof ECDHECredentials)) {
+                        continue;
+                    }
+                    if (ng.equals(((ECDHECredentials)cred).namedGroup)) {
+                        ecdheCredentials = (ECDHECredentials)cred;
+                        break;
+                    }
+                }
+
+                if (ecdheCredentials != null) {
+                    x509Possession = (X509Possession)poss;
+                    break;
+                }
+            }
+
+            if (x509Possession == null || ecdheCredentials == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No sufficient ECDHE key agreement parameters negotiated");
+            }
+
+            return new ECDHEKAKeyDerivation(shc,
+                x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);
+        }
+
+        private SSLKeyDerivation createClientKeyDerivation(
+                ClientHandshakeContext chc) throws IOException {
+            ECDHEPossession ecdhePossession = null;
+            X509Credentials x509Credentials = null;
+            for (SSLPossession poss : chc.handshakePossessions) {
+                if (!(poss instanceof ECDHEPossession)) {
+                    continue;
+                }
+
+                NamedGroup ng = ((ECDHEPossession)poss).namedGroup;
+                for (SSLCredentials cred : chc.handshakeCredentials) {
+                    if (!(cred instanceof X509Credentials)) {
+                        continue;
+                    }
+
+                    PublicKey publicKey = ((X509Credentials)cred).popPublicKey;
+                    if (!publicKey.getAlgorithm().equals("EC")) {
+                        continue;
+                    }
+                    ECParameterSpec params =
+                            ((ECPublicKey)publicKey).getParams();
+                    NamedGroup namedGroup = NamedGroup.valueOf(params);
+                    if (namedGroup == null) {
+                        // unlikely, should have been checked previously
+                        chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                            "Unsupported EC server cert for ECDH key exchange");
+                    }
+
+                    if (ng.equals(namedGroup)) {
+                        x509Credentials = (X509Credentials)cred;
+                        break;
+                    }
+                }
+
+                if (x509Credentials != null) {
+                    ecdhePossession = (ECDHEPossession)poss;
+                    break;
+                }
+            }
+
+            if (ecdhePossession == null || x509Credentials == null) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No sufficient ECDH key agreement parameters negotiated");
+            }
+
+            return new ECDHEKAKeyDerivation(chc,
+                ecdhePossession.privateKey, x509Credentials.popPublicKey);
+        }
+    }
+
+    private static final
+            class ECDHEKAGenerator implements SSLKeyAgreementGenerator {
+        // Prevent instantiation of this class.
+        private ECDHEKAGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+            ECDHEPossession ecdhePossession = null;
+            ECDHECredentials ecdheCredentials = null;
+            for (SSLPossession poss : context.handshakePossessions) {
+                if (!(poss instanceof ECDHEPossession)) {
+                    continue;
+                }
+
+                NamedGroup ng = ((ECDHEPossession)poss).namedGroup;
+                for (SSLCredentials cred : context.handshakeCredentials) {
+                    if (!(cred instanceof ECDHECredentials)) {
+                        continue;
+                    }
+                    if (ng.equals(((ECDHECredentials)cred).namedGroup)) {
+                        ecdheCredentials = (ECDHECredentials)cred;
+                        break;
+                    }
+                }
+
+                if (ecdheCredentials != null) {
+                    ecdhePossession = (ECDHEPossession)poss;
+                    break;
+                }
+            }
+
+            if (ecdhePossession == null || ecdheCredentials == null) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No sufficient ECDHE key agreement parameters negotiated");
+            }
+
+            return new ECDHEKAKeyDerivation(context,
+                ecdhePossession.privateKey, ecdheCredentials.popPublicKey);
+        }
+    }
+
+    private static final
+            class ECDHEKAKeyDerivation implements SSLKeyDerivation {
+        private final HandshakeContext context;
+        private final PrivateKey localPrivateKey;
+        private final PublicKey peerPublicKey;
+
+        ECDHEKAKeyDerivation(HandshakeContext context,
+                PrivateKey localPrivateKey,
+                PublicKey peerPublicKey) {
+            this.context = context;
+            this.localPrivateKey = localPrivateKey;
+            this.peerPublicKey = peerPublicKey;
+        }
+
+        @Override
+        public SecretKey deriveKey(String algorithm,
+                AlgorithmParameterSpec params) throws IOException {
+            if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
+                return t12DeriveKey(algorithm, params);
+            } else {
+                return t13DeriveKey(algorithm, params);
+            }
+        }
+
+        private SecretKey t12DeriveKey(String algorithm,
+                AlgorithmParameterSpec params) throws IOException {
+            try {
+                KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
+                ka.init(localPrivateKey);
+                ka.doPhase(peerPublicKey, true);
+                SecretKey preMasterSecret =
+                        ka.generateSecret("TlsPremasterSecret");
+
+                SSLMasterKeyDerivation mskd =
+                        SSLMasterKeyDerivation.valueOf(
+                                context.negotiatedProtocol);
+                if (mskd == null) {
+                    // unlikely
+                    throw new SSLHandshakeException(
+                            "No expected master key derivation for protocol: " +
+                            context.negotiatedProtocol.name);
+                }
+                SSLKeyDerivation kd = mskd.createKeyDerivation(
+                        context, preMasterSecret);
+                return kd.deriveKey("MasterSecret", params);
+            } catch (GeneralSecurityException gse) {
+                throw (SSLHandshakeException) new SSLHandshakeException(
+                    "Could not generate secret").initCause(gse);
+            }
+        }
+
+        private SecretKey t13DeriveKey(String algorithm,
+                AlgorithmParameterSpec params) throws IOException {
+            try {
+                KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
+                ka.init(localPrivateKey);
+                ka.doPhase(peerPublicKey, true);
+                SecretKey sharedSecret =
+                        ka.generateSecret("TlsPremasterSecret");
+
+                HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
+                SSLKeyDerivation kd = context.handshakeKeyDerivation;
+                HKDF hkdf = new HKDF(hashAlg.name);
+                if (kd == null) {   // No PSK is in use.
+                    // If PSK is not in use Early Secret will still be
+                    // HKDF-Extract(0, 0).
+                    byte[] zeros = new byte[hashAlg.hashLength];
+                    SecretKeySpec ikm =
+                            new SecretKeySpec(zeros, "TlsPreSharedSecret");
+                    SecretKey earlySecret =
+                            hkdf.extract(zeros, ikm, "TlsEarlySecret");
+                    kd = new SSLSecretDerivation(context, earlySecret);
+                }
+
+                // derive salt secret
+                SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
+
+                // derive handshake secret
+                return hkdf.extract(saltSecret, sharedSecret, algorithm);
+            } catch (GeneralSecurityException gse) {
+                throw (SSLHandshakeException) new SSLHandshakeException(
+                    "Could not generate secret").initCause(gse);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.CryptoPrimitive;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Locale;
+import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
+import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.HexDumpEncoder;
+
+/**
+ * Pack of the ServerKeyExchange handshake message.
+ */
+final class ECDHServerKeyExchange {
+    static final SSLConsumer ecdheHandshakeConsumer =
+            new ECDHServerKeyExchangeConsumer();
+    static final HandshakeProducer ecdheHandshakeProducer =
+            new ECDHServerKeyExchangeProducer();
+
+    /**
+     * The ECDH ServerKeyExchange handshake message.
+     */
+    private static final
+            class ECDHServerKeyExchangeMessage extends HandshakeMessage {
+        private static final byte CURVE_NAMED_CURVE = (byte)0x03;
+
+        // id of the named curve
+        private final NamedGroup namedGroup;
+
+        // encoded public point
+        private final byte[] publicPoint;
+
+        // signature bytes, or null if anonymous
+        private final byte[] paramsSignature;
+
+        // public key object encapsulated in this message
+        private final ECPublicKey publicKey;
+
+        private final boolean useExplicitSigAlgorithm;
+
+        // the signature algorithm used by this ServerKeyExchange message
+        private final SignatureScheme signatureScheme;
+
+        ECDHServerKeyExchangeMessage(
+                HandshakeContext handshakeContext) throws IOException {
+            super(handshakeContext);
+
+            // This happens in server side only.
+            ServerHandshakeContext shc =
+                    (ServerHandshakeContext)handshakeContext;
+
+            ECDHEPossession ecdhePossession = null;
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof ECDHEPossession) {
+                    ecdhePossession = (ECDHEPossession)possession;
+                    if (x509Possession != null) {
+                        break;
+                    }
+                } else if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    if (ecdhePossession != null) {
+                        break;
+                    }
+                }
+            }
+
+            if (ecdhePossession == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No ECDHE credentials negotiated for server key exchange");
+            }
+
+            publicKey = ecdhePossession.publicKey;
+            ECParameterSpec params = publicKey.getParams();
+            ECPoint point = publicKey.getW();
+            publicPoint = JsseJce.encodePoint(point, params.getCurve());
+
+            this.namedGroup = NamedGroup.valueOf(params);
+            if ((namedGroup == null) || (namedGroup.oid == null) ) {
+                // unlikely
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unnamed EC parameter spec: " + params);
+            }
+
+            if (x509Possession == null) {
+                // anonymous, no authentication, no signature
+                paramsSignature = null;
+                signatureScheme = null;
+                useExplicitSigAlgorithm = false;
+            } else {
+                useExplicitSigAlgorithm =
+                        shc.negotiatedProtocol.useTLS12PlusSpec();
+                Signature signer = null;
+                if (useExplicitSigAlgorithm) {
+                    signatureScheme = SignatureScheme.getPreferableAlgorithm(
+                            shc.peerRequestedSignatureSchemes,
+                            x509Possession.popPrivateKey,
+                            shc.negotiatedProtocol);
+                    if (signatureScheme == null) {
+                        // Unlikely, the credentials generator should have
+                        // selected the preferable signature algorithm properly.
+                        shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                                "No preferred signature algorithm for " +
+                                x509Possession.popPrivateKey.getAlgorithm() +
+                                "  key");
+                    }
+                    try {
+                        signer = signatureScheme.getSignature(
+                                x509Possession.popPrivateKey);
+                    } catch (NoSuchAlgorithmException | InvalidKeyException |
+                            InvalidAlgorithmParameterException nsae) {
+                        shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Unsupported signature algorithm: " +
+                            signatureScheme.name, nsae);
+                    }
+                } else {
+                    signatureScheme = null;
+                    try {
+                        signer = getSignature(
+                                x509Possession.popPrivateKey.getAlgorithm(),
+                                x509Possession.popPrivateKey);
+                    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+                        shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Unsupported signature algorithm: " +
+                            x509Possession.popPrivateKey.getAlgorithm(), e);
+                    }
+                }
+
+                byte[] signature = null;
+                try {
+                    updateSignature(signer, shc.clientHelloRandom.randomBytes,
+                            shc.serverHelloRandom.randomBytes,
+                            namedGroup.id, publicPoint);
+                    signature = signer.sign();
+                } catch (SignatureException ex) {
+                    shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failed to sign ecdhe parameters: " +
+                        x509Possession.popPrivateKey.getAlgorithm(), ex);
+                }
+                paramsSignature = signature;
+            }
+        }
+
+        ECDHServerKeyExchangeMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // This happens in client side only.
+            ClientHandshakeContext chc =
+                    (ClientHandshakeContext)handshakeContext;
+
+            byte curveType = (byte)Record.getInt8(m);
+            if (curveType != CURVE_NAMED_CURVE) {
+                // Unlikely as only the named curves should be negotiated.
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unsupported ECCurveType: " + curveType);
+            }
+
+            int namedGroupId = Record.getInt16(m);
+            this.namedGroup = NamedGroup.valueOf(namedGroupId);
+            if (namedGroup == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unknown named group ID: " + namedGroupId);
+            }
+
+            if (!SupportedGroups.isSupported(namedGroup)) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unsupported named group: " + namedGroup);
+            }
+
+            if (namedGroup.oid == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Unknown named EC curve: " + namedGroup);
+            }
+
+            ECParameterSpec parameters =
+                    JsseJce.getECParameterSpec(namedGroup.oid);
+            if (parameters == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No supported EC parameter: " + namedGroup);
+            }
+
+            publicPoint = Record.getBytes8(m);
+            if (publicPoint.length == 0) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Insufficient ECPoint data: " + namedGroup);
+            }
+
+            ECPublicKey ecPublicKey = null;
+            try {
+                ECPoint point =
+                        JsseJce.decodePoint(publicPoint, parameters.getCurve());
+                KeyFactory factory = JsseJce.getKeyFactory("EC");
+                ecPublicKey = (ECPublicKey)factory.generatePublic(
+                    new ECPublicKeySpec(point, parameters));
+            } catch (NoSuchAlgorithmException |
+                    InvalidKeySpecException | IOException ex) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid ECPoint: " + namedGroup, ex);
+            }
+
+            publicKey = ecPublicKey;
+
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials cd : chc.handshakeCredentials) {
+                if (cd instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)cd;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null) {
+                // anonymous, no authentication, no signature
+                if (m.hasRemaining()) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid DH ServerKeyExchange: unknown extra data");
+                }
+                this.signatureScheme = null;
+                this.paramsSignature = null;
+                this.useExplicitSigAlgorithm = false;
+
+                return;
+            }
+
+            this.useExplicitSigAlgorithm =
+                    chc.negotiatedProtocol.useTLS12PlusSpec();
+            if (useExplicitSigAlgorithm) {
+                int ssid = Record.getInt16(m);
+                signatureScheme = SignatureScheme.valueOf(ssid);
+                if (signatureScheme == null) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid signature algorithm (" + ssid +
+                        ") used in ECDH ServerKeyExchange handshake message");
+                }
+
+                if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Unsupported signature algorithm (" +
+                        signatureScheme.name +
+                        ") used in ECDH ServerKeyExchange handshake message");
+                }
+            } else {
+                signatureScheme = null;
+            }
+
+            // read and verify the signature
+            paramsSignature = Record.getBytes16(m);
+            Signature signer;
+            if (useExplicitSigAlgorithm) {
+                try {
+                    signer = signatureScheme.getSignature(
+                            x509Credentials.popPublicKey);
+                } catch (NoSuchAlgorithmException | InvalidKeyException |
+                        InvalidAlgorithmParameterException nsae) {
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm: " +
+                        signatureScheme.name, nsae);
+
+                    return;     // make the compiler happe
+                }
+            } else {
+                try {
+                    signer = getSignature(
+                            x509Credentials.popPublicKey.getAlgorithm(),
+                            x509Credentials.popPublicKey);
+                } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Unsupported signature algorithm: " +
+                        x509Credentials.popPublicKey.getAlgorithm(), e);
+
+                    return;     // make the compiler happe
+                }
+            }
+
+            try {
+                updateSignature(signer,
+                        chc.clientHelloRandom.randomBytes,
+                        chc.serverHelloRandom.randomBytes,
+                        namedGroup.id, publicPoint);
+
+                if (!signer.verify(paramsSignature)) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid ECDH ServerKeyExchange signature");
+                }
+            } catch (SignatureException ex) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Cannot verify ECDH ServerKeyExchange signature", ex);
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.SERVER_KEY_EXCHANGE;
+        }
+
+        @Override
+        public int messageLength() {
+            int sigLen = 0;
+            if (paramsSignature != null) {
+                sigLen = 2 + paramsSignature.length;
+                if (useExplicitSigAlgorithm) {
+                    sigLen += SignatureScheme.sizeInRecord();
+                }
+            }
+
+            return 4 + publicPoint.length + sigLen;
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putInt8(CURVE_NAMED_CURVE);
+            hos.putInt16(namedGroup.id);
+            hos.putBytes8(publicPoint);
+            if (paramsSignature != null) {
+                if (useExplicitSigAlgorithm) {
+                    hos.putInt16(signatureScheme.id);
+                }
+
+                hos.putBytes16(paramsSignature);
+            }
+        }
+
+        @Override
+        public String toString() {
+            if (useExplicitSigAlgorithm) {
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"ECDH ServerKeyExchange\": '{'\n" +
+                    "  \"parameters\": '{'\n" +
+                    "    \"named group\": \"{0}\"\n" +
+                    "    \"ecdh public\": '{'\n" +
+                    "{1}\n" +
+                    "    '}',\n" +
+                    "  '}',\n" +
+                    "  \"digital signature\":  '{'\n" +
+                    "    \"signature algorithm\": \"{2}\"\n" +
+                    "    \"signature\": '{'\n" +
+                    "{3}\n" +
+                    "    '}',\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                Object[] messageFields = {
+                    namedGroup.name,
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(publicPoint), "      "),
+                    signatureScheme.name,
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(paramsSignature), "      ")
+                };
+                return messageFormat.format(messageFields);
+            } else if (paramsSignature != null) {
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"ECDH ServerKeyExchange\": '{'\n" +
+                    "  \"parameters\":  '{'\n" +
+                    "    \"named group\": \"{0}\"\n" +
+                    "    \"ecdh public\": '{'\n" +
+                    "{1}\n" +
+                    "    '}',\n" +
+                    "  '}',\n" +
+                    "  \"signature\": '{'\n" +
+                    "{2}\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                Object[] messageFields = {
+                    namedGroup.name,
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(publicPoint), "      "),
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(paramsSignature), "    ")
+                };
+
+                return messageFormat.format(messageFields);
+            } else {    // anonymous
+                MessageFormat messageFormat = new MessageFormat(
+                    "\"ECDH ServerKeyExchange\": '{'\n" +
+                    "  \"parameters\":  '{'\n" +
+                    "    \"named group\": \"{0}\"\n" +
+                    "    \"ecdh public\": '{'\n" +
+                    "{1}\n" +
+                    "    '}',\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                Object[] messageFields = {
+                    namedGroup.name,
+                    Utilities.indent(
+                            hexEncoder.encodeBuffer(publicPoint), "      "),
+                };
+
+                return messageFormat.format(messageFields);
+            }
+        }
+
+        private static Signature getSignature(String keyAlgorithm,
+                Key key) throws NoSuchAlgorithmException, InvalidKeyException {
+            Signature signer = null;
+            switch (keyAlgorithm) {
+                case "EC":
+                    signer = JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA);
+                    break;
+                case "RSA":
+                    signer = RSASignature.getInstance();
+                    break;
+                default:
+                    throw new NoSuchAlgorithmException(
+                        "neither an RSA or a EC key : " + keyAlgorithm);
+            }
+
+            if (signer != null) {
+                if (key instanceof PublicKey) {
+                    signer.initVerify((PublicKey)(key));
+                } else {
+                    signer.initSign((PrivateKey)key);
+                }
+            }
+
+            return signer;
+        }
+
+        private static void updateSignature(Signature sig,
+                byte[] clntNonce, byte[] svrNonce, int namedGroupId,
+                byte[] publicPoint) throws SignatureException {
+            sig.update(clntNonce);
+            sig.update(svrNonce);
+
+            sig.update(CURVE_NAMED_CURVE);
+            sig.update((byte)((namedGroupId >> 8) & 0xFF));
+            sig.update((byte)(namedGroupId & 0xFF));
+            sig.update((byte)publicPoint.length);
+            sig.update(publicPoint);
+        }
+    }
+
+    /**
+     * The ECDH "ServerKeyExchange" handshake message producer.
+     */
+    private static final
+            class ECDHServerKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private ECDHServerKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ECDHServerKeyExchangeMessage skem =
+                    new ECDHServerKeyExchangeMessage(shc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced ECDH ServerKeyExchange handshake message", skem);
+            }
+
+            // Output the handshake message.
+            skem.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The ECDH "ServerKeyExchange" handshake message consumer.
+     */
+    private static final
+            class ECDHServerKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ECDHServerKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            ECDHServerKeyExchangeMessage skem =
+                    new ECDHServerKeyExchangeMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming ECDH ServerKeyExchange handshake message", skem);
+            }
+
+            //
+            // validate
+            //
+            // check constraints of EC PublicKey
+            if (!chc.algorithmConstraints.permits(
+                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                    skem.publicKey)) {
+                chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                        "ECDH ServerKeyExchange does not comply " +
+                        "to algorithm constraints");
+            }
+
+            //
+            // update
+            //
+            chc.handshakeCredentials.add(
+                    new ECDHECredentials(skem.publicKey, skem.namedGroup));
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+import static sun.security.ssl.SSLExtension.CH_EC_POINT_FORMATS;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+
+/**
+ * Pack of the "ec_point_formats" extensions [RFC 4492].
+ */
+final class ECPointFormatsExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHECPointFormatsProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHECPointFormatsConsumer();
+
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHECPointFormatsConsumer();
+
+    static final SSLStringizer epfStringizer =
+            new ECPointFormatsStringizer();
+
+    /**
+     * The "ec_point_formats" extension.
+     */
+    static class ECPointFormatsSpec implements SSLExtensionSpec {
+        static final ECPointFormatsSpec DEFAULT =
+            new ECPointFormatsSpec(new byte[] {ECPointFormat.UNCOMPRESSED.id});
+
+        final byte[] formats;
+
+        ECPointFormatsSpec(byte[] formats) {
+            this.formats = formats;
+        }
+
+        private ECPointFormatsSpec(ByteBuffer m) throws IOException {
+            if (!m.hasRemaining()) {
+                throw new SSLProtocolException(
+                    "Invalid ec_point_formats extension: " +
+                    "insufficient data");
+            }
+
+            this.formats = Record.getBytes8(m);
+        }
+
+        private boolean hasUncompressedFormat() {
+            for (byte format : formats) {
+                if (format == ECPointFormat.UNCOMPRESSED.id) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"formats\": '['{0}']'", Locale.ENGLISH);
+            if (formats == null || formats.length ==  0) {
+                Object[] messageFields = {
+                        "<no EC point format specified>"
+                    };
+                return messageFormat.format(messageFields);
+            } else {
+                StringBuilder builder = new StringBuilder(512);
+                boolean isFirst = true;
+                for (byte pf : formats) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        builder.append(", ");
+                    }
+
+                    builder.append(ECPointFormat.nameOf(pf));
+                }
+
+                Object[] messageFields = {
+                        builder.toString()
+                    };
+
+                return messageFormat.format(messageFields);
+            }
+        }
+    }
+
+    private static final class ECPointFormatsStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new ECPointFormatsSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    private static enum ECPointFormat {
+        UNCOMPRESSED                    ((byte)0, "uncompressed"),
+        ANSIX962_COMPRESSED_PRIME       ((byte)1, "ansiX962_compressed_prime"),
+        FMT_ANSIX962_COMPRESSED_CHAR2   ((byte)2, "ansiX962_compressed_char2");
+
+        final byte id;
+        final String name;
+
+        private ECPointFormat(byte id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        static String nameOf(int id) {
+            for (ECPointFormat pf: ECPointFormat.values()) {
+                if (pf.id == id) {
+                    return pf.name;
+                }
+            }
+            return "UNDEFINED-EC-POINT-FORMAT(" + id + ")";
+        }
+    }
+
+    /**
+     * Network data producer of a "ec_point_formats" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHECPointFormatsProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHECPointFormatsProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable ec_point_formats extension");
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            //
+            // produce the extension only if EC cipher suite is activated.
+            if (NamedGroupType.NAMED_GROUP_ECDHE.isSupported(
+                    chc.activeCipherSuites)) {
+                // We are using uncompressed ECPointFormat only at present.
+                byte[] extData = new byte[] {0x01, 0x00};
+
+                // Update the context.
+                chc.handshakeExtensions.put(
+                    CH_EC_POINT_FORMATS, ECPointFormatsSpec.DEFAULT);
+
+                return extData;
+            }
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Need no ec_point_formats extension");
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Network data consumer of a "ec_point_formats" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHECPointFormatsConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHECPointFormatsConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable ec_point_formats extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            ECPointFormatsSpec spec;
+            try {
+                spec = new ECPointFormatsSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // per RFC 4492, uncompressed points must always be supported.
+            if (!spec.hasUncompressedFormat()) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Invalid ec_point_formats extension data: " +
+                    "peer does not support uncompressed points");
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
+
+            // No impact on session resumption, as only uncompressed points
+            // are supported at present.
+        }
+    }
+
+    /**
+     * Network data consumer of a "ec_point_formats" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class SHECPointFormatsConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHECPointFormatsConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to "ec_point_formats" extension request only
+            ECPointFormatsSpec requestedSpec = (ECPointFormatsSpec)
+                    chc.handshakeExtensions.get(CH_EC_POINT_FORMATS);
+            if (requestedSpec == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected ec_point_formats extension in ServerHello");
+            }
+
+            // Parse the extension.
+            ECPointFormatsSpec spec;
+            try {
+                spec = new ECPointFormatsSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // per RFC 4492, uncompressed points must always be supported.
+            if (!spec.hasUncompressedFormat()) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Invalid ec_point_formats extension data: " +
+                        "peer does not support uncompressed points");
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
+
+            // No impact on session resumption, as only uncompressed points
+            // are supported at present.
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/EllipticPointFormatsExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2006, 2016, 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.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.net.ssl.SSLProtocolException;
-
-final class EllipticPointFormatsExtension extends HelloExtension {
-
-    static final int FMT_UNCOMPRESSED = 0;
-    static final int FMT_ANSIX962_COMPRESSED_PRIME = 1;
-    static final int FMT_ANSIX962_COMPRESSED_CHAR2 = 2;
-
-    static final HelloExtension DEFAULT =
-            new EllipticPointFormatsExtension(new byte[] {FMT_UNCOMPRESSED});
-
-    private final byte[] formats;
-
-    private EllipticPointFormatsExtension(byte[] formats) {
-        super(ExtensionType.EXT_EC_POINT_FORMATS);
-        this.formats = formats;
-    }
-
-    EllipticPointFormatsExtension(HandshakeInStream s, int len)
-            throws IOException {
-        super(ExtensionType.EXT_EC_POINT_FORMATS);
-        formats = s.getBytes8();
-        // RFC 4492 says uncompressed points must always be supported.
-        // Check just to make sure.
-        boolean uncompressed = false;
-        for (int format : formats) {
-            if (format == FMT_UNCOMPRESSED) {
-                uncompressed = true;
-                break;
-            }
-        }
-        if (uncompressed == false) {
-            throw new SSLProtocolException
-                ("Peer does not support uncompressed points");
-        }
-    }
-
-    @Override
-    int length() {
-        return 5 + formats.length;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(formats.length + 1);
-        s.putBytes8(formats);
-    }
-
-    private static String toString(byte format) {
-        int f = format & 0xff;
-        switch (f) {
-        case FMT_UNCOMPRESSED:
-            return "uncompressed";
-        case FMT_ANSIX962_COMPRESSED_PRIME:
-            return "ansiX962_compressed_prime";
-        case FMT_ANSIX962_COMPRESSED_CHAR2:
-            return "ansiX962_compressed_char2";
-        default:
-            return "unknown-" + f;
-        }
-    }
-
-    @Override
-    public String toString() {
-        List<String> list = new ArrayList<String>();
-        for (byte format : formats) {
-            list.add(toString(format));
-        }
-        return "Extension " + type + ", formats: " + list;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Locale;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the EncryptedExtensions handshake message.
+ */
+final class EncryptedExtensions {
+    static final HandshakeProducer handshakeProducer =
+        new EncryptedExtensionsProducer();
+    static final SSLConsumer handshakeConsumer =
+        new EncryptedExtensionsConsumer();
+
+    /**
+     * The EncryptedExtensions handshake message.
+     */
+    static final class EncryptedExtensionsMessage extends HandshakeMessage {
+        private final SSLExtensions extensions;
+
+        EncryptedExtensionsMessage(
+                HandshakeContext handshakeContext) throws IOException {
+            super(handshakeContext);
+            this.extensions = new SSLExtensions(this);
+        }
+
+        EncryptedExtensionsMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // struct {
+            //     Extension extensions<0..2^16-1>;
+            // } EncryptedExtensions;
+            if (m.remaining() < 2) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid EncryptedExtensions handshake message: " +
+                        "no sufficient data");
+            }
+
+            SSLExtension[] encryptedExtensions =
+                    handshakeContext.sslConfig.getEnabledExtensions(
+                            SSLHandshake.ENCRYPTED_EXTENSIONS);
+            this.extensions = new SSLExtensions(this, m, encryptedExtensions);
+        }
+
+        @Override
+        SSLHandshake handshakeType() {
+            return SSLHandshake.ENCRYPTED_EXTENSIONS;
+        }
+
+        @Override
+        int messageLength() {
+            int extLen = extensions.length();
+            if (extLen == 0) {
+                extLen = 2;     // empty extensions
+            }
+            return extLen;
+        }
+
+        @Override
+        void send(HandshakeOutStream hos) throws IOException {
+            // Is it an empty extensions?
+            if (extensions.length() == 0) {
+                hos.putInt16(0);
+            } else {
+                extensions.send(hos);
+            }
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"EncryptedExtensions\": [\n" +
+                    "{0}\n" +
+                    "]",
+                    Locale.ENGLISH);
+            Object[] messageFields = {
+                Utilities.indent(extensions.toString())
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The EncryptedExtensions handshake message consumer.
+     */
+    private static final class EncryptedExtensionsProducer
+            implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private EncryptedExtensionsProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            EncryptedExtensionsMessage eem =
+                    new EncryptedExtensionsMessage(shc);
+            SSLExtension[] extTypes =
+                    shc.sslConfig.getEnabledExtensions(
+                            SSLHandshake.ENCRYPTED_EXTENSIONS,
+                            shc.negotiatedProtocol);
+            eem.extensions.produce(shc, extTypes);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced EncryptedExtensions message", eem);
+            }
+
+            // Output the handshake message.
+            eem.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The EncryptedExtensions handshake message consumer.
+     */
+    private static final class EncryptedExtensionsConsumer
+            implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private EncryptedExtensionsConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.remove(SSLHandshake.ENCRYPTED_EXTENSIONS.id);
+
+            EncryptedExtensionsMessage eem =
+                    new EncryptedExtensionsMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming EncryptedExtensions handshake message", eem);
+            }
+
+            //
+            // validate
+            //
+            SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.ENCRYPTED_EXTENSIONS);
+            eem.extensions.consumeOnLoad(chc, extTypes);
+
+            //
+            // update
+            //
+            eem.extensions.consumeOnTrade(chc, extTypes);
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/EphemeralKeyManager.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/EphemeralKeyManager.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,46 +27,365 @@
 package sun.security.ssl;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import javax.net.ssl.SSLProtocolException;
+import static sun.security.ssl.SSLExtension.CH_EXTENDED_MASTER_SECRET;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import static sun.security.ssl.SSLExtension.SH_EXTENDED_MASTER_SECRET;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
 
 /**
- * Extended Master Secret TLS extension (TLS 1.0+). This extension
- * defines how to calculate the TLS connection master secret and
- * mitigates some types of man-in-the-middle attacks.
- *
- * See further information in
- * <a href="https://tools.ietf.org/html/rfc7627">RFC 7627</a>.
- *
- * @author Martin Balao (mbalao@redhat.com)
+ * Pack of the "extended_master_secret" extensions [RFC 7627].
  */
-final class ExtendedMasterSecretExtension extends HelloExtension {
-    ExtendedMasterSecretExtension() {
-        super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+final class ExtendedMasterSecretExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHExtendedMasterSecretProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHExtendedMasterSecretConsumer();
+    static final HandshakeAbsence chOnLoadAbsence =
+            new CHExtendedMasterSecretAbsence();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHExtendedMasterSecretProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHExtendedMasterSecretConsumer();
+    static final HandshakeAbsence shOnLoadAbsence =
+            new SHExtendedMasterSecretAbsence();
+
+    static final SSLStringizer emsStringizer =
+            new ExtendedMasterSecretStringizer();
+
+    /**
+     * The "extended_master_secret" extension.
+     */
+    static final class ExtendedMasterSecretSpec implements SSLExtensionSpec {
+        // A nominal object that does not holding any real renegotiation info.
+        static final ExtendedMasterSecretSpec NOMINAL =
+                new ExtendedMasterSecretSpec();
+
+        private ExtendedMasterSecretSpec() {
+            // blank
+        }
+
+        private ExtendedMasterSecretSpec(ByteBuffer m) throws IOException {
+            // Parse the extension.
+            if (m.hasRemaining()) {
+                throw new SSLProtocolException(
+                    "Invalid extended_master_secret extension data: " +
+                    "not empty");
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "<empty>";
+        }
+    }
+
+    private static final
+            class ExtendedMasterSecretStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new ExtendedMasterSecretSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
     }
 
-    ExtendedMasterSecretExtension(HandshakeInStream s,
-            int len) throws IOException {
-        super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+    /**
+     * Network data producer of a "extended_master_secret" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHExtendedMasterSecretProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHExtendedMasterSecretProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
+                    !SSLConfiguration.useExtendedMasterSecret ||
+                    !chc.conContext.protocolVersion.useTLS10PlusSpec()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extended_master_secret extension");
+                }
+
+                return null;
+            }
+
+            if (chc.handshakeSession == null ||
+                    chc.handshakeSession.useExtendedMasterSecret) {
+                byte[] extData = new byte[0];
+                chc.handshakeExtensions.put(CH_EXTENDED_MASTER_SECRET,
+                        ExtendedMasterSecretSpec.NOMINAL);
+
+                return extData;
+            }
+
+            return null;
+        }
+    }
 
-        if (len != 0) {
-            throw new SSLProtocolException("Invalid " + type + " extension");
+    /**
+     * Network data producer of a "extended_master_secret" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class CHExtendedMasterSecretConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHExtendedMasterSecretConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
+                    !SSLConfiguration.useExtendedMasterSecret ||
+                    !shc.negotiatedProtocol.useTLS10PlusSpec()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Ignore unavailable extension: " +
+                            CH_EXTENDED_MASTER_SECRET.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            ExtendedMasterSecretSpec spec;
+            try {
+                spec = new ExtendedMasterSecretSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            if (shc.isResumption && shc.resumingSession != null &&
+                    !shc.resumingSession.useExtendedMasterSecret) {
+                // 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.
+                shc.isResumption = false;
+                shc.resumingSession = null;
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "abort session resumption which did not use " +
+                        "Extended Master Secret extension");
+                }
+            }
+
+            // Update the context.
+            //
+            shc.handshakeExtensions.put(
+                CH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
+
+            // No impact on session resumption.
         }
     }
 
-    @Override
-    int length() {
-        return 4;       // 4: extension type and length fields
+    /**
+     * The absence processing if a "extended_master_secret" extension is
+     * not present in the ClientHello handshake message.
+     */
+    private static final
+            class CHExtendedMasterSecretAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
+                    !SSLConfiguration.useExtendedMasterSecret) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Ignore unavailable extension: " +
+                            CH_EXTENDED_MASTER_SECRET.name);
+                }
+                return;     // ignore the extension
+            }
+
+            if (shc.negotiatedProtocol.useTLS10PlusSpec() &&
+                    !SSLConfiguration.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.
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "Extended Master Secret extension is required");
+            }
+
+            if (shc.isResumption && shc.resumingSession != null) {
+                if (shc.resumingSession.useExtendedMasterSecret) {
+                    // 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.
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Missing Extended Master Secret extension " +
+                            "on session resumption");
+                } else {
+                    // For abbreviated handshake request, if neither the
+                    // original session nor the new ClientHello uses the
+                    // extension, the server SHOULD abort the handshake.
+                    if (!SSLConfiguration.allowLegacyResumption) {
+                        shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Missing Extended Master Secret extension " +
+                            "on session resumption");
+                    } else {  // Otherwise, continue with a full handshake.
+                        shc.isResumption = false;
+                        shc.resumingSession = null;
+                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                            SSLLogger.fine(
+                                "abort session resumption, " +
+                                "missing Extended Master Secret extension");
+                        }
+                    }
+                }
+            }
+        }
     }
 
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);    // ExtensionType extension_type;
-        s.putInt16(0);          // extension_data length
+    /**
+     * Network data producer of a "extended_master_secret" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class SHExtendedMasterSecretProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHExtendedMasterSecretProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            if (shc.handshakeSession.useExtendedMasterSecret) {
+                byte[] extData = new byte[0];
+                shc.handshakeExtensions.put(SH_EXTENDED_MASTER_SECRET,
+                        ExtendedMasterSecretSpec.NOMINAL);
+
+                return extData;
+            }
+
+            return null;
+        }
     }
 
-    @Override
-    public String toString() {
-        return "Extension " + type;
+    /**
+     * Network data consumer of a "extended_master_secret" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class SHExtendedMasterSecretConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHExtendedMasterSecretConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to the client extended_master_secret extension
+            // request, which is mandatory for ClientHello message.
+            ExtendedMasterSecretSpec requstedSpec = (ExtendedMasterSecretSpec)
+                    chc.handshakeExtensions.get(CH_EXTENDED_MASTER_SECRET);
+            if (requstedSpec == null) {
+                chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
+                        "Server sent the extended_master_secret " +
+                        "extension improperly");
+            }
+
+            // Parse the extension.
+            ExtendedMasterSecretSpec spec;
+            try {
+                spec = new ExtendedMasterSecretSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            if (chc.isResumption && chc.resumingSession != null &&
+                    !chc.resumingSession.useExtendedMasterSecret) {
+                chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
+                        "Server sent an unexpected extended_master_secret " +
+                        "extension on session resumption");
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                SH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * The absence processing if a "extended_master_secret" extension is
+     * not present in the ServerHello handshake message.
+     */
+    private static final
+            class SHExtendedMasterSecretAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            if (SSLConfiguration.useExtendedMasterSecret &&
+                    !SSLConfiguration.allowLegacyMasterSecret) {
+                // For full handshake, if a client receives a ServerHello
+                // without the extension, it SHOULD abort the handshake if
+                // it does not wish to interoperate with legacy servers.
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Extended Master Secret extension is required");
+            }
+
+            if (chc.isResumption && chc.resumingSession != null) {
+                if (chc.resumingSession.useExtendedMasterSecret) {
+                    // For abbreviated handshake, if the original session used
+                    // the "extended_master_secret" extension but the new
+                    // ServerHello does not contain the extension, the client
+                    // MUST abort the handshake.
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Missing Extended Master Secret extension " +
+                            "on session resumption");
+                } else if (SSLConfiguration.useExtendedMasterSecret &&
+                        !SSLConfiguration.allowLegacyResumption &&
+                        chc.negotiatedProtocol.useTLS10PlusSpec()) {
+                    // Unlikely, abbreviated handshake should be discarded.
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Extended Master Secret extension is required");
+                }
+            }
+        }
     }
 }
 
--- a/src/java.base/share/classes/sun/security/ssl/ExtensionType.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2006, 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.util.ArrayList;
-import java.util.List;
-
-final class ExtensionType {
-
-    final int id;
-    final String name;
-
-    private ExtensionType(int id, String name) {
-        this.id = id;
-        this.name = name;
-    }
-
-    @Override
-    public String toString() {
-        return name;
-    }
-
-    static List<ExtensionType> knownExtensions = new ArrayList<>(16);
-
-    static ExtensionType get(int id) {
-        for (ExtensionType ext : knownExtensions) {
-            if (ext.id == id) {
-                return ext;
-            }
-        }
-        return new ExtensionType(id, "type_" + id);
-    }
-
-    private static ExtensionType e(int id, String name) {
-        ExtensionType ext = new ExtensionType(id, name);
-        knownExtensions.add(ext);
-        return ext;
-    }
-
-    // extensions defined in RFC 3546
-    static final ExtensionType EXT_SERVER_NAME =
-            e(0x0000, "server_name");            // IANA registry value: 0
-    static final ExtensionType EXT_MAX_FRAGMENT_LENGTH =
-            e(0x0001, "max_fragment_length");    // IANA registry value: 1
-    static final ExtensionType EXT_CLIENT_CERTIFICATE_URL =
-            e(0x0002, "client_certificate_url"); // IANA registry value: 2
-    static final ExtensionType EXT_TRUSTED_CA_KEYS =
-            e(0x0003, "trusted_ca_keys");        // IANA registry value: 3
-    static final ExtensionType EXT_TRUNCATED_HMAC =
-            e(0x0004, "truncated_hmac");         // IANA registry value: 4
-    static final ExtensionType EXT_STATUS_REQUEST =
-            e(0x0005, "status_request");         // IANA registry value: 5
-
-    // extensions defined in RFC 4681
-    static final ExtensionType EXT_USER_MAPPING =
-            e(0x0006, "user_mapping");           // IANA registry value: 6
-
-    // extensions defined in RFC 5081
-    static final ExtensionType EXT_CERT_TYPE =
-            e(0x0009, "cert_type");              // IANA registry value: 9
-
-    // extensions defined in RFC 4492 (ECC) and RFC 7919 (FFDHE)
-    static final ExtensionType EXT_SUPPORTED_GROUPS =
-            e(0x000A, "supported_groups");       // IANA registry value: 10
-    static final ExtensionType EXT_EC_POINT_FORMATS =
-            e(0x000B, "ec_point_formats");       // IANA registry value: 11
-
-    // extensions defined in RFC 5054
-    static final ExtensionType EXT_SRP =
-            e(0x000C, "srp");                    // IANA registry value: 12
-
-    // extensions defined in RFC 5246
-    static final ExtensionType EXT_SIGNATURE_ALGORITHMS =
-            e(0x000D, "signature_algorithms");   // IANA registry value: 13
-
-    // extension defined in RFC 7301 (ALPN)
-    static final ExtensionType EXT_ALPN =
-            e(0x0010, "application_layer_protocol_negotiation");
-                                                 // IANA registry value: 16
-
-    // extensions defined in RFC 6961
-    static final ExtensionType EXT_STATUS_REQUEST_V2 =
-            e(0x0011, "status_request_v2");      // IANA registry value: 17
-
-    // extensions defined in RFC 7627
-    static final ExtensionType EXT_EXTENDED_MASTER_SECRET =
-            e(0x0017, "extended_master_secret"); // IANA registry value: 23
-
-    // extensions defined in RFC 5746
-    static final ExtensionType EXT_RENEGOTIATION_INFO =
-            e(0xff01, "renegotiation_info");     // IANA registry value: 65281
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/Finished.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,1077 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import sun.security.internal.spec.TlsPrfParameterSpec;
+import sun.security.ssl.CipherSuite.HashAlg;
+import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
+import sun.security.ssl.SSLBasicKeyDerivation.SecretSizeSpec;
+import sun.security.ssl.SSLCipher.SSLReadCipher;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.util.HexDumpEncoder;
+
+/**
+ * Pack of the Finished handshake message.
+ */
+final class Finished {
+    static final SSLConsumer t12HandshakeConsumer =
+        new T12FinishedConsumer();
+    static final HandshakeProducer t12HandshakeProducer =
+        new T12FinishedProducer();
+
+    static final SSLConsumer t13HandshakeConsumer =
+        new T13FinishedConsumer();
+    static final HandshakeProducer t13HandshakeProducer =
+        new T13FinishedProducer();
+
+    /**
+     * The Finished handshake message.
+     */
+    private static final class FinishedMessage extends HandshakeMessage {
+        private final byte[] verifyData;
+
+        FinishedMessage(HandshakeContext context) throws IOException {
+            super(context);
+
+            VerifyDataScheme vds =
+                    VerifyDataScheme.valueOf(context.negotiatedProtocol);
+
+            byte[] vd = null;
+            try {
+                vd = vds.createVerifyData(context, false);
+            } catch (IOException ioe) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Failed to generate verify_data", ioe);
+            }
+
+            this.verifyData = vd;
+        }
+
+        FinishedMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+            int verifyDataLen = 12;
+            if (context.negotiatedProtocol == ProtocolVersion.SSL30) {
+                verifyDataLen = 36;
+            } else if (context.negotiatedProtocol.useTLS13PlusSpec()) {
+                verifyDataLen =
+                        context.negotiatedCipherSuite.hashAlg.hashLength;
+            }
+
+            if (m.remaining() != verifyDataLen) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Inappropriate finished message: need " + verifyDataLen +
+                    " but remaining " + m.remaining() + " bytes verify_data");
+            }
+
+            this.verifyData = new byte[verifyDataLen];
+            m.get(verifyData);
+
+            VerifyDataScheme vd =
+                    VerifyDataScheme.valueOf(context.negotiatedProtocol);
+            byte[] myVerifyData;
+            try {
+                myVerifyData = vd.createVerifyData(context, true);
+            } catch (IOException ioe) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Failed to generate verify_data", ioe);
+                return;     // make the compiler happy
+            }
+            if (!MessageDigest.isEqual(myVerifyData, verifyData)) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "The Finished message cannot be verified.");
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.FINISHED;
+        }
+
+        @Override
+        public int messageLength() {
+            return verifyData.length;
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.write(verifyData);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"Finished\": '{'\n" +
+                    "  \"verify data\": '{'\n" +
+                    "{0}\n" +
+                    "  '}'" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                    Utilities.indent(hexEncoder.encode(verifyData), "    "),
+                };
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    interface VerifyDataGenerator {
+        byte[] createVerifyData(HandshakeContext context,
+                boolean isValidation) throws IOException;
+    }
+
+    enum VerifyDataScheme {
+        SSL30       ("kdf_ssl30", new S30VerifyDataGenerator()),
+        TLS10       ("kdf_tls10", new T10VerifyDataGenerator()),
+        TLS12       ("kdf_tls12", new T12VerifyDataGenerator()),
+        TLS13       ("kdf_tls13", new T13VerifyDataGenerator());
+
+        final String name;
+        final VerifyDataGenerator generator;
+
+        VerifyDataScheme(String name, VerifyDataGenerator verifyDataGenerator) {
+            this.name = name;
+            this.generator = verifyDataGenerator;
+        }
+
+        static VerifyDataScheme valueOf(ProtocolVersion protocolVersion) {
+            switch (protocolVersion) {
+                case SSL30:
+                    return VerifyDataScheme.SSL30;
+                case TLS10:
+                case TLS11:
+                case DTLS10:
+                    return VerifyDataScheme.TLS10;
+                case TLS12:
+                case DTLS12:
+                    return VerifyDataScheme.TLS12;
+                case TLS13:
+                    return VerifyDataScheme.TLS13;
+                default:
+                    return null;
+            }
+        }
+
+        public byte[] createVerifyData(HandshakeContext context,
+                boolean isValidation) throws IOException {
+            if (generator != null) {
+                return generator.createVerifyData(context, isValidation);
+            }
+
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    // SSL 3.0
+    private static final
+            class S30VerifyDataGenerator implements VerifyDataGenerator {
+        @Override
+        public byte[] createVerifyData(HandshakeContext context,
+                boolean isValidation) throws IOException {
+            HandshakeHash handshakeHash = context.handshakeHash;
+            SecretKey masterSecretKey =
+                    context.handshakeSession.getMasterSecret();
+
+            boolean useClientLabel =
+                    (context.sslConfig.isClientMode && !isValidation) ||
+                    (!context.sslConfig.isClientMode && isValidation);
+            return handshakeHash.digest(useClientLabel, masterSecretKey);
+        }
+    }
+
+    // TLS 1.0, TLS 1.1, DTLS 1.0
+    private static final
+            class T10VerifyDataGenerator implements VerifyDataGenerator {
+        @Override
+        public byte[] createVerifyData(HandshakeContext context,
+                boolean isValidation) throws IOException {
+            HandshakeHash handshakeHash = context.handshakeHash;
+            SecretKey masterSecretKey =
+                    context.handshakeSession.getMasterSecret();
+
+            boolean useClientLabel =
+                    (context.sslConfig.isClientMode && !isValidation) ||
+                    (!context.sslConfig.isClientMode && isValidation);
+            String tlsLabel;
+            if (useClientLabel) {
+                tlsLabel = "client finished";
+            } else {
+                tlsLabel = "server finished";
+            }
+
+            try {
+                byte[] seed = handshakeHash.digest();
+                String prfAlg = "SunTlsPrf";
+                HashAlg hashAlg = H_NONE;
+
+                /*
+                 * RFC 5246/7.4.9 says that finished messages can
+                 * be ciphersuite-specific in both length/PRF hash
+                 * algorithm.  If we ever run across a different
+                 * length, this call will need to be updated.
+                 */
+                @SuppressWarnings("deprecation")
+                TlsPrfParameterSpec spec = new TlsPrfParameterSpec(
+                    masterSecretKey, tlsLabel, seed, 12,
+                    hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
+                KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg);
+                kg.init(spec);
+                SecretKey prfKey = kg.generateKey();
+                if (!"RAW".equals(prfKey.getFormat())) {
+                    throw new ProviderException(
+                        "Invalid PRF output, format must be RAW. " +
+                        "Format received: " + prfKey.getFormat());
+                }
+                byte[] finished = prfKey.getEncoded();
+                return finished;
+            } catch (GeneralSecurityException e) {
+                throw new RuntimeException("PRF failed", e);
+            }
+        }
+    }
+
+    // TLS 1.2
+    private static final
+            class T12VerifyDataGenerator implements VerifyDataGenerator {
+        @Override
+        public byte[] createVerifyData(HandshakeContext context,
+                boolean isValidation) throws IOException {
+            CipherSuite cipherSuite = context.negotiatedCipherSuite;
+            HandshakeHash handshakeHash = context.handshakeHash;
+            SecretKey masterSecretKey =
+                    context.handshakeSession.getMasterSecret();
+
+            boolean useClientLabel =
+                    (context.sslConfig.isClientMode && !isValidation) ||
+                    (!context.sslConfig.isClientMode && isValidation);
+            String tlsLabel;
+            if (useClientLabel) {
+                tlsLabel = "client finished";
+            } else {
+                tlsLabel = "server finished";
+            }
+
+            try {
+                byte[] seed = handshakeHash.digest();
+                String prfAlg = "SunTls12Prf";
+                HashAlg hashAlg = cipherSuite.hashAlg;
+
+                /*
+                 * RFC 5246/7.4.9 says that finished messages can
+                 * be ciphersuite-specific in both length/PRF hash
+                 * algorithm.  If we ever run across a different
+                 * length, this call will need to be updated.
+                 */
+                @SuppressWarnings("deprecation")
+                TlsPrfParameterSpec spec = new TlsPrfParameterSpec(
+                    masterSecretKey, tlsLabel, seed, 12,
+                    hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
+                KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg);
+                kg.init(spec);
+                SecretKey prfKey = kg.generateKey();
+                if (!"RAW".equals(prfKey.getFormat())) {
+                    throw new ProviderException(
+                        "Invalid PRF output, format must be RAW. " +
+                        "Format received: " + prfKey.getFormat());
+                }
+                byte[] finished = prfKey.getEncoded();
+                return finished;
+            } catch (GeneralSecurityException e) {
+                throw new RuntimeException("PRF failed", e);
+            }
+        }
+    }
+
+    // TLS 1.2
+    private static final
+            class T13VerifyDataGenerator implements VerifyDataGenerator {
+        private static final byte[] hkdfLabel = "tls13 finished".getBytes();
+        private static final byte[] hkdfContext = new byte[0];
+
+        @Override
+        public byte[] createVerifyData(HandshakeContext context,
+                boolean isValidation) throws IOException {
+            // create finished secret key
+            HashAlg hashAlg =
+                    context.negotiatedCipherSuite.hashAlg;
+            SecretKey secret = isValidation ?
+                    context.baseReadSecret : context.baseWriteSecret;
+            SSLBasicKeyDerivation kdf = new SSLBasicKeyDerivation(
+                    secret, hashAlg.name,
+                    hkdfLabel, hkdfContext, hashAlg.hashLength);
+            AlgorithmParameterSpec keySpec =
+                    new SecretSizeSpec(hashAlg.hashLength);
+            SecretKey finishedSecret =
+                    kdf.deriveKey("TlsFinishedSecret", keySpec);
+
+            String hmacAlg =
+                "Hmac" + hashAlg.name.replace("-", "");
+            try {
+                Mac hmac = JsseJce.getMac(hmacAlg);
+                hmac.init(finishedSecret);
+                return hmac.doFinal(context.handshakeHash.digest());
+            } catch (NoSuchAlgorithmException |InvalidKeyException ex) {
+                throw new ProviderException(
+                        "Failed to generate verify_data", ex);
+            }
+        }
+    }
+
+    /**
+     * The "Finished" handshake message producer.
+     */
+    private static final
+            class T12FinishedProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T12FinishedProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+            if (hc.sslConfig.isClientMode) {
+                return onProduceFinished(
+                        (ClientHandshakeContext)context, message);
+            } else {
+                return onProduceFinished(
+                        (ServerHandshakeContext)context, message);
+            }
+        }
+
+        private byte[] onProduceFinished(ClientHandshakeContext chc,
+                HandshakeMessage message) throws IOException {
+            // Refresh handshake hash
+            chc.handshakeHash.update();
+
+            FinishedMessage fm = new FinishedMessage(chc);
+
+            // Change write cipher and delivery ChangeCipherSpec message.
+            ChangeCipherSpec.t10Producer.produce(chc, message);
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced client Finished handshake message", fm);
+            }
+
+            // Output the handshake message.
+            fm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            /*
+             * save server verify data for secure renegotiation
+             */
+            if (chc.conContext.secureRenegotiation) {
+                chc.conContext.clientVerifyData = fm.verifyData;
+            }
+
+            // update the consumers and producers
+            if (!chc.isResumption) {
+                chc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id,
+                        ChangeCipherSpec.t10Consumer);
+                chc.handshakeConsumers.put(
+                        SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
+                chc.conContext.inputRecord.expectingFinishFlight();
+            } else {
+                if (chc.handshakeSession.isRejoinable()) {
+                    ((SSLSessionContextImpl)chc.sslContext.
+                        engineGetClientSessionContext()).put(
+                            chc.handshakeSession);
+                }
+                chc.conContext.conSession = chc.handshakeSession.finish();
+                chc.conContext.protocolVersion = chc.negotiatedProtocol;
+
+                // handshake context cleanup.
+                chc.handshakeFinished = true;
+
+                // May need to retransmit the last flight for DTLS.
+                if (!chc.sslContext.isDTLS()) {
+                    chc.conContext.finishHandshake();
+                }
+            }
+
+            // The handshake message has been delivered.
+            return null;
+        }
+
+        private byte[] onProduceFinished(ServerHandshakeContext shc,
+                HandshakeMessage message) throws IOException {
+            // Refresh handshake hash
+            shc.handshakeHash.update();
+
+            FinishedMessage fm = new FinishedMessage(shc);
+
+            // Change write cipher and delivery ChangeCipherSpec message.
+            ChangeCipherSpec.t10Producer.produce(shc, message);
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced server Finished handshake message", fm);
+            }
+
+            // Output the handshake message.
+            fm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            /*
+             * save client verify data for secure renegotiation
+             */
+            if (shc.conContext.secureRenegotiation) {
+                shc.conContext.serverVerifyData = fm.verifyData;
+            }
+
+            // update the consumers and producers
+            if (shc.isResumption) {
+                shc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id,
+                        ChangeCipherSpec.t10Consumer);
+                shc.handshakeConsumers.put(
+                        SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
+                shc.conContext.inputRecord.expectingFinishFlight();
+            } else {
+                if (shc.handshakeSession.isRejoinable()) {
+                    ((SSLSessionContextImpl)shc.sslContext.
+                        engineGetServerSessionContext()).put(
+                            shc.handshakeSession);
+                }
+                shc.conContext.conSession = shc.handshakeSession.finish();
+                shc.conContext.protocolVersion = shc.negotiatedProtocol;
+
+                // handshake context cleanup.
+                shc.handshakeFinished = true;
+
+                // May need to retransmit the last flight for DTLS.
+                if (!shc.sslContext.isDTLS()) {
+                    shc.conContext.finishHandshake();
+                }
+            }
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "Finished" handshake message consumer.
+     */
+    private static final class T12FinishedConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T12FinishedConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+
+            // This consumer can be used only once.
+            hc.handshakeConsumers.remove(SSLHandshake.FINISHED.id);
+
+            // We should not be processing finished messages unless
+            // we have received ChangeCipherSpec
+            if (hc.conContext.consumers.containsKey(
+                    ContentType.CHANGE_CIPHER_SPEC.id)) {
+                hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Missing ChangeCipherSpec message");
+            }
+
+            if (hc.sslConfig.isClientMode) {
+                onConsumeFinished((ClientHandshakeContext)context, message);
+            } else {
+                onConsumeFinished((ServerHandshakeContext)context, message);
+            }
+        }
+
+        private void onConsumeFinished(ClientHandshakeContext chc,
+                ByteBuffer message) throws IOException {
+            FinishedMessage fm = new FinishedMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming server Finished handshake message", fm);
+            }
+
+            if (chc.conContext.secureRenegotiation) {
+                chc.conContext.serverVerifyData = fm.verifyData;
+            }
+
+            if (!chc.isResumption) {
+                if (chc.handshakeSession.isRejoinable()) {
+                    ((SSLSessionContextImpl)chc.sslContext.
+                        engineGetClientSessionContext()).put(
+                            chc.handshakeSession);
+                }
+                chc.conContext.conSession = chc.handshakeSession.finish();
+                chc.conContext.protocolVersion = chc.negotiatedProtocol;
+
+                // handshake context cleanup.
+                chc.handshakeFinished = true;
+
+                // May need to retransmit the last flight for DTLS.
+                if (!chc.sslContext.isDTLS()) {
+                    chc.conContext.finishHandshake();
+                }
+            } else {
+                chc.handshakeProducers.put(SSLHandshake.FINISHED.id,
+                        SSLHandshake.FINISHED);
+            }
+
+            //
+            // produce
+            //
+            SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] {
+                SSLHandshake.FINISHED
+            };
+
+            for (SSLHandshake hs : probableHandshakeMessages) {
+                HandshakeProducer handshakeProducer =
+                        chc.handshakeProducers.remove(hs.id);
+                if (handshakeProducer != null) {
+                    handshakeProducer.produce(chc, fm);
+                }
+            }
+        }
+
+        private void onConsumeFinished(ServerHandshakeContext shc,
+                ByteBuffer message) throws IOException {
+            FinishedMessage fm = new FinishedMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming client Finished handshake message", fm);
+            }
+
+            if (shc.conContext.secureRenegotiation) {
+                shc.conContext.clientVerifyData = fm.verifyData;
+            }
+
+            if (shc.isResumption) {
+                if (shc.handshakeSession.isRejoinable()) {
+                    ((SSLSessionContextImpl)shc.sslContext.
+                        engineGetServerSessionContext()).put(
+                            shc.handshakeSession);
+                }
+                shc.conContext.conSession = shc.handshakeSession.finish();
+                shc.conContext.protocolVersion = shc.negotiatedProtocol;
+
+                // handshake context cleanup.
+                shc.handshakeFinished = true;
+
+                // May need to retransmit the last flight for DTLS.
+                if (!shc.sslContext.isDTLS()) {
+                    shc.conContext.finishHandshake();
+                }
+            } else {
+                shc.handshakeProducers.put(SSLHandshake.FINISHED.id,
+                        SSLHandshake.FINISHED);
+            }
+
+            //
+            // produce
+            //
+            SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] {
+                SSLHandshake.FINISHED
+            };
+
+            for (SSLHandshake hs : probableHandshakeMessages) {
+                HandshakeProducer handshakeProducer =
+                        shc.handshakeProducers.remove(hs.id);
+                if (handshakeProducer != null) {
+                    handshakeProducer.produce(shc, fm);
+                }
+            }
+        }
+    }
+
+    /**
+     * The "Finished" handshake message producer.
+     */
+    private static final
+            class T13FinishedProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T13FinishedProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+            if (hc.sslConfig.isClientMode) {
+                return onProduceFinished(
+                        (ClientHandshakeContext)context, message);
+            } else {
+                return onProduceFinished(
+                        (ServerHandshakeContext)context, message);
+            }
+        }
+
+        private byte[] onProduceFinished(ClientHandshakeContext chc,
+                HandshakeMessage message) throws IOException {
+            // Refresh handshake hash
+            chc.handshakeHash.update();
+
+            FinishedMessage fm = new FinishedMessage(chc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced client Finished handshake message", fm);
+            }
+
+            // Output the handshake message.
+            fm.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // save server verify data for secure renegotiation
+            if (chc.conContext.secureRenegotiation) {
+                chc.conContext.clientVerifyData = fm.verifyData;
+            }
+
+            // update the context
+            // Change client/server application traffic secrets.
+            SSLKeyDerivation kd = chc.handshakeKeyDerivation;
+            if (kd == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "no key derivation");
+                return null;    // make the compiler happy
+            }
+
+            SSLTrafficKeyDerivation kdg =
+                    SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+            if (kdg == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                        chc.negotiatedProtocol);
+                return null;    // make the compiler happy
+            }
+
+            try {
+                // update the application traffic read keys.
+                SecretKey writeSecret = kd.deriveKey(
+                        "TlsClientAppTrafficSecret", null);
+
+                SSLKeyDerivation writeKD =
+                        kdg.createKeyDerivation(chc, writeSecret);
+                SecretKey writeKey = writeKD.deriveKey(
+                        "TlsKey", null);
+                SecretKey writeIvSecret = writeKD.deriveKey(
+                        "TlsIv", null);
+                IvParameterSpec writeIv =
+                        new IvParameterSpec(writeIvSecret.getEncoded());
+                SSLWriteCipher writeCipher =
+                        chc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
+                                Authenticator.valueOf(chc.negotiatedProtocol),
+                                chc.negotiatedProtocol, writeKey, writeIv,
+                                chc.sslContext.getSecureRandom());
+
+                chc.baseWriteSecret = writeSecret;
+                chc.conContext.outputRecord.changeWriteCiphers(
+                        writeCipher, false);
+
+            } catch (GeneralSecurityException gse) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failure to derive application secrets", gse);
+                return null;    // make the compiler happy
+            }
+
+            // The resumption master secret is stored in the session so
+            // it can be used after the handshake is completed.
+            SSLSecretDerivation sd = ((SSLSecretDerivation) kd).forContext(chc);
+            SecretKey resumptionMasterSecret = sd.deriveKey(
+            "TlsResumptionMasterSecret", null);
+            chc.handshakeSession.setResumptionMasterSecret(resumptionMasterSecret);
+
+            chc.conContext.conSession = chc.handshakeSession.finish();
+            chc.conContext.protocolVersion = chc.negotiatedProtocol;
+
+            // handshake context cleanup.
+            chc.handshakeFinished = true;
+            chc.conContext.finishHandshake();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+
+        private byte[] onProduceFinished(ServerHandshakeContext shc,
+                HandshakeMessage message) throws IOException {
+            // Refresh handshake hash
+            shc.handshakeHash.update();
+
+            FinishedMessage fm = new FinishedMessage(shc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced server Finished handshake message", fm);
+            }
+
+            // Output the handshake message.
+            fm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // Change client/server application traffic secrets.
+            SSLKeyDerivation kd = shc.handshakeKeyDerivation;
+            if (kd == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "no key derivation");
+                return null;    // make the compiler happy
+            }
+
+            SSLTrafficKeyDerivation kdg =
+                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+            if (kdg == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                        shc.negotiatedProtocol);
+                return null;    // make the compiler happy
+            }
+
+            // derive salt secret
+            try {
+                SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
+
+                // derive application secrets
+                HashAlg hashAlg = shc.negotiatedCipherSuite.hashAlg;
+                HKDF hkdf = new HKDF(hashAlg.name);
+                byte[] zeros = new byte[hashAlg.hashLength];
+                SecretKeySpec sharedSecret =
+                        new SecretKeySpec(zeros, "TlsZeroSecret");
+                SecretKey masterSecret =
+                    hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret");
+
+                SSLKeyDerivation secretKD =
+                        new SSLSecretDerivation(shc, masterSecret);
+
+                // update the handshake traffic write keys.
+                SecretKey writeSecret = secretKD.deriveKey(
+                        "TlsServerAppTrafficSecret", null);
+                SSLKeyDerivation writeKD =
+                        kdg.createKeyDerivation(shc, writeSecret);
+                SecretKey writeKey = writeKD.deriveKey(
+                        "TlsKey", null);
+                SecretKey writeIvSecret = writeKD.deriveKey(
+                        "TlsIv", null);
+                IvParameterSpec writeIv =
+                        new IvParameterSpec(writeIvSecret.getEncoded());
+                SSLWriteCipher writeCipher =
+                        shc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
+                                Authenticator.valueOf(shc.negotiatedProtocol),
+                                shc.negotiatedProtocol, writeKey, writeIv,
+                                shc.sslContext.getSecureRandom());
+
+                shc.baseWriteSecret = writeSecret;
+                shc.conContext.outputRecord.changeWriteCiphers(
+                        writeCipher, false);
+
+                // update the context for the following key derivation
+                shc.handshakeKeyDerivation = secretKD;
+            } catch (GeneralSecurityException gse) {
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failure to derive application secrets", gse);
+                return null;    // make the compiler happy
+            }
+
+            /*
+             * save client verify data for secure renegotiation
+             */
+            if (shc.conContext.secureRenegotiation) {
+                shc.conContext.serverVerifyData = fm.verifyData;
+            }
+
+            // update the context
+            shc.handshakeConsumers.put(
+                    SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "Finished" handshake message consumer.
+     */
+    private static final class T13FinishedConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private T13FinishedConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in handshake context only.
+            HandshakeContext hc = (HandshakeContext)context;
+            if (hc.sslConfig.isClientMode) {
+                onConsumeFinished(
+                        (ClientHandshakeContext)context, message);
+            } else {
+                onConsumeFinished(
+                        (ServerHandshakeContext)context, message);
+            }
+        }
+
+        private void onConsumeFinished(ClientHandshakeContext chc,
+                ByteBuffer message) throws IOException {
+            FinishedMessage fm = new FinishedMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming server Finished handshake message", fm);
+            }
+
+            // Save client verify data for secure renegotiation.
+            if (chc.conContext.secureRenegotiation) {
+                chc.conContext.serverVerifyData = fm.verifyData;
+            }
+
+            //
+            // validate
+            //
+            // blank
+
+            //
+            // update
+            //
+            // A change_cipher_spec record received after the peer's Finished
+            // message MUST be treated as an unexpected record type.
+            chc.conContext.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
+
+            // Change client/server application traffic secrets.
+            // Refresh handshake hash
+            chc.handshakeHash.update();
+            SSLKeyDerivation kd = chc.handshakeKeyDerivation;
+            if (kd == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "no key derivation");
+                return;    // make the compiler happy
+            }
+
+            SSLTrafficKeyDerivation kdg =
+                    SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+            if (kdg == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                        chc.negotiatedProtocol);
+                return;    // make the compiler happy
+            }
+
+            // save the session
+            if (!chc.isResumption && chc.handshakeSession.isRejoinable()) {
+                SSLSessionContextImpl sessionContext = (SSLSessionContextImpl)
+                chc.sslContext.engineGetClientSessionContext();
+                sessionContext.put(chc.handshakeSession);
+            }
+
+            // derive salt secret
+            try {
+                SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
+
+                // derive application secrets
+                HashAlg hashAlg = chc.negotiatedCipherSuite.hashAlg;
+                HKDF hkdf = new HKDF(hashAlg.name);
+                byte[] zeros = new byte[hashAlg.hashLength];
+                SecretKeySpec sharedSecret =
+                        new SecretKeySpec(zeros, "TlsZeroSecret");
+                SecretKey masterSecret =
+                    hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret");
+
+                SSLKeyDerivation secretKD =
+                        new SSLSecretDerivation(chc, masterSecret);
+
+                // update the handshake traffic read keys.
+                SecretKey readSecret = secretKD.deriveKey(
+                        "TlsServerAppTrafficSecret", null);
+                SSLKeyDerivation writeKD =
+                        kdg.createKeyDerivation(chc, readSecret);
+                SecretKey readKey = writeKD.deriveKey(
+                        "TlsKey", null);
+                SecretKey readIvSecret = writeKD.deriveKey(
+                        "TlsIv", null);
+                IvParameterSpec readIv =
+                        new IvParameterSpec(readIvSecret.getEncoded());
+                SSLReadCipher readCipher =
+                        chc.negotiatedCipherSuite.bulkCipher.createReadCipher(
+                                Authenticator.valueOf(chc.negotiatedProtocol),
+                                chc.negotiatedProtocol, readKey, readIv,
+                                chc.sslContext.getSecureRandom());
+
+                chc.baseReadSecret = readSecret;
+                chc.conContext.inputRecord.changeReadCiphers(readCipher);
+
+                // update the context for the following key derivation
+                chc.handshakeKeyDerivation = secretKD;
+            } catch (GeneralSecurityException gse) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failure to derive application secrets", gse);
+                return;    // make the compiler happy
+            }
+
+            //
+            // produce
+            //
+            chc.handshakeProducers.put(SSLHandshake.FINISHED.id,
+                        SSLHandshake.FINISHED);
+            SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] {
+                // full handshake messages
+                SSLHandshake.CERTIFICATE,
+                SSLHandshake.CERTIFICATE_VERIFY,
+                SSLHandshake.FINISHED
+            };
+
+            for (SSLHandshake hs : probableHandshakeMessages) {
+                HandshakeProducer handshakeProducer =
+                        chc.handshakeProducers.remove(hs.id);
+                if (handshakeProducer != null) {
+                    handshakeProducer.produce(chc, null);
+                }
+            }
+        }
+
+        private void onConsumeFinished(ServerHandshakeContext shc,
+                ByteBuffer message) throws IOException {
+            FinishedMessage fm = new FinishedMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming client Finished handshake message", fm);
+            }
+
+            if (shc.conContext.secureRenegotiation) {
+                shc.conContext.clientVerifyData = fm.verifyData;
+            }
+
+            //
+            // validate
+            //
+            // blank
+
+            //
+            // update
+            //
+            // Change client/server application traffic secrets.
+            SSLKeyDerivation kd = shc.handshakeKeyDerivation;
+            if (kd == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "no key derivation");
+                return;    // make the compiler happy
+            }
+
+            SSLTrafficKeyDerivation kdg =
+                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+            if (kdg == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                        shc.negotiatedProtocol);
+                return;    // make the compiler happy
+            }
+
+            // save the session
+            if (!shc.isResumption && shc.handshakeSession.isRejoinable()) {
+                SSLSessionContextImpl sessionContext = (SSLSessionContextImpl)
+                shc.sslContext.engineGetServerSessionContext();
+                sessionContext.put(shc.handshakeSession);
+            }
+
+            try {
+                // update the application traffic read keys.
+                SecretKey readSecret = kd.deriveKey(
+                        "TlsClientAppTrafficSecret", null);
+
+                SSLKeyDerivation readKD =
+                        kdg.createKeyDerivation(shc, readSecret);
+                SecretKey readKey = readKD.deriveKey(
+                        "TlsKey", null);
+                SecretKey readIvSecret = readKD.deriveKey(
+                        "TlsIv", null);
+                IvParameterSpec readIv =
+                        new IvParameterSpec(readIvSecret.getEncoded());
+                SSLReadCipher readCipher =
+                        shc.negotiatedCipherSuite.bulkCipher.createReadCipher(
+                                Authenticator.valueOf(shc.negotiatedProtocol),
+                                shc.negotiatedProtocol, readKey, readIv,
+                                shc.sslContext.getSecureRandom());
+
+                shc.baseReadSecret = readSecret;
+                shc.conContext.inputRecord.changeReadCiphers(readCipher);
+
+                // The resumption master secret is stored in the session so
+                // it can be used after the handshake is completed.
+                shc.handshakeHash.update();
+                SSLSecretDerivation sd = ((SSLSecretDerivation)kd).forContext(shc);
+                SecretKey resumptionMasterSecret = sd.deriveKey(
+                "TlsResumptionMasterSecret", null);
+                shc.handshakeSession.setResumptionMasterSecret(resumptionMasterSecret);
+            } catch (GeneralSecurityException gse) {
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failure to derive application secrets", gse);
+                return;    // make the compiler happy
+            }
+
+            //  update connection context
+            shc.conContext.conSession = shc.handshakeSession.finish();
+            shc.conContext.protocolVersion = shc.negotiatedProtocol;
+
+            // handshake context cleanup.
+            shc.handshakeFinished = true;
+
+            // May need to retransmit the last flight for DTLS.
+            if (!shc.sslContext.isDTLS()) {
+                shc.conContext.finishHandshake();
+            }
+
+            //
+            // produce
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                "Sending new session ticket");
+            }
+            NewSessionTicket.kickstartProducer.produce(shc);
+
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/HKDF.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.security.NoSuchAlgorithmException;
+import java.security.InvalidKeyException;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.Objects;
+
+/**
+ * An implementation of the HKDF key derivation algorithm outlined in RFC 5869,
+ * specific to the needs of TLS 1.3 key derivation in JSSE.  This is not a
+ * general purpose HKDF implementation and is suited only to single-key output
+ * derivations.
+ *
+ * HKDF objects are created by specifying a message digest algorithm.  That
+ * digest algorithm will be used by the HMAC function as part of the HKDF
+ * derivation process.
+ */
+final class HKDF {
+    private final String hmacAlg;
+    private final Mac hmacObj;
+    private final int hmacLen;
+
+    /**
+     * Create an HDKF object, specifying the underlying message digest
+     * algorithm.
+     *
+     * @param hashAlg a standard name corresponding to a supported message
+     * digest algorithm.
+     *
+     * @throws NoSuchAlgorithmException if that message digest algorithm does
+     * not have an HMAC variant supported on any available provider.
+     */
+    HKDF(String hashAlg) throws NoSuchAlgorithmException {
+        Objects.requireNonNull(hashAlg,
+                "Must provide underlying HKDF Digest algorithm.");
+        hmacAlg = "Hmac" + hashAlg.replace("-", "");
+        hmacObj = JsseJce.getMac(hmacAlg);
+        hmacLen = hmacObj.getMacLength();
+    }
+
+    /**
+     * Perform the HMAC-Extract derivation.
+     *
+     * @param salt a salt value, implemented as a {@code SecretKey}.  A
+     * {@code null} value is allowed, which will internally use an array of
+     * zero bytes the same size as the underlying hash output length.
+     * @param inputKey the input keying material provided as a
+     * {@code SecretKey}.
+     * @param keyAlg the algorithm name assigned to the resulting
+     * {@code SecretKey} object.
+     *
+     * @return a {@code SecretKey} that is the result of the HKDF extract
+     * operation.
+     *
+     * @throws InvalidKeyException if the {@code salt} parameter cannot be
+     * used to initialize the underlying HMAC.
+     */
+    SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
+            throws InvalidKeyException {
+        if (salt == null) {
+            salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt");
+        }
+        hmacObj.init(salt);
+
+        return new SecretKeySpec(hmacObj.doFinal(inputKey.getEncoded()),
+                keyAlg);
+    }
+
+    /**
+     * Perform the HMAC-Extract derivation.
+     *
+     * @param salt a salt value as cleartext bytes.  A {@code null} value is
+     * allowed, which will internally use an array of zero bytes the same
+     * size as the underlying hash output length.
+     * @param inputKey the input keying material provided as a
+     * {@code SecretKey}.
+     * @param keyAlg the algorithm name assigned to the resulting
+     * {@code SecretKey} object.
+     *
+     * @return a {@code SecretKey} that is the result of the HKDF extract
+     * operation.
+     *
+     * @throws InvalidKeyException if the {@code salt} parameter cannot be
+     * used to initialize the underlying HMAC.
+     */
+    SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
+            throws InvalidKeyException {
+        if (salt == null) {
+            salt = new byte[hmacLen];
+        }
+        return extract(new SecretKeySpec(salt, "HKDF-Salt"), inputKey, keyAlg);
+    }
+
+    /**
+     * Perform the HKDF-Expand derivation for a single-key output.
+     *
+     * @param pseudoRandKey the pseudo random key (PRK).
+     * @param info optional context-specific info.  A {@code null} value is
+     * allowed in which case a zero-length byte array will be used.
+     * @param outLen the length of the resulting {@code SecretKey}
+     * @param keyAlg the algorithm name applied to the resulting
+     * {@code SecretKey}
+     *
+     * @return the resulting key derivation as a {@code SecretKey} object
+     *
+     * @throws InvalidKeyException if the underlying HMAC operation cannot
+     * be initialized using the provided {@code pseudoRandKey} object.
+     */
+    SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
+            String keyAlg) throws InvalidKeyException {
+        byte[] kdfOutput;
+
+        // Calculate the number of rounds of HMAC that are needed to
+        // meet the requested data.  Then set up the buffers we will need.
+        Objects.requireNonNull(pseudoRandKey, "A null PRK is not allowed.");
+
+        // Output from the expand operation must be <= 255 * hmac length
+        if (outLen > 255 * hmacLen) {
+            throw new IllegalArgumentException("Requested output length " +
+                    "exceeds maximum length allowed for HKDF expansion");
+        }
+        hmacObj.init(pseudoRandKey);
+        if (info == null) {
+            info = new byte[0];
+        }
+        int rounds = (outLen + hmacLen - 1) / hmacLen;
+        kdfOutput = new byte[rounds * hmacLen];
+        int offset = 0;
+        int tLength = 0;
+
+        for (int i = 0; i < rounds ; i++) {
+
+            // Calculate this round
+            try {
+                 // Add T(i).  This will be an empty string on the first
+                 // iteration since tLength starts at zero.  After the first
+                 // iteration, tLength is changed to the HMAC length for the
+                 // rest of the loop.
+                hmacObj.update(kdfOutput,
+                        Math.max(0, offset - hmacLen), tLength);
+                hmacObj.update(info);                       // Add info
+                hmacObj.update((byte)(i + 1));              // Add round number
+                hmacObj.doFinal(kdfOutput, offset);
+
+                tLength = hmacLen;
+                offset += hmacLen;                       // For next iteration
+            } catch (ShortBufferException sbe) {
+                // This really shouldn't happen given that we've
+                // sized the buffers to their largest possible size up-front,
+                // but just in case...
+                throw new RuntimeException(sbe);
+            }
+        }
+
+        return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeAbsence.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Interface for handshake message or extension absence on handshake
+ * message processing.
+ *
+ * This is typically used after the SSLSession object created, so that the
+ * extension can update/impact the session object.
+ */
+interface HandshakeAbsence {
+    void absent(ConnectionContext context,
+            HandshakeMessage message) throws IOException;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeConsumer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+interface HandshakeConsumer {
+    // message: the handshake message to be consumed.
+    void consume(ConnectionContext context,
+            HandshakeMessage message) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.AlgorithmConstraints;
+import java.security.CryptoPrimitive;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeMode;
+
+abstract class HandshakeContext implements ConnectionContext {
+    // System properties
+
+    // By default, disable the unsafe legacy session renegotiation.
+    static final boolean allowUnsafeRenegotiation =
+            Utilities.getBooleanProperty(
+                    "sun.security.ssl.allowUnsafeRenegotiation", false);
+
+    // For maximum interoperability and backward compatibility, RFC 5746
+    // allows server (or client) to accept ClientHello (or ServerHello)
+    // message without the secure renegotiation_info extension or SCSV.
+    //
+    // For maximum security, RFC 5746 also allows server (or client) to
+    // reject such message with a fatal "handshake_failure" alert.
+    //
+    // By default, allow such legacy hello messages.
+    static final boolean allowLegacyHelloMessages =
+            Utilities.getBooleanProperty(
+                    "sun.security.ssl.allowLegacyHelloMessages", true);
+
+    // registered handshake message actors
+    LinkedHashMap<Byte, SSLConsumer>  handshakeConsumers;
+    final HashMap<Byte, HandshakeProducer>  handshakeProducers;
+
+    // context
+    final SSLContextImpl                    sslContext;
+    final TransportContext                  conContext;
+    final SSLConfiguration                  sslConfig;
+
+    // consolidated parameters
+    final List<ProtocolVersion>             activeProtocols;
+    final List<CipherSuite>                 activeCipherSuites;
+    final AlgorithmConstraints              algorithmConstraints;
+    final ProtocolVersion                   maximumActiveProtocol;
+
+    // output stream
+    final HandshakeOutStream                handshakeOutput;
+
+    // handshake transcript hash
+    final HandshakeHash                     handshakeHash;
+
+    // negotiated security parameters
+    SSLSessionImpl                          handshakeSession;
+    boolean                                 handshakeFinished;
+    // boolean                                 isInvalidated;
+
+    boolean                                 kickstartMessageDelivered;
+
+    // Resumption
+    boolean                                 isResumption;
+    SSLSessionImpl                          resumingSession;
+
+    final Queue<Map.Entry<Byte, ByteBuffer>> delegatedActions;
+    volatile boolean                        taskDelegated = false;
+    volatile Exception                      delegatedThrown = null;
+
+    ProtocolVersion                         negotiatedProtocol;
+    CipherSuite                             negotiatedCipherSuite;
+    final List<SSLPossession>               handshakePossessions;
+    final List<SSLCredentials>              handshakeCredentials;
+    SSLKeyDerivation                        handshakeKeyDerivation;
+    SSLKeyExchange                          handshakeKeyExchange;
+    SecretKey                               baseReadSecret;
+    SecretKey                               baseWriteSecret;
+
+    // protocol version being established
+    int                                     clientHelloVersion;
+    String                                  applicationProtocol;
+
+    RandomCookie                            clientHelloRandom;
+    RandomCookie                            serverHelloRandom;
+    byte[]                                  certRequestContext;
+
+    ////////////////////
+    // Extensions
+
+    // the extensions used in the handshake
+    final Map<SSLExtension, SSLExtension.SSLExtensionSpec>
+                                            handshakeExtensions;
+
+    // MaxFragmentLength
+    int                                     maxFragmentLength;
+
+    // SignatureScheme
+    List<SignatureScheme>                   localSupportedSignAlgs;
+    List<SignatureScheme>                   peerRequestedSignatureSchemes;
+    List<SignatureScheme>                   peerRequestedCertSignSchemes;
+
+    // SupportedGroups
+    List<NamedGroup>                        clientRequestedNamedGroups;
+
+    // HelloRetryRequest
+    NamedGroup                              serverSelectedNamedGroup;
+
+    // if server name indicator is negotiated
+    //
+    // May need a public API for the indication in the future.
+    List<SNIServerName>                     requestedServerNames;
+    SNIServerName                           negotiatedServerName;
+
+    // OCSP Stapling info
+    boolean                                 staplingActive = false;
+
+    protected HandshakeContext(SSLContextImpl sslContext,
+            TransportContext conContext) throws IOException {
+        this.sslContext = sslContext;
+        this.conContext = conContext;
+        this.sslConfig = (SSLConfiguration)conContext.sslConfig.clone();
+
+        this.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols,
+                sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
+        if (activeProtocols.isEmpty()) {
+            throw new SSLHandshakeException(
+                "No appropriate protocol (protocol is disabled or " +
+                "cipher suites are inappropriate)");
+        }
+
+        ProtocolVersion maximumVersion = ProtocolVersion.NONE;
+        for (ProtocolVersion pv : this.activeProtocols) {
+            if (maximumVersion == ProtocolVersion.NONE ||
+                    pv.compare(maximumVersion) > 0) {
+                maximumVersion = pv;
+            }
+        }
+        this.maximumActiveProtocol = maximumVersion;
+        this.activeCipherSuites = getActiveCipherSuites(this.activeProtocols,
+                sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
+        if (activeCipherSuites.isEmpty()) {
+            throw new SSLHandshakeException("No appropriate cipher suite");
+        }
+        this.algorithmConstraints =
+                new SSLAlgorithmConstraints(sslConfig.algorithmConstraints);
+
+        this.handshakeConsumers = new LinkedHashMap<>();
+        this.handshakeProducers = new HashMap<>();
+        this.handshakeHash = conContext.inputRecord.handshakeHash;
+        this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
+
+        this.handshakeFinished = false;
+        this.kickstartMessageDelivered = false;
+
+        this.delegatedActions = new LinkedList<>();
+        this.handshakeExtensions = new HashMap<>();
+        this.handshakePossessions = new LinkedList<>();
+        this.handshakeCredentials = new LinkedList<>();
+        this.requestedServerNames = null;
+        this.negotiatedServerName = null;
+        this.negotiatedCipherSuite = conContext.cipherSuite;
+        initialize();
+    }
+
+    /**
+     * Constructor for PostHandshakeContext
+     */
+    HandshakeContext(TransportContext conContext) {
+        this.sslContext = conContext.sslContext;
+        this.conContext = conContext;
+        this.sslConfig = conContext.sslConfig;
+
+        this.negotiatedProtocol = conContext.protocolVersion;
+        this.negotiatedCipherSuite = conContext.cipherSuite;
+        this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
+        this.delegatedActions = new LinkedList<>();
+
+        this.handshakeProducers = null;
+        this.handshakeHash = null;
+        this.activeProtocols = null;
+        this.activeCipherSuites = null;
+        this.algorithmConstraints = null;
+        this.maximumActiveProtocol = null;
+        this.handshakeExtensions = Collections.emptyMap();  // Not in TLS13
+        this.handshakePossessions = null;
+        this.handshakeCredentials = null;
+    }
+
+    // Initialize the non-final class variables.
+    private void initialize() {
+        ProtocolVersion inputHelloVersion;
+        ProtocolVersion outputHelloVersion;
+        if (conContext.isNegotiated) {
+            inputHelloVersion = conContext.protocolVersion;
+            outputHelloVersion = conContext.protocolVersion;
+        } else {
+            if (activeProtocols.contains(ProtocolVersion.SSL20Hello)) {
+                inputHelloVersion = ProtocolVersion.SSL20Hello;
+
+                // Per TLS 1.3 protocol, implementation MUST NOT send an SSL
+                // version 2.0 compatible CLIENT-HELLO.
+                if (maximumActiveProtocol.useTLS13PlusSpec()) {
+                    outputHelloVersion = maximumActiveProtocol;
+                } else {
+                    outputHelloVersion = ProtocolVersion.SSL20Hello;
+                }
+            } else {
+                inputHelloVersion = maximumActiveProtocol;
+                outputHelloVersion = maximumActiveProtocol;
+            }
+        }
+
+        conContext.inputRecord.setHelloVersion(inputHelloVersion);
+        conContext.outputRecord.setHelloVersion(outputHelloVersion);
+
+        if (!conContext.isNegotiated) {
+            conContext.protocolVersion = maximumActiveProtocol;
+        }
+        conContext.outputRecord.setVersion(conContext.protocolVersion);
+    }
+
+    private static List<ProtocolVersion> getActiveProtocols(
+            List<ProtocolVersion> enabledProtocols,
+            List<CipherSuite> enabledCipherSuites,
+            AlgorithmConstraints algorithmConstraints) {
+        boolean enabledSSL20Hello = false;
+        ArrayList<ProtocolVersion> protocols = new ArrayList<>(4);
+        for (ProtocolVersion protocol : enabledProtocols) {
+            if (!enabledSSL20Hello && protocol == ProtocolVersion.SSL20Hello) {
+                enabledSSL20Hello = true;
+                continue;
+            }
+
+            if (!algorithmConstraints.permits(
+                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                    protocol.name, null)) {
+                // Ignore disabled protocol.
+                continue;
+            }
+
+            boolean found = false;
+            Map<NamedGroupType, Boolean> cachedStatus =
+                    new EnumMap<>(NamedGroupType.class);
+            for (CipherSuite suite : enabledCipherSuites) {
+                if (suite.isAvailable() && suite.supports(protocol)) {
+                    if (isActivatable(suite,
+                            algorithmConstraints, cachedStatus)) {
+                        protocols.add(protocol);
+                        found = true;
+                        break;
+                    }
+                } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                        "Ignore unsupported cipher suite: " + suite +
+                             " for " + protocol);
+                }
+            }
+
+            if (!found && (SSLLogger.isOn) && SSLLogger.isOn("handshake")) {
+                SSLLogger.fine(
+                    "No available cipher suite for " + protocol);
+            }
+        }
+
+        if (!protocols.isEmpty()) {
+            if (enabledSSL20Hello) {
+                protocols.add(ProtocolVersion.SSL20Hello);
+            }
+            Collections.sort(protocols);
+        }
+
+        return Collections.unmodifiableList(protocols);
+    }
+
+    private static List<CipherSuite> getActiveCipherSuites(
+            List<ProtocolVersion> enabledProtocols,
+            List<CipherSuite> enabledCipherSuites,
+            AlgorithmConstraints algorithmConstraints) {
+
+        List<CipherSuite> suites = new LinkedList<>();
+        if (enabledProtocols != null && !enabledProtocols.isEmpty()) {
+            Map<NamedGroupType, Boolean> cachedStatus =
+                    new EnumMap<>(NamedGroupType.class);
+            for (CipherSuite suite : enabledCipherSuites) {
+                if (!suite.isAvailable()) {
+                    continue;
+                }
+
+                boolean isSupported = false;
+                for (ProtocolVersion protocol : enabledProtocols) {
+                    if (!suite.supports(protocol)) {
+                        continue;
+                    }
+                    if (isActivatable(suite,
+                            algorithmConstraints, cachedStatus)) {
+                        suites.add(suite);
+                        isSupported = true;
+                        break;
+                    }
+                }
+
+                if (!isSupported &&
+                        SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.finest(
+                            "Ignore unsupported cipher suite: " + suite);
+                }
+            }
+        }
+
+        return Collections.unmodifiableList(suites);
+    }
+
+    /**
+     * Parse the handshake record and return the contentType
+     */
+    static byte getHandshakeType(TransportContext conContext,
+            Plaintext plaintext) throws IOException {
+        //     struct {
+        //         HandshakeType msg_type;    /* handshake type */
+        //         uint24 length;             /* bytes in message */
+        //         select (HandshakeType) {
+        //             ...
+        //         } body;
+        //     } Handshake;
+
+        if (plaintext.contentType != ContentType.HANDSHAKE.id) {
+            conContext.fatal(Alert.INTERNAL_ERROR,
+                "Unexpected operation for record: " + plaintext.contentType);
+
+            return 0;
+        }
+
+        if (plaintext.fragment == null || plaintext.fragment.remaining() < 4) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Invalid handshake message: insufficient data");
+
+            return 0;
+        }
+
+        byte handshakeType = (byte)Record.getInt8(plaintext.fragment);
+        int handshakeLen = Record.getInt24(plaintext.fragment);
+        if (handshakeLen != plaintext.fragment.remaining()) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Invalid handshake message: insufficient handshake body");
+
+            return 0;
+        }
+
+        return handshakeType;
+    }
+
+    void dispatch(byte handshakeType, Plaintext plaintext) throws IOException {
+        if (conContext.transport.useDelegatedTask()) {
+            boolean hasDelegated = !delegatedActions.isEmpty();
+            if (hasDelegated ||
+                   (handshakeType != SSLHandshake.FINISHED.id &&
+                    handshakeType != SSLHandshake.KEY_UPDATE.id &&
+                    handshakeType != SSLHandshake.NEW_SESSION_TICKET.id)) {
+                if (!hasDelegated) {
+                    taskDelegated = false;
+                    delegatedThrown = null;
+                }
+
+                // Clone the fragment for delegated actions.
+                //
+                // The plaintext may share the application buffers.  It is
+                // fine to use shared buffers if no delegated actions.
+                // However, for delegated actions, the shared buffers may be
+                // polluted in application layer before the delegated actions
+                // executed.
+                ByteBuffer fragment = ByteBuffer.wrap(
+                        new byte[plaintext.fragment.remaining()]);
+                fragment.put(plaintext.fragment);
+                fragment = fragment.rewind();
+
+                delegatedActions.add(new SimpleImmutableEntry<>(
+                        handshakeType,
+                        fragment
+                    ));
+            } else {
+                dispatch(handshakeType, plaintext.fragment);
+            }
+        } else {
+            dispatch(handshakeType, plaintext.fragment);
+        }
+    }
+
+    void dispatch(byte handshakeType,
+            ByteBuffer fragment) throws IOException {
+        SSLConsumer consumer;
+        if (handshakeType == SSLHandshake.HELLO_REQUEST.id) {
+            // For TLS 1.2 and prior versions, the HelloRequest message MAY
+            // be sent by the server at any time.
+            consumer = SSLHandshake.HELLO_REQUEST;
+        } else {
+            consumer = handshakeConsumers.get(handshakeType);
+        }
+
+        if (consumer == null) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected handshake message: " +
+                    SSLHandshake.nameOf(handshakeType));
+            return;
+        }
+
+        try {
+            consumer.consume(this, fragment);
+        } catch (UnsupportedOperationException unsoe) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unsupported handshake message: " +
+                    SSLHandshake.nameOf(handshakeType), unsoe);
+        }
+
+        // update handshake hash after handshake message consumption.
+        handshakeHash.consume();
+    }
+
+    abstract void kickstart() throws IOException;
+
+    /**
+     * Check if the given cipher suite is enabled and available within
+     * the current active cipher suites.
+     *
+     * Does not check if the required server certificates are available.
+     */
+    boolean isNegotiable(CipherSuite cs) {
+        return isNegotiable(activeCipherSuites, cs);
+    }
+
+    /**
+     * Check if the given cipher suite is enabled and available within
+     * the proposed cipher suite list.
+     *
+     * Does not check if the required server certificates are available.
+     */
+    static final boolean isNegotiable(
+            List<CipherSuite> proposed, CipherSuite cs) {
+        return proposed.contains(cs) && cs.isNegotiable();
+    }
+
+    /**
+     * Check if the given cipher suite is enabled and available within
+     * the proposed cipher suite list and specific protocol version.
+     *
+     * Does not check if the required server certificates are available.
+     */
+    static final boolean isNegotiable(List<CipherSuite> proposed,
+            ProtocolVersion protocolVersion, CipherSuite cs) {
+        return proposed.contains(cs) &&
+                cs.isNegotiable() && cs.supports(protocolVersion);
+    }
+
+    /**
+     * Check if the given protocol version is enabled and available.
+     */
+    boolean isNegotiable(ProtocolVersion protocolVersion) {
+        return activeProtocols.contains(protocolVersion);
+    }
+
+    /**
+     * Set the active protocol version and propagate it to the SSLSocket
+     * and our handshake streams. Called from ClientHandshaker
+     * and ServerHandshaker with the negotiated protocol version.
+     */
+    void setVersion(ProtocolVersion protocolVersion) {
+        this.conContext.protocolVersion = protocolVersion;
+    }
+
+    private static boolean isActivatable(CipherSuite suite,
+            AlgorithmConstraints algorithmConstraints,
+            Map<NamedGroupType, Boolean> cachedStatus) {
+
+        if (algorithmConstraints.permits(
+                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
+            if (suite.keyExchange == null) {
+                // TLS 1.3, no definition of key exchange in cipher suite.
+                return true;
+            }
+
+            boolean available;
+            NamedGroupType groupType = suite.keyExchange.groupType;
+            if (groupType != NAMED_GROUP_NONE) {
+                Boolean checkedStatus = cachedStatus.get(groupType);
+                if (checkedStatus == null) {
+                    available = SupportedGroups.isActivatable(
+                            algorithmConstraints, groupType);
+                    cachedStatus.put(groupType, available);
+
+                    if (!available &&
+                            SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                        SSLLogger.fine("No activated named group");
+                    }
+                } else {
+                    available = checkedStatus;
+                }
+
+                if (!available && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+                    SSLLogger.fine(
+                        "No active named group, ignore " + suite);
+                }
+                return available;
+            } else {
+                return true;
+            }
+        } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
+            SSLLogger.fine("Ignore disabled cipher suite: " + suite);
+        }
+
+        return false;
+    }
+
+    List<SNIServerName> getRequestedServerNames() {
+        if (requestedServerNames == null) {
+            return Collections.<SNIServerName>emptyList();
+        }
+        return requestedServerNames;
+    }
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,514 +23,623 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.ByteArrayOutputStream;
-import java.security.*;
-import java.util.Locale;
+import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.LinkedList;
+import javax.crypto.SecretKey;
+import sun.security.util.MessageDigestSpi2;
+
+final class HandshakeHash {
+    private TranscriptHash transcriptHash;
+    private LinkedList<byte[]> reserves;    // one handshake message per entry
+    private boolean hasBeenUsed;
+
+    HandshakeHash() {
+        this.transcriptHash = new CacheOnlyHash();
+        this.reserves = new LinkedList<>();
+        this.hasBeenUsed = false;
+    }
 
-/**
- * Abstraction for the SSL/TLS hash of all handshake messages that is
- * maintained to verify the integrity of the negotiation. Internally,
- * it consists of an MD5 and an SHA1 digest. They are used in the client
- * and server finished messages and in certificate verify messages (if sent).
- *
- * This class transparently deals with cloneable and non-cloneable digests.
- *
- * This class now supports TLS 1.2 also. The key difference for TLS 1.2
- * is that you cannot determine the hash algorithms for CertificateVerify
- * at a early stage. On the other hand, it's simpler than TLS 1.1 (and earlier)
- * that there is no messy MD5+SHA1 digests.
- *
- * You need to obey these conventions when using this class:
- *
- * 1. protocolDetermined(version) should be called when the negotiated
- * protocol version is determined.
- *
- * 2. Before protocolDetermined() is called, only update(), and reset()
- * and setFinishedAlg() can be called.
- *
- * 3. After protocolDetermined() is called, reset() cannot be called.
- *
- * 4. After protocolDetermined() is called, if the version is pre-TLS 1.2,
- * getFinishedHash() cannot be called. Otherwise,
- * getMD5Clone() and getSHAClone() cannot be called.
- *
- * 5. getMD5Clone() and getSHAClone() can only be called after
- * protocolDetermined() is called and version is pre-TLS 1.2.
- *
- * 6. getFinishedHash() can only be called after protocolDetermined()
- * and setFinishedAlg() have been called and the version is TLS 1.2.
- *
- * Suggestion: Call protocolDetermined() and setFinishedAlg()
- * as early as possible.
- *
- * Example:
- * <pre>
- * HandshakeHash hh = new HandshakeHash(...)
- * hh.protocolDetermined(ProtocolVersion.TLS12);
- * hh.update(clientHelloBytes);
- * hh.setFinishedAlg("SHA-256");
- * hh.update(serverHelloBytes);
- * ...
- * hh.update(CertificateVerifyBytes);
- * ...
- * hh.update(finished1);
- * byte[] finDigest1 = hh.getFinishedHash();
- * hh.update(finished2);
- * byte[] finDigest2 = hh.getFinishedHash();
- * </pre>
- */
-final class HandshakeHash {
+    // fix the negotiated protocol version and cipher suite
+    void determine(ProtocolVersion protocolVersion,
+            CipherSuite cipherSuite) {
+        if (!(transcriptHash instanceof CacheOnlyHash)) {
+            throw new IllegalStateException(
+                    "Not expected instance of transcript hash");
+        }
 
-    // Common
-
-    // -1:  unknown
-    //  1:  <=TLS 1.1
-    //  2:  TLS 1.2
-    private int version = -1;
-    private ByteArrayOutputStream data = new ByteArrayOutputStream();
-
-    // For TLS 1.1
-    private MessageDigest md5, sha;
-    private final int clonesNeeded;    // needs to be saved for later use
-
-    // For TLS 1.2
-    private MessageDigest finMD;
-
-    // Cache for input record handshake hash computation
-    private ByteArrayOutputStream reserve = new ByteArrayOutputStream();
+        CacheOnlyHash coh = (CacheOnlyHash)transcriptHash;
+        if (protocolVersion.useTLS13PlusSpec()) {
+            transcriptHash = new T13HandshakeHash(cipherSuite);
+        } else if (protocolVersion.useTLS12PlusSpec()) {
+            transcriptHash = new T12HandshakeHash(cipherSuite);
+        } else if (protocolVersion.useTLS10PlusSpec()) {
+            transcriptHash = new T10HandshakeHash(cipherSuite);
+        } else {
+            transcriptHash = new S30HandshakeHash(cipherSuite);
+        }
 
-    /**
-     * Create a new HandshakeHash. needCertificateVerify indicates whether
-     * a hash for the certificate verify message is required.
-     */
-    HandshakeHash(boolean needCertificateVerify) {
-        // We may rework the code later, but for now we use hard-coded number
-        // of clones if the underlying MessageDigests are not cloneable.
-        //
-        // The number used here is based on the current handshake protocols and
-        // implementation.  It may be changed if the handshake processe gets
-        // changed in the future, for example adding a new extension that
-        // requires handshake hash.  Please be careful about the number of
-        // clones if additional handshak hash is required in the future.
-        //
-        // For the current implementation, the handshake hash is required for
-        // the following items:
-        //     . CertificateVerify handshake message (optional)
-        //     . client Finished handshake message
-        //     . server Finished Handshake message
-        //     . the extended Master Secret extension [RFC 7627]
-        //
-        // Note that a late call to server setNeedClientAuth dose not update
-        // the number of clones.  We may address the issue later.
-        //
-        // Note for safety, we allocate one more clone for the current
-        // implementation.  We may consider it more carefully in the future
-        // for the exact number or rework the code in a different way.
-        clonesNeeded = needCertificateVerify ? 5 : 4;
+        byte[] reserved = coh.baos.toByteArray();
+        if (reserved.length != 0) {
+            transcriptHash.update(reserved, 0, reserved.length);
+        }
     }
 
-    void reserve(ByteBuffer input) {
+    HandshakeHash copy() {
+        if (transcriptHash instanceof CacheOnlyHash) {
+            HandshakeHash result = new HandshakeHash();
+            result.transcriptHash = ((CacheOnlyHash)transcriptHash).copy();
+            result.reserves = new LinkedList<>(reserves);
+            result.hasBeenUsed = hasBeenUsed;
+            return result;
+        } else {
+            throw new IllegalStateException("Hash does not support copying");
+        }
+    }
+
+    void receive(byte[] input) {
+        reserves.add(Arrays.copyOf(input, input.length));
+    }
+
+    void receive(ByteBuffer input, int length) {
         if (input.hasArray()) {
-            reserve.write(input.array(),
+            int from = input.position() + input.arrayOffset();
+            int to = from + length;
+            reserves.add(Arrays.copyOfRange(input.array(), from, to));
+        } else {
+            int inPos = input.position();
+            byte[] holder = new byte[length];
+            input.get(holder);
+            input.position(inPos);
+            reserves.add(Arrays.copyOf(holder, holder.length));
+        }
+    }
+    void receive(ByteBuffer input) {
+        receive(input, input.remaining());
+    }
+
+    // For HelloRetryRequest only! Please use this method very carefully!
+    void push(byte[] input) {
+        reserves.push(Arrays.copyOf(input, input.length));
+    }
+
+    // For PreSharedKey to modify the state of the PSK binder hash
+    byte[] removeLastReceived() {
+        return reserves.removeLast();
+    }
+
+    void deliver(byte[] input) {
+        update();
+        transcriptHash.update(input, 0, input.length);
+    }
+
+    void deliver(byte[] input, int offset, int length) {
+        update();
+        transcriptHash.update(input, offset, length);
+    }
+
+    void deliver(ByteBuffer input) {
+        update();
+        if (input.hasArray()) {
+            transcriptHash.update(input.array(),
                     input.position() + input.arrayOffset(), input.remaining());
         } else {
             int inPos = input.position();
             byte[] holder = new byte[input.remaining()];
             input.get(holder);
             input.position(inPos);
-            reserve.write(holder, 0, holder.length);
-        }
-    }
-
-    void reserve(byte[] b, int offset, int len) {
-        reserve.write(b, offset, len);
-    }
-
-    void reload() {
-        if (reserve.size() != 0) {
-            byte[] bytes = reserve.toByteArray();
-            reserve.reset();
-            update(bytes, 0, bytes.length);
+            transcriptHash.update(holder, 0, holder.length);
         }
     }
 
-    void update(ByteBuffer input) {
-
-        // reload if there are reserved messages.
-        reload();
-
-        int inPos = input.position();
-        switch (version) {
-            case 1:
-                md5.update(input);
-                input.position(inPos);
-
-                sha.update(input);
-                input.position(inPos);
-
-                break;
-            default:
-                if (finMD != null) {
-                    finMD.update(input);
-                    input.position(inPos);
-                }
-                if (input.hasArray()) {
-                    data.write(input.array(),
-                            inPos + input.arrayOffset(), input.remaining());
-                } else {
-                    byte[] holder = new byte[input.remaining()];
-                    input.get(holder);
-                    input.position(inPos);
-                    data.write(holder, 0, holder.length);
-                }
-                break;
+    // Use one handshake message if it has not been used.
+    void utilize() {
+        if (hasBeenUsed) {
+            return;
+        }
+        if (reserves.size() != 0) {
+            byte[] holder = reserves.remove();
+            transcriptHash.update(holder, 0, holder.length);
+            hasBeenUsed = true;
         }
     }
 
-    void update(byte handshakeType, byte[] handshakeBody) {
-
-        // reload if there are reserved messages.
-        reload();
-
-        switch (version) {
-            case 1:
-                md5.update(handshakeType);
-                sha.update(handshakeType);
-
-                md5.update((byte)((handshakeBody.length >> 16) & 0xFF));
-                sha.update((byte)((handshakeBody.length >> 16) & 0xFF));
-                md5.update((byte)((handshakeBody.length >> 8) & 0xFF));
-                sha.update((byte)((handshakeBody.length >> 8) & 0xFF));
-                md5.update((byte)(handshakeBody.length & 0xFF));
-                sha.update((byte)(handshakeBody.length & 0xFF));
-
-                md5.update(handshakeBody);
-                sha.update(handshakeBody);
-                break;
-            default:
-                if (finMD != null) {
-                    finMD.update(handshakeType);
-                    finMD.update((byte)((handshakeBody.length >> 16) & 0xFF));
-                    finMD.update((byte)((handshakeBody.length >> 8) & 0xFF));
-                    finMD.update((byte)(handshakeBody.length & 0xFF));
-                    finMD.update(handshakeBody);
-                }
-                data.write(handshakeType);
-                data.write((byte)((handshakeBody.length >> 16) & 0xFF));
-                data.write((byte)((handshakeBody.length >> 8) & 0xFF));
-                data.write((byte)(handshakeBody.length & 0xFF));
-                data.write(handshakeBody, 0, handshakeBody.length);
-                break;
+    // Consume one handshake message if it has not been consumed.
+    void consume() {
+        if (hasBeenUsed) {
+            hasBeenUsed = false;
+            return;
+        }
+        if (reserves.size() != 0) {
+            byte[] holder = reserves.remove();
+            transcriptHash.update(holder, 0, holder.length);
         }
     }
 
-    void update(byte[] b, int offset, int len) {
-
-        // reload if there are reserved messages.
-        reload();
+    void update() {
+        while (reserves.size() != 0) {
+            byte[] holder = reserves.remove();
+            transcriptHash.update(holder, 0, holder.length);
+        }
+        hasBeenUsed = false;
+    }
 
-        switch (version) {
-            case 1:
-                md5.update(b, offset, len);
-                sha.update(b, offset, len);
-                break;
-            default:
-                if (finMD != null) {
-                    finMD.update(b, offset, len);
-                }
-                data.write(b, offset, len);
-                break;
-        }
+    byte[] digest() {
+        // Note that the reserve handshake message may be not a part of
+        // the expected digest.
+        return transcriptHash.digest();
+    }
+
+    void finish() {
+        this.transcriptHash = new CacheOnlyHash();
+        this.reserves = new LinkedList<>();
+        this.hasBeenUsed = false;
+    }
+
+    // Optional
+    byte[] archived() {
+        // Note that the reserve handshake message may be not a part of
+        // the expected digest.
+        return transcriptHash.archived();
     }
 
-    /**
-     * Reset the remaining digests. Note this does *not* reset the number of
-     * digest clones that can be obtained. Digests that have already been
-     * cloned and are gone remain gone.
-     */
-    void reset() {
-        if (version != -1) {
-            throw new RuntimeException(
-                    "reset() can be only be called before protocolDetermined");
-        }
-        data.reset();
+    // Optional, TLS 1.0/1.1 only
+    byte[] digest(String algorithm) {
+        T10HandshakeHash hh = (T10HandshakeHash)transcriptHash;
+        return hh.digest(algorithm);
+    }
+
+    // Optional, SSL 3.0 only
+    byte[] digest(String algorithm, SecretKey masterSecret) {
+        S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
+        return hh.digest(algorithm, masterSecret);
+    }
+
+    // Optional, SSL 3.0 only
+    byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
+        S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
+        return hh.digest(useClientLabel, masterSecret);
     }
 
-
-    void protocolDetermined(ProtocolVersion pv) {
+    public boolean isHashable(byte handshakeType) {
+        return handshakeType != SSLHandshake.HELLO_REQUEST.id &&
+               handshakeType != SSLHandshake.HELLO_VERIFY_REQUEST.id;
+    }
 
-        // Do not set again, will ignore
-        if (version != -1) {
-            return;
+    interface TranscriptHash {
+        void update(byte[] input, int offset, int length);
+        byte[] digest();
+        byte[] archived();  // optional
+    }
+
+    // For cache only.
+    private static final class CacheOnlyHash implements TranscriptHash {
+        private final ByteArrayOutputStream baos;
+
+        CacheOnlyHash() {
+            this.baos = new ByteArrayOutputStream();
         }
 
-        if (pv.maybeDTLSProtocol()) {
-            version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1;
-        } else {
-            version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
+        @Override
+        public void update(byte[] input, int offset, int length) {
+            baos.write(input, offset, length);
         }
-        switch (version) {
-            case 1:
-                // initiate md5, sha and call update on saved array
-                try {
-                    md5 = CloneableDigest.getDigest("MD5", clonesNeeded);
-                    sha = CloneableDigest.getDigest("SHA", clonesNeeded);
-                } catch (NoSuchAlgorithmException e) {
-                    throw new RuntimeException
-                                ("Algorithm MD5 or SHA not available", e);
-                }
-                byte[] bytes = data.toByteArray();
-                update(bytes, 0, bytes.length);
-                break;
-            case 2:
-                break;
+
+        @Override
+        public byte[] digest() {
+            throw new IllegalStateException(
+                    "Not expected call to handshake hash digest");
         }
-    }
-
-    /////////////////////////////////////////////////////////////
-    // Below are old methods for pre-TLS 1.1
-    /////////////////////////////////////////////////////////////
 
-    /**
-     * Return a new MD5 digest updated with all data hashed so far.
-     */
-    MessageDigest getMD5Clone() {
-        if (version != 1) {
-            throw new RuntimeException(
-                    "getMD5Clone() can be only be called for TLS 1.1");
+        @Override
+        public byte[] archived() {
+            return baos.toByteArray();
         }
-        return cloneDigest(md5);
-    }
 
-    /**
-     * Return a new SHA digest updated with all data hashed so far.
-     */
-    MessageDigest getSHAClone() {
-        if (version != 1) {
-            throw new RuntimeException(
-                    "getSHAClone() can be only be called for TLS 1.1");
-        }
-        return cloneDigest(sha);
-    }
-
-    private static MessageDigest cloneDigest(MessageDigest digest) {
-        try {
-            return (MessageDigest)digest.clone();
-        } catch (CloneNotSupportedException e) {
-            // cannot occur for digests generated via CloneableDigest
-            throw new RuntimeException("Could not clone digest", e);
+        CacheOnlyHash copy() {
+            CacheOnlyHash result = new CacheOnlyHash();
+            try {
+                baos.writeTo(result.baos);
+            } catch (IOException ex) {
+                throw new RuntimeException("unable to to clone hash state");
+            }
+            return result;
         }
     }
 
-    /////////////////////////////////////////////////////////////
-    // Below are new methods for TLS 1.2
-    /////////////////////////////////////////////////////////////
+    static final class S30HandshakeHash implements TranscriptHash {
+        static final byte[] MD5_pad1 = genPad(0x36, 48);
+        static final byte[] MD5_pad2 = genPad(0x5c, 48);
+
+        static final byte[] SHA_pad1 = genPad(0x36, 40);
+        static final byte[] SHA_pad2 = genPad(0x5c, 40);
+
+        private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };
+        private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };
+
+        private final MessageDigest mdMD5;
+        private final MessageDigest mdSHA;
+        private final TranscriptHash md5;
+        private final TranscriptHash sha;
+        private final ByteArrayOutputStream baos;
+
+        S30HandshakeHash(CipherSuite cipherSuite) {
+            this.mdMD5 = JsseJce.getMessageDigest("MD5");
+            this.mdSHA = JsseJce.getMessageDigest("SHA");
+
+            boolean hasArchived = false;
+            if (mdMD5 instanceof Cloneable) {
+                md5 = new CloneableHash(mdMD5);
+            } else {
+                hasArchived = true;
+                md5 = new NonCloneableHash(mdMD5);
+            }
+            if (mdSHA instanceof Cloneable) {
+                sha = new CloneableHash(mdSHA);
+            } else {
+                hasArchived = true;
+                sha = new NonCloneableHash(mdSHA);
+            }
 
-    private static String normalizeAlgName(String alg) {
-        alg = alg.toUpperCase(Locale.US);
-        if (alg.startsWith("SHA")) {
-            if (alg.length() == 3) {
-                return "SHA-1";
+            if (hasArchived) {
+                this.baos = null;
+            } else {
+                this.baos = new ByteArrayOutputStream();
+            }
+        }
+
+        @Override
+        public void update(byte[] input, int offset, int length) {
+            md5.update(input, offset, length);
+            sha.update(input, offset, length);
+            if (baos != null) {
+                baos.write(input, offset, length);
             }
-            if (alg.charAt(3) != '-') {
-                return "SHA-" + alg.substring(3);
+        }
+
+        @Override
+        public byte[] digest() {
+            byte[] digest = new byte[36];
+            System.arraycopy(md5.digest(), 0, digest, 0, 16);
+            System.arraycopy(sha.digest(), 0, digest, 16, 20);
+
+            return digest;
+        }
+
+        @Override
+        public byte[] archived() {
+            if (baos != null) {
+                return baos.toByteArray();
+            } else if (md5 instanceof NonCloneableHash) {
+                return md5.archived();
+            } else {
+                return sha.archived();
             }
         }
-        return alg;
-    }
-    /**
-     * Specifies the hash algorithm used in Finished. This should be called
-     * based in info in ServerHello.
-     * Can be called multiple times.
-     */
-    void setFinishedAlg(String s) {
-        if (s == null) {
-            throw new RuntimeException(
-                    "setFinishedAlg's argument cannot be null");
+
+        byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
+            MessageDigest md5Clone = cloneMd5();
+            MessageDigest shaClone = cloneSha();
+
+            if (useClientLabel) {
+                md5Clone.update(SSL_CLIENT);
+                shaClone.update(SSL_CLIENT);
+            } else {
+                md5Clone.update(SSL_SERVER);
+                shaClone.update(SSL_SERVER);
+            }
+
+            updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
+            updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
+
+            byte[] digest = new byte[36];
+            System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
+            System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
+
+            return digest;
+        }
+
+        byte[] digest(String algorithm, SecretKey masterSecret) {
+            if ("RSA".equalsIgnoreCase(algorithm)) {
+                MessageDigest md5Clone = cloneMd5();
+                MessageDigest shaClone = cloneSha();
+                updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
+                updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
+
+                byte[] digest = new byte[36];
+                System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
+                System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
+
+                return digest;
+            } else {
+                MessageDigest shaClone = cloneSha();
+                updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
+                return shaClone.digest();
+            }
+        }
+
+        private static byte[] genPad(int b, int count) {
+            byte[] padding = new byte[count];
+            Arrays.fill(padding, (byte)b);
+            return padding;
+        }
+
+        private MessageDigest cloneMd5() {
+            MessageDigest md5Clone;
+            if (mdMD5 instanceof Cloneable) {
+                try {
+                    md5Clone = (MessageDigest)mdMD5.clone();
+                } catch (CloneNotSupportedException ex) {   // unlikely
+                    throw new RuntimeException(
+                            "MessageDigest does no support clone operation");
+                }
+            } else {
+                md5Clone = JsseJce.getMessageDigest("MD5");
+                md5Clone.update(md5.archived());
+            }
+
+            return md5Clone;
+        }
+
+        private MessageDigest cloneSha() {
+            MessageDigest shaClone;
+            if (mdSHA instanceof Cloneable) {
+                try {
+                    shaClone = (MessageDigest)mdSHA.clone();
+                } catch (CloneNotSupportedException ex) {   // unlikely
+                    throw new RuntimeException(
+                            "MessageDigest does no support clone operation");
+                }
+            } else {
+                shaClone = JsseJce.getMessageDigest("SHA");
+                shaClone.update(sha.archived());
+            }
+
+            return shaClone;
+        }
+
+        private static void updateDigest(MessageDigest md,
+                byte[] pad1, byte[] pad2, SecretKey masterSecret) {
+            byte[] keyBytes = "RAW".equals(masterSecret.getFormat())
+                            ? masterSecret.getEncoded() : null;
+            if (keyBytes != null) {
+                md.update(keyBytes);
+            } else {
+                digestKey(md, masterSecret);
+            }
+            md.update(pad1);
+            byte[] temp = md.digest();
+
+            if (keyBytes != null) {
+                md.update(keyBytes);
+            } else {
+                digestKey(md, masterSecret);
+            }
+            md.update(pad2);
+            md.update(temp);
         }
 
-        // Can be called multiple times, but only set once
-        if (finMD != null) return;
+        private static void digestKey(MessageDigest md, SecretKey key) {
+            try {
+                if (md instanceof MessageDigestSpi2) {
+                    ((MessageDigestSpi2)md).engineUpdate(key);
+                } else {
+                    throw new Exception(
+                        "Digest does not support implUpdate(SecretKey)");
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(
+                    "Could not obtain encoded key and "
+                    + "MessageDigest cannot digest key", e);
+            }
+        }
+    }
+
+    // TLS 1.0 and TLS 1.1
+    static final class T10HandshakeHash implements TranscriptHash {
+        private final TranscriptHash md5;
+        private final TranscriptHash sha;
+        private final ByteArrayOutputStream baos;
+
+        T10HandshakeHash(CipherSuite cipherSuite) {
+            MessageDigest mdMD5 = JsseJce.getMessageDigest("MD5");
+            MessageDigest mdSHA = JsseJce.getMessageDigest("SHA");
+
+            boolean hasArchived = false;
+            if (mdMD5 instanceof Cloneable) {
+                md5 = new CloneableHash(mdMD5);
+            } else {
+                hasArchived = true;
+                md5 = new NonCloneableHash(mdMD5);
+            }
+            if (mdSHA instanceof Cloneable) {
+                sha = new CloneableHash(mdSHA);
+            } else {
+                hasArchived = true;
+                sha = new NonCloneableHash(mdSHA);
+            }
 
-        try {
-            // See comment in the contructor.
-            finMD = CloneableDigest.getDigest(normalizeAlgName(s), 4);
-        } catch (NoSuchAlgorithmException e) {
-            throw new Error(e);
+            if (hasArchived) {
+                this.baos = null;
+            } else {
+                this.baos = new ByteArrayOutputStream();
+            }
+        }
+
+        @Override
+        public void update(byte[] input, int offset, int length) {
+            md5.update(input, offset, length);
+            sha.update(input, offset, length);
+            if (baos != null) {
+                baos.write(input, offset, length);
+            }
         }
-        finMD.update(data.toByteArray());
+
+        @Override
+        public byte[] digest() {
+            byte[] digest = new byte[36];
+            System.arraycopy(md5.digest(), 0, digest, 0, 16);
+            System.arraycopy(sha.digest(), 0, digest, 16, 20);
+
+            return digest;
+        }
+
+        byte[] digest(String algorithm) {
+            if ("RSA".equalsIgnoreCase(algorithm)) {
+                return digest();
+            } else {
+                return sha.digest();
+            }
+        }
+
+        @Override
+        public byte[] archived() {
+            if (baos != null) {
+                return baos.toByteArray();
+            } else if (md5 instanceof NonCloneableHash) {
+                return md5.archived();
+            } else {
+                return sha.archived();
+            }
+        }
     }
 
-    byte[] getAllHandshakeMessages() {
-        return data.toByteArray();
+    static final class T12HandshakeHash implements TranscriptHash {
+        private final TranscriptHash transcriptHash;
+        private final ByteArrayOutputStream baos;
+
+        T12HandshakeHash(CipherSuite cipherSuite) {
+            MessageDigest md =
+                    JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
+            if (md instanceof Cloneable) {
+                transcriptHash = new CloneableHash(md);
+                this.baos = null;
+            } else {
+                transcriptHash = new NonCloneableHash(md);
+                this.baos = new ByteArrayOutputStream();
+            }
+        }
+
+        @Override
+        public void update(byte[] input, int offset, int length) {
+            transcriptHash.update(input, offset, length);
+            if (baos != null) {
+                baos.write(input, offset, length);
+            }
+        }
+
+        @Override
+        public byte[] digest() {
+            return transcriptHash.digest();
+        }
+
+        @Override
+        public byte[] archived() {
+            if (baos != null) {
+                return baos.toByteArray();
+            } else {
+                return transcriptHash.archived();
+            }
+        }
     }
 
-    /**
-     * Calculates the hash in Finished. Must be called after setFinishedAlg().
-     * This method can be called twice, for Finished messages of the server
-     * side and client side respectively.
-     */
-    byte[] getFinishedHash() {
-        try {
-            return cloneDigest(finMD).digest();
-        } catch (Exception e) {
-            throw new Error("Error during hash calculation", e);
+    static final class T13HandshakeHash implements TranscriptHash {
+        private final TranscriptHash transcriptHash;
+        private final ByteArrayOutputStream baos;
+
+        T13HandshakeHash(CipherSuite cipherSuite) {
+            MessageDigest md =
+                    JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
+            if (md instanceof Cloneable) {
+                transcriptHash = new CloneableHash(md);
+                this.baos = null;
+            } else {
+                transcriptHash = new NonCloneableHash(md);
+                this.baos = new ByteArrayOutputStream();
+            }
+        }
+
+        @Override
+        public void update(byte[] input, int offset, int length) {
+            transcriptHash.update(input, offset, length);
+            if (baos != null) {
+                baos.write(input, offset, length);
+            }
+        }
+
+        @Override
+        public byte[] digest() {
+            return transcriptHash.digest();
+        }
+
+        @Override
+        public byte[] archived() {
+            if (baos != null) {
+                return baos.toByteArray();
+            } else {
+                return transcriptHash.archived();
+            }
+
+            // throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    static final class CloneableHash implements TranscriptHash {
+        private final MessageDigest md;
+
+        CloneableHash(MessageDigest md) {
+            this.md = md;
+        }
+
+        @Override
+        public void update(byte[] input, int offset, int length) {
+            md.update(input, offset, length);
+        }
+
+        @Override
+        public byte[] digest() {
+            try {
+                return ((MessageDigest)md.clone()).digest();
+            } catch (CloneNotSupportedException ex) {
+                // unlikely
+                return new byte[0];
+            }
+        }
+
+        @Override
+        public byte[] archived() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    static final class NonCloneableHash implements TranscriptHash {
+        private final MessageDigest md;
+        private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        NonCloneableHash(MessageDigest md) {
+            this.md = md;
+        }
+
+        @Override
+        public void update(byte[] input, int offset, int length) {
+            baos.write(input, offset, length);
+        }
+
+        @Override
+        public byte[] digest() {
+            byte[] bytes = baos.toByteArray();
+            md.reset();
+            return md.digest(bytes);
+        }
+
+        @Override
+        public byte[] archived() {
+            return baos.toByteArray();
         }
     }
 }
-
-/**
- * A wrapper for MessageDigests that simulates cloning of non-cloneable
- * digests. It uses the standard MessageDigest API and therefore can be used
- * transparently in place of a regular digest.
- *
- * Note that we extend the MessageDigest class directly rather than
- * MessageDigestSpi. This works because MessageDigest was originally designed
- * this way in the JDK 1.1 days which allows us to avoid creating an internal
- * provider.
- *
- * It can be "cloned" a limited number of times, which is specified at
- * construction time. This is achieved by internally maintaining n digests
- * in parallel. Consequently, it is only 1/n-th times as fast as the original
- * digest.
- *
- * Example:
- *   MessageDigest md = CloneableDigest.getDigest("SHA", 2);
- *   md.update(data1);
- *   MessageDigest md2 = (MessageDigest)md.clone();
- *   md2.update(data2);
- *   byte[] d1 = md2.digest(); // digest of data1 || data2
- *   md.update(data3);
- *   byte[] d2 = md.digest();  // digest of data1 || data3
- *
- * This class is not thread safe.
- *
- */
-final class CloneableDigest extends MessageDigest implements Cloneable {
-
-    /**
-     * The individual MessageDigests. Initially, all elements are non-null.
-     * When clone() is called, the non-null element with the maximum index is
-     * returned and the array element set to null.
-     *
-     * All non-null element are always in the same state.
-     */
-    private final MessageDigest[] digests;
-
-    private CloneableDigest(MessageDigest digest, int n, String algorithm)
-            throws NoSuchAlgorithmException {
-        super(algorithm);
-        digests = new MessageDigest[n];
-        digests[0] = digest;
-        for (int i = 1; i < n; i++) {
-            digests[i] = JsseJce.getMessageDigest(algorithm);
-        }
-    }
-
-    /**
-     * Return a MessageDigest for the given algorithm that can be cloned the
-     * specified number of times. If the default implementation supports
-     * cloning, it is returned. Otherwise, an instance of this class is
-     * returned.
-     */
-    static MessageDigest getDigest(String algorithm, int n)
-            throws NoSuchAlgorithmException {
-        MessageDigest digest = JsseJce.getMessageDigest(algorithm);
-        try {
-            digest.clone();
-            // already cloneable, use it
-            return digest;
-        } catch (CloneNotSupportedException e) {
-            return new CloneableDigest(digest, n, algorithm);
-        }
-    }
-
-    /**
-     * Check if this object is still usable. If it has already been cloned the
-     * maximum number of times, there are no digests left and this object can no
-     * longer be used.
-     */
-    private void checkState() {
-        // XXX handshaking currently doesn't stop updating hashes...
-        // if (digests[0] == null) {
-        //     throw new IllegalStateException("no digests left");
-        // }
-    }
-
-    @Override
-    protected int engineGetDigestLength() {
-        checkState();
-        return digests[0].getDigestLength();
-    }
-
-    @Override
-    protected void engineUpdate(byte b) {
-        checkState();
-        for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
-            digests[i].update(b);
-        }
-    }
-
-    @Override
-    protected void engineUpdate(byte[] b, int offset, int len) {
-        checkState();
-        for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
-            digests[i].update(b, offset, len);
-        }
-    }
-
-    @Override
-    protected byte[] engineDigest() {
-        checkState();
-        byte[] digest = digests[0].digest();
-        digestReset();
-        return digest;
-    }
-
-    @Override
-    protected int engineDigest(byte[] buf, int offset, int len)
-            throws DigestException {
-        checkState();
-        int n = digests[0].digest(buf, offset, len);
-        digestReset();
-        return n;
-    }
-
-    /**
-     * Reset all digests after a digest() call. digests[0] has already been
-     * implicitly reset by the digest() call and does not need to be reset
-     * again.
-     */
-    private void digestReset() {
-        for (int i = 1; (i < digests.length) && (digests[i] != null); i++) {
-            digests[i].reset();
-        }
-    }
-
-    @Override
-    protected void engineReset() {
-        checkState();
-        for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
-            digests[i].reset();
-        }
-    }
-
-    @Override
-    public Object clone() {
-        checkState();
-        for (int i = digests.length - 1; i >= 0; i--) {
-            if (digests[i] != null) {
-                MessageDigest digest = digests[i];
-                digests[i] = null;
-                return digest;
-            }
-        }
-        // cannot occur
-        throw new InternalError();
-    }
-
-}
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-/*
- * Copyright (c) 1996, 2015, 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.ByteArrayInputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import javax.net.ssl.SSLException;
-
-/**
- * InputStream for handshake data, used internally only. Contains the
- * handshake message buffer and methods to parse them.
- *
- * Once a new handshake record arrives, it is buffered in this class until
- * processed by the Handshaker. The buffer may also contain incomplete
- * handshake messages in case the message is split across multiple records.
- * Handshaker.processRecord deals with all that. It may also contain
- * handshake messages larger than the default buffer size (e.g. large
- * certificate messages). The buffer is grown dynamically to handle that.
- *
- * Note that this class only handles Handshake messages in TLS format.
- * DTLS Handshake messages should be converted into TLS format before
- * calling into this method.
- *
- * @author David Brownell
- */
-
-// This class is used to handle plain text handshake messages.
-//
-public final class HandshakeInStream extends ByteArrayInputStream {
-
-    /*
-     * Construct the stream; we'll be accumulating hashes of the
-     * input records using two sets of digests.
-     */
-    HandshakeInStream() {
-        super(new byte[0]);     // lazy to alloacte the internal buffer
-    }
-
-    //
-    // overridden ByteArrayInputStream methods
-    //
-
-    @Override
-    public int read(byte[] b) throws IOException {
-        if (super.read(b) != b.length) {
-            throw new SSLException("Unexpected end of handshake data");
-        }
-
-        return b.length;
-    }
-
-    //
-    // handshake input stream management functions
-    //
-
-    /*
-     * Here's an incoming record with handshake data.  Queue the contents;
-     * it might be one or more entire messages, complete a message that's
-     * partly queued, or both.
-     */
-    void incomingRecord(ByteBuffer in) throws IOException {
-        int len;
-
-        // Move any unread data to the front of the buffer.
-        if (pos != 0) {
-            len = count - pos;
-            if (len != 0) {
-                System.arraycopy(buf, pos, buf, 0, len);
-            }
-            pos = 0;
-            count = len;
-        }
-
-        // Grow buffer if needed.
-        len = in.remaining() + count;
-        if (buf.length < len) {
-            byte[] newbuf = new byte[len];
-            if (count != 0) {
-                System.arraycopy(buf, 0, newbuf, 0, count);
-            }
-            buf = newbuf;
-        }
-
-        // Append the incoming record to the buffer
-        in.get(buf, count, in.remaining());
-        count = len;
-    }
-
-    //
-    // Message parsing methods
-    //
-
-    /*
-     * Read 8, 16, 24, and 32 bit SSL integer data types, encoded
-     * in standard big-endian form.
-     */
-    int getInt8() throws IOException {
-        verifyLength(1);
-        return read();
-    }
-
-    int getInt16() throws IOException {
-        verifyLength(2);
-        return (getInt8() << 8) | getInt8();
-    }
-
-    int getInt24() throws IOException {
-        verifyLength(3);
-        return (getInt8() << 16) | (getInt8() << 8) | getInt8();
-    }
-
-    int getInt32() throws IOException {
-        verifyLength(4);
-        return (getInt8() << 24) | (getInt8() << 16)
-             | (getInt8() << 8) | getInt8();
-    }
-
-    /*
-     * Read byte vectors with 8, 16, and 24 bit length encodings.
-     */
-    byte[] getBytes8() throws IOException {
-        int len = getInt8();
-        verifyLength(len);
-        byte[] b = new byte[len];
-
-        read(b);
-        return b;
-    }
-
-    public byte[] getBytes16() throws IOException {
-        int len = getInt16();
-        verifyLength(len);
-        byte[] b = new byte[len];
-
-        read(b);
-        return b;
-    }
-
-    byte[] getBytes24() throws IOException {
-        int len = getInt24();
-        verifyLength(len);
-        byte[] b = new byte[len];
-
-        read(b);
-        return b;
-    }
-
-    // Is a length greater than available bytes in the record?
-    private void verifyLength(int len) throws SSLException {
-        if (len > available()) {
-            throw new SSLException("Unexpected end of handshake data");
-        }
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2430 +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.math.BigInteger;
-import java.security.*;
-import java.security.interfaces.*;
-import java.security.spec.*;
-import java.security.cert.*;
-import java.security.cert.Certificate;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-import java.lang.reflect.*;
-
-import javax.security.auth.x500.X500Principal;
-
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.DHPublicKeySpec;
-
-import javax.net.ssl.*;
-
-import sun.security.internal.spec.TlsPrfParameterSpec;
-import sun.security.ssl.CipherSuite.*;
-import static sun.security.ssl.CipherSuite.PRF.*;
-import sun.security.util.KeyUtil;
-import sun.security.util.MessageDigestSpi2;
-import sun.security.provider.certpath.OCSPResponse;
-
-/**
- * Many data structures are involved in the handshake messages.  These
- * classes are used as structures, with public data members.  They are
- * not visible outside the SSL package.
- *
- * Handshake messages all have a common header format, and they are all
- * encoded in a "handshake data" SSL record substream.  The base class
- * here (HandshakeMessage) provides a common framework and records the
- * SSL record type of the particular handshake message.
- *
- * This file contains subclasses for all the basic handshake messages.
- * All handshake messages know how to encode and decode themselves on
- * SSL streams; this facilitates using the same code on SSL client and
- * server sides, although they don't send and receive the same messages.
- *
- * Messages also know how to print themselves, which is quite handy
- * for debugging.  They always identify their type, and can optionally
- * dump all of their content.
- *
- * @author David Brownell
- */
-public abstract class HandshakeMessage {
-
-    /* Class and subclass dynamic debugging support */
-    public static final Debug debug = Debug.getInstance("ssl");
-
-    // enum HandshakeType:
-    //
-    // Please update the isUnsupported() method accordingly if the handshake
-    // types get updated in the future.
-    static final byte   ht_hello_request          = 0;      // RFC 5246
-    static final byte   ht_client_hello           = 1;      // RFC 5246
-    static final byte   ht_server_hello           = 2;      // RFC 5246
-    static final byte   ht_hello_verify_request   = 3;      // RFC 6347
-    static final byte   ht_new_session_ticket     = 4;      // RFC 4507
-
-    static final byte   ht_certificate            = 11;     // RFC 5246
-    static final byte   ht_server_key_exchange    = 12;     // RFC 5246
-    static final byte   ht_certificate_request    = 13;     // RFC 5246
-    static final byte   ht_server_hello_done      = 14;     // RFC 5246
-    static final byte   ht_certificate_verify     = 15;     // RFC 5246
-    static final byte   ht_client_key_exchange    = 16;     // RFC 5246
-
-    static final byte   ht_finished               = 20;     // RFC 5246
-    static final byte   ht_certificate_url        = 21;     // RFC 6066
-    static final byte   ht_certificate_status     = 22;     // RFC 6066
-    static final byte   ht_supplemental_data      = 23;     // RFC 4680
-
-    static final byte   ht_not_applicable         = -1;     // N/A
-
-    /*
-     * SSL 3.0 MAC padding constants.
-     * Also used by CertificateVerify and Finished during the handshake.
-     */
-    static final byte[] MD5_pad1 = genPad(0x36, 48);
-    static final byte[] MD5_pad2 = genPad(0x5c, 48);
-
-    static final byte[] SHA_pad1 = genPad(0x36, 40);
-    static final byte[] SHA_pad2 = genPad(0x5c, 40);
-
-    // default constructor
-    HandshakeMessage() {
-    }
-
-    /**
-     * Utility method to convert a BigInteger to a byte array in unsigned
-     * format as needed in the handshake messages. BigInteger uses
-     * 2's complement format, i.e. it prepends an extra zero if the MSB
-     * is set. We remove that.
-     */
-    static byte[] toByteArray(BigInteger bi) {
-        byte[] b = bi.toByteArray();
-        if ((b.length > 1) && (b[0] == 0)) {
-            int n = b.length - 1;
-            byte[] newarray = new byte[n];
-            System.arraycopy(b, 1, newarray, 0, n);
-            b = newarray;
-        }
-        return b;
-    }
-
-    static boolean isUnsupported(byte handshakeType) {
-        return (handshakeType != ht_hello_request) &&
-               (handshakeType != ht_client_hello) &&
-               (handshakeType != ht_server_hello) &&
-               (handshakeType != ht_hello_verify_request) &&
-               (handshakeType != ht_new_session_ticket) &&
-               (handshakeType != ht_certificate) &&
-               (handshakeType != ht_server_key_exchange) &&
-               (handshakeType != ht_certificate_request) &&
-               (handshakeType != ht_server_hello_done) &&
-               (handshakeType != ht_certificate_verify) &&
-               (handshakeType != ht_client_key_exchange) &&
-               (handshakeType != ht_finished) &&
-               (handshakeType != ht_certificate_url) &&
-               (handshakeType != ht_certificate_status) &&
-               (handshakeType != ht_supplemental_data);
-    }
-
-    private static byte[] genPad(int b, int count) {
-        byte[] padding = new byte[count];
-        Arrays.fill(padding, (byte)b);
-        return padding;
-    }
-
-    /*
-     * Write a handshake message on the (handshake) output stream.
-     * This is just a four byte header followed by the data.
-     *
-     * NOTE that huge messages -- notably, ones with huge cert
-     * chains -- are handled correctly.
-     */
-    final void write(HandshakeOutStream s) throws IOException {
-        int len = messageLength();
-        if (len >= Record.OVERFLOW_OF_INT24) {
-            throw new SSLException("Handshake message too big"
-                + ", type = " + messageType() + ", len = " + len);
-        }
-        s.write(messageType());
-        s.putInt24(len);
-        send(s);
-        s.complete();
-    }
-
-    /*
-     * Subclasses implement these methods so those kinds of
-     * messages can be emitted.  Base class delegates to subclass.
-     */
-    abstract int  messageType();
-    abstract int  messageLength();
-    abstract void send(HandshakeOutStream s) throws IOException;
-
-    /*
-     * Write a descriptive message on the output stream; for debugging.
-     */
-    abstract void print(PrintStream p) throws IOException;
-
-//
-// NOTE:  the rest of these classes are nested within this one, and are
-// imported by other classes in this package.  There are a few other
-// handshake message classes, not neatly nested here because of current
-// licensing requirement for native (RSA) methods.  They belong here,
-// but those native methods complicate things a lot!
-//
-
-
-/*
- * HelloRequest ... SERVER --> CLIENT
- *
- * Server can ask the client to initiate a new handshake, e.g. to change
- * session parameters after a connection has been (re)established.
- */
-static final class HelloRequest extends HandshakeMessage {
-    @Override
-    int messageType() { return ht_hello_request; }
-
-    HelloRequest() { }
-
-    HelloRequest(HandshakeInStream in) throws IOException
-    {
-        // nothing in this message
-    }
-
-    @Override
-    int messageLength() { return 0; }
-
-    @Override
-    void send(HandshakeOutStream out) throws IOException
-    {
-        // nothing in this messaage
-    }
-
-    @Override
-    void print(PrintStream out) throws IOException
-    {
-        out.println("*** HelloRequest (empty)");
-    }
-
-}
-
-/*
- * HelloVerifyRequest ... SERVER --> CLIENT  [DTLS only]
- *
- * The definition of HelloVerifyRequest is as follows:
- *
- *     struct {
- *       ProtocolVersion server_version;
- *       opaque cookie<0..2^8-1>;
- *     } HelloVerifyRequest;
- *
- * For DTLS protocols, once the client has transmitted the ClientHello message,
- * it expects to see a HelloVerifyRequest from the server.  However, if the
- * server's message is lost, the client knows that either the ClientHello or
- * the HelloVerifyRequest has been lost and retransmits. [RFC 6347]
- */
-static final class HelloVerifyRequest extends HandshakeMessage {
-    ProtocolVersion     protocolVersion;
-    byte[]              cookie;         // 1 to 2^8 - 1 bytes
-
-    HelloVerifyRequest(HelloCookieManager helloCookieManager,
-            ClientHello clientHelloMsg) {
-
-        this.protocolVersion = clientHelloMsg.protocolVersion;
-        this.cookie = helloCookieManager.getCookie(clientHelloMsg);
-    }
-
-    HelloVerifyRequest(
-            HandshakeInStream input, int messageLength) throws IOException {
-
-        this.protocolVersion =
-                ProtocolVersion.valueOf(input.getInt8(), input.getInt8());
-        this.cookie = input.getBytes8();
-
-        // Is it a valid cookie?
-        HelloCookieManager.checkCookie(protocolVersion, cookie);
-    }
-
-    @Override
-    int messageType() {
-        return ht_hello_verify_request;
-    }
-
-    @Override
-    int messageLength() {
-        return 2 + cookie.length;       // 2: the length of protocolVersion
-    }
-
-    @Override
-    void send(HandshakeOutStream hos) throws IOException {
-        hos.putInt8(protocolVersion.major);
-        hos.putInt8(protocolVersion.minor);
-        hos.putBytes8(cookie);
-    }
-
-    @Override
-    void print(PrintStream out) throws IOException {
-        out.println("*** HelloVerifyRequest");
-        if (debug != null && Debug.isOn("verbose")) {
-            out.println("server_version: " + protocolVersion);
-            Debug.println(out, "cookie", cookie);
-        }
-    }
-}
-
-/*
- * ClientHello ... CLIENT --> SERVER
- *
- * Client initiates handshake by telling server what it wants, and what it
- * can support (prioritized by what's first in the ciphe suite list).
- *
- * By RFC2246:7.4.1.2 it's explicitly anticipated that this message
- * will have more data added at the end ... e.g. what CAs the client trusts.
- * Until we know how to parse it, we will just read what we know
- * about, and let our caller handle the jumps over unknown data.
- */
-static final class ClientHello extends HandshakeMessage {
-
-    ProtocolVersion             protocolVersion;
-    RandomCookie                clnt_random;
-    SessionId                   sessionId;
-    byte[]                      cookie;                     // DTLS only
-    private CipherSuiteList     cipherSuites;
-    private final boolean       isDTLS;
-    byte[]                      compression_methods;
-
-    HelloExtensions extensions = new HelloExtensions();
-
-    private static final byte[]  NULL_COMPRESSION = new byte[] {0};
-
-    ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,
-            SessionId sessionId, CipherSuiteList cipherSuites,
-            boolean isDTLS) {
-
-        this.isDTLS = isDTLS;
-        this.protocolVersion = protocolVersion;
-        this.sessionId = sessionId;
-        this.cipherSuites = cipherSuites;
-        if (isDTLS) {
-            this.cookie = new byte[0];
-        } else {
-            this.cookie = null;
-        }
-
-        clnt_random = new RandomCookie(generator);
-        compression_methods = NULL_COMPRESSION;
-    }
-
-    ClientHello(HandshakeInStream s,
-            int messageLength, boolean isDTLS) throws IOException {
-
-        this.isDTLS = isDTLS;
-
-        protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8());
-        clnt_random = new RandomCookie(s);
-        sessionId = new SessionId(s.getBytes8());
-        sessionId.checkLength(protocolVersion);
-        if (isDTLS) {
-            cookie = s.getBytes8();
-        } else {
-            cookie = null;
-        }
-
-        cipherSuites = new CipherSuiteList(s);
-        compression_methods = s.getBytes8();
-        if (messageLength() != messageLength) {
-            extensions = new HelloExtensions(s);
-        }
-    }
-
-    CipherSuiteList getCipherSuites() {
-        return cipherSuites;
-    }
-
-    // add renegotiation_info extension
-    void addRenegotiationInfoExtension(byte[] clientVerifyData) {
-        HelloExtension renegotiationInfo = new RenegotiationInfoExtension(
-                    clientVerifyData, new byte[0]);
-        extensions.add(renegotiationInfo);
-    }
-
-    // add server_name extension
-    void addSNIExtension(List<SNIServerName> serverNames) {
-        try {
-            extensions.add(new ServerNameExtension(serverNames));
-        } catch (IOException ioe) {
-            // ignore the exception and return
-        }
-    }
-
-    // add signature_algorithm extension
-    void addSignatureAlgorithmsExtension(
-            Collection<SignatureAndHashAlgorithm> algorithms) {
-        HelloExtension signatureAlgorithm =
-                new SignatureAlgorithmsExtension(algorithms);
-        extensions.add(signatureAlgorithm);
-    }
-
-    void addExtendedMasterSecretExtension() {
-        extensions.add(new ExtendedMasterSecretExtension());
-    }
-
-    void addMFLExtension(int maximumPacketSize) {
-        HelloExtension maxFragmentLength =
-                new MaxFragmentLengthExtension(maximumPacketSize);
-        extensions.add(maxFragmentLength);
-    }
-
-    void updateHelloCookie(MessageDigest cookieDigest) {
-        //
-        // Just use HandshakeOutStream to compute the hello verify cookie.
-        // Not actually used to output handshake message records.
-        //
-        HandshakeOutStream hos = new HandshakeOutStream(null);
-
-        try {
-            send(hos, false);    // Do not count hello verify cookie.
-        } catch (IOException ioe) {
-            // unlikely to happen
-        }
-
-        cookieDigest.update(hos.toByteArray());
-    }
-
-    // Add status_request extension type
-    void addCertStatusRequestExtension() {
-        extensions.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest()));
-    }
-
-    // Add status_request_v2 extension type
-    void addCertStatusReqListV2Extension() {
-        // Create a default OCSPStatusRequest that we can use for both
-        // OCSP_MULTI and OCSP request list items.
-        OCSPStatusRequest osr = new OCSPStatusRequest();
-        List<CertStatusReqItemV2> itemList = new ArrayList<>(2);
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                osr));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr));
-        extensions.add(new CertStatusReqListV2Extension(itemList));
-    }
-
-    // add application_layer_protocol_negotiation extension
-    void addALPNExtension(String[] applicationProtocols) throws SSLException {
-        extensions.add(new ALPNExtension(applicationProtocols));
-    }
-
-    @Override
-    int messageType() { return ht_client_hello; }
-
-    @Override
-    int messageLength() {
-        /*
-         * Add fixed size parts of each field...
-         * version + random + session + cipher + compress
-         */
-        return (2 + 32 + 1 + 2 + 1
-            + sessionId.length()                /* ... + variable parts */
-            + (isDTLS ? (1 + cookie.length) : 0)
-            + (cipherSuites.size() * 2)
-            + compression_methods.length)
-            + extensions.length();
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        send(s, true);  // Count hello verify cookie.
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** ClientHello, " + protocolVersion);
-
-        if (debug != null && Debug.isOn("verbose")) {
-            s.print("RandomCookie:  ");
-            clnt_random.print(s);
-
-            s.print("Session ID:  ");
-            s.println(sessionId);
-
-            if (isDTLS) {
-                Debug.println(s, "cookie", cookie);
-            }
-
-            s.println("Cipher Suites: " + cipherSuites);
-
-            Debug.println(s, "Compression Methods", compression_methods);
-            extensions.print(s);
-            s.println("***");
-        }
-    }
-
-    private void send(HandshakeOutStream s,
-            boolean computeCookie) throws IOException {
-        s.putInt8(protocolVersion.major);
-        s.putInt8(protocolVersion.minor);
-        clnt_random.send(s);
-        s.putBytes8(sessionId.getId());
-        if (isDTLS && computeCookie) {
-            s.putBytes8(cookie);
-        }
-        cipherSuites.send(s);
-        s.putBytes8(compression_methods);
-        extensions.send(s);
-    }
-
-}
-
-/*
- * ServerHello ... SERVER --> CLIENT
- *
- * Server chooses protocol options from among those it supports and the
- * client supports.  Then it sends the basic session descriptive parameters
- * back to the client.
- */
-static final
-class ServerHello extends HandshakeMessage
-{
-    @Override
-    int messageType() { return ht_server_hello; }
-
-    ProtocolVersion     protocolVersion;
-    RandomCookie        svr_random;
-    SessionId           sessionId;
-    CipherSuite         cipherSuite;
-    byte                compression_method;
-    HelloExtensions extensions = new HelloExtensions();
-
-    ServerHello() {
-        // empty
-    }
-
-    ServerHello(HandshakeInStream input, int messageLength)
-            throws IOException {
-        protocolVersion = ProtocolVersion.valueOf(input.getInt8(),
-                                                  input.getInt8());
-        svr_random = new RandomCookie(input);
-        sessionId = new SessionId(input.getBytes8());
-        sessionId.checkLength(protocolVersion);
-        cipherSuite = CipherSuite.valueOf(input.getInt8(), input.getInt8());
-        compression_method = (byte)input.getInt8();
-        if (messageLength() != messageLength) {
-            extensions = new HelloExtensions(input);
-        }
-    }
-
-    @Override
-    int messageLength()
-    {
-        // almost fixed size, except session ID and extensions:
-        //      major + minor = 2
-        //      random = 32
-        //      session ID len field = 1
-        //      cipher suite + compression = 3
-        //      extensions: if present, 2 + length of extensions
-        return 38 + sessionId.length() + extensions.length();
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException
-    {
-        s.putInt8(protocolVersion.major);
-        s.putInt8(protocolVersion.minor);
-        svr_random.send(s);
-        s.putBytes8(sessionId.getId());
-        s.putInt8(cipherSuite.id >> 8);
-        s.putInt8(cipherSuite.id & 0xff);
-        s.putInt8(compression_method);
-        extensions.send(s);
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException
-    {
-        s.println("*** ServerHello, " + protocolVersion);
-
-        if (debug != null && Debug.isOn("verbose")) {
-            s.print("RandomCookie:  ");
-            svr_random.print(s);
-
-            s.print("Session ID:  ");
-            s.println(sessionId);
-
-            s.println("Cipher Suite: " + cipherSuite);
-            s.println("Compression Method: " + compression_method);
-            extensions.print(s);
-            s.println("***");
-        }
-    }
-}
-
-
-/*
- * CertificateMsg ... send by both CLIENT and SERVER
- *
- * Each end of a connection may need to pass its certificate chain to
- * the other end.  Such chains are intended to validate an identity with
- * reference to some certifying authority.  Examples include companies
- * like Verisign, or financial institutions.  There's some control over
- * the certifying authorities which are sent.
- *
- * NOTE: that these messages might be huge, taking many handshake records.
- * Up to 2^48 bytes of certificate may be sent, in records of at most 2^14
- * bytes each ... up to 2^32 records sent on the output stream.
- */
-static final
-class CertificateMsg extends HandshakeMessage
-{
-    @Override
-    int messageType() { return ht_certificate; }
-
-    private X509Certificate[] chain;
-
-    private List<byte[]> encodedChain;
-
-    private int messageLength;
-
-    CertificateMsg(X509Certificate[] certs) {
-        chain = certs;
-    }
-
-    CertificateMsg(HandshakeInStream input) throws IOException {
-        int chainLen = input.getInt24();
-        List<Certificate> v = new ArrayList<>(4);
-
-        CertificateFactory cf = null;
-        while (chainLen > 0) {
-            byte[] cert = input.getBytes24();
-            chainLen -= (3 + cert.length);
-            try {
-                if (cf == null) {
-                    cf = CertificateFactory.getInstance("X.509");
-                }
-                v.add(cf.generateCertificate(new ByteArrayInputStream(cert)));
-            } catch (CertificateException e) {
-                throw (SSLProtocolException)new SSLProtocolException(
-                    e.getMessage()).initCause(e);
-            }
-        }
-
-        chain = v.toArray(new X509Certificate[v.size()]);
-    }
-
-    @Override
-    int messageLength() {
-        if (encodedChain == null) {
-            messageLength = 3;
-            encodedChain = new ArrayList<byte[]>(chain.length);
-            try {
-                for (X509Certificate cert : chain) {
-                    byte[] b = cert.getEncoded();
-                    encodedChain.add(b);
-                    messageLength += b.length + 3;
-                }
-            } catch (CertificateEncodingException e) {
-                encodedChain = null;
-                throw new RuntimeException("Could not encode certificates", e);
-            }
-        }
-        return messageLength;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt24(messageLength() - 3);
-        for (byte[] b : encodedChain) {
-            s.putBytes24(b);
-        }
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** Certificate chain");
-
-        if (chain.length == 0) {
-            s.println("<Empty>");
-        } else if (debug != null && Debug.isOn("verbose")) {
-            for (int i = 0; i < chain.length; i++) {
-                s.println("chain [" + i + "] = " + chain[i]);
-            }
-        }
-        s.println("***");
-    }
-
-    X509Certificate[] getCertificateChain() {
-        return chain.clone();
-    }
-}
-
-/*
- * CertificateStatus ... SERVER --> CLIENT
- *
- * When a ClientHello asserting the status_request or status_request_v2
- * extensions is accepted by the server, it will fetch and return one
- * or more status responses in this handshake message.
- *
- * NOTE: Like the Certificate handshake message, this can potentially
- * be a very large message both due to the size of multiple status
- * responses and the certificate chains that are often attached to them.
- * Up to 2^24 bytes of status responses may be sent, possibly fragmented
- * over multiple TLS records.
- */
-static final class CertificateStatus extends HandshakeMessage
-{
-    private final StatusRequestType statusType;
-    private int encodedResponsesLen;
-    private int messageLength = -1;
-    private List<byte[]> encodedResponses;
-
-    @Override
-    int messageType() { return ht_certificate_status; }
-
-    /**
-     * Create a CertificateStatus message from the certificates and their
-     * respective OCSP responses
-     *
-     * @param type an indication of the type of response (OCSP or OCSP_MULTI)
-     * @param responses a {@code List} of OCSP responses in DER-encoded form.
-     *      For the OCSP type, only the first entry in the response list is
-     *      used, and must correspond to the end-entity certificate sent to the
-     *      peer.  Zero-length or null values for the response data are not
-     *      allowed for the OCSP type.  For the OCSP_MULTI type, each entry in
-     *      the list should match its corresponding certificate sent in the
-     *      Server Certificate message.  Where an OCSP response does not exist,
-     *      either a zero-length array or a null value should be used.
-     *
-     * @throws SSLException if an unsupported StatusRequestType or invalid
-     *      OCSP response data is provided.
-     */
-    CertificateStatus(StatusRequestType type, X509Certificate[] chain,
-            Map<X509Certificate, byte[]> responses) {
-        statusType = type;
-        encodedResponsesLen = 0;
-        encodedResponses = new ArrayList<>(chain.length);
-
-        Objects.requireNonNull(chain, "Null chain not allowed");
-        Objects.requireNonNull(responses, "Null responses not allowed");
-
-        if (statusType == StatusRequestType.OCSP) {
-            // Just get the response for the end-entity certificate
-            byte[] respDER = responses.get(chain[0]);
-            if (respDER != null && respDER.length > 0) {
-                encodedResponses.add(respDER);
-                encodedResponsesLen = 3 + respDER.length;
-            } else {
-                throw new IllegalArgumentException("Zero-length or null " +
-                        "OCSP Response");
-            }
-        } else if (statusType == StatusRequestType.OCSP_MULTI) {
-            for (X509Certificate cert : chain) {
-                byte[] respDER = responses.get(cert);
-                if (respDER != null) {
-                    encodedResponses.add(respDER);
-                    encodedResponsesLen += (respDER.length + 3);
-                } else {
-                    // If we cannot find a response for a given certificate
-                    // then use a zero-length placeholder.
-                    encodedResponses.add(new byte[0]);
-                    encodedResponsesLen += 3;
-                }
-            }
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported StatusResponseType: " + statusType);
-        }
-    }
-
-    /**
-     * Decode the CertificateStatus handshake message coming from a
-     * {@code HandshakeInputStream}.
-     *
-     * @param input the {@code HandshakeInputStream} containing the
-     * CertificateStatus message bytes.
-     *
-     * @throws SSLHandshakeException if a zero-length response is found in the
-     * OCSP response type, or an unsupported response type is detected.
-     * @throws IOException if a decoding error occurs.
-     */
-    CertificateStatus(HandshakeInStream input) throws IOException {
-        encodedResponsesLen = 0;
-        encodedResponses = new ArrayList<>();
-
-        statusType = StatusRequestType.get(input.getInt8());
-        if (statusType == StatusRequestType.OCSP) {
-            byte[] respDER = input.getBytes24();
-            // Convert the incoming bytes to a OCSPResponse strucutre
-            if (respDER.length > 0) {
-                encodedResponses.add(respDER);
-                encodedResponsesLen = 3 + respDER.length;
-            } else {
-                throw new SSLHandshakeException("Zero-length OCSP Response");
-            }
-        } else if (statusType == StatusRequestType.OCSP_MULTI) {
-            int respListLen = input.getInt24();
-            encodedResponsesLen = respListLen;
-
-            // Add each OCSP reponse into the array list in the order
-            // we receive them off the wire.  A zero-length array is
-            // allowed for ocsp_multi, and means that a response for
-            // a given certificate is not available.
-            while (respListLen > 0) {
-                byte[] respDER = input.getBytes24();
-                encodedResponses.add(respDER);
-                respListLen -= (respDER.length + 3);
-            }
-
-            if (respListLen != 0) {
-                throw new SSLHandshakeException(
-                        "Bad OCSP response list length");
-            }
-        } else {
-            throw new SSLHandshakeException("Unsupported StatusResponseType: " +
-                    statusType);
-        }
-    }
-
-    /**
-     * Get the length of the CertificateStatus message.
-     *
-     * @return the length of the message in bytes.
-     */
-    @Override
-    int messageLength() {
-        int len = 1;            // Length + Status type
-
-        if (messageLength == -1) {
-            if (statusType == StatusRequestType.OCSP) {
-                len += encodedResponsesLen;
-            } else if (statusType == StatusRequestType.OCSP_MULTI) {
-                len += 3 + encodedResponsesLen;
-            }
-            messageLength = len;
-        }
-
-        return messageLength;
-    }
-
-    /**
-     * Encode the CertificateStatus handshake message and place it on a
-     * {@code HandshakeOutputStream}.
-     *
-     * @param s the HandshakeOutputStream that will the message bytes.
-     *
-     * @throws IOException if an encoding error occurs.
-     */
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt8(statusType.id);
-        if (statusType == StatusRequestType.OCSP) {
-            s.putBytes24(encodedResponses.get(0));
-        } else if (statusType == StatusRequestType.OCSP_MULTI) {
-            s.putInt24(encodedResponsesLen);
-            for (byte[] respBytes : encodedResponses) {
-                if (respBytes != null) {
-                    s.putBytes24(respBytes);
-                } else {
-                    s.putBytes24(null);
-                }
-            }
-        } else {
-            // It is highly unlikely that we will fall into this section of
-            // the code.
-            throw new SSLHandshakeException("Unsupported status_type: " +
-                    statusType.id);
-        }
-    }
-
-    /**
-     * Display a human-readable representation of the CertificateStatus message.
-     *
-     * @param s the PrintStream used to display the message data.
-     *
-     * @throws IOException if any errors occur while parsing the OCSP response
-     * bytes into a readable form.
-     */
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** CertificateStatus");
-        if (debug != null && Debug.isOn("verbose")) {
-            s.println("Type: " + statusType);
-            if (statusType == StatusRequestType.OCSP) {
-                OCSPResponse oResp = new OCSPResponse(encodedResponses.get(0));
-                s.println(oResp);
-            } else if (statusType == StatusRequestType.OCSP_MULTI) {
-                int numResponses = encodedResponses.size();
-                s.println(numResponses +
-                        (numResponses == 1 ? " entry:" : " entries:"));
-                for (byte[] respDER : encodedResponses) {
-                    if (respDER.length > 0) {
-                        OCSPResponse oResp = new OCSPResponse(respDER);
-                        s.println(oResp);
-                    } else {
-                        s.println("<Zero-length entry>");
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Get the type of CertificateStatus message
-     *
-     * @return the {@code StatusRequestType} for this CertificateStatus
-     *      message.
-     */
-    StatusRequestType getType() {
-        return statusType;
-    }
-
-    /**
-     * Get the list of non-zero length OCSP responses.
-     * The responses returned in this list can be used to map to
-     * {@code X509Certificate} objects provided by the peer and
-     * provided to a {@code PKIXRevocationChecker}.
-     *
-     * @return an unmodifiable List of zero or more byte arrays, each one
-     *      consisting of a single status response.
-     */
-    List<byte[]> getResponses() {
-        return Collections.unmodifiableList(encodedResponses);
-    }
-}
-
-/*
- * ServerKeyExchange ... SERVER --> CLIENT
- *
- * The cipher suite selected, when combined with the certificate exchanged,
- * implies one of several different kinds of key exchange.  Most current
- * cipher suites require the server to send more than its certificate.
- *
- * The primary exceptions are when a server sends an encryption-capable
- * RSA public key in its cert, to be used with RSA (or RSA_export) key
- * exchange; and when a server sends its Diffie-Hellman cert.  Those kinds
- * of key exchange do not require a ServerKeyExchange message.
- *
- * Key exchange can be viewed as having three modes, which are explicit
- * for the Diffie-Hellman flavors and poorly specified for RSA ones:
- *
- *      - "Ephemeral" keys.  Here, a "temporary" key is allocated by the
- *        server, and signed.  Diffie-Hellman keys signed using RSA or
- *        DSS are ephemeral (DHE flavor).  RSA keys get used to do the same
- *        thing, to cut the key size down to 512 bits (export restrictions)
- *        or for signing-only RSA certificates.
- *
- *      - Anonymity.  Here no server certificate is sent, only the public
- *        key of the server.  This case is subject to man-in-the-middle
- *        attacks.  This can be done with Diffie-Hellman keys (DH_anon) or
- *        with RSA keys, but is only used in SSLv3 for DH_anon.
- *
- *      - "Normal" case.  Here a server certificate is sent, and the public
- *        key there is used directly in exchanging the premaster secret.
- *        For example, Diffie-Hellman "DH" flavor, and any RSA flavor with
- *        only 512 bit keys.
- *
- * If a server certificate is sent, there is no anonymity.  However,
- * when a certificate is sent, ephemeral keys may still be used to
- * exchange the premaster secret.  That's how RSA_EXPORT often works,
- * as well as how the DHE_* flavors work.
- */
-abstract static class ServerKeyExchange extends HandshakeMessage
-{
-    @Override
-    int messageType() { return ht_server_key_exchange; }
-}
-
-
-/*
- * Using RSA for Key Exchange:  exchange a session key that's not as big
- * as the signing-only key.  Used for export applications, since exported
- * RSA encryption keys can't be bigger than 512 bytes.
- *
- * This is never used when keys are 512 bits or smaller, and isn't used
- * on "US Domestic" ciphers in any case.
- */
-static final
-class RSA_ServerKeyExchange extends ServerKeyExchange
-{
-    private byte[] rsa_modulus;     // 1 to 2^16 - 1 bytes
-    private byte[] rsa_exponent;    // 1 to 2^16 - 1 bytes
-
-    private Signature signature;
-    private byte[] signatureBytes;
-
-    /*
-     * Hash the nonces and the ephemeral RSA public key.
-     */
-    private void updateSignature(byte[] clntNonce, byte[] svrNonce)
-            throws SignatureException {
-        int tmp;
-
-        signature.update(clntNonce);
-        signature.update(svrNonce);
-
-        tmp = rsa_modulus.length;
-        signature.update((byte)(tmp >> 8));
-        signature.update((byte)(tmp & 0x0ff));
-        signature.update(rsa_modulus);
-
-        tmp = rsa_exponent.length;
-        signature.update((byte)(tmp >> 8));
-        signature.update((byte)(tmp & 0x0ff));
-        signature.update(rsa_exponent);
-    }
-
-
-    /*
-     * Construct an RSA server key exchange message, using data
-     * known _only_ to the server.
-     *
-     * The client knows the public key corresponding to this private
-     * key, from the Certificate message sent previously.  To comply
-     * with US export regulations we use short RSA keys ... either
-     * long term ones in the server's X509 cert, or else ephemeral
-     * ones sent using this message.
-     */
-    RSA_ServerKeyExchange(PublicKey ephemeralKey, PrivateKey privateKey,
-            RandomCookie clntNonce, RandomCookie svrNonce, SecureRandom sr)
-            throws GeneralSecurityException {
-        RSAPublicKeySpec rsaKey = JsseJce.getRSAPublicKeySpec(ephemeralKey);
-        rsa_modulus = toByteArray(rsaKey.getModulus());
-        rsa_exponent = toByteArray(rsaKey.getPublicExponent());
-        signature = RSASignature.getInstance();
-        signature.initSign(privateKey, sr);
-        updateSignature(clntNonce.random_bytes, svrNonce.random_bytes);
-        signatureBytes = signature.sign();
-    }
-
-
-    /*
-     * Parse an RSA server key exchange message, using data known
-     * to the client (and, in some situations, eavesdroppers).
-     */
-    RSA_ServerKeyExchange(HandshakeInStream input)
-            throws IOException, NoSuchAlgorithmException {
-        signature = RSASignature.getInstance();
-        rsa_modulus = input.getBytes16();
-        rsa_exponent = input.getBytes16();
-        signatureBytes = input.getBytes16();
-    }
-
-    /*
-     * Get the ephemeral RSA public key that will be used in this
-     * SSL connection.
-     */
-    PublicKey getPublicKey() {
-        try {
-            KeyFactory kfac = JsseJce.getKeyFactory("RSA");
-            // modulus and exponent are always positive
-            RSAPublicKeySpec kspec = new RSAPublicKeySpec(
-                new BigInteger(1, rsa_modulus),
-                new BigInteger(1, rsa_exponent));
-            return kfac.generatePublic(kspec);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /*
-     * Verify the signed temporary key using the hashes computed
-     * from it and the two nonces.  This is called by clients
-     * with "exportable" RSA flavors.
-     */
-    boolean verify(PublicKey certifiedKey, RandomCookie clntNonce,
-            RandomCookie svrNonce) throws GeneralSecurityException {
-        signature.initVerify(certifiedKey);
-        updateSignature(clntNonce.random_bytes, svrNonce.random_bytes);
-        return signature.verify(signatureBytes);
-    }
-
-    @Override
-    int messageLength() {
-        return 6 + rsa_modulus.length + rsa_exponent.length
-               + signatureBytes.length;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putBytes16(rsa_modulus);
-        s.putBytes16(rsa_exponent);
-        s.putBytes16(signatureBytes);
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** RSA ServerKeyExchange");
-
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(s, "RSA Modulus", rsa_modulus);
-            Debug.println(s, "RSA Public Exponent", rsa_exponent);
-        }
-    }
-}
-
-
-/*
- * Using Diffie-Hellman algorithm for key exchange.  All we really need to
- * do is securely get Diffie-Hellman keys (using the same P, G parameters)
- * to our peer, then we automatically have a shared secret without need
- * to exchange any more data.  (D-H only solutions, such as SKIP, could
- * eliminate key exchange negotiations and get faster connection setup.
- * But they still need a signature algorithm like DSS/DSA to support the
- * trusted distribution of keys without relying on unscalable physical
- * key distribution systems.)
- *
- * This class supports several DH-based key exchange algorithms, though
- * perhaps eventually each deserves its own class.  Notably, this has
- * basic support for DH_anon and its DHE_DSS and DHE_RSA signed variants.
- */
-static final
-class DH_ServerKeyExchange extends ServerKeyExchange
-{
-    // Fix message encoding, see 4348279
-    private static final boolean dhKeyExchangeFix =
-        Debug.getBooleanProperty("com.sun.net.ssl.dhKeyExchangeFix", true);
-
-    private byte[]                dh_p;        // 1 to 2^16 - 1 bytes
-    private byte[]                dh_g;        // 1 to 2^16 - 1 bytes
-    private byte[]                dh_Ys;       // 1 to 2^16 - 1 bytes
-
-    private byte[]                signature;
-
-    // protocol version being established using this ServerKeyExchange message
-    ProtocolVersion protocolVersion;
-
-    // the preferable signature algorithm used by this ServerKeyExchange message
-    private SignatureAndHashAlgorithm preferableSignatureAlgorithm;
-
-    /*
-     * Construct from initialized DH key object, for DH_anon
-     * key exchange.
-     */
-    DH_ServerKeyExchange(DHCrypt obj, ProtocolVersion protocolVersion) {
-        this.protocolVersion = protocolVersion;
-        this.preferableSignatureAlgorithm = null;
-
-        // The DH key has been validated in the constructor of DHCrypt.
-        setValues(obj);
-        signature = null;
-    }
-
-    /*
-     * Construct from initialized DH key object and the key associated
-     * with the cert chain which was sent ... for DHE_DSS and DHE_RSA
-     * key exchange.  (Constructor called by server.)
-     */
-    DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte[] clntNonce,
-            byte[] svrNonce, SecureRandom sr,
-            SignatureAndHashAlgorithm signAlgorithm,
-            ProtocolVersion protocolVersion) throws GeneralSecurityException {
-
-        this.protocolVersion = protocolVersion;
-
-        // The DH key has been validated in the constructor of DHCrypt.
-        setValues(obj);
-
-        Signature sig;
-        if (protocolVersion.useTLS12PlusSpec()) {
-            this.preferableSignatureAlgorithm = signAlgorithm;
-            sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
-        } else {
-            this.preferableSignatureAlgorithm = null;
-            if (key.getAlgorithm().equals("DSA")) {
-                sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA);
-            } else {
-                sig = RSASignature.getInstance();
-            }
-        }
-
-        sig.initSign(key, sr);
-        updateSignature(sig, clntNonce, svrNonce);
-        signature = sig.sign();
-    }
-
-    /*
-     * Construct a DH_ServerKeyExchange message from an input
-     * stream, as if sent from server to client for use with
-     * DH_anon key exchange
-     */
-    DH_ServerKeyExchange(HandshakeInStream input,
-            ProtocolVersion protocolVersion)
-            throws IOException, GeneralSecurityException {
-
-        this.protocolVersion = protocolVersion;
-        this.preferableSignatureAlgorithm = null;
-
-        dh_p = input.getBytes16();
-        dh_g = input.getBytes16();
-        dh_Ys = input.getBytes16();
-        KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys),
-                                             new BigInteger(1, dh_p),
-                                             new BigInteger(1, dh_g)));
-
-        signature = null;
-    }
-
-    /*
-     * Construct a DH_ServerKeyExchange message from an input stream
-     * and a certificate, as if sent from server to client for use with
-     * DHE_DSS or DHE_RSA key exchange.  (Called by client.)
-     */
-    DH_ServerKeyExchange(HandshakeInStream input, PublicKey publicKey,
-            byte[] clntNonce, byte[] svrNonce, int messageSize,
-            Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs,
-            ProtocolVersion protocolVersion)
-            throws IOException, GeneralSecurityException {
-
-        this.protocolVersion = protocolVersion;
-
-        // read params: ServerDHParams
-        dh_p = input.getBytes16();
-        dh_g = input.getBytes16();
-        dh_Ys = input.getBytes16();
-        KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys),
-                                             new BigInteger(1, dh_p),
-                                             new BigInteger(1, dh_g)));
-
-        // read the signature and hash algorithm
-        if (protocolVersion.useTLS12PlusSpec()) {
-            int hash = input.getInt8();         // hash algorithm
-            int signature = input.getInt8();    // signature algorithm
-
-            preferableSignatureAlgorithm =
-                SignatureAndHashAlgorithm.valueOf(hash, signature, 0);
-
-            // Is it a local supported signature algorithm?
-            if (!localSupportedSignAlgs.contains(
-                    preferableSignatureAlgorithm)) {
-                throw new SSLHandshakeException(
-                    "Unsupported SignatureAndHashAlgorithm in " +
-                    "ServerKeyExchange message: " +
-                    preferableSignatureAlgorithm);
-            }
-        } else {
-            this.preferableSignatureAlgorithm = null;
-        }
-
-        // read the signature
-        byte[] signature;
-        if (dhKeyExchangeFix) {
-            signature = input.getBytes16();
-        } else {
-            messageSize -= (dh_p.length + 2);
-            messageSize -= (dh_g.length + 2);
-            messageSize -= (dh_Ys.length + 2);
-
-            signature = new byte[messageSize];
-            input.read(signature);
-        }
-
-        Signature sig;
-        String algorithm = publicKey.getAlgorithm();
-        if (protocolVersion.useTLS12PlusSpec()) {
-            sig = JsseJce.getSignature(
-                        preferableSignatureAlgorithm.getAlgorithmName());
-        } else {
-                switch (algorithm) {
-                    case "DSA":
-                        sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA);
-                        break;
-                    case "RSA":
-                        sig = RSASignature.getInstance();
-                        break;
-                    default:
-                        throw new SSLKeyException(
-                            "neither an RSA or a DSA key: " + algorithm);
-                }
-        }
-
-        sig.initVerify(publicKey);
-        updateSignature(sig, clntNonce, svrNonce);
-
-        if (sig.verify(signature) == false ) {
-            throw new SSLKeyException("Server D-H key verification failed");
-        }
-    }
-
-    /* Return the Diffie-Hellman modulus */
-    BigInteger getModulus() {
-        return new BigInteger(1, dh_p);
-    }
-
-    /* Return the Diffie-Hellman base/generator */
-    BigInteger getBase() {
-        return new BigInteger(1, dh_g);
-    }
-
-    /* Return the server's Diffie-Hellman public key */
-    BigInteger getServerPublicKey() {
-        return new BigInteger(1, dh_Ys);
-    }
-
-    /*
-     * Update sig with nonces and Diffie-Hellman public key.
-     */
-    private void updateSignature(Signature sig, byte[] clntNonce,
-            byte[] svrNonce) throws SignatureException {
-        int tmp;
-
-        sig.update(clntNonce);
-        sig.update(svrNonce);
-
-        tmp = dh_p.length;
-        sig.update((byte)(tmp >> 8));
-        sig.update((byte)(tmp & 0x0ff));
-        sig.update(dh_p);
-
-        tmp = dh_g.length;
-        sig.update((byte)(tmp >> 8));
-        sig.update((byte)(tmp & 0x0ff));
-        sig.update(dh_g);
-
-        tmp = dh_Ys.length;
-        sig.update((byte)(tmp >> 8));
-        sig.update((byte)(tmp & 0x0ff));
-        sig.update(dh_Ys);
-    }
-
-    private void setValues(DHCrypt obj) {
-        dh_p = toByteArray(obj.getModulus());
-        dh_g = toByteArray(obj.getBase());
-        dh_Ys = toByteArray(obj.getPublicKey());
-    }
-
-    @Override
-    int messageLength() {
-        int temp = 6;   // overhead for p, g, y(s) values.
-
-        temp += dh_p.length;
-        temp += dh_g.length;
-        temp += dh_Ys.length;
-
-        if (signature != null) {
-            if (protocolVersion.useTLS12PlusSpec()) {
-                temp += SignatureAndHashAlgorithm.sizeInRecord();
-            }
-
-            temp += signature.length;
-            if (dhKeyExchangeFix) {
-                temp += 2;
-            }
-        }
-
-        return temp;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putBytes16(dh_p);
-        s.putBytes16(dh_g);
-        s.putBytes16(dh_Ys);
-
-        if (signature != null) {
-            if (protocolVersion.useTLS12PlusSpec()) {
-                s.putInt8(preferableSignatureAlgorithm.getHashValue());
-                s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
-            }
-
-            if (dhKeyExchangeFix) {
-                s.putBytes16(signature);
-            } else {
-                s.write(signature);
-            }
-        }
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** Diffie-Hellman ServerKeyExchange");
-
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(s, "DH Modulus", dh_p);
-            Debug.println(s, "DH Base", dh_g);
-            Debug.println(s, "Server DH Public Key", dh_Ys);
-
-            if (signature == null) {
-                s.println("Anonymous");
-            } else {
-                if (protocolVersion.useTLS12PlusSpec()) {
-                    s.println("Signature Algorithm " +
-                        preferableSignatureAlgorithm.getAlgorithmName());
-                }
-
-                s.println("Signed with a DSA or RSA public key");
-            }
-        }
-    }
-}
-
-/*
- * ECDH server key exchange message. Sent by the server for ECDHE and ECDH_anon
- * ciphersuites to communicate its ephemeral public key (including the
- * EC domain parameters).
- *
- * We support named curves only, no explicitly encoded curves.
- */
-static final
-class ECDH_ServerKeyExchange extends ServerKeyExchange {
-
-    // constants for ECCurveType
-    private static final int CURVE_EXPLICIT_PRIME = 1;
-    private static final int CURVE_EXPLICIT_CHAR2 = 2;
-    private static final int CURVE_NAMED_CURVE    = 3;
-
-    // id of the named group we are using
-    private int groupId;
-
-    // encoded public point
-    private byte[] pointBytes;
-
-    // signature bytes (or null if anonymous)
-    private byte[] signatureBytes;
-
-    // public key object encapsulated in this message
-    private ECPublicKey publicKey;
-
-    // protocol version being established using this ServerKeyExchange message
-    ProtocolVersion protocolVersion;
-
-    // the preferable signature algorithm used by this ServerKeyExchange message
-    private SignatureAndHashAlgorithm preferableSignatureAlgorithm;
-
-    ECDH_ServerKeyExchange(ECDHCrypt obj, PrivateKey privateKey,
-            byte[] clntNonce, byte[] svrNonce, SecureRandom sr,
-            SignatureAndHashAlgorithm signAlgorithm,
-            ProtocolVersion protocolVersion)
-            throws SSLHandshakeException, GeneralSecurityException {
-
-        this.protocolVersion = protocolVersion;
-
-        publicKey = (ECPublicKey)obj.getPublicKey();
-        ECParameterSpec params = publicKey.getParams();
-        ECPoint point = publicKey.getW();
-        pointBytes = JsseJce.encodePoint(point, params.getCurve());
-
-        NamedGroup namedGroup = NamedGroup.valueOf(params);
-        if ((namedGroup == null) || (namedGroup.oid == null) ){
-            // unlikely
-            throw new SSLHandshakeException(
-                "Unnamed EC parameter spec: " + params);
-        }
-        groupId = namedGroup.id;
-
-        if (privateKey == null) {
-            // ECDH_anon
-            return;
-        }
-
-        Signature sig;
-        if (protocolVersion.useTLS12PlusSpec()) {
-            this.preferableSignatureAlgorithm = signAlgorithm;
-            sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
-        } else {
-            sig = getSignature(privateKey.getAlgorithm());
-        }
-        sig.initSign(privateKey, sr);
-
-        updateSignature(sig, clntNonce, svrNonce);
-        signatureBytes = sig.sign();
-    }
-
-    /*
-     * Parse an ECDH server key exchange message.
-     */
-    ECDH_ServerKeyExchange(HandshakeInStream input, PublicKey signingKey,
-            byte[] clntNonce, byte[] svrNonce,
-            Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs,
-            ProtocolVersion protocolVersion)
-            throws IOException, GeneralSecurityException {
-
-        this.protocolVersion = protocolVersion;
-
-        // read params: ServerECDHParams
-        int curveType = input.getInt8();
-        ECParameterSpec parameters;
-        // These parsing errors should never occur as we negotiated
-        // the supported curves during the exchange of the Hello messages.
-        if (curveType == CURVE_NAMED_CURVE) {
-            groupId = input.getInt16();
-            NamedGroup namedGroup = NamedGroup.valueOf(groupId);
-            if (namedGroup == null) {
-                throw new SSLHandshakeException(
-                    "Unknown named group ID: " + groupId);
-            }
-
-            if (!SupportedGroupsExtension.supports(namedGroup)) {
-                throw new SSLHandshakeException(
-                    "Unsupported named group: " + namedGroup);
-            }
-
-            if (namedGroup.oid == null) {
-                throw new SSLHandshakeException(
-                    "Unknown named EC curve: " + namedGroup);
-            }
-
-            parameters = JsseJce.getECParameterSpec(namedGroup.oid);
-            if (parameters == null) {
-                throw new SSLHandshakeException(
-                    "No supported EC parameter for named group: " + namedGroup);
-            }
-        } else {
-            throw new SSLHandshakeException(
-                "Unsupported ECCurveType: " + curveType);
-        }
-        pointBytes = input.getBytes8();
-
-        ECPoint point = JsseJce.decodePoint(pointBytes, parameters.getCurve());
-        KeyFactory factory = JsseJce.getKeyFactory("EC");
-        publicKey = (ECPublicKey)factory.generatePublic(
-            new ECPublicKeySpec(point, parameters));
-
-        if (signingKey == null) {
-            // ECDH_anon
-            return;
-        }
-
-        // read the signature and hash algorithm
-        if (protocolVersion.useTLS12PlusSpec()) {
-            int hash = input.getInt8();         // hash algorithm
-            int signature = input.getInt8();    // signature algorithm
-
-            preferableSignatureAlgorithm =
-                SignatureAndHashAlgorithm.valueOf(hash, signature, 0);
-
-            // Is it a local supported signature algorithm?
-            if (!localSupportedSignAlgs.contains(
-                    preferableSignatureAlgorithm)) {
-                throw new SSLHandshakeException(
-                        "Unsupported SignatureAndHashAlgorithm in " +
-                        "ServerKeyExchange message: " +
-                        preferableSignatureAlgorithm);
-            }
-        }
-
-        // read the signature
-        signatureBytes = input.getBytes16();
-
-        // verify the signature
-        Signature sig;
-        if (protocolVersion.useTLS12PlusSpec()) {
-            sig = JsseJce.getSignature(
-                        preferableSignatureAlgorithm.getAlgorithmName());
-        } else {
-            sig = getSignature(signingKey.getAlgorithm());
-        }
-        sig.initVerify(signingKey);
-
-        updateSignature(sig, clntNonce, svrNonce);
-
-        if (sig.verify(signatureBytes) == false ) {
-            throw new SSLKeyException(
-                "Invalid signature on ECDH server key exchange message");
-        }
-    }
-
-    /*
-     * Get the ephemeral EC public key encapsulated in this message.
-     */
-    ECPublicKey getPublicKey() {
-        return publicKey;
-    }
-
-    private static Signature getSignature(String keyAlgorithm)
-            throws NoSuchAlgorithmException {
-            switch (keyAlgorithm) {
-                case "EC":
-                    return JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA);
-                case "RSA":
-                    return RSASignature.getInstance();
-                default:
-                    throw new NoSuchAlgorithmException(
-                        "neither an RSA or a EC key : " + keyAlgorithm);
-            }
-    }
-
-    private void updateSignature(Signature sig, byte[] clntNonce,
-            byte[] svrNonce) throws SignatureException {
-        sig.update(clntNonce);
-        sig.update(svrNonce);
-
-        sig.update((byte)CURVE_NAMED_CURVE);
-        sig.update((byte)(groupId >> 8));
-        sig.update((byte)groupId);
-        sig.update((byte)pointBytes.length);
-        sig.update(pointBytes);
-    }
-
-    @Override
-    int messageLength() {
-        int sigLen = 0;
-        if (signatureBytes != null) {
-            sigLen = 2 + signatureBytes.length;
-            if (protocolVersion.useTLS12PlusSpec()) {
-                sigLen += SignatureAndHashAlgorithm.sizeInRecord();
-            }
-        }
-
-        return 4 + pointBytes.length + sigLen;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt8(CURVE_NAMED_CURVE);
-        s.putInt16(groupId);
-        s.putBytes8(pointBytes);
-
-        if (signatureBytes != null) {
-            if (protocolVersion.useTLS12PlusSpec()) {
-                s.putInt8(preferableSignatureAlgorithm.getHashValue());
-                s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
-            }
-
-            s.putBytes16(signatureBytes);
-        }
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** ECDH ServerKeyExchange");
-
-        if (debug != null && Debug.isOn("verbose")) {
-            if (signatureBytes == null) {
-                s.println("Anonymous");
-            } else {
-                if (protocolVersion.useTLS12PlusSpec()) {
-                    s.println("Signature Algorithm " +
-                            preferableSignatureAlgorithm.getAlgorithmName());
-                }
-            }
-
-            s.println("Server key: " + publicKey);
-        }
-    }
-}
-
-static final class DistinguishedName {
-
-    /*
-     * DER encoded distinguished name.
-     * TLS requires that its not longer than 65535 bytes.
-     */
-    byte[] name;
-
-    DistinguishedName(HandshakeInStream input) throws IOException {
-        name = input.getBytes16();
-    }
-
-    DistinguishedName(X500Principal dn) {
-        name = dn.getEncoded();
-    }
-
-    X500Principal getX500Principal() throws IOException {
-        try {
-            return new X500Principal(name);
-        } catch (IllegalArgumentException e) {
-            throw (SSLProtocolException)new SSLProtocolException(
-                e.getMessage()).initCause(e);
-        }
-    }
-
-    int length() {
-        return 2 + name.length;
-    }
-
-    void send(HandshakeOutStream output) throws IOException {
-        output.putBytes16(name);
-    }
-
-    void print(PrintStream output) throws IOException {
-        X500Principal principal = new X500Principal(name);
-        output.println("<" + principal.toString() + ">");
-    }
-}
-
-/*
- * CertificateRequest ... SERVER --> CLIENT
- *
- * Authenticated servers may ask clients to authenticate themselves
- * in turn, using this message.
- *
- * Prior to TLS 1.2, the structure of the message is defined as:
- *     struct {
- *         ClientCertificateType certificate_types<1..2^8-1>;
- *         DistinguishedName certificate_authorities<0..2^16-1>;
- *     } CertificateRequest;
- *
- * In TLS 1.2, the structure is changed to:
- *     struct {
- *         ClientCertificateType certificate_types<1..2^8-1>;
- *         SignatureAndHashAlgorithm
- *           supported_signature_algorithms<2^16-1>;
- *         DistinguishedName certificate_authorities<0..2^16-1>;
- *     } CertificateRequest;
- *
- */
-static final
-class CertificateRequest extends HandshakeMessage
-{
-    // enum ClientCertificateType
-    static final int   cct_rsa_sign = 1;
-    static final int   cct_dss_sign = 2;
-    static final int   cct_rsa_fixed_dh = 3;
-    static final int   cct_dss_fixed_dh = 4;
-
-    // The existance of these two values is a bug in the SSL specification.
-    // They are never used in the protocol.
-    static final int   cct_rsa_ephemeral_dh = 5;
-    static final int   cct_dss_ephemeral_dh = 6;
-
-    // From RFC 4492 (ECC)
-    static final int    cct_ecdsa_sign       = 64;
-    static final int    cct_rsa_fixed_ecdh   = 65;
-    static final int    cct_ecdsa_fixed_ecdh = 66;
-
-    private static final byte[] TYPES_NO_ECC = { cct_rsa_sign, cct_dss_sign };
-    private static final byte[] TYPES_ECC =
-        { cct_rsa_sign, cct_dss_sign, cct_ecdsa_sign };
-
-    byte[]                types;               // 1 to 255 types
-    DistinguishedName[]   authorities;         // 3 to 2^16 - 1
-        // ... "3" because that's the smallest DER-encoded X500 DN
-
-    // protocol version being established using this CertificateRequest message
-    ProtocolVersion protocolVersion;
-
-    // supported_signature_algorithms for TLS 1.2 or later
-    private Collection<SignatureAndHashAlgorithm> algorithms;
-
-    // length of supported_signature_algorithms
-    private int algorithmsLen;
-
-    CertificateRequest(X509Certificate[] ca, KeyExchange keyExchange,
-            Collection<SignatureAndHashAlgorithm> signAlgs,
-            ProtocolVersion protocolVersion) throws IOException {
-
-        this.protocolVersion = protocolVersion;
-
-        // always use X500Principal
-        authorities = new DistinguishedName[ca.length];
-        for (int i = 0; i < ca.length; i++) {
-            X500Principal x500Principal = ca[i].getSubjectX500Principal();
-            authorities[i] = new DistinguishedName(x500Principal);
-        }
-        // we support RSA, DSS, and ECDSA client authentication and they
-        // can be used with all ciphersuites. If this changes, the code
-        // needs to be adapted to take keyExchange into account.
-        // We only request ECDSA client auth if we have ECC crypto available.
-        this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC;
-
-        // Use supported_signature_algorithms for TLS 1.2 or later.
-        if (protocolVersion.useTLS12PlusSpec()) {
-            if (signAlgs == null || signAlgs.isEmpty()) {
-                throw new SSLProtocolException(
-                        "No supported signature algorithms");
-            }
-
-            algorithms = new ArrayList<SignatureAndHashAlgorithm>(signAlgs);
-            algorithmsLen =
-                SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size();
-        } else {
-            algorithms = new ArrayList<SignatureAndHashAlgorithm>();
-            algorithmsLen = 0;
-        }
-    }
-
-    CertificateRequest(HandshakeInStream input,
-            ProtocolVersion protocolVersion) throws IOException {
-
-        this.protocolVersion = protocolVersion;
-
-        // Read the certificate_types.
-        types = input.getBytes8();
-
-        // Read the supported_signature_algorithms for TLS 1.2 or later.
-        if (protocolVersion.useTLS12PlusSpec()) {
-            algorithmsLen = input.getInt16();
-            if (algorithmsLen < 2) {
-                throw new SSLProtocolException(
-                    "Invalid supported_signature_algorithms field: " +
-                    algorithmsLen);
-            }
-
-            algorithms = new ArrayList<SignatureAndHashAlgorithm>();
-            int remains = algorithmsLen;
-            int sequence = 0;
-            while (remains > 1) {    // needs at least two bytes
-                int hash = input.getInt8();         // hash algorithm
-                int signature = input.getInt8();    // signature algorithm
-
-                SignatureAndHashAlgorithm algorithm =
-                    SignatureAndHashAlgorithm.valueOf(hash, signature,
-                                                                ++sequence);
-                algorithms.add(algorithm);
-                remains -= 2;  // one byte for hash, one byte for signature
-            }
-
-            if (remains != 0) {
-                throw new SSLProtocolException(
-                    "Invalid supported_signature_algorithms field. remains: " +
-                    remains);
-            }
-        } else {
-            algorithms = new ArrayList<SignatureAndHashAlgorithm>();
-            algorithmsLen = 0;
-        }
-
-        // read the certificate_authorities
-        int len = input.getInt16();
-        ArrayList<DistinguishedName> v = new ArrayList<>();
-        while (len >= 3) {
-            DistinguishedName dn = new DistinguishedName(input);
-            v.add(dn);
-            len -= dn.length();
-        }
-
-        if (len != 0) {
-            throw new SSLProtocolException(
-                "Bad CertificateRequest DN length: " + len);
-        }
-
-        authorities = v.toArray(new DistinguishedName[v.size()]);
-    }
-
-    X500Principal[] getAuthorities() throws IOException {
-        X500Principal[] ret = new X500Principal[authorities.length];
-        for (int i = 0; i < authorities.length; i++) {
-            ret[i] = authorities[i].getX500Principal();
-        }
-        return ret;
-    }
-
-    Collection<SignatureAndHashAlgorithm> getSignAlgorithms() {
-        return algorithms;
-    }
-
-    @Override
-    int messageType() {
-        return ht_certificate_request;
-    }
-
-    @Override
-    int messageLength() {
-        int len = 1 + types.length + 2;
-
-        if (protocolVersion.useTLS12PlusSpec()) {
-            len += algorithmsLen + 2;
-        }
-
-        for (int i = 0; i < authorities.length; i++) {
-            len += authorities[i].length();
-        }
-
-        return len;
-    }
-
-    @Override
-    void send(HandshakeOutStream output) throws IOException {
-        // put certificate_types
-        output.putBytes8(types);
-
-        // put supported_signature_algorithms
-        if (protocolVersion.useTLS12PlusSpec()) {
-            output.putInt16(algorithmsLen);
-            for (SignatureAndHashAlgorithm algorithm : algorithms) {
-                output.putInt8(algorithm.getHashValue());      // hash
-                output.putInt8(algorithm.getSignatureValue()); // signature
-            }
-        }
-
-        // put certificate_authorities
-        int len = 0;
-        for (int i = 0; i < authorities.length; i++) {
-            len += authorities[i].length();
-        }
-
-        output.putInt16(len);
-        for (int i = 0; i < authorities.length; i++) {
-            authorities[i].send(output);
-        }
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** CertificateRequest");
-
-        if (debug != null && Debug.isOn("verbose")) {
-            s.print("Cert Types: ");
-            for (int i = 0; i < types.length; i++) {
-                switch (types[i]) {
-                  case cct_rsa_sign:
-                    s.print("RSA"); break;
-                  case cct_dss_sign:
-                    s.print("DSS"); break;
-                  case cct_rsa_fixed_dh:
-                    s.print("Fixed DH (RSA sig)"); break;
-                  case cct_dss_fixed_dh:
-                    s.print("Fixed DH (DSS sig)"); break;
-                  case cct_rsa_ephemeral_dh:
-                    s.print("Ephemeral DH (RSA sig)"); break;
-                  case cct_dss_ephemeral_dh:
-                    s.print("Ephemeral DH (DSS sig)"); break;
-                  case cct_ecdsa_sign:
-                    s.print("ECDSA"); break;
-                  case cct_rsa_fixed_ecdh:
-                    s.print("Fixed ECDH (RSA sig)"); break;
-                  case cct_ecdsa_fixed_ecdh:
-                    s.print("Fixed ECDH (ECDSA sig)"); break;
-                  default:
-                    s.print("Type-" + (types[i] & 0xff)); break;
-                }
-                if (i != types.length - 1) {
-                    s.print(", ");
-                }
-            }
-            s.println();
-
-            if (protocolVersion.useTLS12PlusSpec()) {
-                StringBuilder sb = new StringBuilder();
-                boolean opened = false;
-                for (SignatureAndHashAlgorithm signAlg : algorithms) {
-                    if (opened) {
-                        sb.append(", ").append(signAlg.getAlgorithmName());
-                    } else {
-                        sb.append(signAlg.getAlgorithmName());
-                        opened = true;
-                    }
-                }
-                s.println("Supported Signature Algorithms: " + sb);
-            }
-
-            s.println("Cert Authorities:");
-            if (authorities.length == 0) {
-                s.println("<Empty>");
-            } else {
-                for (int i = 0; i < authorities.length; i++) {
-                    authorities[i].print(s);
-                }
-            }
-        }
-    }
-}
-
-
-/*
- * ServerHelloDone ... SERVER --> CLIENT
- *
- * When server's done sending its messages in response to the client's
- * "hello" (e.g. its own hello, certificate, key exchange message, perhaps
- * client certificate request) it sends this message to flag that it's
- * done that part of the handshake.
- */
-static final
-class ServerHelloDone extends HandshakeMessage
-{
-    @Override
-    int messageType() { return ht_server_hello_done; }
-
-    ServerHelloDone() { }
-
-    ServerHelloDone(HandshakeInStream input)
-    {
-        // nothing to do
-    }
-
-    @Override
-    int messageLength()
-    {
-        return 0;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException
-    {
-        // nothing to send
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException
-    {
-        s.println("*** ServerHelloDone");
-    }
-}
-
-
-/*
- * CertificateVerify ... CLIENT --> SERVER
- *
- * Sent after client sends signature-capable certificates (e.g. not
- * Diffie-Hellman) to verify.
- */
-static final class CertificateVerify extends HandshakeMessage {
-
-    // the signature bytes
-    private byte[] signature;
-
-    // protocol version being established using this CertificateVerify message
-    ProtocolVersion protocolVersion;
-
-    // the preferable signature algorithm used by this CertificateVerify message
-    private SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
-
-    /*
-     * Create an RSA or DSA signed certificate verify message.
-     */
-    CertificateVerify(ProtocolVersion protocolVersion,
-            HandshakeHash handshakeHash, PrivateKey privateKey,
-            SecretKey masterSecret, SecureRandom sr,
-            SignatureAndHashAlgorithm signAlgorithm)
-            throws GeneralSecurityException {
-
-        this.protocolVersion = protocolVersion;
-
-        String algorithm = privateKey.getAlgorithm();
-        Signature sig = null;
-        if (protocolVersion.useTLS12PlusSpec()) {
-            this.preferableSignatureAlgorithm = signAlgorithm;
-            sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
-        } else {
-            sig = getSignature(protocolVersion, algorithm);
-        }
-        sig.initSign(privateKey, sr);
-        updateSignature(sig, protocolVersion, handshakeHash, algorithm,
-                        masterSecret);
-        signature = sig.sign();
-    }
-
-    //
-    // Unmarshal the signed data from the input stream.
-    //
-    CertificateVerify(HandshakeInStream input,
-            Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs,
-            ProtocolVersion protocolVersion) throws IOException  {
-
-        this.protocolVersion = protocolVersion;
-
-        // read the signature and hash algorithm
-        if (protocolVersion.useTLS12PlusSpec()) {
-            int hashAlg = input.getInt8();         // hash algorithm
-            int signAlg = input.getInt8();         // signature algorithm
-
-            preferableSignatureAlgorithm =
-                SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0);
-
-            // Is it a local supported signature algorithm?
-            if (!localSupportedSignAlgs.contains(
-                    preferableSignatureAlgorithm)) {
-                throw new SSLHandshakeException(
-                    "Unsupported SignatureAndHashAlgorithm in " +
-                    "CertificateVerify message: " + preferableSignatureAlgorithm);
-            }
-        }
-
-        // read the signature
-        signature = input.getBytes16();
-    }
-
-    /*
-     * Get the preferable signature algorithm used by this message
-     */
-    SignatureAndHashAlgorithm getPreferableSignatureAlgorithm() {
-        return preferableSignatureAlgorithm;
-    }
-
-    /*
-     * Verify a certificate verify message. Return the result of verification,
-     * if there is a problem throw a GeneralSecurityException.
-     */
-    boolean verify(ProtocolVersion protocolVersion,
-            HandshakeHash handshakeHash, PublicKey publicKey,
-            SecretKey masterSecret) throws GeneralSecurityException {
-        String algorithm = publicKey.getAlgorithm();
-        Signature sig = null;
-        if (protocolVersion.useTLS12PlusSpec()) {
-            sig = JsseJce.getSignature(
-                        preferableSignatureAlgorithm.getAlgorithmName());
-        } else {
-            sig = getSignature(protocolVersion, algorithm);
-        }
-        sig.initVerify(publicKey);
-        updateSignature(sig, protocolVersion, handshakeHash, algorithm,
-                        masterSecret);
-        return sig.verify(signature);
-    }
-
-    /*
-     * Get the Signature object appropriate for verification using the
-     * given signature algorithm and protocol version.
-     */
-    private static Signature getSignature(ProtocolVersion protocolVersion,
-            String algorithm) throws GeneralSecurityException {
-            switch (algorithm) {
-                case "RSA":
-                    return RSASignature.getInternalInstance();
-                case "DSA":
-                    return JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA);
-                case "EC":
-                    return JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA);
-                default:
-                    throw new SignatureException("Unrecognized algorithm: "
-                        + algorithm);
-            }
-    }
-
-    /*
-     * Update the Signature with the data appropriate for the given
-     * signature algorithm and protocol version so that the object is
-     * ready for signing or verifying.
-     */
-    private static void updateSignature(Signature sig,
-            ProtocolVersion protocolVersion,
-            HandshakeHash handshakeHash, String algorithm, SecretKey masterKey)
-            throws SignatureException {
-
-        if (algorithm.equals("RSA")) {
-            if (!protocolVersion.useTLS12PlusSpec()) {  // TLS1.1-
-                MessageDigest md5Clone = handshakeHash.getMD5Clone();
-                MessageDigest shaClone = handshakeHash.getSHAClone();
-
-                if (!protocolVersion.useTLS10PlusSpec()) {  // SSLv3
-                    updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey);
-                    updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
-                }
-
-                // The signature must be an instance of RSASignature, need
-                // to use these hashes directly.
-                RSASignature.setHashes(sig, md5Clone, shaClone);
-            } else {  // TLS1.2+
-                sig.update(handshakeHash.getAllHandshakeMessages());
-            }
-        } else { // DSA, ECDSA
-            if (!protocolVersion.useTLS12PlusSpec()) {  // TLS1.1-
-                MessageDigest shaClone = handshakeHash.getSHAClone();
-
-                if (!protocolVersion.useTLS10PlusSpec()) {  // SSLv3
-                    updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
-                }
-
-                sig.update(shaClone.digest());
-            } else {  // TLS1.2+
-                sig.update(handshakeHash.getAllHandshakeMessages());
-            }
-        }
-    }
-
-    /*
-     * Update the MessageDigest for SSLv3 certificate verify or finished
-     * message calculation. The digest must already have been updated with
-     * all preceding handshake messages.
-     * Used by the Finished class as well.
-     */
-    private static void updateDigest(MessageDigest md,
-            byte[] pad1, byte[] pad2,
-            SecretKey masterSecret) {
-        // Digest the key bytes if available.
-        // Otherwise (sensitive key), try digesting the key directly.
-        // That is currently only implemented in SunPKCS11 using a private
-        // reflection API, so we avoid that if possible.
-        byte[] keyBytes = "RAW".equals(masterSecret.getFormat())
-                        ? masterSecret.getEncoded() : null;
-        if (keyBytes != null) {
-            md.update(keyBytes);
-        } else {
-            digestKey(md, masterSecret);
-        }
-        md.update(pad1);
-        byte[] temp = md.digest();
-
-        if (keyBytes != null) {
-            md.update(keyBytes);
-        } else {
-            digestKey(md, masterSecret);
-        }
-        md.update(pad2);
-        md.update(temp);
-    }
-
-    private static void digestKey(MessageDigest md, SecretKey key) {
-        try {
-            if (md instanceof MessageDigestSpi2) {
-                ((MessageDigestSpi2)md).engineUpdate(key);
-            } else {
-                throw new Exception(
-                    "Digest does not support implUpdate(SecretKey)");
-            }
-        } catch (Exception e) {
-            throw new RuntimeException(
-                "Could not obtain encoded key and "
-                + "MessageDigest cannot digest key", e);
-        }
-    }
-
-    @Override
-    int messageType() {
-        return ht_certificate_verify;
-    }
-
-    @Override
-    int messageLength() {
-        int temp = 2;
-
-        if (protocolVersion.useTLS12PlusSpec()) {
-            temp += SignatureAndHashAlgorithm.sizeInRecord();
-        }
-
-        return temp + signature.length;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        if (protocolVersion.useTLS12PlusSpec()) {
-            s.putInt8(preferableSignatureAlgorithm.getHashValue());
-            s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
-        }
-
-        s.putBytes16(signature);
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** CertificateVerify");
-
-        if (debug != null && Debug.isOn("verbose")) {
-            if (protocolVersion.useTLS12PlusSpec()) {
-                s.println("Signature Algorithm " +
-                        preferableSignatureAlgorithm.getAlgorithmName());
-            }
-        }
-    }
-}
-
-
-/*
- * FINISHED ... sent by both CLIENT and SERVER
- *
- * This is the FINISHED message as defined in the SSL and TLS protocols.
- * Both protocols define this handshake message slightly differently.
- * This class supports both formats.
- *
- * When handshaking is finished, each side sends a "change_cipher_spec"
- * record, then immediately sends a "finished" handshake message prepared
- * according to the newly adopted cipher spec.
- *
- * NOTE that until this is sent, no application data may be passed, unless
- * some non-default cipher suite has already been set up on this connection
- * connection (e.g. a previous handshake arranged one).
- */
-static final class Finished extends HandshakeMessage {
-
-    // constant for a Finished message sent by the client
-    static final int CLIENT = 1;
-
-    // constant for a Finished message sent by the server
-    static final int SERVER = 2;
-
-    // enum Sender:  "CLNT" and "SRVR"
-    private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };
-    private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };
-
-    /*
-     * Contents of the finished message ("checksum"). For TLS, it
-     * is 12 bytes long, for SSLv3 36 bytes.
-     */
-    private byte[] verifyData;
-
-    /*
-     * Current cipher suite we are negotiating.  TLS 1.2 has
-     * ciphersuite-defined PRF algorithms.
-     */
-    private ProtocolVersion protocolVersion;
-    private CipherSuite cipherSuite;
-
-    /*
-     * Create a finished message to send to the remote peer.
-     */
-    Finished(ProtocolVersion protocolVersion, HandshakeHash handshakeHash,
-            int sender, SecretKey master, CipherSuite cipherSuite) {
-        this.protocolVersion = protocolVersion;
-        this.cipherSuite = cipherSuite;
-        verifyData = getFinished(handshakeHash, sender, master);
-    }
-
-    /*
-     * Constructor that reads FINISHED message from stream.
-     */
-    Finished(ProtocolVersion protocolVersion, HandshakeInStream input,
-            CipherSuite cipherSuite) throws IOException {
-        this.protocolVersion = protocolVersion;
-        this.cipherSuite = cipherSuite;
-        int msgLen = protocolVersion.useTLS10PlusSpec() ?  12 : 36;
-        verifyData = new byte[msgLen];
-        input.read(verifyData);
-    }
-
-    /*
-     * Verify that the hashes here are what would have been produced
-     * according to a given set of inputs.  This is used to ensure that
-     * both client and server are fully in sync, and that the handshake
-     * computations have been successful.
-     */
-    boolean verify(HandshakeHash handshakeHash, int sender, SecretKey master) {
-        byte[] myFinished = getFinished(handshakeHash, sender, master);
-        return MessageDigest.isEqual(myFinished, verifyData);
-    }
-
-    /*
-     * Perform the actual finished message calculation.
-     */
-    private byte[] getFinished(HandshakeHash handshakeHash,
-            int sender, SecretKey masterKey) {
-        byte[] sslLabel;
-        String tlsLabel;
-        if (sender == CLIENT) {
-            sslLabel = SSL_CLIENT;
-            tlsLabel = "client finished";
-        } else if (sender == SERVER) {
-            sslLabel = SSL_SERVER;
-            tlsLabel = "server finished";
-        } else {
-            throw new RuntimeException("Invalid sender: " + sender);
-        }
-
-        if (protocolVersion.useTLS10PlusSpec()) {
-            // TLS 1.0+
-            try {
-                byte[] seed;
-                String prfAlg;
-                PRF prf;
-
-                // Get the KeyGenerator alg and calculate the seed.
-                if (protocolVersion.useTLS12PlusSpec()) {
-                    // TLS 1.2+ or DTLS 1.2+
-                    seed = handshakeHash.getFinishedHash();
-
-                    prfAlg = "SunTls12Prf";
-                    prf = cipherSuite.prfAlg;
-                } else {
-                    // TLS 1.0/1.1, DTLS 1.0
-                    MessageDigest md5Clone = handshakeHash.getMD5Clone();
-                    MessageDigest shaClone = handshakeHash.getSHAClone();
-                    seed = new byte[36];
-                    md5Clone.digest(seed, 0, 16);
-                    shaClone.digest(seed, 16, 20);
-
-                    prfAlg = "SunTlsPrf";
-                    prf = P_NONE;
-                }
-
-                String prfHashAlg = prf.getPRFHashAlg();
-                int prfHashLength = prf.getPRFHashLength();
-                int prfBlockSize = prf.getPRFBlockSize();
-
-                /*
-                 * RFC 5246/7.4.9 says that finished messages can
-                 * be ciphersuite-specific in both length/PRF hash
-                 * algorithm.  If we ever run across a different
-                 * length, this call will need to be updated.
-                 */
-                @SuppressWarnings("deprecation")
-                TlsPrfParameterSpec spec = new TlsPrfParameterSpec(
-                    masterKey, tlsLabel, seed, 12,
-                    prfHashAlg, prfHashLength, prfBlockSize);
-
-                KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg);
-                kg.init(spec);
-                SecretKey prfKey = kg.generateKey();
-                if ("RAW".equals(prfKey.getFormat()) == false) {
-                    throw new ProviderException(
-                        "Invalid PRF output, format must be RAW. " +
-                        "Format received: " + prfKey.getFormat());
-                }
-                byte[] finished = prfKey.getEncoded();
-                return finished;
-            } catch (GeneralSecurityException e) {
-                throw new RuntimeException("PRF failed", e);
-            }
-        } else {
-            // SSLv3
-            MessageDigest md5Clone = handshakeHash.getMD5Clone();
-            MessageDigest shaClone = handshakeHash.getSHAClone();
-            updateDigest(md5Clone, sslLabel, MD5_pad1, MD5_pad2, masterKey);
-            updateDigest(shaClone, sslLabel, SHA_pad1, SHA_pad2, masterKey);
-            byte[] finished = new byte[36];
-            try {
-                md5Clone.digest(finished, 0, 16);
-                shaClone.digest(finished, 16, 20);
-            } catch (DigestException e) {
-                // cannot occur
-                throw new RuntimeException("Digest failed", e);
-            }
-            return finished;
-        }
-    }
-
-    /*
-     * Update the MessageDigest for SSLv3 finished message calculation.
-     * The digest must already have been updated with all preceding handshake
-     * messages. This operation is almost identical to the certificate verify
-     * hash, reuse that code.
-     */
-    private static void updateDigest(MessageDigest md, byte[] sender,
-            byte[] pad1, byte[] pad2, SecretKey masterSecret) {
-        md.update(sender);
-        CertificateVerify.updateDigest(md, pad1, pad2, masterSecret);
-    }
-
-    // get the verify_data of the finished message
-    byte[] getVerifyData() {
-        return verifyData;
-    }
-
-    @Override
-    int messageType() { return ht_finished; }
-
-    @Override
-    int messageLength() {
-        return verifyData.length;
-    }
-
-    @Override
-    void send(HandshakeOutStream out) throws IOException {
-        out.write(verifyData);
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        s.println("*** Finished");
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(s, "verify_data", verifyData);
-            s.println("***");
-        }
-    }
-}
-
-//
-// END of nested classes
-//
-
-}
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -56,11 +56,12 @@
             throw new RuntimeException("handshake message is not available");
         }
 
-        // outputRecord cannot be null
-        outputRecord.encodeHandshake(buf, 0, count);
+        if (outputRecord != null) {
+            outputRecord.encodeHandshake(buf, 0, count);
 
-        // reset the byte array output stream
-        reset();
+            // reset the byte array output stream
+            reset();
+        }   // otherwise, the handshake outstream is temporarily used only.
     }
 
     //
@@ -76,7 +77,9 @@
 
     @Override
     public void flush() throws IOException {
-        outputRecord.flush();
+        if (outputRecord != null) {
+            outputRecord.flush();
+        }
     }
 
     //
@@ -106,6 +109,13 @@
         super.write(i);
     }
 
+    void putInt32(int i) throws IOException {
+        super.write(i >> 24);
+        super.write(i >> 16);
+        super.write(i >> 8);
+        super.write(i);
+    }
+
     /*
      * Put byte arrays with length encoded as 8, 16, 24 bit
      * integers in big-endian format.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeProducer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+interface HandshakeProducer {
+    // return the encoded producing if it has not been dumped to the context
+    //
+    // message: the handshake message responded to, can be null for producing
+    //          of kickstart handshake message
+    byte[] produce(ConnectionContext context,
+            HandshakeMessage message) throws IOException;
+}
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,922 +0,0 @@
-/*
- * Copyright (c) 2015, 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.util.Collections;
-import java.util.List;
-import java.util.LinkedList;
-import java.util.HashMap;
-import javax.net.ssl.SSLProtocolException;
-
-import static sun.security.ssl.CipherSuite.KeyExchange;
-import static sun.security.ssl.CipherSuite.KeyExchange.*;
-import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
-import static sun.security.ssl.HandshakeMessage.*;
-
-/*
- * Handshake state manager.
- *
- * Messages flow for a full handshake:
- *
- *      -                                                         -
- *      |          HelloRequest       (No.0, RFC 5246) [*]        |
- *      |     <--------------------------------------------       |
- *      |                                                         |
- *      |          ClientHello        (No.1, RFC 5246)            |
- *      |     -------------------------------------------->       |
- *      |                                                         |
- *      |   -      HelloVerifyRequest (No.3, RFC 6347)      -     |
- *      | D | <-------------------------------------------- | D   |
- *      | T |                                               | T   |
- *      | L |      ClientHello        (No.1, RFC 5246)      | L   |
- *      | S | --------------------------------------------> | S   |
- *      |   -                                               -     |
- *      |                                                         |
- *   C  |          ServerHello        (No.2, RFC 5246)            |  S
- *   L  |          SupplementalData   (No.23, RFC4680) [*]        |  E
- *   I  |          Certificate        (No.11, RFC 5246) [*]       |  R
- *   E  |          CertificateStatus  (No.22, RFC 6066) [*]       |  V
- *   N  |          ServerKeyExchange  (No.12, RFC 5246) [*]       |  E
- *   T  |          CertificateRequest (No.13, RFC 5246) [*]       |  R
- *      |          ServerHelloDone    (No.14, RFC 5246)           |
- *      |     <--------------------------------------------       |
- *      |                                                         |
- *      |          SupplementalData   (No.23, RFC4680) [*]        |
- *      |          Certificate        (No.11, RFC 5246) [*] Or    |
- *      |              CertificateURL (No.21, RFC6066) [*]        |
- *      |          ClientKeyExchange  (No.16, RFC 5246)           |
- *      |          CertificateVerify  (No.15, RFC 5246) [*]       |
- *      |          [ChangeCipherSpec] (RFC 5246)                  |
- *      |          Finished           (No.20, RFC 5246)           |
- *      |     -------------------------------------------->       |
- *      |                                                         |
- *      |          NewSessionTicket   (No.4, RFC4507) [*]         |
- *      |          [ChangeCipherSpec] (RFC 5246)                  |
- *      |          Finished           (No.20, RFC 5246)           |
- *      |     <--------------------------------------------       |
- *      -                                                         -
- * [*] Indicates optional or situation-dependent messages that are not
- * always sent.
- *
- * Message flow for an abbreviated handshake:
- *      -                                                         -
- *      |          ClientHello        (No.1, RFC 5246)            |
- *      |     -------------------------------------------->       |
- *      |                                                         |
- *   C  |          ServerHello        (No.2, RFC 5246)            |  S
- *   L  |          NewSessionTicket   (No.4, RFC4507) [*]         |  E
- *   I  |          [ChangeCipherSpec] (RFC 5246)                  |  R
- *   E  |          Finished           (No.20, RFC 5246)           |  V
- *   N  |     <--------------------------------------------       |  E
- *   T  |                                                         |  R
- *      |          [ChangeCipherSpec] (RFC 5246)                  |
- *      |          Finished           (No.20, RFC 5246)           |
- *      |     -------------------------------------------->       |
- *      -                                                         -
- *
- *
- * State machine of handshake states:
- *
- *                   +--------------+
- *      START -----> | HelloRequest |
- *        |          +--------------+
- *        |               |
- *        v               v
- *     +---------------------+   -->  +---------------------+
- *     |    ClientHello      |        | HelloVerifyRequest  |
- *     +---------------------+   <--  +---------------------+
- *               |
- *               |
- * =========================================================================
- *               |
- *               v
- *     +---------------------+
- *     |    ServerHello      |  ----------------------------------+------+
- *     +---------------------+  -->  +-------------------------+  |      |
- *                    |              | Server SupplementalData |  |      |
- *                    |              +-------------------------+  |      |
- *                    |                |                          |      |
- *                    v                v                          |      |
- *                +---------------------+                         |      |
- *         +----  | Server Certificate  |                         |      |
- *         |      +---------------------+                         |      |
- *         |          |                                           |      |
- *         |          |   +--------------------+                  |      |
- *         |          +-> | CertificateStatus  |                  |      |
- *         |          |   +--------------------+                  v      |
- *         |          |      |          |     +--------------------+     |
- *         |          v      v          +-->  | ServerKeyExchange  |     |
- *         |  +---------------------+   |     +--------------------+     |
- *         |  | CertificateRequest  |   |         |                      |
- *         |  +---------------------+ <-+---------+                      |
- *         |            |               |         |                      |
- *         v            v               |         |                      |
- *     +---------------------+  <-------+         |                      |
- *     |  ServerHelloDone    |  <-----------------+                      |
- *     +---------------------+                                           |
- *       |         |                                                     |
- *       |         |                                                     |
- *       |         |                                                     |
- * =========================================================================
- *       |         |                                                     |
- *       |         v                                                     |
- *       |   +-------------------------+                                 |
- *       |   | Client SupplementalData | --------------+                 |
- *       |   +-------------------------+               |                 |
- *       |             |                               |                 |
- *       |             v                               |                 |
- *       |   +--------------------+                    |                 |
- *       +-> | Client Certificate | ALT.               |                 |
- *       |   +--------------------+----------------+   |                 |
- *       |                        | CertificateURL |   |                 |
- *       |                        +----------------+   |                 |
- *       v                                             |                 |
- *     +-------------------+  <------------------------+                 |
- *     | ClientKeyExchange |                                             |
- *     +-------------------+                                             |
- *          |           |                                                |
- *          |           v                                                |
- *          |      +-------------------+                                 |
- *          |      | CertificateVerify |                                 |
- *          |      +-------------------+                                 |
- *          |          |                                                 |
- *          v          v                                                 |
- *     +-------------------------+                                       |
- *     | Client ChangeCipherSpec |  <---------------+                    |
- *     +-------------------------+                  |                    |
- *               |                                  |                    |
- *               v                                  |                    |
- *     +-----------------+  (abbreviated)           |                    |
- *     | Client Finished |  -------------> END      |                    |
- *     +-----------------+  (Abbreviated handshake) |                    |
- *                      |                           |                    |
- *                      | (full)                    |                    |
- *                      |                           |                    |
- * ================================                 |                    |
- *                      |                           |                    |
- *                      |                   ================================
- *                      |                           |                    |
- *                      v                           |                    |
- *                 +------------------+             |    (abbreviated)   |
- *                 | NewSessionTicket | <--------------------------------+
- *                 +------------------+             |                    |
- *                      |                           |                    |
- *                      v                           |                    |
- *     +-------------------------+                  |    (abbreviated)   |
- *     | Server ChangeCipherSpec | <-------------------------------------+
- *     +-------------------------+                  |
- *               |                                  |
- *               v                                  |
- *     +-----------------+    (abbreviated)         |
- *     | Server Finished | -------------------------+
- *     +-----------------+
- *            | (full)
- *            v
- *        END (Full handshake)
- *
- *
- * The scenarios of the use of this class:
- * 1. Create an instance of HandshakeStateManager during the initializtion
- *    handshake.
- * 2. If receiving a handshake message, call HandshakeStateManager.check()
- *    to make sure that the message is of the expected handshake type.  And
- *    then call HandshakeStateManager.update() in case handshake states may
- *    be impacted by this new incoming handshake message.
- * 3. On delivering a handshake message, call HandshakeStateManager.update()
- *    in case handshake states may by thie new outgoing handshake message.
- * 4. On receiving and delivering ChangeCipherSpec message, call
- *    HandshakeStateManager.changeCipherSpec() to check the present sequence
- *    of this message, and update the states if necessary.
- */
-final class HandshakeStateManager {
-    // upcoming handshake states.
-    private LinkedList<HandshakeState> upcomingStates;
-    private LinkedList<HandshakeState> alternatives;
-
-    private boolean isDTLS;
-
-    private static final boolean debugIsOn;
-
-    private static final HashMap<Byte, String> handshakeTypes;
-
-    static {
-        debugIsOn = (Handshaker.debug != null) &&
-                Debug.isOn("handshake") && Debug.isOn("verbose");
-        handshakeTypes = new HashMap<>(15);
-
-        handshakeTypes.put(ht_hello_request,            "hello_request");
-        handshakeTypes.put(ht_client_hello,             "client_hello");
-        handshakeTypes.put(ht_server_hello,             "server_hello");
-        handshakeTypes.put(ht_hello_verify_request,     "hello_verify_request");
-        handshakeTypes.put(ht_new_session_ticket,       "session_ticket");
-        handshakeTypes.put(ht_certificate,              "certificate");
-        handshakeTypes.put(ht_server_key_exchange,      "server_key_exchange");
-        handshakeTypes.put(ht_certificate_request,      "certificate_request");
-        handshakeTypes.put(ht_server_hello_done,        "server_hello_done");
-        handshakeTypes.put(ht_certificate_verify,       "certificate_verify");
-        handshakeTypes.put(ht_client_key_exchange,      "client_key_exchange");
-        handshakeTypes.put(ht_finished,                 "finished");
-        handshakeTypes.put(ht_certificate_url,          "certificate_url");
-        handshakeTypes.put(ht_certificate_status,       "certificate_status");
-        handshakeTypes.put(ht_supplemental_data,        "supplemental_data");
-    }
-
-    HandshakeStateManager(boolean isDTLS) {
-        this.upcomingStates = new LinkedList<>();
-        this.alternatives = new LinkedList<>();
-        this.isDTLS = isDTLS;
-    }
-
-    //
-    // enumation of handshake type
-    //
-    static enum HandshakeState {
-        HS_HELLO_REQUEST(
-                "hello_request",
-                HandshakeMessage.ht_hello_request),
-        HS_CLIENT_HELLO(
-                "client_hello",
-                HandshakeMessage.ht_client_hello),
-        HS_HELLO_VERIFY_REQUEST(
-                "hello_verify_request",
-                HandshakeMessage.ht_hello_verify_request),
-        HS_SERVER_HELLO(
-                "server_hello",
-                HandshakeMessage.ht_server_hello),
-        HS_SERVER_SUPPLEMENTAL_DATA(
-                "server supplemental_data",
-                HandshakeMessage.ht_supplemental_data, true),
-        HS_SERVER_CERTIFICATE(
-                "server certificate",
-                HandshakeMessage.ht_certificate),
-        HS_CERTIFICATE_STATUS(
-                "certificate_status",
-                HandshakeMessage.ht_certificate_status, true),
-        HS_SERVER_KEY_EXCHANGE(
-                "server_key_exchange",
-                HandshakeMessage.ht_server_key_exchange, true),
-        HS_CERTIFICATE_REQUEST(
-                "certificate_request",
-                HandshakeMessage.ht_certificate_request, true),
-        HS_SERVER_HELLO_DONE(
-                "server_hello_done",
-                HandshakeMessage.ht_server_hello_done),
-        HS_CLIENT_SUPPLEMENTAL_DATA(
-                "client supplemental_data",
-                HandshakeMessage.ht_supplemental_data, true),
-        HS_CLIENT_CERTIFICATE(
-                "client certificate",
-                HandshakeMessage.ht_certificate, true),
-        HS_CERTIFICATE_URL(
-                "certificate_url",
-                HandshakeMessage.ht_certificate_url, true),
-        HS_CLIENT_KEY_EXCHANGE(
-                "client_key_exchange",
-                HandshakeMessage.ht_client_key_exchange),
-        HS_CERTIFICATE_VERIFY(
-                "certificate_verify",
-                HandshakeMessage.ht_certificate_verify, true),
-        HS_CLIENT_CHANGE_CIPHER_SPEC(
-                "client change_cipher_spec",
-                HandshakeMessage.ht_not_applicable),
-        HS_CLEINT_FINISHED(
-                "client finished",
-                HandshakeMessage.ht_finished),
-        HS_NEW_SESSION_TICKET(
-                "session_ticket",
-                HandshakeMessage.ht_new_session_ticket),
-        HS_SERVER_CHANGE_CIPHER_SPEC(
-                "server change_cipher_spec",
-                HandshakeMessage.ht_not_applicable),
-        HS_SERVER_FINISHED(
-                "server finished",
-                HandshakeMessage.ht_finished);
-
-        final String description;
-        final byte handshakeType;
-        final boolean isOptional;
-
-        HandshakeState(String description, byte handshakeType) {
-            this.description = description;
-            this.handshakeType = handshakeType;
-            this.isOptional = false;
-        }
-
-        HandshakeState(String description,
-                byte handshakeType, boolean isOptional) {
-
-            this.description = description;
-            this.handshakeType = handshakeType;
-            this.isOptional = isOptional;
-        }
-
-        public String toString() {
-            return description + "[" + handshakeType + "]" +
-                    (isOptional ? "(optional)" : "");
-        }
-    }
-
-    boolean isEmpty() {
-        return upcomingStates.isEmpty();
-    }
-
-    List<Byte> check(byte handshakeType) throws SSLProtocolException {
-        List<Byte> ignoredOptional = new LinkedList<>();
-        String exceptionMsg =
-                 "Handshake message sequence violation, " + handshakeType;
-
-        if (debugIsOn) {
-            System.out.println(
-                    "check handshake state: " + toString(handshakeType));
-        }
-
-        if (upcomingStates.isEmpty()) {
-            // Is it a kickstart message?
-            if ((handshakeType != HandshakeMessage.ht_hello_request) &&
-                (handshakeType != HandshakeMessage.ht_client_hello)) {
-
-                throw new SSLProtocolException(
-                    "Handshake message sequence violation, " + handshakeType);
-            }
-
-            // It is a kickstart message.
-            return Collections.emptyList();
-        }
-
-        // Ignore the checking for HelloRequest messages as they
-        // may be sent by the server at any time.
-        if (handshakeType == HandshakeMessage.ht_hello_request) {
-            return Collections.emptyList();
-        }
-
-        for (HandshakeState handshakeState : upcomingStates) {
-            if (handshakeState.handshakeType == handshakeType) {
-                // It's the expected next handshake type.
-                return ignoredOptional;
-            }
-
-            if (handshakeState.isOptional) {
-                ignoredOptional.add(handshakeState.handshakeType);
-                continue;
-            } else {
-                for (HandshakeState alternative : alternatives) {
-                    if (alternative.handshakeType == handshakeType) {
-                        return ignoredOptional;
-                    }
-
-                    if (alternative.isOptional) {
-                        continue;
-                    } else {
-                        throw new SSLProtocolException(exceptionMsg);
-                    }
-                }
-            }
-
-            throw new SSLProtocolException(exceptionMsg);
-        }
-
-        // Not an expected Handshake message.
-        throw new SSLProtocolException(
-                "Handshake message sequence violation, " + handshakeType);
-    }
-
-    void update(HandshakeMessage handshakeMessage,
-            boolean isAbbreviated) throws SSLProtocolException {
-
-        byte handshakeType = (byte)handshakeMessage.messageType();
-        String exceptionMsg =
-                 "Handshake message sequence violation, " + handshakeType;
-
-        if (debugIsOn) {
-            System.out.println(
-                    "update handshake state: " + toString(handshakeType));
-        }
-
-        boolean hasPresentState = false;
-        switch (handshakeType) {
-        case HandshakeMessage.ht_hello_request:
-            //
-            // State machine:
-            //     PRESENT: START
-            //        TO  : ClientHello
-            //
-
-            // No old state to update.
-
-            // Add the upcoming states.
-            if (!upcomingStates.isEmpty()) {
-                // A ClientHello message should be followed.
-                upcomingStates.add(HS_CLIENT_HELLO);
-
-            }   // Otherwise, ignore this HelloRequest message.
-
-            break;
-
-        case HandshakeMessage.ht_client_hello:
-            //
-            // State machine:
-            //     PRESENT: START
-            //              HS_CLIENT_HELLO
-            //        TO  : HS_HELLO_VERIFY_REQUEST (DTLS)
-            //              HS_SERVER_HELLO
-            //
-
-            // Check and update the present state.
-            if (!upcomingStates.isEmpty()) {
-                // The current state should be HS_CLIENT_HELLO.
-                HandshakeState handshakeState = upcomingStates.pop();
-                if (handshakeState != HS_CLIENT_HELLO) {
-                    throw new SSLProtocolException(exceptionMsg);
-                }
-            }
-
-            // Add the upcoming states.
-            ClientHello clientHello = (ClientHello)handshakeMessage;
-            if (isDTLS) {
-                // Is it an initial ClientHello message?
-                if (clientHello.cookie == null ||
-                        clientHello.cookie.length == 0) {
-                    // Is it an abbreviated handshake?
-                    if (clientHello.sessionId.length() != 0) {
-                        // A HelloVerifyRequest message or a ServerHello
-                        // message may follow the abbreviated session
-                        // resuming handshake request.
-                        upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
-                        alternatives.add(HS_SERVER_HELLO);
-                    } else {
-                        // A HelloVerifyRequest message should follow
-                        // the initial ClientHello message.
-                        upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
-                    }
-                } else {
-                    // A HelloVerifyRequest may be followed if the cookie
-                    // cannot be verified.
-                    upcomingStates.add(HS_SERVER_HELLO);
-                    alternatives.add(HS_HELLO_VERIFY_REQUEST);
-                }
-            } else {
-                upcomingStates.add(HS_SERVER_HELLO);
-            }
-
-            break;
-
-        case HandshakeMessage.ht_hello_verify_request:
-            //
-            // State machine:
-            //     PRESENT: HS_HELLO_VERIFY_REQUEST
-            //        TO  : HS_CLIENT_HELLO
-            //
-            // Note that this state may have an alternative option.
-
-            // Check and update the present state.
-            if (!upcomingStates.isEmpty()) {
-                // The current state should be HS_HELLO_VERIFY_REQUEST.
-                HandshakeState handshakeState = upcomingStates.pop();
-                HandshakeState alternative = null;
-                if (!alternatives.isEmpty()) {
-                    alternative = alternatives.pop();
-                }
-
-                if ((handshakeState != HS_HELLO_VERIFY_REQUEST) &&
-                        (alternative != HS_HELLO_VERIFY_REQUEST)) {
-
-                    throw new SSLProtocolException(exceptionMsg);
-                }
-            } else {
-                // No present state.
-                throw new SSLProtocolException(exceptionMsg);
-            }
-
-            // Add the upcoming states.
-            upcomingStates.add(HS_CLIENT_HELLO);
-
-            break;
-
-        case HandshakeMessage.ht_server_hello:
-            //
-            // State machine:
-            //     PRESENT: HS_SERVER_HELLO
-            //        TO  :
-            //          Full handshake state stacks
-            //              (ServerHello Flight)
-            //              HS_SERVER_SUPPLEMENTAL_DATA [optional]
-            //          --> HS_SERVER_CERTIFICATE [optional]
-            //          --> HS_CERTIFICATE_STATUS [optional]
-            //          --> HS_SERVER_KEY_EXCHANGE [optional]
-            //          --> HS_CERTIFICATE_REQUEST [optional]
-            //          --> HS_SERVER_HELLO_DONE
-            //              (Client ClientKeyExchange Flight)
-            //          --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
-            //          --> HS_CLIENT_CERTIFICATE or
-            //              HS_CERTIFICATE_URL
-            //          --> HS_CLIENT_KEY_EXCHANGE
-            //          --> HS_CERTIFICATE_VERIFY [optional]
-            //          --> HS_CLIENT_CHANGE_CIPHER_SPEC
-            //          --> HS_CLEINT_FINISHED
-            //              (Server Finished Flight)
-            //          --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
-            //
-            //          Abbreviated handshake state stacks
-            //              (Server Finished Flight)
-            //              HS_NEW_SESSION_TICKET
-            //          --> HS_SERVER_CHANGE_CIPHER_SPEC
-            //          --> HS_SERVER_FINISHED
-            //              (Client Finished Flight)
-            //          --> HS_CLIENT_CHANGE_CIPHER_SPEC
-            //          --> HS_CLEINT_FINISHED
-            //
-            // Note that this state may have an alternative option.
-
-            // Check and update the present state.
-            if (!upcomingStates.isEmpty()) {
-                // The current state should be HS_SERVER_HELLO
-                HandshakeState handshakeState = upcomingStates.pop();
-                HandshakeState alternative = null;
-                if (!alternatives.isEmpty()) {
-                    alternative = alternatives.pop();
-                }
-
-                if ((handshakeState != HS_SERVER_HELLO) &&
-                        (alternative != HS_SERVER_HELLO)) {
-
-                    throw new SSLProtocolException(exceptionMsg);
-                }
-            } else {
-                // No present state.
-                throw new SSLProtocolException(exceptionMsg);
-            }
-
-            // Add the upcoming states.
-            ServerHello serverHello = (ServerHello)handshakeMessage;
-            HelloExtensions hes = serverHello.extensions;
-
-
-            // Not support SessionTicket extension yet.
-            //
-            // boolean hasSessionTicketExt =
-            //     (hes.get(HandshakeMessage.ht_new_session_ticket) != null);
-
-            if (isAbbreviated) {
-                // Not support SessionTicket extension yet.
-                //
-                // // Mandatory NewSessionTicket message
-                // if (hasSessionTicketExt) {
-                //     upcomingStates.add(HS_NEW_SESSION_TICKET);
-                // }
-
-                // Mandatory server ChangeCipherSpec and Finished messages
-                upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
-                upcomingStates.add(HS_SERVER_FINISHED);
-
-                // Mandatory client ChangeCipherSpec and Finished messages
-                upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
-                upcomingStates.add(HS_CLEINT_FINISHED);
-            } else {
-                // Not support SupplementalData extension yet.
-                //
-                // boolean hasSupplementalDataExt =
-                //     (hes.get(HandshakeMessage.ht_supplemental_data) != null);
-
-                // Not support CertificateURL extension yet.
-                //
-                // boolean hasCertificateUrlExt =
-                //     (hes.get(ExtensionType EXT_CLIENT_CERTIFICATE_URL)
-                //          != null);
-
-                // Not support SupplementalData extension yet.
-                //
-                // // Optional SupplementalData message
-                // if (hasSupplementalDataExt) {
-                //     upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA);
-                // }
-
-                // Need server Certificate message or not?
-                KeyExchange keyExchange = serverHello.cipherSuite.keyExchange;
-                if ((keyExchange != K_KRB5) &&
-                        (keyExchange != K_KRB5_EXPORT) &&
-                        (keyExchange != K_DH_ANON) &&
-                        (keyExchange != K_ECDH_ANON)) {
-                    // Mandatory Certificate message
-                    upcomingStates.add(HS_SERVER_CERTIFICATE);
-                }
-
-                // Optional CertificateStatus message
-                if (hes.get(ExtensionType.EXT_STATUS_REQUEST) != null ||
-                        hes.get(ExtensionType.EXT_STATUS_REQUEST_V2) != null) {
-                    upcomingStates.add(HS_CERTIFICATE_STATUS);
-                }
-
-                // Need ServerKeyExchange message or not?
-                if ((keyExchange == K_RSA_EXPORT) ||
-                        (keyExchange == K_DHE_RSA) ||
-                        (keyExchange == K_DHE_DSS) ||
-                        (keyExchange == K_DH_ANON) ||
-                        (keyExchange == K_ECDHE_RSA) ||
-                        (keyExchange == K_ECDHE_ECDSA) ||
-                        (keyExchange == K_ECDH_ANON)) {
-                    // Optional ServerKeyExchange message
-                    upcomingStates.add(HS_SERVER_KEY_EXCHANGE);
-                }
-
-                // Optional CertificateRequest message
-                upcomingStates.add(HS_CERTIFICATE_REQUEST);
-
-                // Mandatory ServerHelloDone message
-                upcomingStates.add(HS_SERVER_HELLO_DONE);
-
-                // Not support SupplementalData extension yet.
-                //
-                // // Optional SupplementalData message
-                // if (hasSupplementalDataExt) {
-                //     upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA);
-                // }
-
-                // Optional client Certificate message
-                upcomingStates.add(HS_CLIENT_CERTIFICATE);
-
-                // Not support CertificateURL extension yet.
-                //
-                // // Alternative CertificateURL message, optional too.
-                // //
-                // // Please put CertificateURL rather than Certificate
-                // // message in the alternatives list.  So that we can
-                // // simplify the process of this alternative pair later.
-                // if (hasCertificateUrlExt) {
-                //     alternatives.add(HS_CERTIFICATE_URL);
-                // }
-
-                // Mandatory ClientKeyExchange message
-                upcomingStates.add(HS_CLIENT_KEY_EXCHANGE);
-
-                // Optional CertificateVerify message
-                upcomingStates.add(HS_CERTIFICATE_VERIFY);
-
-                // Mandatory client ChangeCipherSpec and Finished messages
-                upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
-                upcomingStates.add(HS_CLEINT_FINISHED);
-
-                // Not support SessionTicket extension yet.
-                //
-                // // Mandatory NewSessionTicket message
-                // if (hasSessionTicketExt) {
-                //     upcomingStates.add(HS_NEW_SESSION_TICKET);
-                // }
-
-                // Mandatory server ChangeCipherSpec and Finished messages
-                upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
-                upcomingStates.add(HS_SERVER_FINISHED);
-            }
-
-            break;
-
-        case HandshakeMessage.ht_certificate:
-            //
-            // State machine:
-            //     PRESENT: HS_CERTIFICATE_URL or
-            //              HS_CLIENT_CERTIFICATE
-            //        TO  : HS_CLIENT_KEY_EXCHANGE
-            //
-            //     Or
-            //
-            //     PRESENT: HS_SERVER_CERTIFICATE
-            //        TO  : HS_CERTIFICATE_STATUS [optional]
-            //              HS_SERVER_KEY_EXCHANGE [optional]
-            //              HS_CERTIFICATE_REQUEST [optional]
-            //              HS_SERVER_HELLO_DONE
-            //
-            // Note that this state may have an alternative option.
-
-            // Check and update the present state.
-            while (!upcomingStates.isEmpty()) {
-                HandshakeState handshakeState = upcomingStates.pop();
-                if (handshakeState.handshakeType == handshakeType) {
-                    hasPresentState = true;
-
-                    // The current state should be HS_CLIENT_CERTIFICATE or
-                    // HS_SERVER_CERTIFICATE.
-                    //
-                    // Note that we won't put HS_CLIENT_CERTIFICATE into
-                    // the alternative list.
-                    if ((handshakeState != HS_CLIENT_CERTIFICATE) &&
-                            (handshakeState != HS_SERVER_CERTIFICATE)) {
-                        throw new SSLProtocolException(exceptionMsg);
-                    }
-
-                    // Is it an expected client Certificate message?
-                    boolean isClientMessage = false;
-                    if (!upcomingStates.isEmpty()) {
-                        // If the next expected message is ClientKeyExchange,
-                        // this one should be an expected client Certificate
-                        // message.
-                        HandshakeState nextState = upcomingStates.getFirst();
-                        if (nextState == HS_CLIENT_KEY_EXCHANGE) {
-                            isClientMessage = true;
-                        }
-                    }
-
-                    if (isClientMessage) {
-                        if (handshakeState != HS_CLIENT_CERTIFICATE) {
-                            throw new SSLProtocolException(exceptionMsg);
-                        }
-
-                        // Not support CertificateURL extension yet.
-                        /*******************************************
-                        // clear up the alternatives list
-                        if (!alternatives.isEmpty()) {
-                            HandshakeState alternative = alternatives.pop();
-
-                            if (alternative != HS_CERTIFICATE_URL) {
-                                throw new SSLProtocolException(exceptionMsg);
-                            }
-                        }
-                        ********************************************/
-                    } else {
-                        if ((handshakeState != HS_SERVER_CERTIFICATE)) {
-                            throw new SSLProtocolException(exceptionMsg);
-                        }
-                    }
-
-                    break;
-                } else if (!handshakeState.isOptional) {
-                    throw new SSLProtocolException(exceptionMsg);
-                }   // Otherwise, looking for next state track.
-            }
-
-            // No present state.
-            if (!hasPresentState) {
-                throw new SSLProtocolException(exceptionMsg);
-            }
-
-            // no new upcoming states.
-
-            break;
-
-        // Not support CertificateURL extension yet.
-        /*************************************************/
-        case HandshakeMessage.ht_certificate_url:
-            //
-            // State machine:
-            //     PRESENT: HS_CERTIFICATE_URL or
-            //              HS_CLIENT_CERTIFICATE
-            //        TO  : HS_CLIENT_KEY_EXCHANGE
-            //
-            // Note that this state may have an alternative option.
-
-            // Check and update the present state.
-            while (!upcomingStates.isEmpty()) {
-                // The current state should be HS_CLIENT_CERTIFICATE.
-                //
-                // Note that we won't put HS_CLIENT_CERTIFICATE into
-                // the alternative list.
-                HandshakeState handshakeState = upcomingStates.pop();
-                if (handshakeState.handshakeType ==
-                        HS_CLIENT_CERTIFICATE.handshakeType) {
-                    hasPresentState = true;
-
-                    // Look for HS_CERTIFICATE_URL state track.
-                    if (!alternatives.isEmpty()) {
-                        HandshakeState alternative = alternatives.pop();
-
-                        if (alternative != HS_CERTIFICATE_URL) {
-                            throw new SSLProtocolException(exceptionMsg);
-                        }
-                    } else {
-                        // No alternative CertificateUR state track.
-                        throw new SSLProtocolException(exceptionMsg);
-                    }
-
-                    if ((handshakeState != HS_CLIENT_CERTIFICATE)) {
-                        throw new SSLProtocolException(exceptionMsg);
-                    }
-
-                    break;
-                } else if (!handshakeState.isOptional) {
-                    throw new SSLProtocolException(exceptionMsg);
-                }   // Otherwise, looking for next state track.
-
-            }
-
-            // No present state.
-            if (!hasPresentState) {
-                // No present state.
-                throw new SSLProtocolException(exceptionMsg);
-            }
-
-            // no new upcoming states.
-
-            break;
-        /*************************************************/
-
-        default:
-            // Check and update the present state.
-            while (!upcomingStates.isEmpty()) {
-                HandshakeState handshakeState = upcomingStates.pop();
-                if (handshakeState.handshakeType == handshakeType) {
-                    hasPresentState = true;
-                    break;
-                } else if (!handshakeState.isOptional) {
-                    throw new SSLProtocolException(exceptionMsg);
-                }   // Otherwise, looking for next state track.
-            }
-
-            // No present state.
-            if (!hasPresentState) {
-                throw new SSLProtocolException(exceptionMsg);
-            }
-
-            // no new upcoming states.
-        }
-
-        if (debugIsOn) {
-            for (HandshakeState handshakeState : upcomingStates) {
-                System.out.println(
-                    "upcoming handshake states: " + handshakeState);
-            }
-            for (HandshakeState handshakeState : alternatives) {
-                System.out.println(
-                    "upcoming handshake alternative state: " + handshakeState);
-            }
-        }
-    }
-
-    void changeCipherSpec(boolean isInput,
-            boolean isClient) throws SSLProtocolException {
-
-        if (debugIsOn) {
-            System.out.println(
-                    "update handshake state: change_cipher_spec");
-        }
-
-        String exceptionMsg = "ChangeCipherSpec message sequence violation";
-
-        HandshakeState expectedState;
-        if ((isClient && isInput) || (!isClient && !isInput)) {
-            expectedState = HS_SERVER_CHANGE_CIPHER_SPEC;
-        } else {
-            expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC;
-        }
-
-        boolean hasPresentState = false;
-
-        // Check and update the present state.
-        while (!upcomingStates.isEmpty()) {
-            HandshakeState handshakeState = upcomingStates.pop();
-            if (handshakeState == expectedState) {
-                hasPresentState = true;
-                break;
-            } else if (!handshakeState.isOptional) {
-                throw new SSLProtocolException(exceptionMsg);
-            }   // Otherwise, looking for next state track.
-        }
-
-        // No present state.
-        if (!hasPresentState) {
-            throw new SSLProtocolException(exceptionMsg);
-        }
-
-        // no new upcoming states.
-
-        if (debugIsOn) {
-            for (HandshakeState handshakeState : upcomingStates) {
-                System.out.println(
-                    "upcoming handshake states: " + handshakeState);
-            }
-            for (HandshakeState handshakeState : alternatives) {
-                System.out.println(
-                    "upcoming handshake alternative state: " + handshakeState);
-            }
-        }
-    }
-
-    private static String toString(byte handshakeType) {
-        String s = handshakeTypes.get(handshakeType);
-        if (s == null) {
-            s = "unknown";
-        }
-        return (s + "[" + handshakeType + "]");
-    }
-}
-
--- a/src/java.base/share/classes/sun/security/ssl/Handshaker.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1623 +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.security.*;
-import java.nio.ByteBuffer;
-import java.util.function.BiFunction;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
-
-import javax.net.ssl.*;
-import sun.security.util.HexDumpEncoder;
-
-import sun.security.internal.spec.*;
-import sun.security.internal.interfaces.TlsMasterSecret;
-
-import sun.security.ssl.HandshakeMessage.*;
-import sun.security.ssl.CipherSuite.*;
-
-import static sun.security.ssl.CipherSuite.PRF.*;
-import static sun.security.ssl.CipherSuite.CipherType.*;
-import static sun.security.ssl.NamedGroupType.*;
-
-/**
- * Handshaker ... processes handshake records from an SSL V3.0
- * data stream, handling all the details of the handshake protocol.
- *
- * Note that the real protocol work is done in two subclasses, the  base
- * class just provides the control flow and key generation framework.
- *
- * @author David Brownell
- */
-abstract class Handshaker {
-
-    // protocol version being established using this Handshaker
-    ProtocolVersion protocolVersion;
-
-    // the currently active protocol version during a renegotiation
-    ProtocolVersion     activeProtocolVersion;
-
-    // security parameters for secure renegotiation.
-    boolean             secureRenegotiation;
-    byte[]              clientVerifyData;
-    byte[]              serverVerifyData;
-
-    // Is it an initial negotiation  or a renegotiation?
-    boolean                     isInitialHandshake;
-
-    // List of enabled protocols
-    private ProtocolList        enabledProtocols;
-
-    // List of enabled CipherSuites
-    private CipherSuiteList     enabledCipherSuites;
-
-    // The endpoint identification protocol
-    String                      identificationProtocol;
-
-    // The cryptographic algorithm constraints
-    AlgorithmConstraints        algorithmConstraints = null;
-
-    // Local supported signature and algorithms
-    private Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs;
-
-    // Peer supported signature and algorithms
-    Collection<SignatureAndHashAlgorithm> peerSupportedSignAlgs;
-
-    /*
-     * List of active protocols
-     *
-     * Active protocols is a subset of enabled protocols, and will
-     * contain only those protocols that have vaild cipher suites
-     * enabled.
-     */
-    private ProtocolList       activeProtocols;
-
-    /*
-     * List of active cipher suites
-     *
-     * Active cipher suites is a subset of enabled cipher suites, and will
-     * contain only those cipher suites available for the active protocols.
-     */
-    private CipherSuiteList     activeCipherSuites;
-
-    // The server name indication and matchers
-    List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList();
-    Collection<SNIMatcher> sniMatchers = Collections.<SNIMatcher>emptyList();
-
-    // List of local ApplicationProtocols
-    String[] localApl = null;
-
-    // Negotiated ALPN value
-    String applicationProtocol = null;
-
-    // Application protocol callback function (for SSLEngine)
-    BiFunction<SSLEngine,List<String>,String>
-        appProtocolSelectorSSLEngine = null;
-
-    // Application protocol callback function (for SSLSocket)
-    BiFunction<SSLSocket,List<String>,String>
-        appProtocolSelectorSSLSocket = null;
-
-    // The maximum expected network packet size for SSL/TLS/DTLS records.
-    int                         maximumPacketSize = 0;
-
-    private boolean             isClient;
-    private boolean             needCertVerify;
-
-    SSLSocketImpl               conn = null;
-    SSLEngineImpl               engine = null;
-
-    HandshakeHash               handshakeHash;
-    HandshakeInStream           input;
-    HandshakeOutStream          output;
-    SSLContextImpl              sslContext;
-    RandomCookie                clnt_random, svr_random;
-    SSLSessionImpl              session;
-
-    HandshakeStateManager       handshakeState;
-    boolean                     clientHelloDelivered;
-    boolean                     serverHelloRequested;
-    boolean                     handshakeActivated;
-    boolean                     handshakeFinished;
-
-    // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
-    CipherSuite         cipherSuite;
-
-    // current key exchange. Never null, initially K_NULL
-    KeyExchange         keyExchange;
-
-    // True if this session is being resumed (fast handshake)
-    boolean             resumingSession;
-
-    // True if it's OK to start a new SSL session
-    boolean             enableNewSession;
-
-    // Whether local cipher suites preference should be honored during
-    // handshaking?
-    //
-    // Note that in this provider, this option only applies to server side.
-    // Local cipher suites preference is always honored in client side in
-    // this provider.
-    boolean preferLocalCipherSuites = false;
-
-    // Temporary storage for the individual keys. Set by
-    // calculateConnectionKeys() and cleared once the ciphers are
-    // activated.
-    private SecretKey clntWriteKey, svrWriteKey;
-    private IvParameterSpec clntWriteIV, svrWriteIV;
-    private SecretKey clntMacSecret, svrMacSecret;
-
-    /*
-     * Delegated task subsystem data structures.
-     *
-     * If thrown is set, we need to propagate this back immediately
-     * on entry into processMessage().
-     *
-     * Data is protected by the SSLEngine.this lock.
-     */
-    private volatile boolean taskDelegated = false;
-    private volatile DelegatedTask<?> delegatedTask = null;
-    private volatile Exception thrown = null;
-
-    // Could probably use a java.util.concurrent.atomic.AtomicReference
-    // here instead of using this lock.  Consider changing.
-    private Object thrownLock = new Object();
-
-    /* Class and subclass dynamic debugging support */
-    static final Debug debug = Debug.getInstance("ssl");
-
-    // By default, disable the unsafe legacy session renegotiation
-    static final boolean allowUnsafeRenegotiation = Debug.getBooleanProperty(
-                    "sun.security.ssl.allowUnsafeRenegotiation", false);
-
-    // For maximum interoperability and backward compatibility, RFC 5746
-    // allows server (or client) to accept ClientHello (or ServerHello)
-    // message without the secure renegotiation_info extension or SCSV.
-    //
-    // For maximum security, RFC 5746 also allows server (or client) to
-    // reject such message with a fatal "handshake_failure" alert.
-    //
-    // By default, allow such legacy hello messages.
-    static final boolean allowLegacyHelloMessages = Debug.getBooleanProperty(
-                    "sun.security.ssl.allowLegacyHelloMessages", true);
-
-    // To prevent the TLS renegotiation issues, by setting system property
-    // "jdk.tls.rejectClientInitiatedRenegotiation" to true, applications in
-    // server side can disable all client initiated SSL renegotiations
-    // regardless of the support of TLS protocols.
-    //
-    // By default, allow client initiated renegotiations.
-    static final boolean rejectClientInitiatedRenego =
-            Debug.getBooleanProperty(
-                "jdk.tls.rejectClientInitiatedRenegotiation", false);
-
-    // To switch off the extended_master_secret extension.
-    static final boolean useExtendedMasterSecret;
-
-    // Allow session resumption without Extended Master Secret extension.
-    static final boolean allowLegacyResumption =
-            Debug.getBooleanProperty("jdk.tls.allowLegacyResumption", true);
-
-    // Allow full handshake without Extended Master Secret extension.
-    static final boolean allowLegacyMasterSecret =
-            Debug.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true);
-
-    // Is it requested to use extended master secret extension?
-    boolean requestedToUseEMS = false;
-
-    // need to dispose the object when it is invalidated
-    boolean invalidated;
-
-    /*
-     * Is this an instance for Datagram Transport Layer Security (DTLS)?
-     */
-    final boolean isDTLS;
-
-    // Is the extended_master_secret extension supported?
-    static {
-        boolean supportExtendedMasterSecret = true;
-        try {
-            KeyGenerator kg =
-                JsseJce.getKeyGenerator("SunTlsExtendedMasterSecret");
-        } catch (NoSuchAlgorithmException nae) {
-            supportExtendedMasterSecret = false;
-        }
-
-        if (supportExtendedMasterSecret) {
-            useExtendedMasterSecret = Debug.getBooleanProperty(
-                    "jdk.tls.useExtendedMasterSecret", true);
-        } else {
-            useExtendedMasterSecret = false;
-        }
-    }
-
-    Handshaker(SSLSocketImpl c, SSLContextImpl context,
-            ProtocolList enabledProtocols, boolean needCertVerify,
-            boolean isClient, ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
-        this.conn = c;
-        this.isDTLS = false;
-        init(context, enabledProtocols, needCertVerify, isClient,
-            activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-            clientVerifyData, serverVerifyData);
-   }
-
-    Handshaker(SSLEngineImpl engine, SSLContextImpl context,
-            ProtocolList enabledProtocols, boolean needCertVerify,
-            boolean isClient, ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData,
-            boolean isDTLS) {
-        this.engine = engine;
-        this.isDTLS = isDTLS;
-        init(context, enabledProtocols, needCertVerify, isClient,
-            activeProtocolVersion, isInitialHandshake, secureRenegotiation,
-            clientVerifyData, serverVerifyData);
-    }
-
-    private void init(SSLContextImpl context, ProtocolList enabledProtocols,
-            boolean needCertVerify, boolean isClient,
-            ProtocolVersion activeProtocolVersion,
-            boolean isInitialHandshake, boolean secureRenegotiation,
-            byte[] clientVerifyData, byte[] serverVerifyData) {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            System.out.println(
-                "Allow unsafe renegotiation: " + allowUnsafeRenegotiation +
-                "\nAllow legacy hello messages: " + allowLegacyHelloMessages +
-                "\nIs initial handshake: " + isInitialHandshake +
-                "\nIs secure renegotiation: " + secureRenegotiation);
-        }
-
-        this.sslContext = context;
-        this.isClient = isClient;
-        this.needCertVerify = needCertVerify;
-        this.activeProtocolVersion = activeProtocolVersion;
-        this.isInitialHandshake = isInitialHandshake;
-        this.secureRenegotiation = secureRenegotiation;
-        this.clientVerifyData = clientVerifyData;
-        this.serverVerifyData = serverVerifyData;
-        this.enableNewSession = true;
-        this.invalidated = false;
-        this.handshakeState = new HandshakeStateManager(isDTLS);
-        this.clientHelloDelivered = false;
-        this.serverHelloRequested = false;
-        this.handshakeActivated = false;
-        this.handshakeFinished = false;
-
-        setCipherSuite(CipherSuite.C_NULL);
-        setEnabledProtocols(enabledProtocols);
-
-        if (conn != null) {
-            algorithmConstraints = new SSLAlgorithmConstraints(conn, true);
-        } else {        // engine != null
-            algorithmConstraints = new SSLAlgorithmConstraints(engine, true);
-        }
-    }
-
-    /*
-     * Reroutes calls to the SSLSocket or SSLEngine (*SE).
-     *
-     * We could have also done it by extra classes
-     * and letting them override, but this seemed much
-     * less involved.
-     */
-    void fatalSE(byte b, String diagnostic) throws IOException {
-        fatalSE(b, diagnostic, null);
-    }
-
-    void fatalSE(byte b, Throwable cause) throws IOException {
-        fatalSE(b, null, cause);
-    }
-
-    void fatalSE(byte b, String diagnostic, Throwable cause)
-            throws IOException {
-        if (conn != null) {
-            conn.fatal(b, diagnostic, cause);
-        } else {
-            engine.fatal(b, diagnostic, cause);
-        }
-    }
-
-    void warningSE(byte b) {
-        if (conn != null) {
-            conn.warning(b);
-        } else {
-            engine.warning(b);
-        }
-    }
-
-    // ONLY used by ClientHandshaker to setup the peer host in SSLSession.
-    String getHostSE() {
-        if (conn != null) {
-            return conn.getHost();
-        } else {
-            return engine.getPeerHost();
-        }
-    }
-
-    // ONLY used by ServerHandshaker to setup the peer host in SSLSession.
-    String getHostAddressSE() {
-        if (conn != null) {
-            return conn.getInetAddress().getHostAddress();
-        } else {
-            /*
-             * This is for caching only, doesn't matter that's is really
-             * a hostname.  The main thing is that it doesn't do
-             * a reverse DNS lookup, potentially slowing things down.
-             */
-            return engine.getPeerHost();
-        }
-    }
-
-    int getPortSE() {
-        if (conn != null) {
-            return conn.getPort();
-        } else {
-            return engine.getPeerPort();
-        }
-    }
-
-    int getLocalPortSE() {
-        if (conn != null) {
-            return conn.getLocalPort();
-        } else {
-            return -1;
-        }
-    }
-
-    AccessControlContext getAccSE() {
-        if (conn != null) {
-            return conn.getAcc();
-        } else {
-            return engine.getAcc();
-        }
-    }
-
-    String getEndpointIdentificationAlgorithmSE() {
-        SSLParameters paras;
-        if (conn != null) {
-            paras = conn.getSSLParameters();
-        } else {
-            paras = engine.getSSLParameters();
-        }
-
-        return paras.getEndpointIdentificationAlgorithm();
-    }
-
-    private void setVersionSE(ProtocolVersion protocolVersion) {
-        if (conn != null) {
-            conn.setVersion(protocolVersion);
-        } else {
-            engine.setVersion(protocolVersion);
-        }
-    }
-
-    /**
-     * Set the active protocol version and propagate it to the SSLSocket
-     * and our handshake streams. Called from ClientHandshaker
-     * and ServerHandshaker with the negotiated protocol version.
-     */
-    void setVersion(ProtocolVersion protocolVersion) {
-        this.protocolVersion = protocolVersion;
-        setVersionSE(protocolVersion);
-    }
-
-    /**
-     * Set the enabled protocols. Called from the constructor or
-     * SSLSocketImpl/SSLEngineImpl.setEnabledProtocols() (if the
-     * handshake is not yet in progress).
-     */
-    void setEnabledProtocols(ProtocolList enabledProtocols) {
-        activeCipherSuites = null;
-        activeProtocols = null;
-
-        this.enabledProtocols = enabledProtocols;
-    }
-
-    /**
-     * Set the enabled cipher suites. Called from
-     * SSLSocketImpl/SSLEngineImpl.setEnabledCipherSuites() (if the
-     * handshake is not yet in progress).
-     */
-    void setEnabledCipherSuites(CipherSuiteList enabledCipherSuites) {
-        activeCipherSuites = null;
-        activeProtocols = null;
-        this.enabledCipherSuites = enabledCipherSuites;
-    }
-
-    /**
-     * Set the algorithm constraints. Called from the constructor or
-     * SSLSocketImpl/SSLEngineImpl.setAlgorithmConstraints() (if the
-     * handshake is not yet in progress).
-     */
-    void setAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) {
-        activeCipherSuites = null;
-        activeProtocols = null;
-
-        this.algorithmConstraints =
-            new SSLAlgorithmConstraints(algorithmConstraints);
-        this.localSupportedSignAlgs = null;
-    }
-
-    Collection<SignatureAndHashAlgorithm> getLocalSupportedSignAlgs() {
-        if (localSupportedSignAlgs == null) {
-            localSupportedSignAlgs =
-                SignatureAndHashAlgorithm.getSupportedAlgorithms(
-                                                    algorithmConstraints);
-        }
-
-        return localSupportedSignAlgs;
-    }
-
-    void setPeerSupportedSignAlgs(
-            Collection<SignatureAndHashAlgorithm> algorithms) {
-        peerSupportedSignAlgs =
-            new ArrayList<SignatureAndHashAlgorithm>(algorithms);
-    }
-
-    Collection<SignatureAndHashAlgorithm> getPeerSupportedSignAlgs() {
-        return peerSupportedSignAlgs;
-    }
-
-
-    /**
-     * Set the identification protocol. Called from the constructor or
-     * SSLSocketImpl/SSLEngineImpl.setIdentificationProtocol() (if the
-     * handshake is not yet in progress).
-     */
-    void setIdentificationProtocol(String protocol) {
-        this.identificationProtocol = protocol;
-    }
-
-    /**
-     * Sets the server name indication of the handshake.
-     */
-    void setSNIServerNames(List<SNIServerName> serverNames) {
-        // The serverNames parameter is unmodifiable.
-        this.serverNames = serverNames;
-    }
-
-    /**
-     * Sets the server name matchers of the handshaking.
-     */
-    void setSNIMatchers(Collection<SNIMatcher> sniMatchers) {
-        // The sniMatchers parameter is unmodifiable.
-        this.sniMatchers = sniMatchers;
-    }
-
-    /**
-     * Sets the maximum packet size of the handshaking.
-     */
-    void setMaximumPacketSize(int maximumPacketSize) {
-        this.maximumPacketSize = maximumPacketSize;
-    }
-
-    /**
-     * Sets the Application Protocol list.
-     */
-    void setApplicationProtocols(String[] apl) {
-        this.localApl = apl;
-    }
-
-    /**
-     * Gets the "negotiated" ALPN value.
-     */
-    String getHandshakeApplicationProtocol() {
-        return applicationProtocol;
-    }
-
-    /**
-     * Sets the Application Protocol selector function for SSLEngine.
-     */
-    void setApplicationProtocolSelectorSSLEngine(
-        BiFunction<SSLEngine,List<String>,String> selector) {
-        this.appProtocolSelectorSSLEngine = selector;
-    }
-
-    /**
-     * Sets the Application Protocol selector function for SSLSocket.
-     */
-    void setApplicationProtocolSelectorSSLSocket(
-        BiFunction<SSLSocket,List<String>,String> selector) {
-        this.appProtocolSelectorSSLSocket = selector;
-    }
-
-    /**
-     * Sets the cipher suites preference.
-     */
-    void setUseCipherSuitesOrder(boolean on) {
-        this.preferLocalCipherSuites = on;
-    }
-
-    /**
-     * Prior to handshaking, activate the handshake and initialize the version,
-     * input stream and output stream.
-     */
-    void activate(ProtocolVersion helloVersion) throws IOException {
-        if (activeProtocols == null) {
-            activeProtocols = getActiveProtocols();
-        }
-
-        if (activeProtocols.collection().isEmpty() ||
-                activeProtocols.max.v == ProtocolVersion.NONE.v) {
-            throw new SSLHandshakeException(
-                    "No appropriate protocol (protocol is disabled or " +
-                    "cipher suites are inappropriate)");
-        }
-
-        if (activeCipherSuites == null) {
-            activeCipherSuites = getActiveCipherSuites();
-        }
-
-        if (activeCipherSuites.collection().isEmpty()) {
-            throw new SSLHandshakeException("No appropriate cipher suite");
-        }
-
-        // temporary protocol version until the actual protocol version
-        // is negotiated in the Hello exchange. This affects the record
-        // version we sent with the ClientHello.
-        if (!isInitialHandshake) {
-            protocolVersion = activeProtocolVersion;
-        } else {
-            protocolVersion = activeProtocols.max;
-        }
-
-        if (helloVersion == null || helloVersion.v == ProtocolVersion.NONE.v) {
-            helloVersion = activeProtocols.helloVersion;
-        }
-
-        // We accumulate digests of the handshake messages so that
-        // we can read/write CertificateVerify and Finished messages,
-        // getting assurance against some particular active attacks.
-        handshakeHash = new HandshakeHash(needCertVerify);
-
-        // Generate handshake input/output stream.
-        if (conn != null) {
-            input = new HandshakeInStream();
-            output = new HandshakeOutStream(conn.outputRecord);
-
-            conn.inputRecord.setHandshakeHash(handshakeHash);
-            conn.inputRecord.setHelloVersion(helloVersion);
-
-            conn.outputRecord.setHandshakeHash(handshakeHash);
-            conn.outputRecord.setHelloVersion(helloVersion);
-            conn.outputRecord.setVersion(protocolVersion);
-        } else if (engine != null) {
-            input = new HandshakeInStream();
-            output = new HandshakeOutStream(engine.outputRecord);
-
-            engine.inputRecord.setHandshakeHash(handshakeHash);
-            engine.inputRecord.setHelloVersion(helloVersion);
-
-            engine.outputRecord.setHandshakeHash(handshakeHash);
-            engine.outputRecord.setHelloVersion(helloVersion);
-            engine.outputRecord.setVersion(protocolVersion);
-        }
-
-        handshakeActivated = true;
-    }
-
-    /**
-     * Set cipherSuite and keyExchange to the given CipherSuite.
-     * Does not perform any verification that this is a valid selection,
-     * this must be done before calling this method.
-     */
-    void setCipherSuite(CipherSuite s) {
-        this.cipherSuite = s;
-        this.keyExchange = s.keyExchange;
-    }
-
-    /**
-     * Check if the given ciphersuite is enabled and available within the
-     * current active cipher suites.
-     *
-     * Does not check if the required server certificates are available.
-     */
-    boolean isNegotiable(CipherSuite s) {
-        if (activeCipherSuites == null) {
-            activeCipherSuites = getActiveCipherSuites();
-        }
-
-        return isNegotiable(activeCipherSuites, s);
-    }
-
-    /**
-     * Check if the given ciphersuite is enabled and available within the
-     * proposed cipher suite list.
-     *
-     * Does not check if the required server certificates are available.
-     */
-    static final boolean isNegotiable(CipherSuiteList proposed, CipherSuite s) {
-        return proposed.contains(s) && s.isNegotiable();
-    }
-
-    /**
-     * Check if the given protocol version is enabled and available.
-     */
-    boolean isNegotiable(ProtocolVersion protocolVersion) {
-        if (activeProtocols == null) {
-            activeProtocols = getActiveProtocols();
-        }
-
-        return activeProtocols.contains(protocolVersion);
-    }
-
-    /**
-     * Select a protocol version from the list. Called from
-     * ServerHandshaker to negotiate protocol version.
-     *
-     * Return the lower of the protocol version suggested in the
-     * clien hello and the highest supported by the server.
-     */
-    ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
-        if (activeProtocols == null) {
-            activeProtocols = getActiveProtocols();
-        }
-
-        return activeProtocols.selectProtocolVersion(protocolVersion);
-    }
-
-    /**
-     * Get the active cipher suites.
-     *
-     * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted,
-     * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT
-     * negotiate these cipher suites in TLS 1.1 or later mode.
-     *
-     * Therefore, when the active protocols only include TLS 1.1 or later,
-     * the client cannot request to negotiate those obsoleted cipher
-     * suites.  That is, the obsoleted suites should not be included in the
-     * client hello. So we need to create a subset of the enabled cipher
-     * suites, the active cipher suites, which does not contain obsoleted
-     * cipher suites of the minimum active protocol.
-     *
-     * Return empty list instead of null if no active cipher suites.
-     */
-    CipherSuiteList getActiveCipherSuites() {
-        if (activeCipherSuites == null) {
-            if (activeProtocols == null) {
-                activeProtocols = getActiveProtocols();
-            }
-
-            ArrayList<CipherSuite> suites = new ArrayList<>();
-            if (!(activeProtocols.collection().isEmpty()) &&
-                    activeProtocols.min.v != ProtocolVersion.NONE.v) {
-                Map<NamedGroupType, Boolean> cachedStatus =
-                        new EnumMap<>(NamedGroupType.class);
-                for (CipherSuite suite : enabledCipherSuites.collection()) {
-                    if (suite.isAvailable() &&
-                            (!activeProtocols.min.obsoletes(suite)) &&
-                            activeProtocols.max.supports(suite)) {
-                        if (isActivatable(suite, cachedStatus)) {
-                            suites.add(suite);
-                        }
-                    } else if (debug != null && Debug.isOn("verbose")) {
-                        if (activeProtocols.min.obsoletes(suite)) {
-                            System.out.println(
-                                "Ignoring obsoleted cipher suite: " + suite);
-                        } else {
-                            System.out.println(
-                                "Ignoring unsupported cipher suite: " + suite);
-                        }
-                    }
-                }
-            }
-            activeCipherSuites = new CipherSuiteList(suites);
-        }
-
-        return activeCipherSuites;
-    }
-
-    /*
-     * Get the active protocol versions.
-     *
-     * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted,
-     * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT
-     * negotiate these cipher suites in TLS 1.1 or later mode.
-     *
-     * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the
-     * only enabled cipher suite, the client cannot request TLS 1.1 or
-     * later, even though TLS 1.1 or later is enabled.  We need to create a
-     * subset of the enabled protocols, called the active protocols, which
-     * contains protocols appropriate to the list of enabled Ciphersuites.
-     *
-     * Return empty list instead of null if no active protocol versions.
-     */
-    ProtocolList getActiveProtocols() {
-        if (activeProtocols == null) {
-            boolean enabledSSL20Hello = false;
-            boolean checkedCurves = false;
-            boolean hasCurves = false;
-            ArrayList<ProtocolVersion> protocols = new ArrayList<>(4);
-            for (ProtocolVersion protocol : enabledProtocols.collection()) {
-                // Need not to check the SSL20Hello protocol.
-                if (protocol.v == ProtocolVersion.SSL20Hello.v) {
-                    enabledSSL20Hello = true;
-                    continue;
-                }
-
-                if (!algorithmConstraints.permits(
-                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                        protocol.name, null)) {
-                    if (debug != null && Debug.isOn("verbose")) {
-                        System.out.println(
-                            "Ignoring disabled protocol: " + protocol);
-                    }
-
-                    continue;
-                }
-
-                boolean found = false;
-                Map<NamedGroupType, Boolean> cachedStatus =
-                        new EnumMap<>(NamedGroupType.class);
-                for (CipherSuite suite : enabledCipherSuites.collection()) {
-                    if (suite.isAvailable() && (!protocol.obsoletes(suite)) &&
-                                               protocol.supports(suite)) {
-                        if (isActivatable(suite, cachedStatus)) {
-                            protocols.add(protocol);
-                            found = true;
-                            break;
-                        }
-                    } else if (debug != null && Debug.isOn("verbose")) {
-                        System.out.println(
-                            "Ignoring unsupported cipher suite: " + suite +
-                                 " for " + protocol);
-                    }
-                }
-
-                if (!found && (debug != null) && Debug.isOn("handshake")) {
-                    System.out.println(
-                        "No available cipher suite for " + protocol);
-                }
-            }
-
-            if (!protocols.isEmpty() && enabledSSL20Hello) {
-                protocols.add(ProtocolVersion.SSL20Hello);
-            }
-
-            activeProtocols = new ProtocolList(protocols);
-        }
-
-        return activeProtocols;
-    }
-
-    private boolean isActivatable(CipherSuite suite,
-            Map<NamedGroupType, Boolean> cachedStatus) {
-
-        if (algorithmConstraints.permits(
-                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
-            boolean available = true;
-            NamedGroupType groupType = suite.keyExchange.groupType;
-            if (groupType != NAMED_GROUP_NONE) {
-                Boolean checkedStatus = cachedStatus.get(groupType);
-                if (checkedStatus == null) {
-                    available = SupportedGroupsExtension.isActivatable(
-                            algorithmConstraints, groupType);
-                    cachedStatus.put(groupType, available);
-
-                    if (!available && debug != null && Debug.isOn("verbose")) {
-                        System.out.println("No activated named group");
-                    }
-                } else {
-                    available = checkedStatus.booleanValue();
-                }
-
-                if (!available && debug != null && Debug.isOn("verbose")) {
-                    System.out.println(
-                        "No active named group, ignore " + suite);
-                }
-
-                return available;
-            } else {
-                return true;
-            }
-        } else if (debug != null && Debug.isOn("verbose")) {
-            System.out.println("Ignoring disabled cipher suite: " + suite);
-        }
-
-        return false;
-    }
-
-    /**
-     * As long as handshaking has not activated, we can
-     * change whether session creations are allowed.
-     *
-     * Callers should do their own checking if handshaking
-     * has activated.
-     */
-    void setEnableSessionCreation(boolean newSessions) {
-        enableNewSession = newSessions;
-    }
-
-    /**
-     * Create a new read cipher and return it to caller.
-     */
-    CipherBox newReadCipher() throws NoSuchAlgorithmException {
-        BulkCipher cipher = cipherSuite.cipher;
-        CipherBox box;
-        if (isClient) {
-            box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV,
-                                   sslContext.getSecureRandom(), false);
-            svrWriteKey = null;
-            svrWriteIV = null;
-        } else {
-            box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV,
-                                   sslContext.getSecureRandom(), false);
-            clntWriteKey = null;
-            clntWriteIV = null;
-        }
-        return box;
-    }
-
-    /**
-     * Create a new write cipher and return it to caller.
-     */
-    CipherBox newWriteCipher() throws NoSuchAlgorithmException {
-        BulkCipher cipher = cipherSuite.cipher;
-        CipherBox box;
-        if (isClient) {
-            box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV,
-                                   sslContext.getSecureRandom(), true);
-            clntWriteKey = null;
-            clntWriteIV = null;
-        } else {
-            box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV,
-                                   sslContext.getSecureRandom(), true);
-            svrWriteKey = null;
-            svrWriteIV = null;
-        }
-        return box;
-    }
-
-    /**
-     * Create a new read MAC and return it to caller.
-     */
-    Authenticator newReadAuthenticator()
-            throws NoSuchAlgorithmException, InvalidKeyException {
-
-        Authenticator authenticator = null;
-        if (cipherSuite.cipher.cipherType == AEAD_CIPHER) {
-            authenticator = new Authenticator(protocolVersion);
-        } else {
-            MacAlg macAlg = cipherSuite.macAlg;
-            if (isClient) {
-                authenticator = macAlg.newMac(protocolVersion, svrMacSecret);
-                svrMacSecret = null;
-            } else {
-                authenticator = macAlg.newMac(protocolVersion, clntMacSecret);
-                clntMacSecret = null;
-            }
-        }
-
-        return authenticator;
-    }
-
-    /**
-     * Create a new write MAC and return it to caller.
-     */
-    Authenticator newWriteAuthenticator()
-            throws NoSuchAlgorithmException, InvalidKeyException {
-
-        Authenticator authenticator = null;
-        if (cipherSuite.cipher.cipherType == AEAD_CIPHER) {
-            authenticator = new Authenticator(protocolVersion);
-        } else {
-            MacAlg macAlg = cipherSuite.macAlg;
-            if (isClient) {
-                authenticator = macAlg.newMac(protocolVersion, clntMacSecret);
-                clntMacSecret = null;
-            } else {
-                authenticator = macAlg.newMac(protocolVersion, svrMacSecret);
-                svrMacSecret = null;
-            }
-        }
-
-        return authenticator;
-    }
-
-    /*
-     * Returns true iff the handshake sequence is done, so that
-     * this freshly created session can become the current one.
-     */
-    boolean isDone() {
-        return started() && handshakeState.isEmpty() && handshakeFinished;
-    }
-
-
-    /*
-     * Returns the session which was created through this
-     * handshake sequence ... should be called after isDone()
-     * returns true.
-     */
-    SSLSessionImpl getSession() {
-        return session;
-    }
-
-    /*
-     * Set the handshake session
-     */
-    void setHandshakeSessionSE(SSLSessionImpl handshakeSession) {
-        if (conn != null) {
-            conn.setHandshakeSession(handshakeSession);
-        } else {
-            engine.setHandshakeSession(handshakeSession);
-        }
-    }
-
-    void expectingFinishFlightSE() {
-        if (conn != null) {
-            conn.expectingFinishFlight();
-        } else {
-            engine.expectingFinishFlight();
-        }
-    }
-
-    /*
-     * Returns true if renegotiation is in use for this connection.
-     */
-    boolean isSecureRenegotiation() {
-        return secureRenegotiation;
-    }
-
-    /*
-     * Returns the verify_data from the Finished message sent by the client.
-     */
-    byte[] getClientVerifyData() {
-        return clientVerifyData;
-    }
-
-    /*
-     * Returns the verify_data from the Finished message sent by the server.
-     */
-    byte[] getServerVerifyData() {
-        return serverVerifyData;
-    }
-
-    /*
-     * This routine is fed SSL handshake records when they become available,
-     * and processes messages found therein.
-     */
-    void processRecord(ByteBuffer record,
-            boolean expectingFinished) throws IOException {
-
-        checkThrown();
-
-        /*
-         * Store the incoming handshake data, then see if we can
-         * now process any completed handshake messages
-         */
-        input.incomingRecord(record);
-
-        /*
-         * We don't need to create a separate delegatable task
-         * for finished messages.
-         */
-        if ((conn != null) || expectingFinished) {
-            processLoop();
-        } else {
-            delegateTask(new PrivilegedExceptionAction<Void>() {
-                @Override
-                public Void run() throws Exception {
-                    processLoop();
-                    return null;
-                }
-            });
-        }
-    }
-
-    /*
-     * On input, we hash messages one at a time since servers may need
-     * to access an intermediate hash to validate a CertificateVerify
-     * message.
-     *
-     * Note that many handshake messages can come in one record (and often
-     * do, to reduce network resource utilization), and one message can also
-     * require multiple records (e.g. very large Certificate messages).
-     */
-    void processLoop() throws IOException {
-
-        // need to read off 4 bytes at least to get the handshake
-        // message type and length.
-        while (input.available() >= 4) {
-            byte messageType;
-            int messageLen;
-
-            /*
-             * See if we can read the handshake message header, and
-             * then the entire handshake message.  If not, wait till
-             * we can read and process an entire message.
-             */
-            input.mark(4);
-
-            messageType = (byte)input.getInt8();
-            if (HandshakeMessage.isUnsupported(messageType)) {
-                throw new SSLProtocolException(
-                    "Received unsupported or unknown handshake message: " +
-                    messageType);
-            }
-
-            messageLen = input.getInt24();
-
-            if (input.available() < messageLen) {
-                input.reset();
-                return;
-            }
-
-            // Set the flags in the message receiving side.
-            if (messageType == HandshakeMessage.ht_client_hello) {
-                clientHelloDelivered = true;
-            } else if (messageType == HandshakeMessage.ht_hello_request) {
-                serverHelloRequested = true;
-            }
-
-            /*
-             * Process the message.  We require
-             * that processMessage() consumes the entire message.  In
-             * lieu of explicit error checks (how?!) we assume that the
-             * data will look like garbage on encoding/processing errors,
-             * and that other protocol code will detect such errors.
-             *
-             * Note that digesting is normally deferred till after the
-             * message has been processed, though to process at least the
-             * client's Finished message (i.e. send the server's) we need
-             * to acccelerate that digesting.
-             *
-             * Also, note that hello request messages are never hashed;
-             * that includes the hello request header, too.
-             */
-            processMessage(messageType, messageLen);
-
-            // Reload if this message has been reserved.
-            //
-            // Note: in the implementation, only certificate_verify and
-            // finished messages are reserved.
-            if ((messageType == HandshakeMessage.ht_finished) ||
-                (messageType == HandshakeMessage.ht_certificate_verify)) {
-
-                handshakeHash.reload();
-            }
-        }
-    }
-
-
-    /**
-     * Returns true iff the handshaker has been activated.
-     *
-     * In activated state, the handshaker may not send any messages out.
-     */
-    boolean activated() {
-        return handshakeActivated;
-    }
-
-    /**
-     * Returns true iff the handshaker has sent any messages.
-     */
-    boolean started() {
-        return (serverHelloRequested || clientHelloDelivered);
-    }
-
-    /*
-     * Used to kickstart the negotiation ... either writing a
-     * ClientHello or a HelloRequest as appropriate, whichever
-     * the subclass returns.  NOP if handshaking's already started.
-     */
-    void kickstart() throws IOException {
-        if ((isClient && clientHelloDelivered) ||
-                (!isClient && serverHelloRequested)) {
-            return;
-        }
-
-        HandshakeMessage m = getKickstartMessage();
-        handshakeState.update(m, resumingSession);
-
-        if (debug != null && Debug.isOn("handshake")) {
-            m.print(System.out);
-        }
-        m.write(output);
-        output.flush();
-
-        // Set the flags in the message delivering side.
-        int handshakeType = m.messageType();
-        if (handshakeType == HandshakeMessage.ht_hello_request) {
-            serverHelloRequested = true;
-        } else {        // HandshakeMessage.ht_client_hello
-            clientHelloDelivered = true;
-        }
-    }
-
-    /**
-     * Both client and server modes can start handshaking; but the
-     * message they send to do so is different.
-     */
-    abstract HandshakeMessage getKickstartMessage() throws SSLException;
-
-    /*
-     * Client and Server side protocols are each driven though this
-     * call, which processes a single message and drives the appropriate
-     * side of the protocol state machine (depending on the subclass).
-     */
-    abstract void processMessage(byte messageType, int messageLen)
-        throws IOException;
-
-    /*
-     * Most alerts in the protocol relate to handshaking problems.
-     * Alerts are detected as the connection reads data.
-     */
-    abstract void handshakeAlert(byte description) throws SSLProtocolException;
-
-    /*
-     * Sends a change cipher spec message and updates the write side
-     * cipher state so that future messages use the just-negotiated spec.
-     */
-    void sendChangeCipherSpec(Finished mesg, boolean lastMessage)
-            throws IOException {
-
-        output.flush(); // i.e. handshake data
-
-        /*
-         * The write cipher state is protected by the connection write lock
-         * so we must grab it while making the change. We also
-         * make sure no writes occur between sending the ChangeCipherSpec
-         * message, installing the new cipher state, and sending the
-         * Finished message.
-         *
-         * We already hold SSLEngine/SSLSocket "this" by virtue
-         * of this being called from the readRecord code.
-         */
-        if (conn != null) {
-            conn.writeLock.lock();
-            try {
-                handshakeState.changeCipherSpec(false, isClient);
-                conn.changeWriteCiphers();
-                if (debug != null && Debug.isOn("handshake")) {
-                    mesg.print(System.out);
-                }
-
-                handshakeState.update(mesg, resumingSession);
-                mesg.write(output);
-                output.flush();
-            } finally {
-                conn.writeLock.unlock();
-            }
-        } else {
-            synchronized (engine.writeLock) {
-                handshakeState.changeCipherSpec(false, isClient);
-                engine.changeWriteCiphers();
-                if (debug != null && Debug.isOn("handshake")) {
-                    mesg.print(System.out);
-                }
-
-                handshakeState.update(mesg, resumingSession);
-                mesg.write(output);
-                output.flush();
-            }
-        }
-
-        if (lastMessage) {
-            handshakeFinished = true;
-        }
-    }
-
-    void receiveChangeCipherSpec() throws IOException {
-        handshakeState.changeCipherSpec(true, isClient);
-    }
-
-    /*
-     * Single access point to key calculation logic.  Given the
-     * pre-master secret and the nonces from client and server,
-     * produce all the keying material to be used.
-     */
-    void calculateKeys(SecretKey preMasterSecret, ProtocolVersion version) {
-        SecretKey master = calculateMasterSecret(preMasterSecret, version);
-        session.setMasterSecret(master);
-        calculateConnectionKeys(master);
-    }
-
-    /*
-     * Calculate the master secret from its various components.  This is
-     * used for key exchange by all cipher suites.
-     *
-     * The master secret is the catenation of three MD5 hashes, each
-     * consisting of the pre-master secret and a SHA1 hash.  Those three
-     * SHA1 hashes are of (different) constant strings, the pre-master
-     * secret, and the nonces provided by the client and the server.
-     */
-    @SuppressWarnings("deprecation")
-    private SecretKey calculateMasterSecret(SecretKey preMasterSecret,
-            ProtocolVersion requestedVersion) {
-
-        if (debug != null && Debug.isOn("keygen")) {
-            HexDumpEncoder      dump = new HexDumpEncoder();
-
-            System.out.println("SESSION KEYGEN:");
-
-            System.out.println("PreMaster Secret:");
-            printHex(dump, preMasterSecret.getEncoded());
-
-            // Nonces are dumped with connection keygen, no
-            // benefit to doing it twice
-        }
-
-        // What algs/params do we need to use?
-        String masterAlg;
-        PRF prf;
-
-        byte majorVersion = protocolVersion.major;
-        byte minorVersion = protocolVersion.minor;
-        if (protocolVersion.isDTLSProtocol()) {
-            // Use TLS version number for DTLS key calculation
-            if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
-                majorVersion = ProtocolVersion.TLS11.major;
-                minorVersion = ProtocolVersion.TLS11.minor;
-
-                masterAlg = "SunTlsMasterSecret";
-                prf = P_NONE;
-            } else {    // DTLS 1.2
-                majorVersion = ProtocolVersion.TLS12.major;
-                minorVersion = ProtocolVersion.TLS12.minor;
-
-                masterAlg = "SunTls12MasterSecret";
-                prf = cipherSuite.prfAlg;
-            }
-        } else {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
-                masterAlg = "SunTls12MasterSecret";
-                prf = cipherSuite.prfAlg;
-            } else {
-                masterAlg = "SunTlsMasterSecret";
-                prf = P_NONE;
-            }
-        }
-
-        String prfHashAlg = prf.getPRFHashAlg();
-        int prfHashLength = prf.getPRFHashLength();
-        int prfBlockSize = prf.getPRFBlockSize();
-
-        TlsMasterSecretParameterSpec spec;
-        if (session.getUseExtendedMasterSecret()) {
-            // reset to use the extended master secret algorithm
-            masterAlg = "SunTlsExtendedMasterSecret";
-
-            byte[] sessionHash = null;
-            if (protocolVersion.useTLS12PlusSpec()) {
-                sessionHash = handshakeHash.getFinishedHash();
-            } else {
-                // TLS 1.0/1.1, DTLS 1.0
-                sessionHash = new byte[36];
-                try {
-                    handshakeHash.getMD5Clone().digest(sessionHash, 0, 16);
-                    handshakeHash.getSHAClone().digest(sessionHash, 16, 20);
-                } catch (DigestException de) {
-                    throw new ProviderException(de);
-                }
-            }
-
-            spec = new TlsMasterSecretParameterSpec(
-                    preMasterSecret,
-                    (majorVersion & 0xFF), (minorVersion & 0xFF),
-                    sessionHash,
-                    prfHashAlg, prfHashLength, prfBlockSize);
-        } else {
-            spec = new TlsMasterSecretParameterSpec(
-                    preMasterSecret,
-                    (majorVersion & 0xFF), (minorVersion & 0xFF),
-                    clnt_random.random_bytes, svr_random.random_bytes,
-                    prfHashAlg, prfHashLength, prfBlockSize);
-        }
-
-        try {
-            KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
-            kg.init(spec);
-            return kg.generateKey();
-        } catch (InvalidAlgorithmParameterException |
-                NoSuchAlgorithmException iae) {
-            // unlikely to happen, otherwise, must be a provider exception
-            //
-            // For RSA premaster secrets, do not signal a protocol error
-            // due to the Bleichenbacher attack. See comments further down.
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("RSA master secret generation error:");
-                iae.printStackTrace(System.out);
-            }
-            throw new ProviderException(iae);
-
-        }
-    }
-
-    /*
-     * Calculate the keys needed for this connection, once the session's
-     * master secret has been calculated.  Uses the master key and nonces;
-     * the amount of keying material generated is a function of the cipher
-     * suite that's been negotiated.
-     *
-     * This gets called both on the "full handshake" (where we exchanged
-     * a premaster secret and started a new session) as well as on the
-     * "fast handshake" (where we just resumed a pre-existing session).
-     */
-    @SuppressWarnings("deprecation")
-    void calculateConnectionKeys(SecretKey masterKey) {
-        /*
-         * For both the read and write sides of the protocol, we use the
-         * master to generate MAC secrets and cipher keying material.  Block
-         * ciphers need initialization vectors, which we also generate.
-         *
-         * First we figure out how much keying material is needed.
-         */
-        int hashSize = cipherSuite.macAlg.size;
-        boolean is_exportable = cipherSuite.exportable;
-        BulkCipher cipher = cipherSuite.cipher;
-        int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0;
-
-        // Which algs/params do we need to use?
-        String keyMaterialAlg;
-        PRF prf;
-
-        byte majorVersion = protocolVersion.major;
-        byte minorVersion = protocolVersion.minor;
-        if (protocolVersion.isDTLSProtocol()) {
-            // Use TLS version number for DTLS key calculation
-            if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
-                majorVersion = ProtocolVersion.TLS11.major;
-                minorVersion = ProtocolVersion.TLS11.minor;
-
-                keyMaterialAlg = "SunTlsKeyMaterial";
-                prf = P_NONE;
-            } else {    // DTLS 1.2+
-                majorVersion = ProtocolVersion.TLS12.major;
-                minorVersion = ProtocolVersion.TLS12.minor;
-
-                keyMaterialAlg = "SunTls12KeyMaterial";
-                prf = cipherSuite.prfAlg;
-            }
-        } else {
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
-                keyMaterialAlg = "SunTls12KeyMaterial";
-                prf = cipherSuite.prfAlg;
-            } else {
-                keyMaterialAlg = "SunTlsKeyMaterial";
-                prf = P_NONE;
-            }
-        }
-
-        String prfHashAlg = prf.getPRFHashAlg();
-        int prfHashLength = prf.getPRFHashLength();
-        int prfBlockSize = prf.getPRFBlockSize();
-
-        // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to
-        // protect against the CBC attacks.  AEAD/GCM cipher suites in TLS
-        // v1.2 or later use a fixed IV as the implicit part of the partially
-        // implicit nonce technique described in RFC 5116.
-        int ivSize = cipher.ivSize;
-        if (cipher.cipherType == AEAD_CIPHER) {
-            ivSize = cipher.fixedIvSize;
-        } else if ((cipher.cipherType == BLOCK_CIPHER) &&
-                protocolVersion.useTLS11PlusSpec()) {
-            ivSize = 0;
-        }
-
-        TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec(
-                masterKey, (majorVersion & 0xFF), (minorVersion & 0xFF),
-                clnt_random.random_bytes, svr_random.random_bytes,
-                cipher.algorithm, cipher.keySize, expandedKeySize,
-                ivSize, hashSize,
-                prfHashAlg, prfHashLength, prfBlockSize);
-
-        try {
-            KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
-            kg.init(spec);
-            TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec)kg.generateKey();
-
-            // Return null if cipher keys are not supposed to be generated.
-            clntWriteKey = keySpec.getClientCipherKey();
-            svrWriteKey = keySpec.getServerCipherKey();
-
-            // Return null if IVs are not supposed to be generated.
-            clntWriteIV = keySpec.getClientIv();
-            svrWriteIV = keySpec.getServerIv();
-
-            // Return null if MAC keys are not supposed to be generated.
-            clntMacSecret = keySpec.getClientMacKey();
-            svrMacSecret = keySpec.getServerMacKey();
-        } catch (GeneralSecurityException e) {
-            throw new ProviderException(e);
-        }
-
-        //
-        // Dump the connection keys as they're generated.
-        //
-        if (debug != null && Debug.isOn("keygen")) {
-            synchronized (System.out) {
-                HexDumpEncoder  dump = new HexDumpEncoder();
-
-                System.out.println("CONNECTION KEYGEN:");
-
-                // Inputs:
-                System.out.println("Client Nonce:");
-                printHex(dump, clnt_random.random_bytes);
-                System.out.println("Server Nonce:");
-                printHex(dump, svr_random.random_bytes);
-                System.out.println("Master Secret:");
-                printHex(dump, masterKey.getEncoded());
-
-                // Outputs:
-                if (clntMacSecret != null) {
-                    System.out.println("Client MAC write Secret:");
-                    printHex(dump, clntMacSecret.getEncoded());
-                    System.out.println("Server MAC write Secret:");
-                    printHex(dump, svrMacSecret.getEncoded());
-                } else {
-                    System.out.println("... no MAC keys used for this cipher");
-                }
-
-                if (clntWriteKey != null) {
-                    System.out.println("Client write key:");
-                    printHex(dump, clntWriteKey.getEncoded());
-                    System.out.println("Server write key:");
-                    printHex(dump, svrWriteKey.getEncoded());
-                } else {
-                    System.out.println("... no encryption keys used");
-                }
-
-                if (clntWriteIV != null) {
-                    System.out.println("Client write IV:");
-                    printHex(dump, clntWriteIV.getIV());
-                    System.out.println("Server write IV:");
-                    printHex(dump, svrWriteIV.getIV());
-                } else {
-                    if (protocolVersion.useTLS11PlusSpec()) {
-                        System.out.println(
-                                "... no IV derived for this protocol");
-                    } else {
-                        System.out.println("... no IV used for this cipher");
-                    }
-                }
-                System.out.flush();
-            }
-        }
-    }
-
-    private static void printHex(HexDumpEncoder dump, byte[] bytes) {
-        if (bytes == null) {
-            System.out.println("(key bytes not available)");
-        } else {
-            try {
-                dump.encodeBuffer(bytes, System.out);
-            } catch (IOException e) {
-                // just for debugging, ignore this
-            }
-        }
-    }
-
-    /*
-     * Implement a simple task delegator.
-     *
-     * We are currently implementing this as a single delegator, may
-     * try for parallel tasks later.  Client Authentication could
-     * benefit from this, where ClientKeyExchange/CertificateVerify
-     * could be carried out in parallel.
-     */
-    class DelegatedTask<E> implements Runnable {
-
-        private PrivilegedExceptionAction<E> pea;
-
-        DelegatedTask(PrivilegedExceptionAction<E> pea) {
-            this.pea = pea;
-        }
-
-        public void run() {
-            synchronized (engine) {
-                try {
-                    AccessController.doPrivileged(pea, engine.getAcc());
-                } catch (PrivilegedActionException pae) {
-                    thrown = pae.getException();
-                } catch (RuntimeException rte) {
-                    thrown = rte;
-                }
-                delegatedTask = null;
-                taskDelegated = false;
-            }
-        }
-    }
-
-    private <T> void delegateTask(PrivilegedExceptionAction<T> pea) {
-        delegatedTask = new DelegatedTask<T>(pea);
-        taskDelegated = false;
-        thrown = null;
-    }
-
-    DelegatedTask<?> getTask() {
-        if (!taskDelegated) {
-            taskDelegated = true;
-            return delegatedTask;
-        } else {
-            return null;
-        }
-    }
-
-    /*
-     * See if there are any tasks which need to be delegated
-     *
-     * Locked by SSLEngine.this.
-     */
-    boolean taskOutstanding() {
-        return (delegatedTask != null);
-    }
-
-    /*
-     * The previous caller failed for some reason, report back the
-     * Exception.  We won't worry about Error's.
-     *
-     * Locked by SSLEngine.this.
-     */
-    void checkThrown() throws SSLException {
-        synchronized (thrownLock) {
-            if (thrown != null) {
-
-                String msg = thrown.getMessage();
-
-                if (msg == null) {
-                    msg = "Delegated task threw Exception/Error";
-                }
-
-                /*
-                 * See what the underlying type of exception is.  We should
-                 * throw the same thing.  Chain thrown to the new exception.
-                 */
-                Exception e = thrown;
-                thrown = null;
-
-                if (e instanceof RuntimeException) {
-                    throw new RuntimeException(msg, e);
-                } else if (e instanceof SSLHandshakeException) {
-                    throw (SSLHandshakeException)
-                        new SSLHandshakeException(msg).initCause(e);
-                } else if (e instanceof SSLKeyException) {
-                    throw (SSLKeyException)
-                        new SSLKeyException(msg).initCause(e);
-                } else if (e instanceof SSLPeerUnverifiedException) {
-                    throw (SSLPeerUnverifiedException)
-                        new SSLPeerUnverifiedException(msg).initCause(e);
-                } else if (e instanceof SSLProtocolException) {
-                    throw (SSLProtocolException)
-                        new SSLProtocolException(msg).initCause(e);
-                } else {
-                    /*
-                     * If it's SSLException or any other Exception,
-                     * we'll wrap it in an SSLException.
-                     */
-                    throw new SSLException(msg, e);
-                }
-            }
-        }
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,119 +26,315 @@
 package sun.security.ssl;
 
 import java.io.IOException;
-import javax.net.ssl.SSLProtocolException;
 import java.security.MessageDigest;
 import java.security.SecureRandom;
-
-import sun.security.ssl.HandshakeMessage.ClientHello;
+import java.util.Arrays;
+import static sun.security.ssl.ClientHello.ClientHelloMessage;
 
-/*
- * HelloVerifyRequest cookie manager
+/**
+ *  (D)TLS handshake cookie manager
  */
-final class HelloCookieManager {
-    // the cookie secret life time
-    private static long COOKIE_TIMING_WINDOW = 3600000;     // in milliseconds
-    private static int  COOKIE_MAX_LENGTH_DTLS10 = 32;      // 32 bytes
-    private static int  COOKIE_MAX_LENGTH_DTLS12 = 0xFF;    // 2^8 -1 bytes
+abstract class HelloCookieManager {
+
+    static class Builder {
+
+        final SecureRandom secureRandom;
+
+        private volatile D10HelloCookieManager d10HelloCookieManager;
+        private volatile D13HelloCookieManager d13HelloCookieManager;
+        private volatile T13HelloCookieManager t13HelloCookieManager;
+
+        Builder(SecureRandom secureRandom) {
+            this.secureRandom = secureRandom;
+        }
 
-    private final SecureRandom          secureRandom;
-    private final MessageDigest         cookieDigest;
+        HelloCookieManager valueOf(ProtocolVersion protocolVersion) {
+            if (protocolVersion.isDTLS) {
+                if (protocolVersion.useTLS13PlusSpec()) {
+                    if (d13HelloCookieManager != null) {
+                        return d13HelloCookieManager;
+                    }
+
+                    synchronized (this) {
+                        if (d13HelloCookieManager == null) {
+                            d13HelloCookieManager =
+                                    new D13HelloCookieManager(secureRandom);
+                        }
+                    }
 
-    private int                         cookieVersion;      // allow to wrap
-    private long                        secretLifetime;
-    private byte[]                      cookieSecret;
+                    return d13HelloCookieManager;
+                } else {
+                    if (d10HelloCookieManager != null) {
+                        return d10HelloCookieManager;
+                    }
 
-    private int                         prevCookieVersion;
-    private byte[]                      prevCookieSecret;
+                    synchronized (this) {
+                        if (d10HelloCookieManager == null) {
+                            d10HelloCookieManager =
+                                    new D10HelloCookieManager(secureRandom);
+                        }
+                    }
 
-    HelloCookieManager(SecureRandom secureRandom) {
-        this.secureRandom = secureRandom;
-        this.cookieDigest = JsseJce.getMessageDigest("SHA-256");
+                    return d10HelloCookieManager;
+                }
+            } else {
+                if (protocolVersion.useTLS13PlusSpec()) {
+                    if (t13HelloCookieManager != null) {
+                        return t13HelloCookieManager;
+                    }
 
-        this.cookieVersion = secureRandom.nextInt();
-        this.secretLifetime = 0;
-        this.cookieSecret = null;
+                    synchronized (this) {
+                        if (t13HelloCookieManager == null) {
+                            t13HelloCookieManager =
+                                    new T13HelloCookieManager(secureRandom);
+                        }
+                    }
 
-        this.prevCookieVersion = 0;
-        this.prevCookieSecret = null;
+                    return t13HelloCookieManager;
+                }
+            }
+
+            return null;
+        }
     }
 
-    // Used by server side to generate cookies in HelloVerifyRequest message.
-    synchronized byte[] getCookie(ClientHello clientHelloMsg) {
-        if (secretLifetime < System.currentTimeMillis()) {
-            if (cookieSecret != null) {
-                prevCookieVersion = cookieVersion;
-                prevCookieSecret = cookieSecret.clone();
-            } else {
-                cookieSecret = new byte[32];
+    abstract byte[] createCookie(ServerHandshakeContext context,
+                ClientHelloMessage clientHello) throws IOException;
+
+    abstract boolean isCookieValid(ServerHandshakeContext context,
+            ClientHelloMessage clientHello, byte[] cookie) throws IOException;
+
+    // DTLS 1.0/1.2
+    private static final
+            class D10HelloCookieManager extends HelloCookieManager {
+
+        final SecureRandom secureRandom;
+        private int         cookieVersion;  // allow to wrap, version + sequence
+        private byte[]      cookieSecret;
+        private byte[]      legacySecret;
+
+        D10HelloCookieManager(SecureRandom secureRandom) {
+            this.secureRandom = secureRandom;
+
+            this.cookieVersion = secureRandom.nextInt();
+            this.cookieSecret = new byte[32];
+            this.legacySecret = new byte[32];
+
+            secureRandom.nextBytes(cookieSecret);
+            System.arraycopy(cookieSecret, 0, legacySecret, 0, 32);
+        }
+
+        @Override
+        byte[] createCookie(ServerHandshakeContext context,
+                ClientHelloMessage clientHello) throws IOException {
+            int version;
+            byte[] secret;
+
+            synchronized (this) {
+                version = cookieVersion;
+                secret = cookieSecret;
+
+                // the cookie secret usage limit is 2^24
+                if ((cookieVersion & 0xFFFFFF) == 0) {  // reset the secret
+                    System.arraycopy(cookieSecret, 0, legacySecret, 0, 32);
+                    secureRandom.nextBytes(cookieSecret);
+                }
+
+                cookieVersion++;
             }
 
-            cookieVersion++;
-            secureRandom.nextBytes(cookieSecret);
-            secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW;
+            MessageDigest md = JsseJce.getMessageDigest("SHA-256");
+            byte[] helloBytes = clientHello.getHelloCookieBytes();
+            md.update(helloBytes);
+            byte[] cookie = md.digest(secret);      // 32 bytes
+            cookie[0] = (byte)((version >> 24) & 0xFF);
+
+            return cookie;
         }
 
-        clientHelloMsg.updateHelloCookie(cookieDigest);
-        byte[] cookie = cookieDigest.digest(cookieSecret);      // 32 bytes
-        cookie[0] = (byte)((cookieVersion >> 24) & 0xFF);
-        cookie[1] = (byte)((cookieVersion >> 16) & 0xFF);
-        cookie[2] = (byte)((cookieVersion >> 8) & 0xFF);
-        cookie[3] = (byte)(cookieVersion & 0xFF);
+        @Override
+        boolean isCookieValid(ServerHandshakeContext context,
+            ClientHelloMessage clientHello, byte[] cookie) throws IOException {
+            // no cookie exchange or not a valid cookie length
+            if ((cookie == null) || (cookie.length != 32)) {
+                return false;
+            }
 
-        return cookie;
+            byte[] secret;
+            synchronized (this) {
+                if (((cookieVersion >> 24) & 0xFF) == cookie[0]) {
+                    secret = cookieSecret;
+                } else {
+                    secret = legacySecret;  // including out of window cookies
+                }
+            }
+
+            MessageDigest md = JsseJce.getMessageDigest("SHA-256");
+            byte[] helloBytes = clientHello.getHelloCookieBytes();
+            md.update(helloBytes);
+            byte[] target = md.digest(secret);      // 32 bytes
+            target[0] = cookie[0];
+
+            return Arrays.equals(target, cookie);
+        }
     }
 
-    // Used by server side to check the cookie in ClientHello message.
-    synchronized boolean isValid(ClientHello clientHelloMsg) {
-        byte[] cookie = clientHelloMsg.cookie;
+    private static final
+            class D13HelloCookieManager extends HelloCookieManager {
+        D13HelloCookieManager(SecureRandom secureRandom) {
+        }
+
+        @Override
+        byte[] createCookie(ServerHandshakeContext context,
+                ClientHelloMessage clientHello) throws IOException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
 
-        // no cookie exchange or not a valid cookie length
-        if ((cookie == null) || (cookie.length != 32)) {
-            return false;
+        @Override
+        boolean isCookieValid(ServerHandshakeContext context,
+            ClientHelloMessage clientHello, byte[] cookie) throws IOException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    private static final
+            class T13HelloCookieManager extends HelloCookieManager {
+
+        final SecureRandom secureRandom;
+        private int             cookieVersion;      // version + sequence
+        private final byte[]    cookieSecret;
+        private final byte[]    legacySecret;
+
+        T13HelloCookieManager(SecureRandom secureRandom) {
+            this.secureRandom = secureRandom;
+            this.cookieVersion = secureRandom.nextInt();
+            this.cookieSecret = new byte[64];
+            this.legacySecret = new byte[64];
+
+            secureRandom.nextBytes(cookieSecret);
+            System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
         }
 
-        int version = ((cookie[0] & 0xFF) << 24) |
-                      ((cookie[1] & 0xFF) << 16) |
-                      ((cookie[2] & 0xFF) << 8) |
-                       (cookie[3] & 0xFF);
+        @Override
+        byte[] createCookie(ServerHandshakeContext context,
+                ClientHelloMessage clientHello) throws IOException {
+            int version;
+            byte[] secret;
+
+            synchronized (this) {
+                version = cookieVersion;
+                secret = cookieSecret;
+
+                // the cookie secret usage limit is 2^24
+                if ((cookieVersion & 0xFFFFFF) == 0) {  // reset the secret
+                    System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
+                    secureRandom.nextBytes(cookieSecret);
+                }
+
+                cookieVersion++;        // allow wrapped version number
+            }
+
+            MessageDigest md = JsseJce.getMessageDigest(
+                    context.negotiatedCipherSuite.hashAlg.name);
+            byte[] headerBytes = clientHello.getHeaderBytes();
+            md.update(headerBytes);
+            byte[] headerCookie = md.digest(secret);
 
-        byte[] secret;
-        if (version == cookieVersion) {
-            secret = cookieSecret;
-        } else if (version == prevCookieVersion) {
-            secret = prevCookieSecret;
-        } else {
-            return false;       // may be out of the timing window
-        }
+            // hash of ClientHello handshake message
+            context.handshakeHash.update();
+            byte[] clientHelloHash = context.handshakeHash.digest();
 
-        clientHelloMsg.updateHelloCookie(cookieDigest);
-        byte[] target = cookieDigest.digest(secret);            // 32 bytes
+            // version and cipher suite
+            //
+            // Store the negotiated cipher suite in the cookie as well.
+            // cookie[0]/[1]: cipher suite
+            // cookie[2]: cookie version
+            // + (hash length): Mac(ClientHello header)
+            // + (hash length): Hash(ClientHello)
+            byte[] prefix = new byte[] {
+                    (byte)((context.negotiatedCipherSuite.id >> 8) & 0xFF),
+                    (byte)(context.negotiatedCipherSuite.id & 0xFF),
+                    (byte)((version >> 24) & 0xFF)
+                };
 
-        for (int i = 4; i < 32; i++) {
-            if (cookie[i] != target[i]) {
-                return false;
-            }
+            byte[] cookie = Arrays.copyOf(prefix,
+                prefix.length + headerCookie.length + clientHelloHash.length);
+            System.arraycopy(headerCookie, 0, cookie,
+                prefix.length, headerCookie.length);
+            System.arraycopy(clientHelloHash, 0, cookie,
+                prefix.length + headerCookie.length, clientHelloHash.length);
+
+            return cookie;
         }
 
-        return true;
-    }
+        @Override
+        boolean isCookieValid(ServerHandshakeContext context,
+            ClientHelloMessage clientHello, byte[] cookie) throws IOException {
+            // no cookie exchange or not a valid cookie length
+            if ((cookie == null) || (cookie.length <= 32)) {    // 32: roughly
+                return false;
+            }
+
+            int csId = ((cookie[0] & 0xFF) << 8) | (cookie[1] & 0xFF);
+            CipherSuite cs = CipherSuite.valueOf(csId);
+            if (cs == null || cs.hashAlg == null || cs.hashAlg.hashLength == 0) {
+                return false;
+            }
 
-    // Used by client side to check the cookie in HelloVerifyRequest message.
-    static void checkCookie(ProtocolVersion protocolVersion,
-            byte[] cookie) throws IOException {
-        if (cookie != null && cookie.length != 0) {
-            int limit = COOKIE_MAX_LENGTH_DTLS12;
-            if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
-                limit = COOKIE_MAX_LENGTH_DTLS10;
+            int hashLen = cs.hashAlg.hashLength;
+            if (cookie.length != (3 + hashLen * 2)) {
+                return false;
+            }
+
+            byte[] prevHeadCookie =
+                    Arrays.copyOfRange(cookie, 3, 3 + hashLen);
+            byte[] prevClientHelloHash =
+                    Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length);
+
+            byte[] secret;
+            synchronized (this) {
+                if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) {
+                    secret = cookieSecret;
+                } else {
+                    secret = legacySecret;  // including out of window cookies
+                }
             }
 
-            if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) {
-                throw new SSLProtocolException(
-                        "Invalid HelloVerifyRequest.cookie (length = " +
-                         cookie.length + " bytes)");
+            MessageDigest md = JsseJce.getMessageDigest(cs.hashAlg.name);
+            byte[] headerBytes = clientHello.getHeaderBytes();
+            md.update(headerBytes);
+            byte[] headerCookie = md.digest(secret);
+
+            if (!Arrays.equals(headerCookie, prevHeadCookie)) {
+                return false;
             }
-        }
+
+            // Use the ClientHello hash in the cookie for transtript
+            // hash calculation for stateless HelloRetryRequest.
+            //
+            // Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
+            //   Hash(message_hash ||    /* Handshake type */
+            //     00 00 Hash.length ||  /* Handshake message length (bytes) */
+            //     Hash(ClientHello1) || /* Hash of ClientHello1 */
+            //     HelloRetryRequest || ... || Mn)
 
-        // Otherwise, no cookie exchange.
+            // Reproduce HelloRetryRequest handshake message
+            byte[] hrrMessage =
+                    ServerHello.hrrReproducer.produce(context, clientHello);
+            context.handshakeHash.push(hrrMessage);
+
+            // Construct the 1st ClientHello message for transcript hash
+            byte[] hashedClientHello = new byte[4 + hashLen];
+            hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id;
+            hashedClientHello[1] = (byte)0x00;
+            hashedClientHello[2] = (byte)0x00;
+            hashedClientHello[3] = (byte)(hashLen & 0xFF);
+            System.arraycopy(prevClientHelloHash, 0,
+                    hashedClientHello, 4, hashLen);
+
+            context.handshakeHash.push(hashedClientHello);
+
+            return true;
+        }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/HelloExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2006, 2012, 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.IOException;
-
-abstract class HelloExtension {
-
-    final ExtensionType type;
-
-    HelloExtension(ExtensionType type) {
-        this.type = type;
-    }
-
-    // Length of the encoded extension, including the type and length fields
-    abstract int length();
-
-    abstract void send(HandshakeOutStream s) throws IOException;
-
-    @Override
-    public abstract String toString();
-
-}
--- a/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-/*
- * Copyright (c) 2006, 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.IOException;
-import java.io.PrintStream;
-import java.util.*;
-import javax.net.ssl.*;
-
-/**
- * This file contains all the classes relevant to TLS Extensions for the
- * ClientHello and ServerHello messages. The extension mechanism and
- * several extensions are defined in RFC 6066. Additional extensions are
- * defined in the ECC RFC 4492 and the ALPN extension is defined in RFC 7301.
- *
- * Currently, only the two ECC extensions are fully supported.
- *
- * The classes contained in this file are:
- *  . HelloExtensions: a List of extensions as used in the client hello
- *      and server hello messages.
- *  . ExtensionType: an enum style class for the extension type
- *  . HelloExtension: abstract base class for all extensions. All subclasses
- *      must be immutable.
- *
- *  . UnknownExtension: used to represent all parsed extensions that we do not
- *      explicitly support.
- *  . ServerNameExtension: the server_name extension.
- *  . SignatureAlgorithmsExtension: the signature_algorithms extension.
- *  . SupportedGroupsExtension: the supported groups extension.
- *  . EllipticPointFormatsExtension: the ECC supported point formats
- *      (compressed/uncompressed) extension.
- *  . ALPNExtension: the application_layer_protocol_negotiation extension.
- *
- * @since   1.6
- * @author  Andreas Sterbenz
- */
-final class HelloExtensions {
-
-    private List<HelloExtension> extensions;
-    private int encodedLength;
-
-    HelloExtensions() {
-        extensions = Collections.emptyList();
-    }
-
-    HelloExtensions(HandshakeInStream s) throws IOException {
-        int len = s.getInt16();
-        extensions = new ArrayList<HelloExtension>();
-        encodedLength = len + 2;
-        while (len > 0) {
-            int type = s.getInt16();
-            int extlen = s.getInt16();
-            ExtensionType extType = ExtensionType.get(type);
-            HelloExtension extension;
-            if (extType == ExtensionType.EXT_SERVER_NAME) {
-                extension = new ServerNameExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) {
-                extension = new SignatureAlgorithmsExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_SUPPORTED_GROUPS) {
-                extension = new SupportedGroupsExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) {
-                extension = new EllipticPointFormatsExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
-                extension = new RenegotiationInfoExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_ALPN) {
-                extension = new ALPNExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
-                extension = new MaxFragmentLengthExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_STATUS_REQUEST) {
-                extension = new CertStatusReqExtension(s, extlen);
-            } else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) {
-                extension = new CertStatusReqListV2Extension(s, extlen);
-            } else if (extType == ExtensionType.EXT_EXTENDED_MASTER_SECRET) {
-                extension = new ExtendedMasterSecretExtension(s, extlen);
-            } else {
-                extension = new UnknownExtension(s, extlen, extType);
-            }
-            extensions.add(extension);
-            len -= extlen + 4;
-        }
-        if (len != 0) {
-            throw new SSLProtocolException(
-                        "Error parsing extensions: extra data");
-        }
-    }
-
-    // Return the List of extensions. Must not be modified by the caller.
-    List<HelloExtension> list() {
-        return extensions;
-    }
-
-    void add(HelloExtension ext) {
-        if (extensions.isEmpty()) {
-            extensions = new ArrayList<HelloExtension>();
-        }
-        extensions.add(ext);
-        encodedLength = -1;
-    }
-
-    HelloExtension get(ExtensionType type) {
-        for (HelloExtension ext : extensions) {
-            if (ext.type == type) {
-                return ext;
-            }
-        }
-        return null;
-    }
-
-    int length() {
-        if (encodedLength >= 0) {
-            return encodedLength;
-        }
-        if (extensions.isEmpty()) {
-            encodedLength = 0;
-        } else {
-            encodedLength = 2;
-            for (HelloExtension ext : extensions) {
-                encodedLength += ext.length();
-            }
-        }
-        return encodedLength;
-    }
-
-    void send(HandshakeOutStream s) throws IOException {
-        int length = length();
-        if (length == 0) {
-            return;
-        }
-        s.putInt16(length - 2);
-        for (HelloExtension ext : extensions) {
-            ext.send(s);
-        }
-    }
-
-    void print(PrintStream s) throws IOException {
-        for (HelloExtension ext : extensions) {
-            s.println(ext.toString());
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/HelloRequest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the HelloRequest handshake message.
+ */
+final class HelloRequest {
+    static final SSLProducer kickstartProducer =
+        new HelloRequestKickstartProducer();
+
+    static final SSLConsumer handshakeConsumer =
+        new HelloRequestConsumer();
+    static final HandshakeProducer handshakeProducer =
+        new HelloRequestProducer();
+
+    /**
+     * The HelloRequest handshake message.
+     *
+     * [RFC 5246] The HelloRequest message MAY be sent by the server at any
+     * time.  HelloRequest is a simple notification that the client should
+     * begin the negotiation process anew.
+     *
+     *      struct { } HelloRequest;
+     */
+    static final class HelloRequestMessage extends HandshakeMessage {
+        HelloRequestMessage(HandshakeContext handshakeContext) {
+            super(handshakeContext);
+        }
+
+        HelloRequestMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+            if (m.hasRemaining()) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Error parsing HelloRequest message: not empty");
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.HELLO_REQUEST;
+        }
+
+        @Override
+        public int messageLength() {
+            return 0;
+        }
+
+        @Override
+        public void send(HandshakeOutStream s) throws IOException {
+            // empty, nothing to send
+        }
+
+        @Override
+        public String toString() {
+            return "<empty>";
+        }
+    }
+
+    /**
+     * The "HelloRequest" handshake message kick start producer.
+     */
+    private static final
+            class HelloRequestKickstartProducer implements SSLProducer {
+        // Prevent instantiation of this class.
+        private HelloRequestKickstartProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            HelloRequestMessage hrm = new HelloRequestMessage(shc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced HelloRequest handshake message", hrm);
+            }
+
+            // Output the handshake message.
+            hrm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // update the context
+
+            // What's the expected response?
+            shc.handshakeConsumers.put(
+                    SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "HelloRequest" handshake message producer.
+     */
+    private static final class HelloRequestProducer
+            implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private HelloRequestProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            HelloRequestMessage hrm = new HelloRequestMessage(shc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced HelloRequest handshake message", hrm);
+            }
+
+            // Output the handshake message.
+            hrm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // update the context
+
+            // What's the expected response?
+            shc.handshakeConsumers.put(
+                    SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "HelloRequest" handshake message consumer.
+     */
+    private static final class HelloRequestConsumer
+            implements SSLConsumer {
+
+        // Prevent instantiation of this class.
+        private HelloRequestConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // For TLS 1.2 and prior versions, the HelloRequest message MAY
+            // be sent by the server at any time.  Please don't clean up this
+            // handshake consumer.
+            HelloRequestMessage hrm = new HelloRequestMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming HelloRequest handshake message", hrm);
+            }
+
+            if (!chc.kickstartMessageDelivered) {
+                if (!chc.conContext.secureRenegotiation &&
+                        !HandshakeContext.allowUnsafeRenegotiation) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "Unsafe renegotiation is not allowed");
+                }
+
+                if (!chc.conContext.secureRenegotiation) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                                "Continue with insecure renegotiation");
+                    }
+                }
+
+                // update the responders
+                chc.handshakeProducers.put(
+                        SSLHandshake.CLIENT_HELLO.id,
+                        SSLHandshake.CLIENT_HELLO);
+
+                //
+                // produce response handshake message
+                //
+                SSLHandshake.CLIENT_HELLO.produce(context, hrm);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ingore HelloRequest, handshaking is in progress");
+                }
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Locale;
+import sun.security.ssl.ClientHello.ClientHelloMessage;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the HelloVerifyRequest handshake message.
+ */
+final class HelloVerifyRequest {
+    static final SSLConsumer handshakeConsumer =
+            new HelloVerifyRequestConsumer();
+    static final HandshakeProducer handshakeProducer =
+            new HelloVerifyRequestProducer();
+
+    /**
+     * The HelloVerifyRequest handshake message [RFC 6347].
+     */
+    static final class HelloVerifyRequestMessage extends HandshakeMessage {
+        final int                   serverVersion;
+        final byte[]                cookie;
+
+        HelloVerifyRequestMessage(HandshakeContext context,
+                HandshakeMessage message) throws IOException {
+            super(context);
+            // This happens in server side only.
+            ServerHandshakeContext shc =
+                    (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            HelloCookieManager hcMgr =
+                shc.sslContext.getHelloCookieManager(ProtocolVersion.DTLS10);
+            this.serverVersion = shc.clientHelloVersion;
+            this.cookie = hcMgr.createCookie(shc, clientHello);
+        }
+
+        HelloVerifyRequestMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+            // This happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            //  struct {
+            //      ProtocolVersion server_version;
+            //      opaque cookie<0..2^8-1>;
+            //  } HelloVerifyRequest;
+            if (m.remaining() < 3) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid HelloVerifyRequest: no sufficient data");
+            }
+
+            byte major = m.get();
+            byte minor = m.get();
+            this.serverVersion = ((major & 0xFF) << 8) | (minor & 0xFF);
+            this.cookie = Record.getBytes8(m);
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.HELLO_VERIFY_REQUEST;
+        }
+
+        @Override
+        public int messageLength() {
+            return 3 + cookie.length;   //  2: the length of protocol version
+                                        // +1: the cookie length
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putInt8((byte)((serverVersion >>> 8) & 0xFF));
+            hos.putInt8((byte)(serverVersion & 0xFF));
+            hos.putBytes8(cookie);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"HelloVerifyRequest\": '{'\n" +
+                "  \"server version\"      : \"{0}\",\n" +
+                "  \"cookie\"              : \"{1}\",\n" +
+                "'}'",
+                Locale.ENGLISH);
+            Object[] messageFields = {
+                ProtocolVersion.nameOf(serverVersion),
+                Utilities.toHexString(cookie),
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "HelloVerifyRequest" handshake message producer.
+     */
+    private static final
+            class HelloVerifyRequestProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private HelloVerifyRequestProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // clean up this producer
+            shc.handshakeProducers.remove(SSLHandshake.HELLO_VERIFY_REQUEST.id);
+
+            HelloVerifyRequestMessage hvrm =
+                    new HelloVerifyRequestMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced HelloVerifyRequest handshake message", hvrm);
+            }
+
+            // Output the handshake message.
+            hvrm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // update the context
+
+            // Stateless, clean up the handshake context as well?
+            shc.handshakeHash.finish();     // forgot about the handshake hash
+            shc.handshakeExtensions.clear();
+
+            // What's the expected response?
+            shc.handshakeConsumers.put(
+                    SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "HelloVerifyRequest" handshake message consumer.
+     */
+    private static final class HelloVerifyRequestConsumer
+            implements SSLConsumer {
+
+        // Prevent instantiation of this class.
+        private HelloVerifyRequestConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.remove(SSLHandshake.HELLO_VERIFY_REQUEST.id);
+            if (!chc.handshakeConsumers.isEmpty()) {
+                chc.handshakeConsumers.remove(SSLHandshake.SERVER_HELLO.id);
+            }
+            if (!chc.handshakeConsumers.isEmpty()) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "No more message expected before " +
+                        "HelloVerifyRequest is processed");
+            }
+
+            // Refresh handshake hash.
+            chc.handshakeHash.finish();     // forgot about the handshake hash
+
+            HelloVerifyRequestMessage hvrm =
+                    new HelloVerifyRequestMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming HelloVerifyRequest handshake message", hvrm);
+            }
+
+            // Note that HelloVerifyRequest.server_version is used solely to
+            // indicate packet formatting, and not as part of version
+            // negotiation.  Need not to check version values match for
+            // HelloVerifyRequest message.
+            chc.initialClientHelloMsg.setHelloCookie(hvrm.cookie);
+
+            //
+            // produce response handshake message
+            //
+            SSLHandshake.CLIENT_HELLO.produce(context, hvrm);
+        }
+    }
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/InputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/InputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,16 +25,14 @@
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.util.*;
-
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
 import javax.crypto.BadPaddingException;
-
-import javax.net.ssl.*;
-
-import sun.security.util.HexDumpEncoder;
-
+import sun.security.ssl.SSLCipher.SSLReadCipher;
 
 /**
  * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input
@@ -42,15 +40,12 @@
  *
  * @author David Brownell
  */
-class InputRecord implements Record, Closeable {
-
-    /* Class and subclass dynamic debugging support */
-    static final Debug debug = Debug.getInstance("ssl");
+abstract class InputRecord implements Record, Closeable {
+    SSLReadCipher       readCipher;
+    // Needed for KeyUpdate, used after Handshake.Finished
+    TransportContext            tc;
 
-    Authenticator       readAuthenticator;
-    CipherBox           readCipher;
-
-    HandshakeHash       handshakeHash;
+    final HandshakeHash handshakeHash;
     boolean             isClosed;
 
     // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
@@ -61,10 +56,11 @@
     // fragment size
     int                 fragmentSize;
 
-    InputRecord() {
-        this.readCipher = CipherBox.NULL;
-        this.readAuthenticator = null;      // Please override this assignment.
-        this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
+    InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) {
+        this.readCipher = readCipher;
+        this.helloVersion = ProtocolVersion.TLS10;
+        this.handshakeHash = handshakeHash;
+        this.isClosed = false;
         this.fragmentSize = Record.maxDataSize;
     }
 
@@ -72,41 +68,9 @@
         this.helloVersion = helloVersion;
     }
 
-    ProtocolVersion getHelloVersion() {
-        return helloVersion;
-    }
-
-    /*
-     * Set instance for the computation of handshake hashes.
-     *
-     * For handshaking, we need to be able to hash every byte above the
-     * record marking layer.  This is where we're guaranteed to see those
-     * bytes, so this is where we can hash them ... especially in the
-     * case of hashing the initial V2 message!
-     */
-    void setHandshakeHash(HandshakeHash handshakeHash) {
-        if (handshakeHash != null) {
-            byte[] reserved = null;
-            if (this.handshakeHash != null) {
-                reserved = this.handshakeHash.getAllHandshakeMessages();
-            }
-            if ((reserved != null) && (reserved.length != 0)) {
-                handshakeHash.update(reserved, 0, reserved.length);
-
-               if (debug != null && Debug.isOn("data")) {
-                    Debug.printHex(
-                        "[reserved] handshake hash: len = " + reserved.length,
-                        reserved);
-               }
-            }
-        }
-
-        this.handshakeHash = handshakeHash;
-    }
-
     boolean seqNumIsHuge() {
-        return (readAuthenticator != null) &&
-                        readAuthenticator.seqNumIsHuge();
+        return (readCipher.authenticator != null) &&
+                        readCipher.authenticator.seqNumIsHuge();
     }
 
     boolean isEmpty() {
@@ -118,6 +82,11 @@
         // blank
     }
 
+    // apply to DTLS SSLEngine
+    void finishHandshake() {
+        // blank
+    }
+
     /**
      * Prevent any more data from being read into this record,
      * and flag the record as holding no data.
@@ -130,9 +99,12 @@
         }
     }
 
+    synchronized boolean isClosed() {
+        return isClosed;
+    }
+
     // apply to SSLSocket and SSLEngine
-    void changeReadCiphers(
-            Authenticator readAuthenticator, CipherBox readCipher) {
+    void changeReadCiphers(SSLReadCipher readCipher) {
 
         /*
          * Dispose of any intermediate state in the underlying cipher.
@@ -144,7 +116,6 @@
          */
         readCipher.dispose();
 
-        this.readAuthenticator = readAuthenticator;
         this.readCipher = readCipher;
     }
 
@@ -160,24 +131,22 @@
      * @return -1 if there are not enough bytes to tell (small header),
      */
     // apply to SSLEngine only
-    int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
+    int bytesInCompletePacket(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
+
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    // apply to SSLSocket only
+    int bytesInCompletePacket() throws IOException {
         throw new UnsupportedOperationException();
     }
 
     // apply to SSLSocket only
-    int bytesInCompletePacket(InputStream is) throws IOException {
+    void setReceiverStream(InputStream inputStream) {
         throw new UnsupportedOperationException();
     }
 
-    /**
-     * Return true if the specified record protocol version is out of the
-     * range of the possible supported versions.
-     */
-    void checkRecordVersion(ProtocolVersion version,
-            boolean allowSSL20Hello) throws SSLException {
-        // blank
-    }
-
     // apply to DTLS SSLEngine only
     Plaintext acquirePlaintext()
             throws IOException, BadPaddingException {
@@ -186,17 +155,8 @@
 
     // read, decrypt and decompress the network record.
     //
-    // apply to SSLEngine only
-    Plaintext decode(ByteBuffer netData)
-            throws IOException, BadPaddingException {
-        throw new UnsupportedOperationException();
-    }
-
-    // apply to SSLSocket only
-    Plaintext decode(InputStream is, ByteBuffer destination)
-            throws IOException, BadPaddingException {
-        throw new UnsupportedOperationException();
-    }
+    abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
+            int srcsLength) throws IOException, BadPaddingException;
 
     // apply to SSLSocket only
     void setDeliverStream(OutputStream outputStream) {
@@ -216,9 +176,7 @@
 
     // Not apply to DTLS
     static ByteBuffer convertToClientHello(ByteBuffer packet) {
-
         int srcPos = packet.position();
-        int srcLim = packet.limit();
 
         byte firstByte = packet.get();
         byte secondByte = packet.get();
@@ -244,7 +202,7 @@
         //  1: length byte of ClientHello.session_id
         //  2: length bytes of ClientHello.cipher_suites
         //  2: empty ClientHello.compression_methods
-        int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3 );
+        int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3);
         byte[] converted = new byte[requiredSize];
 
         /*
@@ -252,7 +210,7 @@
          * that's now buffered up.  (Lengths are fixed up later).
          */
         // Note: need not to set the header actually.
-        converted[0] = ct_handshake;
+        converted[0] = ContentType.HANDSHAKE.id;
         converted[1] = majorVersion;
         converted[2] = minorVersion;
         // header [3..4] for handshake message length
@@ -325,7 +283,7 @@
         j = pointer + 2;
         for (int i = 0; i < cipherSpecLen; i += 3) {
             if (packet.get() != 0) {
-                // Ignore version 2.0 specifix cipher suite.  Clients
+                // Ignore version 2.0 specific cipher suite.  Clients
                 // should also include the version 3.0 equivalent in
                 // the V2ClientHello message.
                 packet.get();           // ignore the 2nd byte
@@ -372,237 +330,61 @@
         return ByteBuffer.wrap(converted, 5, pointer - 5);  // 5: header size
     }
 
-    static ByteBuffer decrypt(Authenticator authenticator, CipherBox box,
-            byte contentType, ByteBuffer bb) throws BadPaddingException {
-
-        return decrypt(authenticator, box, contentType, bb, null);
-    }
-
-    static ByteBuffer decrypt(Authenticator authenticator,
-            CipherBox box, byte contentType, ByteBuffer bb,
-            byte[] sequence) throws BadPaddingException {
-
-        BadPaddingException reservedBPE = null;
-        int tagLen =
-            (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
-        int cipheredLength = bb.remaining();
-        int srcPos = bb.position();
-        if (!box.isNullCipher()) {
-            try {
-                // apply explicit nonce for AEAD/CBC cipher suites if needed
-                int nonceSize = box.applyExplicitNonce(
-                        authenticator, contentType, bb, sequence);
-
-                // decrypt the content
-                if (box.isAEADMode()) {
-                    // DON'T decrypt the nonce_explicit for AEAD mode
-                    bb.position(srcPos + nonceSize);
-                }   // The explicit IV for CBC mode can be decrypted.
-
-                // Note that the CipherBox.decrypt() does not change
-                // the capacity of the buffer.
-                box.decrypt(bb, tagLen);
-                // We don't actually remove the nonce.
-                bb.position(srcPos + nonceSize);
-            } catch (BadPaddingException bpe) {
-                // RFC 2246 states that decryption_failed should be used
-                // for this purpose. However, that allows certain attacks,
-                // so we just send bad record MAC. We also need to make
-                // sure to always check the MAC to avoid a timing attack
-                // for the same issue. See paper by Vaudenay et al and the
-                // update in RFC 4346/5246.
-                //
-                // Failover to message authentication code checking.
-                reservedBPE = bpe;
-            }
-        }
+    // Extract an SSL/(D)TLS record from the specified source buffers.
+    static ByteBuffer extract(
+            ByteBuffer[] buffers, int offset, int length, int headerSize) {
 
-        // Requires message authentication code for null, stream and block
-        // cipher suites.
-        if ((authenticator instanceof MAC) && (tagLen != 0)) {
-            MAC signer = (MAC)authenticator;
-            int contentLen = bb.remaining() - tagLen;
-
-            // Note that although it is not necessary, we run the same MAC
-            // computation and comparison on the payload for both stream
-            // cipher and CBC block cipher.
-            if (contentLen < 0) {
-                // negative data length, something is wrong
-                if (reservedBPE == null) {
-                    reservedBPE = new BadPaddingException("bad record");
+        boolean hasFullHeader = false;
+        int contentLen = -1;
+        for (int i = offset, j = 0;
+                i < (offset + length) && j < headerSize; i++) {
+            int remains = buffers[i].remaining();
+            int pos = buffers[i].position();
+            for (int k = 0; k < remains && j < headerSize; j++, k++) {
+                byte b = buffers[i].get(pos + k);
+                if (j == (headerSize - 2)) {
+                    contentLen = ((b & 0xFF) << 8);
+                } else if (j == (headerSize -1)) {
+                    contentLen |= (b & 0xFF);
+                    hasFullHeader = true;
+                    break;
                 }
-
-                // set offset of the dummy MAC
-                contentLen = cipheredLength - tagLen;
-                bb.limit(srcPos + cipheredLength);
-            }
-
-            // Run MAC computation and comparison on the payload.
-            //
-            // MAC data would be stripped off during the check.
-            if (checkMacTags(contentType, bb, signer, sequence, false)) {
-                if (reservedBPE == null) {
-                    reservedBPE = new BadPaddingException("bad record MAC");
-                }
-            }
-
-            // Run MAC computation and comparison on the remainder.
-            //
-            // It is only necessary for CBC block cipher.  It is used to get a
-            // constant time of MAC computation and comparison on each record.
-            if (box.isCBCMode()) {
-                int remainingLen = calculateRemainingLen(
-                                        signer, cipheredLength, contentLen);
-
-                // NOTE: remainingLen may be bigger (less than 1 block of the
-                // hash algorithm of the MAC) than the cipheredLength.
-                //
-                // Is it possible to use a static buffer, rather than allocate
-                // it dynamically?
-                remainingLen += signer.MAClen();
-                ByteBuffer temporary = ByteBuffer.allocate(remainingLen);
-
-                // Won't need to worry about the result on the remainder. And
-                // then we won't need to worry about what's actual data to
-                // check MAC tag on.  We start the check from the header of the
-                // buffer so that we don't need to construct a new byte buffer.
-                checkMacTags(contentType, temporary, signer, sequence, true);
             }
         }
 
-        // Is it a failover?
-        if (reservedBPE != null) {
-            throw reservedBPE;
-        }
-
-        return bb.slice();
-    }
-
-    /*
-     * Run MAC computation and comparison
-     *
-     */
-    private static boolean checkMacTags(byte contentType, ByteBuffer bb,
-            MAC signer, byte[] sequence, boolean isSimulated) {
-
-        int tagLen = signer.MAClen();
-        int position = bb.position();
-        int lim = bb.limit();
-        int macOffset = lim - tagLen;
-
-        bb.limit(macOffset);
-        byte[] hash = signer.compute(contentType, bb, sequence, isSimulated);
-        if (hash == null || tagLen != hash.length) {
-            // Something is wrong with MAC implementation.
-            throw new RuntimeException("Internal MAC error");
+        if (!hasFullHeader) {
+            throw new BufferUnderflowException();
         }
 
-        bb.position(macOffset);
-        bb.limit(lim);
-        try {
-            int[] results = compareMacTags(bb, hash);
-            return (results[0] != 0);
-        } finally {
-            // reset to the data
-            bb.position(position);
-            bb.limit(macOffset);
-        }
-    }
-
-    /*
-     * A constant-time comparison of the MAC tags.
-     *
-     * Please DON'T change the content of the ByteBuffer parameter!
-     */
-    private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
-
-        // An array of hits is used to prevent Hotspot optimization for
-        // the purpose of a constant-time check.
-        int[] results = {0, 0};     // {missed #, matched #}
-
-        // The caller ensures there are enough bytes available in the buffer.
-        // So we won't need to check the remaining of the buffer.
-        for (int i = 0; i < tag.length; i++) {
-            if (bb.get() != tag[i]) {
-                results[0]++;       // mismatched bytes
-            } else {
-                results[1]++;       // matched bytes
+        int packetLen = headerSize + contentLen;
+        int remains = 0;
+        for (int i = offset; i < offset + length; i++) {
+            remains += buffers[i].remaining();
+            if (remains >= packetLen) {
+                break;
             }
         }
 
-        return results;
-    }
-
-    /*
-     * Run MAC computation and comparison
-     *
-     * Please DON'T change the content of the byte buffer parameter!
-     */
-    private static boolean checkMacTags(byte contentType, byte[] buffer,
-            int offset, int contentLen, MAC signer, boolean isSimulated) {
-
-        int tagLen = signer.MAClen();
-        byte[] hash = signer.compute(
-                contentType, buffer, offset, contentLen, isSimulated);
-        if (hash == null || tagLen != hash.length) {
-            // Something is wrong with MAC implementation.
-            throw new RuntimeException("Internal MAC error");
+        if (remains < packetLen) {
+            throw new BufferUnderflowException();
         }
 
-        int[] results = compareMacTags(buffer, offset + contentLen, hash);
-        return (results[0] != 0);
-    }
-
-    /*
-     * A constant-time comparison of the MAC tags.
-     *
-     * Please DON'T change the content of the byte buffer parameter!
-     */
-    private static int[] compareMacTags(
-            byte[] buffer, int offset, byte[] tag) {
+        byte[] packet = new byte[packetLen];
+        int packetOffset = 0;
+        int packetSpaces = packetLen;
+        for (int i = offset; i < offset + length; i++) {
+            if (buffers[i].hasRemaining()) {
+                int len = Math.min(packetSpaces, buffers[i].remaining());
+                buffers[i].get(packet, packetOffset, len);
+                packetOffset += len;
+                packetSpaces -= len;
+            }
 
-        // An array of hits is used to prevent Hotspot optimization for
-        // the purpose of a constant-time check.
-        int[] results = {0, 0};    // {missed #, matched #}
-
-        // The caller ensures there are enough bytes available in the buffer.
-        // So we won't need to check the length of the buffer.
-        for (int i = 0; i < tag.length; i++) {
-            if (buffer[offset + i] != tag[i]) {
-                results[0]++;       // mismatched bytes
-            } else {
-                results[1]++;       // matched bytes
+            if (packetSpaces <= 0) {
+                break;
             }
         }
 
-        return results;
-    }
-
-    /*
-     * Calculate the length of a dummy buffer to run MAC computation
-     * and comparison on the remainder.
-     *
-     * The caller MUST ensure that the fullLen is not less than usedLen.
-     */
-    private static int calculateRemainingLen(
-            MAC signer, int fullLen, int usedLen) {
-
-        int blockLen = signer.hashBlockLen();
-        int minimalPaddingLen = signer.minimalPaddingLen();
-
-        // (blockLen - minimalPaddingLen) is the maximum message size of
-        // the last block of hash function operation. See FIPS 180-4, or
-        // MD5 specification.
-        fullLen += 13 - (blockLen - minimalPaddingLen);
-        usedLen += 13 - (blockLen - minimalPaddingLen);
-
-        // Note: fullLen is always not less than usedLen, and blockLen
-        // is always bigger than minimalPaddingLen, so we don't worry
-        // about negative values. 0x01 is added to the result to ensure
-        // that the return value is positive.  The extra one byte does
-        // not impact the overall MAC compression function evaluations.
-        return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) -
-                Math.ceil(usedLen/(1.0d * blockLen))) * blockLen;
+        return ByteBuffer.wrap(packet);
     }
 }
-
--- a/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/JsseJce.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,25 +25,16 @@
 
 package sun.security.ssl;
 
-import java.util.*;
 import java.math.BigInteger;
-
 import java.security.*;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.*;
-
+import java.util.*;
 import javax.crypto.*;
-
-// explicit import to override the Provider class in this package
-import java.security.Provider;
-
-// need internal Sun classes for FIPS tricks
+import sun.security.jca.ProviderList;
 import sun.security.jca.Providers;
-import sun.security.jca.ProviderList;
-
+import static sun.security.ssl.SunJSSE.cryptoProvider;
 import sun.security.util.ECUtil;
-
-import static sun.security.ssl.SunJSSE.cryptoProvider;
 import static sun.security.util.SecurityConstants.PROVIDER_VER;
 
 /**
@@ -53,18 +44,11 @@
  * @author  Andreas Sterbenz
  */
 final class JsseJce {
+    static final boolean ALLOW_ECC =
+            Utilities.getBooleanProperty("com.sun.net.ssl.enableECC", true);
 
     private static final ProviderList fipsProviderList;
 
-    // Flag indicating whether Kerberos crypto is available.
-    // If true, then all the Kerberos-based crypto we need is available.
-    private static final boolean kerberosAvailable;
-    static {
-        ClientKeyExchangeService p =
-                ClientKeyExchangeService.find("KRB5");
-        kerberosAvailable = (p != null);
-    }
-
     static {
         // force FIPS flag initialization
         // Because isFIPS() is synchronized and cryptoProvider is not modified
@@ -116,37 +100,45 @@
      * Can be used for encryption, decryption, signing, verifying.
      */
     static final String CIPHER_RSA_PKCS1 = "RSA/ECB/PKCS1Padding";
+
     /**
      * JCE transformation string for the stream cipher RC4.
      */
     static final String CIPHER_RC4 = "RC4";
+
     /**
      * JCE transformation string for DES in CBC mode without padding.
      */
     static final String CIPHER_DES = "DES/CBC/NoPadding";
+
     /**
      * JCE transformation string for (3-key) Triple DES in CBC mode
      * without padding.
      */
     static final String CIPHER_3DES = "DESede/CBC/NoPadding";
+
     /**
      * JCE transformation string for AES in CBC mode
      * without padding.
      */
     static final String CIPHER_AES = "AES/CBC/NoPadding";
+
     /**
      * JCE transformation string for AES in GCM mode
      * without padding.
      */
     static final String CIPHER_AES_GCM = "AES/GCM/NoPadding";
+
     /**
      * JCA identifier string for DSA, i.e. a DSA with SHA-1.
      */
     static final String SIGNATURE_DSA = "DSA";
+
     /**
      * JCA identifier string for ECDSA, i.e. a ECDSA with SHA-1.
      */
     static final String SIGNATURE_ECDSA = "SHA1withECDSA";
+
     /**
      * JCA identifier string for Raw DSA, i.e. a DSA signature without
      * hashing where the application provides the SHA-1 hash of the data.
@@ -154,17 +146,20 @@
      * for compatibility.
      */
     static final String SIGNATURE_RAWDSA = "RawDSA";
+
     /**
      * JCA identifier string for Raw ECDSA, i.e. a DSA signature without
      * hashing where the application provides the SHA-1 hash of the data.
      */
     static final String SIGNATURE_RAWECDSA = "NONEwithECDSA";
+
     /**
      * JCA identifier string for Raw RSA, i.e. a RSA PKCS#1 v1.5 signature
      * without hashing where the application provides the hash of the data.
      * Used for RSA client authentication with a 36 byte hash.
      */
     static final String SIGNATURE_RAWRSA = "NONEwithRSA";
+
     /**
      * JCA identifier string for the SSL/TLS style RSA Signature. I.e.
      * an signature using RSA with PKCS#1 v1.5 padding signing a
@@ -180,10 +175,6 @@
         return EcAvailability.isAvailable;
     }
 
-    static boolean isKerberosAvailable() {
-        return kerberosAvailable;
-    }
-
     /**
      * Return an JCE cipher implementation for the specified algorithm.
      */
@@ -299,7 +290,8 @@
         for (Provider.Service s : cryptoProvider.getServices()) {
             if (s.getType().equals("SecureRandom")) {
                 try {
-                    return SecureRandom.getInstance(s.getAlgorithm(), cryptoProvider);
+                    return SecureRandom.getInstance(
+                            s.getAlgorithm(), cryptoProvider);
                 } catch (NoSuchAlgorithmException ee) {
                     // ignore
                 }
@@ -394,7 +386,7 @@
     // See Effective Java Second Edition: Item 71.
     private static class EcAvailability {
         // Is EC crypto available?
-        private final static boolean isAvailable;
+        private static final boolean isAvailable;
 
         static {
             boolean mediator = true;
--- a/src/java.base/share/classes/sun/security/ssl/KeyManagerFactoryImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/KeyManagerFactoryImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -91,9 +91,11 @@
                 keyManager = new X509KeyManagerImpl(
                         Collections.<Builder>emptyList());
             } else {
-                if (SunJSSE.isFIPS() && (ks.getProvider() != SunJSSE.cryptoProvider)) {
-                    throw new KeyStoreException("FIPS mode: KeyStore must be "
-                        + "from provider " + SunJSSE.cryptoProvider.getName());
+                if (SunJSSE.isFIPS() &&
+                        (ks.getProvider() != SunJSSE.cryptoProvider)) {
+                    throw new KeyStoreException(
+                        "FIPS mode: KeyStore must be " +
+                        "from provider " + SunJSSE.cryptoProvider.getName());
                 }
                 try {
                     Builder builder = Builder.newInstance(ks,
@@ -114,7 +116,6 @@
                 "Parameters must be instance of KeyStoreBuilderParameters");
             }
             if (SunJSSE.isFIPS()) {
-                // XXX should be fixed
                 throw new InvalidAlgorithmParameterException
                     ("FIPS mode: KeyStoreBuilderParameters not supported");
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,954 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import javax.net.ssl.SSLProtocolException;
+import sun.security.ssl.DHKeyExchange.DHECredentials;
+import sun.security.ssl.DHKeyExchange.DHEPossession;
+import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
+import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
+import sun.security.ssl.KeyShareExtension.CHKeyShareSpec;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+import sun.security.util.HexDumpEncoder;
+
+/**
+ * Pack of the "key_share" extensions.
+ */
+final class KeyShareExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHKeyShareProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHKeyShareConsumer();
+    static final SSLStringizer chStringizer =
+            new CHKeyShareStringizer();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHKeyShareProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHKeyShareConsumer();
+    static final HandshakeAbsence shOnLoadAbsence =
+            new SHKeyShareAbsence();
+    static final SSLStringizer shStringizer =
+            new SHKeyShareStringizer();
+
+    static final HandshakeProducer hrrNetworkProducer =
+            new HRRKeyShareProducer();
+    static final ExtensionConsumer hrrOnLoadConsumer =
+            new HRRKeyShareConsumer();
+    static final HandshakeProducer hrrNetworkReproducer =
+            new HRRKeyShareReproducer();
+    static final SSLStringizer hrrStringizer =
+            new HRRKeyShareStringizer();
+
+    /**
+     * The key share entry used in "key_share" extensions.
+     */
+    private static final class KeyShareEntry {
+        final int namedGroupId;
+        final byte[] keyExchange;
+
+        private KeyShareEntry(int namedGroupId, byte[] keyExchange) {
+            this.namedGroupId = namedGroupId;
+            this.keyExchange = keyExchange;
+        }
+
+        private byte[] getEncoded() {
+            byte[] buffer = new byte[keyExchange.length + 4];
+                                            //  2: named group id
+                                            // +2: key exchange length
+            ByteBuffer m = ByteBuffer.wrap(buffer);
+            try {
+                Record.putInt16(m, namedGroupId);
+                Record.putBytes16(m, keyExchange);
+            } catch (IOException ioe) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                        "Unlikely IOException", ioe);
+                }
+            }
+
+            return buffer;
+        }
+
+        private int getEncodedSize() {
+            return keyExchange.length + 4;  //  2: named group id
+                                            // +2: key exchange length
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\n'{'\n" +
+                "  \"named group\": {0}\n" +
+                "  \"key_exchange\": '{'\n" +
+                "{1}\n" +
+                "  '}'\n" +
+                "'}',", Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                NamedGroup.nameOf(namedGroupId),
+                Utilities.indent(hexEncoder.encode(keyExchange), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "key_share" extension in a ClientHello handshake message.
+     */
+    static final class CHKeyShareSpec implements SSLExtensionSpec {
+        final List<KeyShareEntry> clientShares;
+
+        private CHKeyShareSpec(List<KeyShareEntry> clientShares) {
+            this.clientShares = clientShares;
+        }
+
+        private CHKeyShareSpec(ByteBuffer buffer) throws IOException {
+            // struct {
+            //      KeyShareEntry client_shares<0..2^16-1>;
+            // } KeyShareClientHello;
+            if (buffer.remaining() < 2) {
+                throw new SSLProtocolException(
+                    "Invalid key_share extension: " +
+                    "insufficient data (length=" + buffer.remaining() + ")");
+            }
+
+            int listLen = Record.getInt16(buffer);
+            if (listLen != buffer.remaining()) {
+                throw new SSLProtocolException(
+                    "Invalid key_share extension: " +
+                    "incorrect list length (length=" + listLen + ")");
+            }
+
+            List<KeyShareEntry> keyShares = new LinkedList<>();
+            while (buffer.hasRemaining()) {
+                int namedGroupId = Record.getInt16(buffer);
+                byte[] keyExchange = Record.getBytes16(buffer);
+                if (keyExchange.length == 0) {
+                    throw new SSLProtocolException(
+                        "Invalid key_share extension: empty key_exchange");
+                }
+
+                keyShares.add(new KeyShareEntry(namedGroupId, keyExchange));
+            }
+
+            this.clientShares = Collections.unmodifiableList(keyShares);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"client_shares\": '['{0}\n']'", Locale.ENGLISH);
+
+            StringBuilder builder = new StringBuilder(512);
+            for (KeyShareEntry entry : clientShares) {
+                builder.append(entry.toString());
+            }
+
+            Object[] messageFields = {
+                Utilities.indent(builder.toString())
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    private static final class CHKeyShareStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new CHKeyShareSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of the extension in a ClientHello
+     * handshake message.
+     */
+    private static final
+            class CHKeyShareProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHKeyShareProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable key_share extension");
+                }
+                return null;
+            }
+
+            List<NamedGroup> namedGroups;
+            if (chc.serverSelectedNamedGroup != null) {
+                // Response to HelloRetryRequest
+                namedGroups = Arrays.asList(chc.serverSelectedNamedGroup);
+            } else {
+                namedGroups = chc.clientRequestedNamedGroups;
+                if (namedGroups == null || namedGroups.isEmpty()) {
+                    // No supported groups.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "Ignore key_share extension, no supported groups");
+                    }
+                    return null;
+                }
+            }
+
+            List<KeyShareEntry> keyShares = new LinkedList<>();
+            for (NamedGroup ng : namedGroups) {
+                SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
+                if (ke == null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "No key exchange for named group " + ng.name);
+                    }
+                    continue;
+                }
+
+                SSLPossession[] poses = ke.createPossessions(chc);
+                for (SSLPossession pos : poses) {
+                    // update the context
+                    chc.handshakePossessions.add(pos);
+                    if (!(pos instanceof ECDHEPossession) &&
+                            !(pos instanceof DHEPossession)) {
+                        // May need more possesion types in the future.
+                        continue;
+                    }
+
+                    keyShares.add(new KeyShareEntry(ng.id, pos.encode()));
+                }
+
+                // One key share entry only.  Too much key share entries makes
+                // the ClientHello handshake message really big.
+                if (!keyShares.isEmpty()) {
+                    break;
+                }
+            }
+
+            int listLen = 0;
+            for (KeyShareEntry entry : keyShares) {
+                listLen += entry.getEncodedSize();
+            }
+            byte[] extData = new byte[listLen + 2];     //  2: list length
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, listLen);
+            for (KeyShareEntry entry : keyShares) {
+                m.put(entry.getEncoded());
+            }
+
+            // update the context
+            chc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE,
+                    new CHKeyShareSpec(keyShares));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of the extension in a ClientHello
+     * handshake message.
+     */
+    private static final class CHKeyShareConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHKeyShareConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            if (shc.handshakeExtensions.containsKey(SSLExtension.CH_KEY_SHARE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "The key_share extension has been loaded");
+                }
+                return;
+            }
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable key_share extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension
+            CHKeyShareSpec spec;
+            try {
+                spec = new CHKeyShareSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            List<SSLCredentials> credentials = new LinkedList<>();
+            for (KeyShareEntry entry : spec.clientShares) {
+                NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
+                if (ng == null || !SupportedGroups.isActivatable(
+                        shc.sslConfig.algorithmConstraints, ng)) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                                "Ignore unsupported named group: " +
+                                NamedGroup.nameOf(entry.namedGroupId));
+                    }
+                    continue;
+                }
+
+                if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                    try {
+                        ECDHECredentials ecdhec =
+                            ECDHECredentials.valueOf(ng, entry.keyExchange);
+                        if (ecdhec != null) {
+                            if (!shc.algorithmConstraints.permits(
+                                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                                    ecdhec.popPublicKey)) {
+                                SSLLogger.warning(
+                                        "ECDHE key share entry does not " +
+                                        "comply to algorithm constraints");
+                            } else {
+                                credentials.add(ecdhec);
+                            }
+                        }
+                    } catch (IOException | GeneralSecurityException ex) {
+                        SSLLogger.warning(
+                                "Cannot decode named group: " +
+                                NamedGroup.nameOf(entry.namedGroupId));
+                    }
+                } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+                    try {
+                        DHECredentials dhec =
+                                DHECredentials.valueOf(ng, entry.keyExchange);
+                        if (dhec != null) {
+                            if (!shc.algorithmConstraints.permits(
+                                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                                    dhec.popPublicKey)) {
+                                SSLLogger.warning(
+                                        "DHE key share entry does not " +
+                                        "comply to algorithm constraints");
+                            } else {
+                                credentials.add(dhec);
+                            }
+                        }
+                    } catch (IOException | GeneralSecurityException ex) {
+                        SSLLogger.warning(
+                                "Cannot decode named group: " +
+                                NamedGroup.nameOf(entry.namedGroupId));
+                    }
+                }
+            }
+
+            if (!credentials.isEmpty()) {
+                shc.handshakeCredentials.addAll(credentials);
+            } else {
+                // New handshake credentials are required from the client side.
+                shc.handshakeProducers.put(
+                        SSLHandshake.HELLO_RETRY_REQUEST.id,
+                        SSLHandshake.HELLO_RETRY_REQUEST);
+            }
+
+            // update the context
+            shc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE, spec);
+        }
+    }
+
+    /**
+     * The key share entry used in ServerHello "key_share" extensions.
+     */
+    static final class SHKeyShareSpec implements SSLExtensionSpec {
+        final KeyShareEntry serverShare;
+
+        SHKeyShareSpec(KeyShareEntry serverShare) {
+            this.serverShare = serverShare;
+        }
+
+        private SHKeyShareSpec(ByteBuffer buffer) throws IOException {
+            // struct {
+            //      KeyShareEntry server_share;
+            // } KeyShareServerHello;
+            if (buffer.remaining() < 5) {       // 5: minimal server_share
+                throw new SSLProtocolException(
+                    "Invalid key_share extension: " +
+                    "insufficient data (length=" + buffer.remaining() + ")");
+            }
+
+            int namedGroupId = Record.getInt16(buffer);
+            byte[] keyExchange = Record.getBytes16(buffer);
+
+            if (buffer.hasRemaining()) {
+                throw new SSLProtocolException(
+                    "Invalid key_share extension: unknown extra data");
+            }
+
+            this.serverShare = new KeyShareEntry(namedGroupId, keyExchange);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"server_share\": '{'\n" +
+                "  \"named group\": {0}\n" +
+                "  \"key_exchange\": '{'\n" +
+                "{1}\n" +
+                "  '}'\n" +
+                "'}',", Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                NamedGroup.nameOf(serverShare.namedGroupId),
+                Utilities.indent(
+                        hexEncoder.encode(serverShare.keyExchange), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    private static final class SHKeyShareStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new SHKeyShareSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of the extension in a ServerHello
+     * handshake message.
+     */
+    private static final class SHKeyShareProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHKeyShareProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to key_share request only
+            CHKeyShareSpec kss =
+                    (CHKeyShareSpec)shc.handshakeExtensions.get(
+                            SSLExtension.CH_KEY_SHARE);
+            if (kss == null) {
+                // Unlikely, no key_share extension requested.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Ignore, no client key_share extension");
+                }
+                return null;
+            }
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Ignore, no available server key_share extension");
+                }
+                return null;
+            }
+
+            // use requested key share entries
+            if ((shc.handshakeCredentials == null) ||
+                    shc.handshakeCredentials.isEmpty()) {
+                // Unlikely, HelloRetryRequest should be used ealier.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "No available client key share entries");
+                }
+                return null;
+            }
+
+            KeyShareEntry keyShare = null;
+            for (SSLCredentials cd : shc.handshakeCredentials) {
+                NamedGroup ng = null;
+                if (cd instanceof ECDHECredentials) {
+                    ng = ((ECDHECredentials)cd).namedGroup;
+                } else if (cd instanceof DHECredentials) {
+                    ng = ((DHECredentials)cd).namedGroup;
+                }
+
+                if (ng == null) {
+                    continue;
+                }
+
+                SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
+                if (ke == null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning(
+                            "No key exchange for named group " + ng.name);
+                    }
+                    continue;
+                }
+
+                SSLPossession[] poses = ke.createPossessions(shc);
+                for (SSLPossession pos : poses) {
+                    if (!(pos instanceof ECDHEPossession) &&
+                            !(pos instanceof DHEPossession)) {
+                        // May need more possesion types in the future.
+                        continue;
+                    }
+
+                    // update the context
+                    shc.handshakeKeyExchange = ke;
+                    shc.handshakePossessions.add(pos);
+                    keyShare = new KeyShareEntry(ng.id, pos.encode());
+                    break;
+                }
+
+                if (keyShare != null) {
+                    for (Map.Entry<Byte, HandshakeProducer> me :
+                            ke.getHandshakeProducers(shc)) {
+                        shc.handshakeProducers.put(
+                                me.getKey(), me.getValue());
+                    }
+
+                    // We have got one! Don't forgor to break.
+                    break;
+                }
+            }
+
+            if (keyShare == null) {
+                // Unlikely, HelloRetryRequest should be used instead ealier.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "No available server key_share extension");
+                }
+                return null;
+            }
+
+            byte[] extData = keyShare.getEncoded();
+
+            // update the context
+            SHKeyShareSpec spec = new SHKeyShareSpec(keyShare);
+            shc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of the extension in a ServerHello
+     * handshake message.
+     */
+    private static final class SHKeyShareConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHKeyShareConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // Happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            if (chc.clientRequestedNamedGroups == null ||
+                    chc.clientRequestedNamedGroups.isEmpty()) {
+                // No supported groups.
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected key_share extension in ServerHello");
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported key_share extension in ServerHello");
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Parse the extension
+            SHKeyShareSpec spec;
+            try {
+                spec = new SHKeyShareSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            KeyShareEntry keyShare = spec.serverShare;
+            NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
+            if (ng == null || !SupportedGroups.isActivatable(
+                    chc.sslConfig.algorithmConstraints, ng)) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported named group: " +
+                        NamedGroup.nameOf(keyShare.namedGroupId));
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
+            if (ke == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "No key exchange for named group " + ng.name);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            SSLCredentials credentials = null;
+            if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                try {
+                    ECDHECredentials ecdhec =
+                            ECDHECredentials.valueOf(ng, keyShare.keyExchange);
+                    if (ecdhec != null) {
+                        if (!chc.algorithmConstraints.permits(
+                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                                ecdhec.popPublicKey)) {
+                            chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                                    "ECDHE key share entry does not " +
+                                    "comply to algorithm constraints");
+                        } else {
+                            credentials = ecdhec;
+                        }
+                    }
+                } catch (IOException | GeneralSecurityException ex) {
+                    chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                            "Cannot decode named group: " +
+                            NamedGroup.nameOf(keyShare.namedGroupId));
+                }
+            } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+                try {
+                    DHECredentials dhec =
+                            DHECredentials.valueOf(ng, keyShare.keyExchange);
+                    if (dhec != null) {
+                        if (!chc.algorithmConstraints.permits(
+                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                                dhec.popPublicKey)) {
+                            chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                                    "DHE key share entry does not " +
+                                    "comply to algorithm constraints");
+                        } else {
+                            credentials = dhec;
+                        }
+                    }
+                } catch (IOException | GeneralSecurityException ex) {
+                    chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                            "Cannot decode named group: " +
+                            NamedGroup.nameOf(keyShare.namedGroupId));
+                }
+            } else {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported named group: " +
+                        NamedGroup.nameOf(keyShare.namedGroupId));
+            }
+
+            if (credentials == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported named group: " + ng.name);
+            }
+
+            // update the context
+            chc.handshakeKeyExchange = ke;
+            chc.handshakeCredentials.add(credentials);
+            chc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
+        }
+    }
+
+    /**
+     * The absence processing if the extension is not present in
+     * the ServerHello handshake message.
+     */
+    private static final class SHKeyShareAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Cannot use the previous requested key shares any more.
+            if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
+                SSLLogger.fine(
+                        "No key_share extension in ServerHello, " +
+                        "cleanup the key shares if necessary");
+            }
+            chc.handshakePossessions.clear();
+        }
+    }
+
+    /**
+     * The key share entry used in HelloRetryRequest "key_share" extensions.
+     */
+    static final class HRRKeyShareSpec implements SSLExtensionSpec {
+        final int selectedGroup;
+
+        HRRKeyShareSpec(NamedGroup serverGroup) {
+            this.selectedGroup = serverGroup.id;
+        }
+
+        private HRRKeyShareSpec(ByteBuffer buffer) throws IOException {
+            // struct {
+            //     NamedGroup selected_group;
+            // } KeyShareHelloRetryRequest;
+            if (buffer.remaining() != 2) {
+                throw new SSLProtocolException(
+                    "Invalid key_share extension: " +
+                    "improper data (length=" + buffer.remaining() + ")");
+            }
+
+            this.selectedGroup = Record.getInt16(buffer);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"selected group\": '['{0}']'", Locale.ENGLISH);
+
+            Object[] messageFields = {
+                    NamedGroup.nameOf(selectedGroup)
+                };
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    private static final class HRRKeyShareStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new HRRKeyShareSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of the extension in a HelloRetryRequest
+     * handshake message.
+     */
+    private static final
+            class HRRKeyShareProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private HRRKeyShareProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext) context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported key_share extension in HelloRetryRequest");
+                return null;    // make the compiler happy.
+            }
+
+            if (shc.clientRequestedNamedGroups == null ||
+                    shc.clientRequestedNamedGroups.isEmpty()) {
+                // No supported groups.
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected key_share extension in HelloRetryRequest");
+                return null;    // make the compiler happy.
+            }
+
+            NamedGroup selectedGroup = null;
+            for (NamedGroup ng : shc.clientRequestedNamedGroups) {
+                if (SupportedGroups.isActivatable(
+                        shc.sslConfig.algorithmConstraints, ng)) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                                "HelloRetryRequest selected named group: " +
+                                ng.name);
+                    }
+
+                    selectedGroup = ng;
+                    break;
+                }
+            }
+
+            if (selectedGroup == null) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        new IOException("No common named group"));
+                return null;    // make the complier happy
+            }
+
+            byte[] extdata = new byte[] {
+                    (byte)((selectedGroup.id >> 8) & 0xFF),
+                    (byte)(selectedGroup.id & 0xFF)
+                };
+
+            // update the context
+            shc.serverSelectedNamedGroup = selectedGroup;
+            shc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE,
+                    new HRRKeyShareSpec(selectedGroup));
+
+            return extdata;
+        }
+    }
+
+    /**
+     * Network data producer of the extension for stateless
+     * HelloRetryRequest reconstruction.
+     */
+    private static final
+            class HRRKeyShareReproducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private HRRKeyShareReproducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext) context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported key_share extension in HelloRetryRequest");
+                return null;    // make the compiler happy.
+            }
+
+            CHKeyShareSpec spec = (CHKeyShareSpec)shc.handshakeExtensions.get(
+                    SSLExtension.CH_KEY_SHARE);
+            if (spec != null && spec.clientShares != null &&
+                    spec.clientShares.size() == 1) {
+                int namedGroupId = spec.clientShares.get(0).namedGroupId;
+
+                byte[] extdata = new byte[] {
+                        (byte)((namedGroupId >> 8) & 0xFF),
+                        (byte)(namedGroupId & 0xFF)
+                    };
+
+                return extdata;
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Network data consumer of the extension in a HelloRetryRequest
+     * handshake message.
+     */
+    private static final
+            class HRRKeyShareConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private HRRKeyShareConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported key_share extension in HelloRetryRequest");
+                return;     // make the compiler happy.
+            }
+
+            if (chc.clientRequestedNamedGroups == null ||
+                    chc.clientRequestedNamedGroups.isEmpty()) {
+                // No supported groups.
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected key_share extension in HelloRetryRequest");
+                return;     // make the compiler happy.
+            }
+
+            // Parse the extension
+            HRRKeyShareSpec spec;
+            try {
+                spec = new HRRKeyShareSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            NamedGroup serverGroup = NamedGroup.valueOf(spec.selectedGroup);
+            if (serverGroup == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unsupported HelloRetryRequest selected group: " +
+                                NamedGroup.nameOf(spec.selectedGroup));
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            if (!chc.clientRequestedNamedGroups.contains(serverGroup)) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected HelloRetryRequest selected group: " +
+                                serverGroup.name);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // update the context
+
+            // When sending the new ClientHello, the client MUST replace the
+            // original "key_share" extension with one containing only a new
+            // KeyShareEntry for the group indicated in the selected_group
+            // field of the triggering HelloRetryRequest.
+            //
+            chc.serverSelectedNamedGroup = serverGroup;
+            chc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE, spec);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SSLCipher.SSLReadCipher;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Pack of the KeyUpdate handshake message.
+ */
+final class KeyUpdate {
+    static final SSLProducer kickstartProducer =
+        new KeyUpdateKickstartProducer();
+
+    static final SSLConsumer handshakeConsumer =
+        new KeyUpdateConsumer();
+    static final HandshakeProducer handshakeProducer =
+        new KeyUpdateProducer();
+
+    /**
+     * The KeyUpdate handshake message.
+     *
+     * The KeyUpdate handshake message is used to indicate that the sender is
+     * updating its sending cryptographic keys.
+     *
+     *       enum {
+     *           update_not_requested(0), update_requested(1), (255)
+     *       } KeyUpdateRequest;
+     *
+     *       struct {
+     *           KeyUpdateRequest request_update;
+     *       } KeyUpdate;
+     */
+    static final class KeyUpdateMessage extends HandshakeMessage {
+        private final KeyUpdateRequest status;
+
+        KeyUpdateMessage(PostHandshakeContext context,
+                KeyUpdateRequest status) {
+            super(context);
+            this.status = status;
+        }
+
+        KeyUpdateMessage(PostHandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+
+            if (m.remaining() != 1) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "KeyUpdate has an unexpected length of "+
+                        m.remaining());
+            }
+
+            byte request = m.get();
+            this.status = KeyUpdateRequest.valueOf(request);
+            if (status == null) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid KeyUpdate message value: " +
+                        KeyUpdateRequest.nameOf(request));
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.KEY_UPDATE;
+        }
+
+        @Override
+        public int messageLength() {
+            // one byte enum
+            return 1;
+        }
+
+        @Override
+        public void send(HandshakeOutStream s) throws IOException {
+            s.putInt8(status.id);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"KeyUpdate\": '{'\n" +
+                    "  \"request_update\": {0}\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            Object[] messageFields = {
+                status.name
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    enum KeyUpdateRequest {
+        NOTREQUESTED        ((byte)0, "update_not_requested"),
+        REQUESTED           ((byte)1, "update_requested");
+
+        final byte id;
+        final String name;
+
+        private KeyUpdateRequest(byte id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        static KeyUpdateRequest valueOf(byte id) {
+            for (KeyUpdateRequest kur : KeyUpdateRequest.values()) {
+                if (kur.id == id) {
+                    return kur;
+                }
+            }
+
+            return null;
+        }
+
+        static String nameOf(byte id) {
+            for (KeyUpdateRequest kur : KeyUpdateRequest.values()) {
+                if (kur.id == id) {
+                    return kur.name;
+                }
+            }
+
+            return "<UNKNOWN KeyUpdateRequest TYPE: " + (id & 0x0FF) + ">";
+        }
+    }
+
+    private static final
+            class KeyUpdateKickstartProducer implements SSLProducer {
+        // Prevent instantiation of this class.
+        private KeyUpdateKickstartProducer() {
+            // blank
+        }
+
+        // Produce kickstart handshake message.
+        @Override
+        public byte[] produce(ConnectionContext context) throws IOException {
+            PostHandshakeContext hc = (PostHandshakeContext)context;
+            return handshakeProducer.produce(context,
+                    new KeyUpdateMessage(hc, KeyUpdateRequest.REQUESTED));
+        }
+    }
+
+    /**
+     * The "KeyUpdate" handshake message consumer.
+     */
+    private static final class KeyUpdateConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private KeyUpdateConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            PostHandshakeContext hc = (PostHandshakeContext)context;
+            KeyUpdateMessage km = new KeyUpdateMessage(hc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming KeyUpdate post-handshake message", km);
+            }
+
+            // Update read key and IV.
+            SSLTrafficKeyDerivation kdg =
+                SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
+            if (kdg == null) {
+                // unlikely
+                hc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                                hc.conContext.protocolVersion);
+                return;
+            }
+
+            SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
+                    hc.conContext.inputRecord.readCipher.baseSecret);
+            if (skd == null) {
+                // unlikely
+                hc.conContext.fatal(Alert.INTERNAL_ERROR, "no key derivation");
+                return;
+            }
+
+            SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
+            SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
+            SecretKey key = kd.deriveKey("TlsKey", null);
+            IvParameterSpec ivSpec = new IvParameterSpec(
+                    kd.deriveKey("TlsIv", null).getEncoded());
+            try {
+                SSLReadCipher rc =
+                    hc.negotiatedCipherSuite.bulkCipher.createReadCipher(
+                        Authenticator.valueOf(hc.conContext.protocolVersion),
+                        hc.conContext.protocolVersion, key, ivSpec,
+                        hc.sslContext.getSecureRandom());
+                hc.conContext.inputRecord.changeReadCiphers(rc);
+                hc.conContext.inputRecord.readCipher.baseSecret = nplus1;
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine("KeyUpdate: read key updated");
+                }
+            } catch (GeneralSecurityException gse) {
+                hc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failure to derive read secrets", gse);
+                return;
+            }
+
+            if (km.status == KeyUpdateRequest.REQUESTED) {
+                // Update the write key and IV.
+                handshakeProducer.produce(hc,
+                    new KeyUpdateMessage(hc, KeyUpdateRequest.NOTREQUESTED));
+                return;
+            }
+
+            // clean handshake context
+            hc.conContext.finishPostHandshake();
+        }
+    }
+
+    /**
+     * The "KeyUpdate" handshake message producer.
+     */
+    private static final class KeyUpdateProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private KeyUpdateProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            PostHandshakeContext hc = (PostHandshakeContext)context;
+            KeyUpdateMessage km = (KeyUpdateMessage)message;
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced KeyUpdate post-handshake message", km);
+            }
+
+            // Update the write key and IV.
+            SSLTrafficKeyDerivation kdg =
+                SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
+            if (kdg == null) {
+                // unlikely
+                hc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                                hc.conContext.protocolVersion);
+                return null;
+            }
+
+            SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
+                    hc.conContext.outputRecord.writeCipher.baseSecret);
+            if (skd == null) {
+                // unlikely
+                hc.conContext.fatal(Alert.INTERNAL_ERROR, "no key derivation");
+                return null;
+            }
+
+            SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
+            SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
+            SecretKey key = kd.deriveKey("TlsKey", null);
+            IvParameterSpec ivSpec = new IvParameterSpec(
+                    kd.deriveKey("TlsIv", null).getEncoded());
+
+            SSLWriteCipher wc;
+            try {
+                wc = hc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
+                        Authenticator.valueOf(hc.conContext.protocolVersion),
+                        hc.conContext.protocolVersion, key, ivSpec,
+                        hc.sslContext.getSecureRandom());
+            } catch (GeneralSecurityException gse) {
+                hc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failure to derive write secrets", gse);
+                return null;
+            }
+
+            // Output the handshake message.
+            km.write(hc.handshakeOutput);
+            hc.handshakeOutput.flush();
+
+            // change write cipher
+            hc.conContext.outputRecord.changeWriteCiphers(wc, false);
+            hc.conContext.outputRecord.writeCipher.baseSecret = nplus1;
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("KeyUpdate: write key updated");
+            }
+
+            // clean handshake context
+            hc.conContext.finishPostHandshake();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/MAC.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-/*
- * Copyright (c) 1996, 2015, 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.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-
-import java.nio.ByteBuffer;
-
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-
-import sun.security.ssl.CipherSuite.MacAlg;
-import static sun.security.ssl.CipherSuite.*;
-import static sun.security.ssl.CipherSuite.MacAlg.*;
-
-/**
- * This class computes the "Message Authentication Code" (MAC) for each
- * SSL stream and block cipher message.  This is essentially a shared-secret
- * signature, used to provide integrity protection for SSL messages.  The
- * MAC is actually one of several keyed hashes, as associated with the cipher
- * suite and protocol version. (SSL v3.0 uses one construct, TLS uses another.)
- *
- * @author David Brownell
- * @author Andreas Sterbenz
- */
-final class MAC extends Authenticator {
-
-    static final MAC TLS_NULL = new MAC(false);
-
-    // Value of the null MAC is fixed
-    private static final byte[] nullMAC = new byte[0];
-
-    // internal identifier for the MAC algorithm
-    private final MacAlg macAlg;
-
-    // JCE Mac object
-    private final Mac mac;
-
-    MAC(boolean isDTLS) {
-        super(isDTLS);
-
-        macAlg = M_NULL;
-        mac = null;
-    }
-
-    /**
-     * Set up, configured for the given MAC type and version.
-     */
-    MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
-            throws NoSuchAlgorithmException, InvalidKeyException {
-        super(protocolVersion);
-        this.macAlg = macAlg;
-
-        String algorithm;
-
-        // using SSL MAC computation?
-        boolean useSSLMac = (protocolVersion.v < ProtocolVersion.TLS10.v);
-
-        if (macAlg == M_MD5) {
-            algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
-        } else if (macAlg == M_SHA) {
-            algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
-        } else if (macAlg == M_SHA256) {
-            algorithm = "HmacSHA256";    // TLS 1.2+
-        } else if (macAlg == M_SHA384) {
-            algorithm = "HmacSHA384";    // TLS 1.2+
-        } else {
-            throw new RuntimeException("Unknown Mac " + macAlg);
-        }
-
-        mac = JsseJce.getMac(algorithm);
-        mac.init(key);
-    }
-
-    /**
-     * Returns the length of the MAC.
-     */
-    int MAClen() {
-        return macAlg.size;
-    }
-
-    /**
-     * Returns the hash function block length of the MAC alorithm.
-     */
-    int hashBlockLen() {
-        return macAlg.hashBlockSize;
-    }
-
-    /**
-     * Returns the hash function minimal padding length of the MAC alorithm.
-     */
-    int minimalPaddingLen() {
-        return macAlg.minimalPaddingSize;
-    }
-
-    /**
-     * Computes and returns the MAC for the data in this byte array.
-     *
-     * @param type record type
-     * @param buf compressed record on which the MAC is computed
-     * @param offset start of compressed record data
-     * @param len the size of the compressed record
-     * @param isSimulated if true, simulate the MAC computation
-     *
-     * @return the MAC result
-     */
-    final byte[] compute(byte type, byte buf[],
-            int offset, int len, boolean isSimulated) {
-        if (macAlg.size == 0) {
-            return nullMAC;
-        }
-
-        if (!isSimulated) {
-            // Uses the implicit sequence number for the computation.
-            byte[] additional = acquireAuthenticationBytes(type, len, null);
-            mac.update(additional);
-        }
-        mac.update(buf, offset, len);
-
-        return mac.doFinal();
-    }
-
-    /**
-     * Compute and returns the MAC for the remaining data
-     * in this ByteBuffer.
-     *
-     * On return, the bb position == limit, and limit will
-     * have not changed.
-     *
-     * @param type record type
-     * @param bb a ByteBuffer in which the position and limit
-     *          demarcate the data to be MAC'd.
-     * @param isSimulated if true, simulate the MAC computation
-     * @param sequence the explicit sequence number, or null if using
-     *        the implicit sequence number for the computation
-     *
-     * @return the MAC result
-     */
-    final byte[] compute(byte type, ByteBuffer bb,
-            byte[] sequence, boolean isSimulated) {
-
-        if (macAlg.size == 0) {
-            return nullMAC;
-        }
-
-        if (!isSimulated) {
-            // Uses the explicit sequence number for the computation.
-            byte[] additional =
-                    acquireAuthenticationBytes(type, bb.remaining(), sequence);
-            mac.update(additional);
-        }
-        mac.update(bb);
-
-        return mac.doFinal();
-    }
-
-    /**
-     * Compute and returns the MAC for the remaining data
-     * in this ByteBuffer.
-     *
-     * On return, the bb position == limit, and limit will
-     * have not changed.
-     *
-     * @param type record type
-     * @param bb a ByteBuffer in which the position and limit
-     *        demarcate the data to be MAC'd.
-     * @param isSimulated if true, simulate the MAC computation
-     *
-     * @return the MAC result
-     */
-    final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
-        // Uses the implicit sequence number for the computation.
-        return compute(type, bb, null, isSimulated);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLProtocolException;
+import static sun.security.ssl.SSLExtension.CH_MAX_FRAGMENT_LENGTH;
+import static sun.security.ssl.SSLExtension.EE_MAX_FRAGMENT_LENGTH;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import static sun.security.ssl.SSLExtension.SH_MAX_FRAGMENT_LENGTH;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "max_fragment_length" extensions [RFC6066].
+ */
+final class MaxFragExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHMaxFragmentLengthProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHMaxFragmentLengthConsumer();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHMaxFragmentLengthProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHMaxFragmentLengthConsumer();
+    static final HandshakeConsumer shOnTradeConsumer =
+            new SHMaxFragmentLengthUpdate();
+
+    static final HandshakeProducer eeNetworkProducer =
+            new EEMaxFragmentLengthProducer();
+    static final ExtensionConsumer eeOnLoadConsumer =
+            new EEMaxFragmentLengthConsumer();
+    static final HandshakeConsumer eeOnTradeConsumer =
+            new EEMaxFragmentLengthUpdate();
+
+    static final SSLStringizer maxFragLenStringizer =
+            new MaxFragLenStringizer();
+
+    /**
+     * The "max_fragment_length" extension [RFC 6066].
+     */
+    static final class MaxFragLenSpec implements SSLExtensionSpec {
+        byte id;
+
+        private MaxFragLenSpec(byte id) {
+            this.id = id;
+        }
+
+        private MaxFragLenSpec(ByteBuffer buffer) throws IOException {
+            if (buffer.remaining() != 1) {
+                throw new SSLProtocolException(
+                    "Invalid max_fragment_length extension data");
+            }
+
+            this.id = buffer.get();
+        }
+
+        @Override
+        public String toString() {
+            return MaxFragLenEnum.nameOf(id);
+        }
+    }
+
+    private static final class MaxFragLenStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new MaxFragLenSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    static enum MaxFragLenEnum {
+        MFL_512     ((byte)0x01,  512,  "2^9"),
+        MFL_1024    ((byte)0x02, 1024,  "2^10"),
+        MFL_2048    ((byte)0x03, 2048,  "2^11"),
+        MFL_4096    ((byte)0x04, 4096,  "2^12");
+
+        final byte id;
+        final int fragmentSize;
+        final String description;
+
+        private MaxFragLenEnum(byte id, int fragmentSize, String description) {
+            this.id = id;
+            this.fragmentSize = fragmentSize;
+            this.description = description;
+        }
+
+        private static MaxFragLenEnum valueOf(byte id) {
+            for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
+                if (mfl.id == id) {
+                    return mfl;
+                }
+            }
+
+            return null;
+        }
+
+        private static String nameOf(byte id) {
+            for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
+                if (mfl.id == id) {
+                    return mfl.description;
+                }
+            }
+
+            return "UNDEFINED-MAX-FRAGMENT-LENGTH(" + id + ")";
+        }
+
+        /**
+         * Returns the best match enum constant of the specified
+         * fragment size.
+         */
+        static MaxFragLenEnum valueOf(int fragmentSize) {
+            if (fragmentSize <= 0) {
+                return null;
+            } else if (fragmentSize < 1024) {
+                return MFL_512;
+            } else if (fragmentSize < 2048) {
+                return MFL_1024;
+            } else if (fragmentSize < 4096) {
+                return MFL_2048;
+            } else if (fragmentSize == 4096) {
+                return MFL_4096;
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Network data producer of a "max_fragment_length" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHMaxFragmentLengthProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHMaxFragmentLengthProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable max_fragment_length extension");
+                }
+                return null;
+            }
+
+            // Produce the extension and update the context.
+            int requestedMFLength;
+            if (chc.isResumption && (chc.resumingSession != null)) {
+                // The same extension should be sent for resumption.
+                requestedMFLength =
+                    chc.resumingSession.getNegotiatedMaxFragSize();
+            } else if (chc.sslConfig.maximumPacketSize != 0) {
+                // Maybe we can calculate the fragment size more accurate
+                // by condering the enabled cipher suites in the future.
+                requestedMFLength = chc.sslConfig.maximumPacketSize;
+                if (chc.sslContext.isDTLS()) {
+                    requestedMFLength -= DTLSRecord.maxPlaintextPlusSize;
+                } else {
+                    requestedMFLength -= SSLRecord.maxPlaintextPlusSize;
+                }
+            } else {
+                // Need no max_fragment_length extension.
+                requestedMFLength = -1;
+            }
+
+            MaxFragLenEnum mfl = MaxFragLenEnum.valueOf(requestedMFLength);
+            if (mfl != null) {
+                // update the context.
+                chc.handshakeExtensions.put(
+                        CH_MAX_FRAGMENT_LENGTH, new MaxFragLenSpec(mfl.id));
+
+                return new byte[] { mfl.id };
+            } else {
+                // log and ignore, no MFL extension.
+                chc.maxFragmentLength = -1;
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "No available max_fragment_length extension can " +
+                        "be used for fragment size of " +
+                        requestedMFLength + "bytes");
+                }
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Network data consumer of a "max_fragment_length" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHMaxFragmentLengthConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHMaxFragmentLengthConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            if (!shc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable max_fragment_length extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            MaxFragLenSpec spec;
+            try {
+                spec = new MaxFragLenSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
+            if (mfle == null) {
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "the requested maximum fragment length is other " +
+                    "than the allowed values");
+            }
+
+            // Update the context.
+            shc.maxFragmentLength = mfle.fragmentSize;
+            shc.handshakeExtensions.put(CH_MAX_FRAGMENT_LENGTH, spec);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * Network data producer of a "max_fragment_length" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class SHMaxFragmentLengthProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHMaxFragmentLengthProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to "max_fragment_length" extension request only
+            MaxFragLenSpec spec = (MaxFragLenSpec)
+                    shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
+            if (spec == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable max_fragment_length extension");
+                }
+                return null;        // ignore the extension
+            }
+
+            if ((shc.maxFragmentLength > 0) &&
+                    (shc.sslConfig.maximumPacketSize != 0)) {
+                int estimatedMaxFragSize =
+                        shc.negotiatedCipherSuite.calculatePacketSize(
+                                shc.maxFragmentLength, shc.negotiatedProtocol,
+                                shc.sslContext.isDTLS());
+                if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
+                    // For better interoperability, abort the maximum
+                    // fragment length negotiation, rather than terminate
+                    // the connection with a fatal alert.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "Abort the maximum fragment length negotiation, " +
+                            "may overflow the maximum packet size limit.");
+                    }
+                    shc.maxFragmentLength = -1;
+                }
+            }
+
+            // update the context
+            if (shc.maxFragmentLength > 0) {
+                shc.handshakeSession.setNegotiatedMaxFragSize(
+                        shc.maxFragmentLength);
+                shc.conContext.inputRecord.changeFragmentSize(
+                        shc.maxFragmentLength);
+                shc.conContext.outputRecord.changeFragmentSize(
+                        shc.maxFragmentLength);
+
+                // The response extension data is the same as the requested one.
+                shc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
+                return new byte[] { spec.id };
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Network data consumer of a "max_fragment_length" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class SHMaxFragmentLengthConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHMaxFragmentLengthConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to "max_fragment_length" extension request only
+            MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
+                    chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
+            if (requestedSpec == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected max_fragment_length extension in ServerHello");
+            }
+
+            // Parse the extension.
+            MaxFragLenSpec spec;
+            try {
+                spec = new MaxFragLenSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            if (spec.id != requestedSpec.id) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "The maximum fragment length response is not requested");
+            }
+
+            MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
+            if (mfle == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "the requested maximum fragment length is other " +
+                    "than the allowed values");
+            }
+
+            // update the context
+            chc.maxFragmentLength = mfle.fragmentSize;
+            chc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
+        }
+    }
+
+    /**
+     * After session creation consuming of a "max_fragment_length"
+     * extension in the ClientHello handshake message.
+     */
+    private static final class SHMaxFragmentLengthUpdate
+            implements HandshakeConsumer {
+
+        // Prevent instantiation of this class.
+        private SHMaxFragmentLengthUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            MaxFragLenSpec spec = (MaxFragLenSpec)
+                    chc.handshakeExtensions.get(SH_MAX_FRAGMENT_LENGTH);
+            if (spec == null) {
+                // Ignore, no "max_fragment_length" extension response.
+                return;
+            }
+
+            if ((chc.maxFragmentLength > 0) &&
+                    (chc.sslConfig.maximumPacketSize != 0)) {
+                int estimatedMaxFragSize =
+                        chc.negotiatedCipherSuite.calculatePacketSize(
+                                chc.maxFragmentLength, chc.negotiatedProtocol,
+                                chc.sslContext.isDTLS());
+                if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
+                    // For better interoperability, abort the maximum
+                    // fragment length negotiation, rather than terminate
+                    // the connection with a fatal alert.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "Abort the maximum fragment length negotiation, " +
+                            "may overflow the maximum packet size limit.");
+                    }
+                    chc.maxFragmentLength = -1;
+                }
+            }
+
+            // update the context
+            if (chc.maxFragmentLength > 0) {
+                chc.handshakeSession.setNegotiatedMaxFragSize(
+                        chc.maxFragmentLength);
+                chc.conContext.inputRecord.changeFragmentSize(
+                        chc.maxFragmentLength);
+                chc.conContext.outputRecord.changeFragmentSize(
+                        chc.maxFragmentLength);
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "max_fragment_length" extension in
+     * the EncryptedExtensions handshake message.
+     */
+    private static final
+            class EEMaxFragmentLengthProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private EEMaxFragmentLengthProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to "max_fragment_length" extension request only
+            MaxFragLenSpec spec = (MaxFragLenSpec)
+                    shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
+            if (spec == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable max_fragment_length extension");
+                }
+                return null;        // ignore the extension
+            }
+
+            if ((shc.maxFragmentLength > 0) &&
+                    (shc.sslConfig.maximumPacketSize != 0)) {
+                int estimatedMaxFragSize =
+                        shc.negotiatedCipherSuite.calculatePacketSize(
+                                shc.maxFragmentLength, shc.negotiatedProtocol,
+                                shc.sslContext.isDTLS());
+                if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
+                    // For better interoperability, abort the maximum
+                    // fragment length negotiation, rather than terminate
+                    // the connection with a fatal alert.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "Abort the maximum fragment length negotiation, " +
+                            "may overflow the maximum packet size limit.");
+                    }
+                    shc.maxFragmentLength = -1;
+                }
+            }
+
+            // update the context
+            if (shc.maxFragmentLength > 0) {
+                shc.handshakeSession.setNegotiatedMaxFragSize(
+                        shc.maxFragmentLength);
+                shc.conContext.inputRecord.changeFragmentSize(
+                        shc.maxFragmentLength);
+                shc.conContext.outputRecord.changeFragmentSize(
+                        shc.maxFragmentLength);
+
+                // The response extension data is the same as the requested one.
+                shc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
+                return new byte[] { spec.id };
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Network data consumer of a "max_fragment_length" extension in the
+     * EncryptedExtensions handshake message.
+     */
+    private static final
+            class EEMaxFragmentLengthConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private EEMaxFragmentLengthConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to "max_fragment_length" extension request only
+            MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
+                    chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
+            if (requestedSpec == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected max_fragment_length extension in ServerHello");
+            }
+
+            // Parse the extension.
+            MaxFragLenSpec spec;
+            try {
+                spec = new MaxFragLenSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            if (spec.id != requestedSpec.id) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "The maximum fragment length response is not requested");
+            }
+
+            MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
+            if (mfle == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "the requested maximum fragment length is other " +
+                    "than the allowed values");
+            }
+
+            // update the context
+            chc.maxFragmentLength = mfle.fragmentSize;
+            chc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
+        }
+    }
+
+    /**
+     * After session creation consuming of a "max_fragment_length"
+     * extension in the EncryptedExtensions handshake message.
+     */
+    private static final
+            class EEMaxFragmentLengthUpdate implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private EEMaxFragmentLengthUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            MaxFragLenSpec spec = (MaxFragLenSpec)
+                    chc.handshakeExtensions.get(EE_MAX_FRAGMENT_LENGTH);
+            if (spec == null) {
+                // Ignore, no "max_fragment_length" extension response.
+                return;
+            }
+
+            if ((chc.maxFragmentLength > 0) &&
+                    (chc.sslConfig.maximumPacketSize != 0)) {
+                int estimatedMaxFragSize =
+                        chc.negotiatedCipherSuite.calculatePacketSize(
+                                chc.maxFragmentLength, chc.negotiatedProtocol,
+                                chc.sslContext.isDTLS());
+                if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
+                    // For better interoperability, abort the maximum
+                    // fragment length negotiation, rather than terminate
+                    // the connection with a fatal alert.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "Abort the maximum fragment length negotiation, " +
+                            "may overflow the maximum packet size limit.");
+                    }
+                    chc.maxFragmentLength = -1;
+                }
+            }
+
+            // update the context
+            if (chc.maxFragmentLength > 0) {
+                chc.handshakeSession.setNegotiatedMaxFragSize(
+                        chc.maxFragmentLength);
+                chc.conContext.inputRecord.changeFragmentSize(
+                        chc.maxFragmentLength);
+                chc.conContext.outputRecord.changeFragmentSize(
+                        chc.maxFragmentLength);
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/MaxFragmentLengthExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-import javax.net.ssl.SSLProtocolException;
-
-/*
- * [RFC6066] TLS specifies a fixed maximum plaintext fragment length of
- * 2^14 bytes.  It may be desirable for constrained clients to negotiate
- * a smaller maximum fragment length due to memory limitations or bandwidth
- * limitations.
- *
- * In order to negotiate smaller maximum fragment lengths, clients MAY
- * include an extension of type "max_fragment_length" in the (extended)
- * client hello.  The "extension_data" field of this extension SHALL
- * contain:
- *
- *    enum{
- *        2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
- *    } MaxFragmentLength;
- *
- * whose value is the desired maximum fragment length.
- */
-final class MaxFragmentLengthExtension extends HelloExtension {
-
-    private static final int MAX_FRAGMENT_LENGTH_512  = 1;      // 2^9
-    private static final int MAX_FRAGMENT_LENGTH_1024 = 2;      // 2^10
-    private static final int MAX_FRAGMENT_LENGTH_2048 = 3;      // 2^11
-    private static final int MAX_FRAGMENT_LENGTH_4096 = 4;      // 2^12
-
-    final int maxFragmentLength;
-
-    MaxFragmentLengthExtension(int fragmentSize) {
-        super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
-
-        if (fragmentSize < 1024) {
-            maxFragmentLength = MAX_FRAGMENT_LENGTH_512;
-        } else if (fragmentSize < 2048) {
-            maxFragmentLength = MAX_FRAGMENT_LENGTH_1024;
-        } else if (fragmentSize < 4096) {
-            maxFragmentLength = MAX_FRAGMENT_LENGTH_2048;
-        } else {
-            maxFragmentLength = MAX_FRAGMENT_LENGTH_4096;
-        }
-    }
-
-    MaxFragmentLengthExtension(HandshakeInStream s, int len)
-                throws IOException {
-        super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
-
-        // check the extension length
-        if (len != 1) {
-            throw new SSLProtocolException("Invalid " + type + " extension");
-        }
-
-        maxFragmentLength = s.getInt8();
-        if ((maxFragmentLength > 4) || (maxFragmentLength < 1)) {
-            throw new SSLProtocolException("Invalid " + type + " extension");
-        }
-    }
-
-    // Length of the encoded extension, including the type and length fields
-    @Override
-    int length() {
-        return 5;               // 4: extension type and length fields
-                                // 1: MaxFragmentLength field
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(1);
-        s.putInt8(maxFragmentLength);
-    }
-
-    int getMaxFragLen() {
-        switch (maxFragmentLength) {
-            case MAX_FRAGMENT_LENGTH_512:
-                return 512;
-            case MAX_FRAGMENT_LENGTH_1024:
-                return 1024;
-            case MAX_FRAGMENT_LENGTH_2048:
-                return 2048;
-            case MAX_FRAGMENT_LENGTH_4096:
-                return 4096;
-        }
-
-        // unlikely to happen
-        return -1;
-    }
-
-    static boolean needFragLenNego(int fragmentSize) {
-        return (fragmentSize > 0) && (fragmentSize <= 4096);
-    }
-
-    static int getValidMaxFragLen(int fragmentSize) {
-        if (fragmentSize < 1024) {
-            return 512;
-        } else if (fragmentSize < 2048) {
-            return 1024;
-        } else if (fragmentSize < 4096) {
-            return 2048;
-        } else if (fragmentSize == 4096) {
-            return 4096;
-        } else {
-            return 16384;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "Extension " + type + ", max_fragment_length: " +
-                "(2^" + (maxFragmentLength + 8) + ")";
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-/*
- * Copyright (c) 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.security.spec.ECParameterSpec;
-import java.security.spec.ECGenParameterSpec;
-import static sun.security.ssl.NamedGroupType.*;
-
-enum NamedGroup {
-    // Elliptic Curves (RFC 4492)
-    //
-    // See sun.security.util.CurveDB for the OIDs
-
-    // NIST K-163
-    SECT163_K1(1, NAMED_GROUP_ECDHE, "sect163k1", "1.3.132.0.1", true),
-
-    SECT163_R1(2, NAMED_GROUP_ECDHE, "sect163r1", "1.3.132.0.2", false),
-
-    // NIST B-163
-    SECT163_R2(3, NAMED_GROUP_ECDHE, "sect163r2", "1.3.132.0.15", true),
-
-    SECT193_R1(4, NAMED_GROUP_ECDHE, "sect193r1", "1.3.132.0.24", false),
-    SECT193_R2(5, NAMED_GROUP_ECDHE, "sect193r2", "1.3.132.0.25", false),
-
-    // NIST K-233
-    SECT233_K1(6, NAMED_GROUP_ECDHE, "sect233k1", "1.3.132.0.26", true),
-
-    // NIST B-233
-    SECT233_R1(7, NAMED_GROUP_ECDHE, "sect233r1", "1.3.132.0.27", true),
-
-    SECT239_K1(8, NAMED_GROUP_ECDHE, "sect239k1", "1.3.132.0.3", false),
-
-    // NIST K-283
-    SECT283_K1(9, NAMED_GROUP_ECDHE, "sect283k1", "1.3.132.0.16", true),
-
-    // NIST B-283
-    SECT283_R1(10, NAMED_GROUP_ECDHE, "sect283r1", "1.3.132.0.17", true),
-
-    // NIST K-409
-    SECT409_K1(11, NAMED_GROUP_ECDHE, "sect409k1", "1.3.132.0.36", true),
-
-    // NIST B-409
-    SECT409_R1(12, NAMED_GROUP_ECDHE, "sect409r1", "1.3.132.0.37", true),
-
-    // NIST K-571
-    SECT571_K1(13, NAMED_GROUP_ECDHE, "sect571k1", "1.3.132.0.38", true),
-
-    // NIST B-571
-    SECT571_R1(14, NAMED_GROUP_ECDHE, "sect571r1", "1.3.132.0.39", true),
-
-    SECP160_K1(15, NAMED_GROUP_ECDHE, "secp160k1", "1.3.132.0.9", false),
-    SECP160_R1(16, NAMED_GROUP_ECDHE, "secp160r1", "1.3.132.0.8", false),
-    SECP160_R2(17, NAMED_GROUP_ECDHE, "secp160r2", "1.3.132.0.30", false),
-    SECP192_K1(18, NAMED_GROUP_ECDHE, "secp192k1", "1.3.132.0.31", false),
-
-    // NIST P-192
-    SECP192_R1(19, NAMED_GROUP_ECDHE, "secp192r1", "1.2.840.10045.3.1.1", true),
-
-    SECP224_K1(20, NAMED_GROUP_ECDHE, "secp224k1", "1.3.132.0.32", false),
-    // NIST P-224
-    SECP224_R1(21, NAMED_GROUP_ECDHE, "secp224r1", "1.3.132.0.33", true),
-
-    SECP256_K1(22, NAMED_GROUP_ECDHE, "secp256k1", "1.3.132.0.10", false),
-
-    // NIST P-256
-    SECP256_R1(23, NAMED_GROUP_ECDHE, "secp256r1", "1.2.840.10045.3.1.7", true),
-
-    // NIST P-384
-    SECP384_R1(24, NAMED_GROUP_ECDHE, "secp384r1", "1.3.132.0.34", true),
-
-    // NIST P-521
-    SECP521_R1(25, NAMED_GROUP_ECDHE, "secp521r1", "1.3.132.0.35", true),
-
-    // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
-    FFDHE_2048(256, NAMED_GROUP_FFDHE, "ffdhe2048",  true),
-    FFDHE_3072(257, NAMED_GROUP_FFDHE, "ffdhe3072",  true),
-    FFDHE_4096(258, NAMED_GROUP_FFDHE, "ffdhe4096",  true),
-    FFDHE_6144(259, NAMED_GROUP_FFDHE, "ffdhe6144",  true),
-    FFDHE_8192(260, NAMED_GROUP_FFDHE, "ffdhe8192",  true);
-
-    int             id;
-    NamedGroupType  type;
-    String          name;
-    String          oid;
-    String          algorithm;
-    boolean         isFips;
-
-    // Constructor used for Elliptic Curve Groups (ECDHE)
-    NamedGroup(int id, NamedGroupType type,
-                String name, String oid, boolean isFips) {
-        this.id = id;
-        this.type = type;
-        this.name = name;
-        this.oid = oid;
-        this.algorithm = "EC";
-        this.isFips = isFips;
-    }
-
-    // Constructor used for Finite Field Diffie-Hellman Groups (FFDHE)
-    NamedGroup(int id, NamedGroupType type, String name, boolean isFips) {
-        this.id = id;
-        this.type = type;
-        this.name = name;
-        this.oid = null;
-        this.algorithm = "DiffieHellman";
-        this.isFips = isFips;
-    }
-
-    static NamedGroup valueOf(int id) {
-        for (NamedGroup group : NamedGroup.values()) {
-            if (group.id == id) {
-                return group;
-            }
-        }
-
-        return null;
-    }
-
-    static NamedGroup nameOf(String name) {
-        for (NamedGroup group : NamedGroup.values()) {
-            if (group.name.equals(name)) {
-                return group;
-            }
-        }
-
-        return null;
-    }
-
-    static NamedGroup valueOf(ECParameterSpec params) {
-        String oid = JsseJce.getNamedCurveOid(params);
-        if ((oid != null) && (!oid.isEmpty())) {
-            for (NamedGroup group : NamedGroup.values()) {
-                if (oid.equals(group.oid)) {
-                    return group;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    @Override
-    public String toString() {
-        return this.name;
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/NamedGroupType.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 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;
-
-enum NamedGroupType {
-    NAMED_GROUP_ECDHE,          // Elliptic Curve Groups (ECDHE)
-    NAMED_GROUP_FFDHE,          // Finite Field Groups (DHE)
-    NAMED_GROUP_NONE            // No predefined named group
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.ProviderException;
+import java.security.SecureRandom;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.Optional;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
+
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the NewSessionTicket handshake message.
+ */
+final class NewSessionTicket {
+    private static final int MAX_TICKET_LIFETIME = 604800;  // seconds, 7 days
+
+    static final SSLConsumer handshakeConsumer =
+        new NewSessionTicketConsumer();
+    static final SSLProducer kickstartProducer =
+        new NewSessionTicketKickstartProducer();
+    static final HandshakeProducer handshakeProducer =
+        new NewSessionTicketProducer();
+
+    /**
+     * The NewSessionTicketMessage handshake message.
+     */
+    static final class NewSessionTicketMessage extends HandshakeMessage {
+        final int ticketLifetime;
+        final int ticketAgeAdd;
+        final byte[] ticketNonce;
+        final byte[] ticket;
+        final SSLExtensions extensions;
+
+        NewSessionTicketMessage(HandshakeContext context,
+                int ticketLifetime, SecureRandom generator,
+                byte[] ticketNonce, byte[] ticket) {
+            super(context);
+
+            this.ticketLifetime = ticketLifetime;
+            this.ticketAgeAdd = generator.nextInt();
+            this.ticketNonce = ticketNonce;
+            this.ticket = ticket;
+            this.extensions = new SSLExtensions(this);
+        }
+
+        NewSessionTicketMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+
+            // struct {
+            //     uint32 ticket_lifetime;
+            //     uint32 ticket_age_add;
+            //     opaque ticket_nonce<0..255>;
+            //     opaque ticket<1..2^16-1>;
+            //     Extension extensions<0..2^16-2>;
+            // } NewSessionTicket;
+            if (m.remaining() < 14) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid NewSessionTicket message: no sufficient data");
+            }
+
+            this.ticketLifetime = Record.getInt32(m);
+            this.ticketAgeAdd = Record.getInt32(m);
+            this.ticketNonce = Record.getBytes8(m);
+
+            if (m.remaining() < 5) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid NewSessionTicket message: no sufficient data");
+            }
+
+            this.ticket = Record.getBytes16(m);
+            if (ticket.length == 0) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No ticket in the NewSessionTicket handshake message");
+            }
+
+            if (m.remaining() < 2) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid NewSessionTicket message: no sufficient data");
+            }
+
+            SSLExtension[] supportedExtensions =
+                    context.sslConfig.getEnabledExtensions(
+                            SSLHandshake.NEW_SESSION_TICKET);
+            this.extensions = new SSLExtensions(this, m, supportedExtensions);
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.NEW_SESSION_TICKET;
+        }
+
+        @Override
+        public int messageLength() {
+            int extLen = extensions.length();
+            if (extLen == 0) {
+                extLen = 2;     // empty extensions
+            }
+
+            return 8 + ticketNonce.length + 1 +
+                       ticket.length + 2 + extLen;
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putInt32(ticketLifetime);
+            hos.putInt32(ticketAgeAdd);
+            hos.putBytes8(ticketNonce);
+            hos.putBytes16(ticket);
+
+            // Is it an empty extensions?
+            if (extensions.length() == 0) {
+                hos.putInt16(0);
+            } else {
+                extensions.send(hos);
+            }
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"NewSessionTicket\": '{'\n" +
+                "  \"ticket_lifetime\"      : \"{0}\",\n" +
+                "  \"ticket_age_add\"       : \"{1}\",\n" +
+                "  \"ticket_nonce\"         : \"{2}\",\n" +
+                "  \"ticket\"               : \"{3}\",\n" +
+                "  \"extensions\"           : [\n" +
+                "{4}\n" +
+                "  ]\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            Object[] messageFields = {
+                ticketLifetime,
+                "<omitted>",    //ticketAgeAdd should not be logged
+                Utilities.toHexString(ticketNonce),
+                Utilities.toHexString(ticket),
+                Utilities.indent(extensions.toString(), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    private static SecretKey derivePreSharedKey(CipherSuite.HashAlg hashAlg,
+            SecretKey resumptionMasterSecret, byte[] nonce) throws IOException {
+        try {
+            HKDF hkdf = new HKDF(hashAlg.name);
+            byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
+                    "tls13 resumption".getBytes(), nonce, hashAlg.hashLength);
+            return hkdf.expand(resumptionMasterSecret, hkdfInfo,
+                    hashAlg.hashLength, "TlsPreSharedKey");
+        } catch  (GeneralSecurityException gse) {
+            throw (SSLHandshakeException) new SSLHandshakeException(
+                    "Could not derive PSK").initCause(gse);
+        }
+    }
+
+    private static final
+            class NewSessionTicketKickstartProducer implements SSLProducer {
+        // Prevent instantiation of this class.
+        private NewSessionTicketKickstartProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is this session resumable?
+            if (!shc.handshakeSession.isRejoinable()) {
+                return null;
+            }
+
+            // What's the requested PSK key exchange modes?
+            //
+            // Note that currently, the NewSessionTicket post-handshake is
+            // produced and delivered only in the current handshake context
+            // if required.
+            PskKeyExchangeModesSpec pkemSpec =
+                    (PskKeyExchangeModesSpec)shc.handshakeExtensions.get(
+                            SSLExtension.PSK_KEY_EXCHANGE_MODES);
+            if (pkemSpec == null || !pkemSpec.contains(
+                PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
+                // Client doesn't support PSK with (EC)DHE key establishment.
+                return null;
+            }
+
+            // get a new session ID
+            SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
+                shc.sslContext.engineGetServerSessionContext();
+            SessionId newId = new SessionId(true,
+                shc.sslContext.getSecureRandom());
+
+            Optional<SecretKey> resumptionMasterSecret =
+                shc.handshakeSession.getResumptionMasterSecret();
+            if (!resumptionMasterSecret.isPresent()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Session has no resumption secret. No ticket sent.");
+                }
+                return null;
+            }
+
+            // construct the PSK and handshake message
+            BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter();
+            byte[] nonceArr = nonce.toByteArray();
+            SecretKey psk = derivePreSharedKey(
+                    shc.negotiatedCipherSuite.hashAlg,
+                    resumptionMasterSecret.get(), nonceArr);
+
+            int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
+            if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Session timeout is too long. No ticket sent.");
+                }
+                return null;
+            }
+            NewSessionTicketMessage nstm = new NewSessionTicketMessage(shc,
+                sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
+                nonceArr, newId.getId());
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced NewSessionTicket handshake message", nstm);
+            }
+
+            // create and cache the new session
+            // The new session must be a child of the existing session so
+            // they will be invalidated together, etc.
+            SSLSessionImpl sessionCopy = new SSLSessionImpl(shc,
+                    shc.handshakeSession.getSuite(), newId,
+                    shc.handshakeSession.getCreationTime());
+            shc.handshakeSession.addChild(sessionCopy);
+            sessionCopy.setPreSharedKey(psk);
+            sessionCopy.setPskIdentity(newId.getId());
+            sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
+            sessionCache.put(sessionCopy);
+
+            // Output the handshake message.
+            nstm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "NewSessionTicket" handshake message producer.
+     */
+    private static final class NewSessionTicketProducer
+            implements HandshakeProducer {
+
+        // Prevent instantiation of this class.
+        private NewSessionTicketProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+
+            // NSTM may be sent in response to handshake messages.
+            // For example: key update
+
+            throw new ProviderException(
+                "NewSessionTicket handshake producer not implemented");
+        }
+    }
+
+    private static final
+            class NewSessionTicketConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private NewSessionTicketConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                            ByteBuffer message) throws IOException {
+
+            // Note: Although the resumption master secret depends on the
+            // client's second flight, servers which do not request client
+            // authentication MAY compute the remainder of the transcript
+            // independently and then send a NewSessionTicket immediately
+            // upon sending its Finished rather than waiting for the client
+            // Finished.
+            //
+            // The consuming happens in client side only.  As the server
+            // may send the NewSessionTicket before handshake complete, the
+            // context may be a PostHandshakeContext or HandshakeContext
+            // instance.
+            HandshakeContext hc = (HandshakeContext)context;
+            NewSessionTicketMessage nstm =
+                    new NewSessionTicketMessage(hc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                "Consuming NewSessionTicket message", nstm);
+            }
+
+            // discard tickets with timeout 0
+            if (nstm.ticketLifetime <= 0 ||
+                nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                    "Discarding NewSessionTicket with lifetime "
+                        + nstm.ticketLifetime, nstm);
+                }
+                return;
+            }
+
+            SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
+                hc.sslContext.engineGetClientSessionContext();
+
+            if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                    "Session cache lifetime is too long. Discarding ticket.");
+                }
+                return;
+            }
+
+            SSLSessionImpl sessionToSave = hc.conContext.conSession;
+
+            Optional<SecretKey> resumptionMasterSecret =
+                sessionToSave.getResumptionMasterSecret();
+            if (!resumptionMasterSecret.isPresent()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                    "Session has no resumption master secret. Ignoring ticket.");
+                }
+                return;
+            }
+
+            // derive the PSK
+            SecretKey psk = derivePreSharedKey(
+                sessionToSave.getSuite().hashAlg, resumptionMasterSecret.get(),
+                nstm.ticketNonce);
+
+            // create and cache the new session
+            // The new session must be a child of the existing session so
+            // they will be invalidated together, etc.
+            SessionId newId =
+                new SessionId(true, hc.sslContext.getSecureRandom());
+            SSLSessionImpl sessionCopy = new SSLSessionImpl(
+                    hc, sessionToSave.getSuite(), newId,
+                    sessionToSave.getCreationTime());
+            sessionToSave.addChild(sessionCopy);
+            sessionCopy.setPreSharedKey(psk);
+            sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
+            sessionCopy.setPskIdentity(nstm.ticket);
+            sessionCache.put(sessionCopy);
+
+            // clean handshake context
+            hc.conContext.finishPostHandshake();
+        }
+    }
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/OCSPStatusRequest.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,358 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-import java.nio.ByteBuffer;
-import java.security.cert.Extension;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Collections;
-import javax.net.ssl.SSLException;
-import sun.security.util.DerValue;
-import sun.security.util.DerInputStream;
-import sun.security.util.DerOutputStream;
-import sun.security.provider.certpath.ResponderId;
-
-/*
- * RFC6066 defines the TLS extension,"status_request" (type 0x5),
- * which allows the client to request that the server perform OCSP
- * on the client's behalf.
- *
- * The RFC defines an OCSPStatusRequest structure:
- *
- *      struct {
- *          ResponderID responder_id_list<0..2^16-1>;
- *          Extensions  request_extensions;
- *      } OCSPStatusRequest;
- */
-final class OCSPStatusRequest implements StatusRequest {
-
-    private final List<ResponderId> responderIds;
-    private final List<Extension> extensions;
-    private int encodedLen;
-    private int ridListLen;
-    private int extListLen;
-
-    /**
-     * Construct a default {@code OCSPStatusRequest} object with empty
-     * responder ID and code extension list fields.
-     */
-    OCSPStatusRequest() {
-        responderIds = new ArrayList<>();
-        extensions = new ArrayList<>();
-        encodedLen = this.length();
-    }
-
-    /**
-     * Construct an {@code OCSPStatusRequest} object using the provided
-     *      {@code ResponderId} and {@code Extension} lists.
-     *
-     * @param respIds the list of {@code ResponderId} objects to be placed
-     *      into the {@code OCSPStatusRequest}.  If the user wishes to place
-     *      no {@code ResponderId} objects in the request, either an empty
-     *      {@code List} or {@code null} is acceptable.
-     * @param exts the list of {@code Extension} objects to be placed into
-     *      the {@code OCSPStatusRequest}  If the user wishes to place
-     *      no {@code Extension} objects in the request, either an empty
-     *      {@code List} or {@code null} is acceptable.
-     */
-    OCSPStatusRequest(List<ResponderId> respIds, List<Extension> exts) {
-        responderIds = new ArrayList<>(respIds != null ? respIds :
-                Collections.emptyList());
-        extensions = new ArrayList<>(exts != null ? exts :
-                Collections.emptyList());
-        encodedLen = this.length();
-    }
-
-    /**
-     * Construct an {@code OCSPStatusRequest} object from data read from
-     * a {@code HandshakeInputStream}
-     *
-     * @param s the {@code HandshakeInputStream} providing the encoded data
-     *
-     * @throws IOException if any decoding errors happen during object
-     *      construction.
-     */
-    OCSPStatusRequest(HandshakeInStream in) throws IOException {
-        responderIds = new ArrayList<>();
-        extensions = new ArrayList<>();
-
-        int ridListBytesRemaining = in.getInt16();
-        while (ridListBytesRemaining != 0) {
-            byte[] ridBytes = in.getBytes16();
-            responderIds.add(new ResponderId(ridBytes));
-            ridListBytesRemaining -= (ridBytes.length + 2);
-            // Make sure that no individual responder ID's length caused an
-            // overrun relative to the outer responder ID list length
-            if (ridListBytesRemaining < 0) {
-                throw new SSLException("Responder ID length overflow: " +
-                        "current rid = " + ridBytes.length + ", remaining = " +
-                        ridListBytesRemaining);
-            }
-        }
-
-        int extensionLength = in.getInt16();
-        if (extensionLength > 0) {
-            byte[] extensionData = new byte[extensionLength];
-            in.read(extensionData);
-            DerInputStream dis = new DerInputStream(extensionData);
-            DerValue[] extSeqContents = dis.getSequence(extensionData.length);
-            for (DerValue extDerVal : extSeqContents) {
-                extensions.add(new sun.security.x509.Extension(extDerVal));
-            }
-        }
-    }
-
-    /**
-     * Construct an {@code OCSPStatusRequest} from its encoded form
-     *
-     * @param requestBytes the status request extension bytes
-     *
-     * @throws IOException if any error occurs during decoding
-     */
-    OCSPStatusRequest(byte[] requestBytes) throws IOException {
-        responderIds = new ArrayList<>();
-        extensions = new ArrayList<>();
-        ByteBuffer reqBuf = ByteBuffer.wrap(requestBytes);
-
-        // Get the ResponderId list length
-        encodedLen = requestBytes.length;
-        ridListLen = Short.toUnsignedInt(reqBuf.getShort());
-        int endOfRidList = reqBuf.position() + ridListLen;
-
-        // The end position of the ResponderId list in the ByteBuffer
-        // should be at least 2 less than the end of the buffer.  This
-        // 2 byte defecit is the minimum length required to encode a
-        // zero-length extensions segment.
-        if (reqBuf.limit() - endOfRidList < 2) {
-            throw new SSLException
-                ("ResponderId List length exceeds provided buffer - Len: "
-                 + ridListLen + ", Buffer: " + reqBuf.remaining());
-        }
-
-        while (reqBuf.position() < endOfRidList) {
-            int ridLength = Short.toUnsignedInt(reqBuf.getShort());
-            // Make sure an individual ResponderId length doesn't
-            // run past the end of the ResponderId list portion of the
-            // provided buffer.
-            if (reqBuf.position() + ridLength > endOfRidList) {
-                throw new SSLException
-                    ("ResponderId length exceeds list length - Off: "
-                     + reqBuf.position() + ", Length: " + ridLength
-                     + ", End offset: " + endOfRidList);
-            }
-
-            // Consume/add the ResponderId
-            if (ridLength > 0) {
-                byte[] ridData = new byte[ridLength];
-                reqBuf.get(ridData);
-                responderIds.add(new ResponderId(ridData));
-            }
-        }
-
-        // Get the Extensions length
-        int extensionsLen = Short.toUnsignedInt(reqBuf.getShort());
-
-        // The end of the extensions should also be the end of the
-        // encoded OCSPStatusRequest
-        if (extensionsLen != reqBuf.remaining()) {
-            throw new SSLException("Incorrect extensions length: Read "
-                    + extensionsLen + ", Data length: " + reqBuf.remaining());
-        }
-
-        // Extensions are a SEQUENCE of Extension
-        if (extensionsLen > 0) {
-            byte[] extensionData = new byte[extensionsLen];
-            reqBuf.get(extensionData);
-            DerInputStream dis = new DerInputStream(extensionData);
-            DerValue[] extSeqContents = dis.getSequence(extensionData.length);
-            for (DerValue extDerVal : extSeqContents) {
-                extensions.add(new sun.security.x509.Extension(extDerVal));
-            }
-        }
-    }
-
-    /**
-     * Obtain the length of the {@code OCSPStatusRequest} object in its
-     *      encoded form
-     *
-     * @return the length of the {@code OCSPStatusRequest} object in its
-     *      encoded form
-     */
-    @Override
-    public int length() {
-        // If we've previously calculated encodedLen simply return it
-        if (encodedLen != 0) {
-            return encodedLen;
-        }
-
-        ridListLen = 0;
-        for (ResponderId rid : responderIds) {
-            ridListLen += rid.length() + 2;
-        }
-
-        extListLen = 0;
-        if (!extensions.isEmpty()) {
-            try {
-                DerOutputStream extSequence = new DerOutputStream();
-                DerOutputStream extEncoding = new DerOutputStream();
-                for (Extension ext : extensions) {
-                    ext.encode(extEncoding);
-                }
-                extSequence.write(DerValue.tag_Sequence, extEncoding);
-                extListLen = extSequence.size();
-            } catch (IOException ioe) {
-                // Not sure what to do here
-            }
-        }
-
-        // Total length is the responder ID list length and extensions length
-        // plus each lists' 2-byte length fields.
-        encodedLen = ridListLen + extListLen + 4;
-
-        return encodedLen;
-    }
-
-    /**
-     * Send the encoded {@code OCSPStatusRequest} out through the provided
-     *      {@code HandshakeOutputStream}
-     *
-     * @param s the {@code HandshakeOutputStream} on which to send the encoded
-     *      data
-     *
-     * @throws IOException if any encoding errors occur
-     */
-    @Override
-    public void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(ridListLen);
-        for (ResponderId rid : responderIds) {
-            s.putBytes16(rid.getEncoded());
-        }
-
-        DerOutputStream seqOut = new DerOutputStream();
-        DerOutputStream extBytes = new DerOutputStream();
-
-        if (extensions.size() > 0) {
-            for (Extension ext : extensions) {
-                ext.encode(extBytes);
-            }
-            seqOut.write(DerValue.tag_Sequence, extBytes);
-        }
-        s.putBytes16(seqOut.toByteArray());
-    }
-
-    /**
-     * Determine if a provided {@code OCSPStatusRequest} objects is equal to
-     *      this one.
-     *
-     * @param obj an {@code OCSPStatusRequest} object to be compared against
-     *
-     * @return {@code true} if the objects are equal, {@code false} otherwise.
-     *      Equivalence is established if the lists of responder IDs and
-     *      extensions between the two objects are also equal.
-     */
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        } else if (this == obj) {
-            return true;
-        } else if (obj instanceof OCSPStatusRequest) {
-            OCSPStatusRequest respObj = (OCSPStatusRequest)obj;
-            return responderIds.equals(respObj.getResponderIds()) &&
-                extensions.equals(respObj.getExtensions());
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns the hash code value for this {@code OCSPStatusRequest}
-     *
-     * @return the hash code value for this {@code OCSPStatusRequest}
-     */
-    @Override
-    public int hashCode() {
-        int result = 17;
-
-        result = 31 * result + responderIds.hashCode();
-        result = 31 * result + extensions.hashCode();
-
-        return result;
-    }
-
-    /**
-     * Create a string representation of this {@code OCSPStatusRequest}
-     *
-     * @return a string representation of this {@code OCSPStatusRequest}
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("OCSPStatusRequest\n");
-        sb.append("    ResponderIds:");
-
-        if (responderIds.isEmpty()) {
-            sb.append(" <EMPTY>");
-        } else {
-            for (ResponderId rid : responderIds) {
-                sb.append("\n    ").append(rid.toString());
-            }
-        }
-
-        sb.append("\n").append("    Extensions:");
-        if (extensions.isEmpty()) {
-            sb.append(" <EMPTY>");
-        } else {
-            for (Extension ext : extensions) {
-                sb.append("\n    ").append(ext.toString());
-            }
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * Get the list of {@code ResponderId} objects for this
-     *      {@code OCSPStatusRequest}
-     *
-     * @return an unmodifiable {@code List} of {@code ResponderId} objects
-     */
-    List<ResponderId> getResponderIds() {
-        return Collections.unmodifiableList(responderIds);
-    }
-
-    /**
-     * Get the list of {@code Extension} objects for this
-     *      {@code OCSPStatusRequest}
-     *
-     * @return an unmodifiable {@code List} of {@code Extension} objects
-     */
-    List<Extension> getExtensions() {
-        return Collections.unmodifiableList(extensions);
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,30 +25,27 @@
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.util.Arrays;
-
-import javax.net.ssl.SSLException;
-import sun.security.util.HexDumpEncoder;
-
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
 
 /**
- * {@code OutputRecord} takes care of the management of SSL/TLS/DTLS output
- * records, including buffering, encryption, handshake messages marshal, etc.
+ * {@code OutputRecord} takes care of the management of SSL/(D)TLS
+ * output records, including buffering, encryption, handshake
+ * messages marshal, etc.
  *
  * @author David Brownell
  */
-abstract class OutputRecord extends ByteArrayOutputStream
-            implements Record, Closeable {
+abstract class OutputRecord
+        extends ByteArrayOutputStream implements Record, Closeable {
+    SSLWriteCipher              writeCipher;
+    // Needed for KeyUpdate, used after Handshake.Finished
+    TransportContext            tc;
 
-    /* Class and subclass dynamic debugging support */
-    static final Debug          debug = Debug.getInstance("ssl");
-
-    Authenticator               writeAuthenticator;
-    CipherBox                   writeCipher;
-
-    HandshakeHash               handshakeHash;
+    final HandshakeHash         handshakeHash;
     boolean                     firstMessage;
 
     // current protocol version, sent as record version
@@ -75,16 +72,18 @@
      * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
      * This is taken from the SSL V3 specification, Appendix E.
      */
-    private static int[] V3toV2CipherMap1 =
+    private static final int[] V3toV2CipherMap1 =
         {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
-    private static int[] V3toV2CipherMap3 =
+    private static final int[] V3toV2CipherMap3 =
         {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
 
-    OutputRecord() {
-        this.writeCipher = CipherBox.NULL;
+    OutputRecord(HandshakeHash handshakeHash, SSLWriteCipher writeCipher) {
+        this.writeCipher = writeCipher;
         this.firstMessage = true;
         this.fragmentSize = Record.maxDataSize;
 
+        this.handshakeHash = handshakeHash;
+
         // Please set packetSize and protocolVersion in the implementation.
     }
 
@@ -100,15 +99,6 @@
     }
 
     /*
-     * For handshaking, we need to be able to hash every byte above the
-     * record marking layer.  This is where we're guaranteed to see those
-     * bytes, so this is where we can hash them.
-     */
-    void setHandshakeHash(HandshakeHash handshakeHash) {
-        this.handshakeHash = handshakeHash;
-    }
-
-    /*
      * Return true iff the record is empty -- to avoid doing the work
      * of sending empty records over the network.
      */
@@ -117,8 +107,8 @@
     }
 
     boolean seqNumIsHuge() {
-        return (writeAuthenticator != null) &&
-                        writeAuthenticator.seqNumIsHuge();
+        return (writeCipher.authenticator != null) &&
+                        writeCipher.authenticator.seqNumIsHuge();
     }
 
     // SSLEngine and SSLSocket
@@ -132,8 +122,10 @@
     abstract void encodeChangeCipherSpec() throws IOException;
 
     // apply to SSLEngine only
-    Ciphertext encode(ByteBuffer[] sources, int offset, int length,
-            ByteBuffer destination) throws IOException {
+    Ciphertext encode(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
+
         throw new UnsupportedOperationException();
     }
 
@@ -143,7 +135,8 @@
     }
 
     // apply to SSLSocket only
-    void deliver(byte[] source, int offset, int length) throws IOException {
+    void deliver(
+            byte[] source, int offset, int length) throws IOException {
         throw new UnsupportedOperationException();
     }
 
@@ -152,15 +145,11 @@
         throw new UnsupportedOperationException();
     }
 
-    // apply to SSLEngine only
-    Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-    void changeWriteCiphers(Authenticator writeAuthenticator,
-            CipherBox writeCipher) throws IOException {
-
-        encodeChangeCipherSpec();
+    void changeWriteCiphers(SSLWriteCipher writeCipher,
+            boolean useChangeCipherSpec) throws IOException {
+        if (useChangeCipherSpec) {
+            encodeChangeCipherSpec();
+        }
 
         /*
          * Dispose of any intermediate state in the underlying cipher.
@@ -172,7 +161,6 @@
          */
         writeCipher.dispose();
 
-        this.writeAuthenticator = writeAuthenticator;
         this.writeCipher = writeCipher;
         this.isFirstAppOutputRecord = true;
     }
@@ -195,6 +183,11 @@
     }
 
     // apply to DTLS SSLEngine
+    void finishHandshake() {
+        // blank
+    }
+
+    // apply to DTLS SSLEngine
     void launchRetransmission() {
         // blank
     }
@@ -207,6 +200,10 @@
         }
     }
 
+    synchronized boolean isClosed() {
+        return isClosed;
+    }
+
     //
     // shared helpers
     //
@@ -216,72 +213,47 @@
     // To be consistent with the spec of SSLEngine.wrap() methods, the
     // destination ByteBuffer's position is updated to reflect the amount
     // of data produced.  The limit remains the same.
-    static long encrypt(Authenticator authenticator,
-            CipherBox encCipher, byte contentType, ByteBuffer destination,
+    static long encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
             int headerOffset, int dstLim, int headerSize,
-            ProtocolVersion protocolVersion, boolean isDTLS) {
-
-        byte[] sequenceNumber = null;
-        int dstContent = destination.position();
-
-        // Acquire the current sequence number before using.
+            ProtocolVersion protocolVersion) {
+        boolean isDTLS = protocolVersion.isDTLS;
         if (isDTLS) {
-            sequenceNumber = authenticator.sequenceNumber();
-        }
-
-        // The sequence number may be shared for different purpose.
-        boolean sharedSequenceNumber = false;
-
-        // "flip" but skip over header again, add MAC & encrypt
-        if (authenticator instanceof MAC) {
-            MAC signer = (MAC)authenticator;
-            if (signer.MAClen() != 0) {
-                byte[] hash = signer.compute(contentType, destination, false);
-
-                /*
-                 * position was advanced to limit in MAC compute above.
-                 *
-                 * Mark next area as writable (above layers should have
-                 * established that we have plenty of room), then write
-                 * out the hash.
-                 */
-                destination.limit(destination.limit() + hash.length);
-                destination.put(hash);
-
-                // reset the position and limit
-                destination.limit(destination.position());
-                destination.position(dstContent);
-
-                // The signer has used and increased the sequence number.
-                if (isDTLS) {
-                    sharedSequenceNumber = true;
-                }
+            if (protocolVersion.useTLS13PlusSpec()) {
+                return d13Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
+            } else {
+                return d10Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
+            }
+        } else {
+            if (protocolVersion.useTLS13PlusSpec()) {
+                return t13Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
+            } else {
+                return t10Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
             }
         }
+    }
 
-        if (!encCipher.isNullCipher()) {
-            if (protocolVersion.useTLS11PlusSpec() &&
-                    (encCipher.isCBCMode() || encCipher.isAEADMode())) {
-                byte[] nonce = encCipher.createExplicitNonce(
-                        authenticator, contentType, destination.remaining());
-                destination.position(headerOffset + headerSize);
-                destination.put(nonce);
-            }
-            if (!encCipher.isAEADMode()) {
-                // The explicit IV in TLS 1.1 and later can be encrypted.
-                destination.position(headerOffset + headerSize);
-            }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+    static long d13Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
 
-            // Encrypt may pad, so again the limit may be changed.
-            encCipher.encrypt(destination, dstLim);
-
-            // The cipher has used and increased the sequence number.
-            if (isDTLS && encCipher.isAEADMode()) {
-                sharedSequenceNumber = true;
-            }
-        } else {
-            destination.position(destination.limit());
-        }
+    private static long d10Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        encCipher.encrypt(contentType, destination);
 
         // Finish out the record header.
         int fragLen = destination.limit() - headerOffset - headerSize;
@@ -289,30 +261,83 @@
         destination.put(headerOffset, contentType);         // content type
         destination.put(headerOffset + 1, protocolVersion.major);
         destination.put(headerOffset + 2, protocolVersion.minor);
-        if (!isDTLS) {
-            // fragment length
-            destination.put(headerOffset + 3, (byte)(fragLen >> 8));
-            destination.put(headerOffset + 4, (byte)fragLen);
-        } else {
-            // epoch and sequence_number
-            destination.put(headerOffset + 3, sequenceNumber[0]);
-            destination.put(headerOffset + 4, sequenceNumber[1]);
-            destination.put(headerOffset + 5, sequenceNumber[2]);
-            destination.put(headerOffset + 6, sequenceNumber[3]);
-            destination.put(headerOffset + 7, sequenceNumber[4]);
-            destination.put(headerOffset + 8, sequenceNumber[5]);
-            destination.put(headerOffset + 9, sequenceNumber[6]);
-            destination.put(headerOffset + 10, sequenceNumber[7]);
+
+        // epoch and sequence_number
+        destination.put(headerOffset + 3, sequenceNumber[0]);
+        destination.put(headerOffset + 4, sequenceNumber[1]);
+        destination.put(headerOffset + 5, sequenceNumber[2]);
+        destination.put(headerOffset + 6, sequenceNumber[3]);
+        destination.put(headerOffset + 7, sequenceNumber[4]);
+        destination.put(headerOffset + 8, sequenceNumber[5]);
+        destination.put(headerOffset + 9, sequenceNumber[6]);
+        destination.put(headerOffset + 10, sequenceNumber[7]);
+
+        // fragment length
+        destination.put(headerOffset + 11, (byte)(fragLen >> 8));
+        destination.put(headerOffset + 12, (byte)fragLen);
+
+        // Update destination position to reflect the amount of data produced.
+        destination.position(destination.limit());
+
+        return Authenticator.toLong(sequenceNumber);
+    }
+
+    static long t13Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        if (!encCipher.isNullCipher()) {
+            // inner plaintext, using zero length padding.
+            int endOfPt = destination.limit();
+            destination.limit(endOfPt + 1);
+            destination.put(endOfPt, contentType);
+        }
 
-            // fragment length
-            destination.put(headerOffset + 11, (byte)(fragLen >> 8));
-            destination.put(headerOffset + 12, (byte)fragLen);
+        // use the right TLSCiphertext.opaque_type and legacy_record_version
+        ProtocolVersion pv = protocolVersion;
+        if (!encCipher.isNullCipher()) {
+            pv = ProtocolVersion.TLS12;
+            contentType = ContentType.APPLICATION_DATA.id;
+        } else if (protocolVersion.useTLS13PlusSpec()) {
+            pv = ProtocolVersion.TLS12;
+        }
+
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        encCipher.encrypt(contentType, destination);
+
+        // Finish out the record header.
+        int fragLen = destination.limit() - headerOffset - headerSize;
+        destination.put(headerOffset, contentType);
+        destination.put(headerOffset + 1, pv.major);
+        destination.put(headerOffset + 2, pv.minor);
+
+        // fragment length
+        destination.put(headerOffset + 3, (byte)(fragLen >> 8));
+        destination.put(headerOffset + 4, (byte)fragLen);
 
-            // Increase the sequence number for next use if it is not shared.
-            if (!sharedSequenceNumber) {
-                authenticator.increaseSequenceNumber();
-            }
-        }
+        // Update destination position to reflect the amount of data produced.
+        destination.position(destination.limit());
+
+        return Authenticator.toLong(sequenceNumber);
+    }
+
+    static long t10Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        encCipher.encrypt(contentType, destination);
+
+        // Finish out the record header.
+        int fragLen = destination.limit() - headerOffset - headerSize;
+
+        destination.put(headerOffset, contentType);         // content type
+        destination.put(headerOffset + 1, protocolVersion.major);
+        destination.put(headerOffset + 2, protocolVersion.minor);
+
+        // fragment length
+        destination.put(headerOffset + 3, (byte)(fragLen >> 8));
+        destination.put(headerOffset + 4, (byte)fragLen);
 
         // Update destination position to reflect the amount of data produced.
         destination.position(destination.limit());
@@ -324,55 +349,78 @@
     //
     // Uses the internal expandable buf variable and the current
     // protocolVersion variable.
-    void encrypt(Authenticator authenticator,
-            CipherBox encCipher, byte contentType, int headerSize) {
+    long encrypt(
+            SSLWriteCipher encCipher, byte contentType, int headerSize) {
+        if (protocolVersion.useTLS13PlusSpec()) {
+            return t13Encrypt(encCipher, contentType, headerSize);
+        } else {
+            return t10Encrypt(encCipher, contentType, headerSize);
+        }
+    }
 
-        int position = headerSize + writeCipher.getExplicitNonceSize();
+    private static final class T13PaddingHolder {
+        private static final byte[] zeros = new byte[16];
+    }
 
-        // "flip" but skip over header again, add MAC & encrypt
-        int macLen = 0;
-        if (authenticator instanceof MAC) {
-            MAC signer = (MAC)authenticator;
-            macLen = signer.MAClen();
-            if (macLen != 0) {
-                byte[] hash = signer.compute(contentType,
-                        buf, position, (count - position), false);
+    long t13Encrypt(
+            SSLWriteCipher encCipher, byte contentType, int headerSize) {
+        if (!encCipher.isNullCipher()) {
+            // inner plaintext
+            write(contentType);
+            write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length);
+        }
 
-                write(hash, 0, hash.length);
-            }
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        int position = headerSize;
+        int contentLen = count - position;
+
+        // ensure the capacity
+        int packetSize = encCipher.calculatePacketSize(contentLen, headerSize);
+        if (packetSize > buf.length) {
+            byte[] newBuf = new byte[packetSize];
+            System.arraycopy(buf, 0, newBuf, 0, count);
+            buf = newBuf;
         }
 
+        // use the right TLSCiphertext.opaque_type and legacy_record_version
+        ProtocolVersion pv = protocolVersion;
         if (!encCipher.isNullCipher()) {
-            // Requires explicit IV/nonce for CBC/AEAD cipher suites for
-            // TLS 1.1 or later.
-            if (protocolVersion.useTLS11PlusSpec() &&
-                    (encCipher.isCBCMode() || encCipher.isAEADMode())) {
+            pv = ProtocolVersion.TLS12;
+            contentType = ContentType.APPLICATION_DATA.id;
+        } else {
+            pv = ProtocolVersion.TLS12;
+        }
 
-                byte[] nonce = encCipher.createExplicitNonce(
-                        authenticator, contentType, (count - position));
-                int noncePos = position - nonce.length;
-                System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
-            }
+        ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
+        count = headerSize + encCipher.encrypt(contentType, destination);
+
+        // Fill out the header, write it and the message.
+        int fragLen = count - headerSize;
 
-            if (!encCipher.isAEADMode()) {
-                // The explicit IV in TLS 1.1 and later can be encrypted.
-                position = headerSize;
-            }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+        buf[0] = contentType;
+        buf[1] = pv.major;
+        buf[2] = pv.minor;
+        buf[3] = (byte)((fragLen >> 8) & 0xFF);
+        buf[4] = (byte)(fragLen & 0xFF);
+
+        return Authenticator.toLong(sequenceNumber);
+    }
 
-            // increase buf capacity if necessary
-            int fragSize = count - position;
-            int packetSize =
-                    encCipher.calculatePacketSize(fragSize, macLen, headerSize);
-            if (packetSize > (buf.length - position)) {
-                byte[] newBuf = new byte[position + packetSize];
-                System.arraycopy(buf, 0, newBuf, 0, count);
-                buf = newBuf;
-            }
+    long t10Encrypt(
+            SSLWriteCipher encCipher, byte contentType, int headerSize) {
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        int position = headerSize + writeCipher.getExplicitNonceSize();
+        int contentLen = count - position;
 
-            // Encrypt may pad, so again the count may be changed.
-            count = position +
-                    encCipher.encrypt(buf, position, (count - position));
+        // ensure the capacity
+        int packetSize = encCipher.calculatePacketSize(contentLen, headerSize);
+        if (packetSize > buf.length) {
+            byte[] newBuf = new byte[packetSize];
+            System.arraycopy(buf, 0, newBuf, 0, count);
+            buf = newBuf;
         }
+        ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
+        count = headerSize + encCipher.encrypt(contentType, destination);
 
         // Fill out the header, write it and the message.
         int fragLen = count - headerSize;
@@ -381,11 +429,12 @@
         buf[2] = protocolVersion.minor;
         buf[3] = (byte)((fragLen >> 8) & 0xFF);
         buf[4] = (byte)(fragLen & 0xFF);
+
+        return Authenticator.toLong(sequenceNumber);
     }
 
     static ByteBuffer encodeV2ClientHello(
             byte[] fragment, int offset, int length) throws IOException {
-
         int v3SessIdLenOffset = offset + 34;      //  2: client_version
                                                   // 32: random
 
@@ -449,7 +498,7 @@
         dstBuf.position(0);
         dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF)));  // pos: 0
         dstBuf.put((byte)(msgLen & 0xFF));                   // pos: 1
-        dstBuf.put(HandshakeMessage.ht_client_hello);        // pos: 2
+        dstBuf.put(SSLHandshake.CLIENT_HELLO.id);            // pos: 2
         dstBuf.put(fragment[offset]);         // major version, pos: 3
         dstBuf.put(fragment[offset + 1]);     // minor version, pos: 4
         dstBuf.put((byte)(v2CSLen >>> 8));                   // pos: 5
--- a/src/java.base/share/classes/sun/security/ssl/Plaintext.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/Plaintext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,16 +34,16 @@
 final class Plaintext {
     static final Plaintext PLAINTEXT_NULL = new Plaintext();
 
-    byte            contentType;
-    byte            majorVersion;
-    byte            minorVersion;
-    int             recordEpoch;    // incremented on every cipher state change
-    long            recordSN;       // contains epcoh number (epoch | sequence)
-    ByteBuffer      fragment;       // null if need to be reassembled
+    final byte       contentType;
+    final byte       majorVersion;
+    final byte       minorVersion;
+    final int        recordEpoch;     // increments on every cipher state change
+    final long       recordSN;        // epoch | sequence number
+    final ByteBuffer fragment;        // null if need to be reassembled
 
-    HandshakeStatus handshakeStatus;    // null if not used or not handshaking
+    HandshakeStatus  handshakeStatus; // null if not used or not handshaking
 
-    Plaintext() {
+    private Plaintext() {
         this.contentType = 0;
         this.majorVersion = 0;
         this.minorVersion = 0;
@@ -53,7 +53,8 @@
         this.handshakeStatus = null;
     }
 
-    Plaintext(byte contentType, byte majorVersion, byte minorVersion,
+    Plaintext(byte contentType,
+            byte majorVersion, byte minorVersion,
             int recordEpoch, long recordSN, ByteBuffer fragment) {
 
         this.contentType = contentType;
@@ -66,6 +67,7 @@
         this.handshakeStatus = null;
     }
 
+    @Override
     public String toString() {
         return "contentType: " + contentType + "/" +
                "majorVersion: " + majorVersion + "/" +
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A compact implementation of HandshakeContext for post-handshake messages
+ */
+final class PostHandshakeContext extends HandshakeContext {
+    private final static Map<Byte, SSLConsumer> consumers = Map.of(
+        SSLHandshake.KEY_UPDATE.id, SSLHandshake.KEY_UPDATE,
+        SSLHandshake.NEW_SESSION_TICKET.id, SSLHandshake.NEW_SESSION_TICKET);
+
+    PostHandshakeContext(TransportContext context) throws IOException {
+        super(context);
+
+        if (!negotiatedProtocol.useTLS13PlusSpec()) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                "Post-handshake not supported in " + negotiatedProtocol.name);
+        }
+
+        handshakeConsumers = new LinkedHashMap<>(consumers);
+        handshakeFinished = true;
+    }
+
+    @Override
+    void kickstart() throws IOException {
+        SSLHandshake.kickstart(this);
+    }
+
+    @Override
+    void dispatch(byte handshakeType, ByteBuffer fragment) throws IOException {
+        SSLConsumer consumer = handshakeConsumers.get(handshakeType);
+        if (consumer == null) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected post-handshake message: " +
+                            SSLHandshake.nameOf(handshakeType));
+            return;
+        }
+
+        try {
+            consumer.consume(this, fragment);
+        } catch (UnsupportedOperationException unsoe) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unsupported post-handshake message: " +
+                            SSLHandshake.nameOf(handshakeType), unsoe);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,796 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.*;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Arrays;
+import java.util.Optional;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import sun.security.ssl.ClientHello.ClientHelloMessage;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import static sun.security.ssl.SSLExtension.*;
+
+/**
+ * Pack of the "pre_shared_key" extension.
+ */
+final class PreSharedKeyExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHPreSharedKeyProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHPreSharedKeyConsumer();
+    static final HandshakeAbsence chOnLoadAbsence =
+            new CHPreSharedKeyAbsence();
+    static final HandshakeConsumer chOnTradeConsumer =
+            new CHPreSharedKeyUpdate();
+    static final SSLStringizer chStringizer =
+            new CHPreSharedKeyStringizer();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHPreSharedKeyProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHPreSharedKeyConsumer();
+    static final HandshakeAbsence shOnLoadAbsence =
+            new SHPreSharedKeyAbsence();
+    static final SSLStringizer shStringizer =
+            new SHPreSharedKeyStringizer();
+
+    private static final class PskIdentity {
+        final byte[] identity;
+        final int obfuscatedAge;
+
+        PskIdentity(byte[] identity, int obfuscatedAge) {
+            this.identity = identity;
+            this.obfuscatedAge = obfuscatedAge;
+        }
+
+        int getEncodedLength() {
+            return 2 + identity.length + 4;
+        }
+
+        void writeEncoded(ByteBuffer m) throws IOException {
+            Record.putBytes16(m, identity);
+            Record.putInt32(m, obfuscatedAge);
+        }
+
+        @Override
+        public String toString() {
+            return "{" + Utilities.toHexString(identity) + "," +
+                obfuscatedAge + "}";
+        }
+    }
+
+    private static final
+            class CHPreSharedKeySpec implements SSLExtensionSpec {
+        final List<PskIdentity> identities;
+        final List<byte[]> binders;
+
+        CHPreSharedKeySpec(List<PskIdentity> identities, List<byte[]> binders) {
+            this.identities = identities;
+            this.binders = binders;
+        }
+
+        CHPreSharedKeySpec(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            // struct {
+            //     PskIdentity identities<7..2^16-1>;
+            //     PskBinderEntry binders<33..2^16-1>;
+            // } OfferedPsks;
+            if (m.remaining() < 44) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid pre_shared_key extension: " +
+                    "insufficient data (length=" + m.remaining() + ")");
+            }
+
+            int idEncodedLength = Record.getInt16(m);
+            if (idEncodedLength < 7) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Invalid pre_shared_key extension: " +
+                    "insufficient identities (length=" + idEncodedLength + ")");
+            }
+
+            identities = new ArrayList<>();
+            int idReadLength = 0;
+            while (idReadLength < idEncodedLength) {
+                byte[] id = Record.getBytes16(m);
+                if (id.length < 1) {
+                    context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid pre_shared_key extension: " +
+                        "insufficient identity (length=" + id.length + ")");
+                }
+                int obfuscatedTicketAge = Record.getInt32(m);
+
+                PskIdentity pskId = new PskIdentity(id, obfuscatedTicketAge);
+                identities.add(pskId);
+                idReadLength += pskId.getEncodedLength();
+            }
+
+            if (m.remaining() < 35) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid pre_shared_key extension: " +
+                        "insufficient binders data (length=" +
+                        m.remaining() + ")");
+            }
+
+            int bindersEncodedLen = Record.getInt16(m);
+            if (bindersEncodedLen < 33) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid pre_shared_key extension: " +
+                        "insufficient binders (length=" +
+                        bindersEncodedLen + ")");
+            }
+
+            binders = new ArrayList<>();
+            int bindersReadLength = 0;
+            while (bindersReadLength < bindersEncodedLen) {
+                byte[] binder = Record.getBytes8(m);
+                if (binder.length < 32) {
+                    context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                            "Invalid pre_shared_key extension: " +
+                            "insufficient binder entry (length=" +
+                            binder.length + ")");
+                }
+                binders.add(binder);
+                bindersReadLength += 1 + binder.length;
+            }
+        }
+
+        int getIdsEncodedLength() {
+            int idEncodedLength = 0;
+            for(PskIdentity curId : identities) {
+                idEncodedLength += curId.getEncodedLength();
+            }
+
+            return idEncodedLength;
+        }
+
+        int getBindersEncodedLength() {
+            int binderEncodedLength = 0;
+            for (byte[] curBinder : binders) {
+                binderEncodedLength += 1 + curBinder.length;
+            }
+
+            return binderEncodedLength;
+        }
+
+        byte[] getEncoded() throws IOException {
+            int idsEncodedLength = getIdsEncodedLength();
+            int bindersEncodedLength = getBindersEncodedLength();
+            int encodedLength = 4 + idsEncodedLength + bindersEncodedLength;
+            byte[] buffer = new byte[encodedLength];
+            ByteBuffer m = ByteBuffer.wrap(buffer);
+            Record.putInt16(m, idsEncodedLength);
+            for(PskIdentity curId : identities) {
+                curId.writeEncoded(m);
+            }
+            Record.putInt16(m, bindersEncodedLength);
+            for (byte[] curBinder : binders) {
+                Record.putBytes8(m, curBinder);
+            }
+
+            return buffer;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"PreSharedKey\": '{'\n" +
+                "  \"identities\"    : \"{0}\",\n" +
+                "  \"binders\"       : \"{1}\",\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            Object[] messageFields = {
+                Utilities.indent(identitiesString()),
+                Utilities.indent(bindersString())
+            };
+
+            return messageFormat.format(messageFields);
+        }
+
+        String identitiesString() {
+            StringBuilder result = new StringBuilder();
+            for(PskIdentity curId : identities) {
+                result.append(curId.toString() + "\n");
+            }
+
+            return result.toString();
+        }
+
+        String bindersString() {
+            StringBuilder result = new StringBuilder();
+            for(byte[] curBinder : binders) {
+                result.append("{" + Utilities.toHexString(curBinder) + "}\n");
+            }
+
+            return result.toString();
+        }
+    }
+
+    private static final
+            class CHPreSharedKeyStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                // As the HandshakeContext parameter of CHPreSharedKeySpec
+                // constructor is used for fatal alert only, we can use
+                // null HandshakeContext here as we don't care about exception.
+                //
+                // Please take care of this code if the CHPreSharedKeySpec
+                // constructor is updated in the future.
+                return (new CHPreSharedKeySpec(null, buffer)).toString();
+            } catch (Exception ex) {
+                // For debug logging only, so please swallow exceptions.
+                return ex.getMessage();
+            }
+        }
+    }
+
+    private static final
+            class SHPreSharedKeySpec implements SSLExtensionSpec {
+        final int selectedIdentity;
+
+        SHPreSharedKeySpec(int selectedIdentity) {
+            this.selectedIdentity = selectedIdentity;
+        }
+
+        SHPreSharedKeySpec(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            if (m.remaining() < 2) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Invalid pre_shared_key extension: " +
+                        "insufficient selected_identity (length=" +
+                        m.remaining() + ")");
+            }
+            this.selectedIdentity = Record.getInt16(m);
+        }
+
+        byte[] getEncoded() throws IOException {
+            return new byte[] {
+                (byte)((selectedIdentity >> 8) & 0xFF),
+                (byte)(selectedIdentity & 0xFF)
+            };
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"PreSharedKey\": '{'\n" +
+                "  \"selected_identity\"      : \"{0}\",\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            Object[] messageFields = {
+                Utilities.byte16HexString(selectedIdentity)
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    private static final
+            class SHPreSharedKeyStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                // As the HandshakeContext parameter of SHPreSharedKeySpec
+                // constructor is used for fatal alert only, we can use
+                // null HandshakeContext here as we don't care about exception.
+                //
+                // Please take care of this code if the SHPreSharedKeySpec
+                // constructor is updated in the future.
+                return (new SHPreSharedKeySpec(null, buffer)).toString();
+            } catch (Exception ex) {
+                // For debug logging only, so please swallow exceptions.
+                return ex.getMessage();
+            }
+        }
+    }
+
+    private static final
+            class CHPreSharedKeyConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHPreSharedKeyConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                            HandshakeMessage message,
+                            ByteBuffer buffer) throws IOException {
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable pre_shared_key extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            CHPreSharedKeySpec pskSpec = null;
+            try {
+                pskSpec = new CHPreSharedKeySpec(shc, buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // The "psk_key_exchange_modes" extension should have been loaded.
+            if (!shc.handshakeExtensions.containsKey(
+                    SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Client sent PSK but not PSK modes, or the PSK " +
+                        "extension is not the last extension");
+            }
+
+            // error if id and binder lists are not the same length
+            if (pskSpec.identities.size() != pskSpec.binders.size()) {
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "PSK extension has incorrect number of binders");
+            }
+
+            if (shc.isResumption) {     // resumingSession may not be set
+                SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
+                        shc.sslContext.engineGetServerSessionContext();
+                int idIndex = 0;
+                for (PskIdentity requestedId : pskSpec.identities) {
+                    SSLSessionImpl s = sessionCache.get(requestedId.identity);
+                    if (s != null && s.isRejoinable() &&
+                            s.getPreSharedKey().isPresent()) {
+                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                            SSLLogger.fine("Resuming session: ", s);
+                        }
+
+                        // binder will be checked later
+                        shc.resumingSession = s;
+                        shc.handshakeExtensions.put(SH_PRE_SHARED_KEY,
+                            new SHPreSharedKeySpec(idIndex));   // for the index
+                        break;
+                    }
+
+                    ++idIndex;
+                }
+
+                if (idIndex == pskSpec.identities.size()) {
+                    // no resumable session
+                    shc.isResumption = false;
+                    shc.resumingSession = null;
+                }
+            }
+
+            // update the context
+            shc.handshakeExtensions.put(
+                    SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
+        }
+    }
+
+    private static final
+            class CHPreSharedKeyUpdate implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CHPreSharedKeyUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            if (!shc.isResumption || shc.resumingSession == null) {
+                // not resuming---nothing to do
+                return;
+            }
+
+            CHPreSharedKeySpec chPsk = (CHPreSharedKeySpec)
+                    shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY);
+            SHPreSharedKeySpec shPsk = (SHPreSharedKeySpec)
+                    shc.handshakeExtensions.get(SSLExtension.SH_PRE_SHARED_KEY);
+            if (chPsk == null || shPsk == null) {
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Required extensions are unavailable");
+            }
+
+            byte[] binder = chPsk.binders.get(shPsk.selectedIdentity);
+
+            // set up PSK binder hash
+            HandshakeHash pskBinderHash = shc.handshakeHash.copy();
+            byte[] lastMessage = pskBinderHash.removeLastReceived();
+            ByteBuffer messageBuf = ByteBuffer.wrap(lastMessage);
+            // skip the type and length
+            messageBuf.position(4);
+            // read to find the beginning of the binders
+            ClientHelloMessage.readPartial(shc.conContext, messageBuf);
+            int length = messageBuf.position();
+            messageBuf.position(0);
+            pskBinderHash.receive(messageBuf, length);
+
+            checkBinder(shc, shc.resumingSession, pskBinderHash, binder);
+        }
+    }
+
+    private static void checkBinder(ServerHandshakeContext shc,
+            SSLSessionImpl session,
+            HandshakeHash pskBinderHash, byte[] binder) throws IOException {
+        Optional<SecretKey> pskOpt = session.getPreSharedKey();
+        if (!pskOpt.isPresent()) {
+            shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "Session has no PSK");
+        }
+        SecretKey psk = pskOpt.get();
+
+        SecretKey binderKey = deriveBinderKey(psk, session);
+        byte[] computedBinder =
+                computeBinder(binderKey, session, pskBinderHash);
+        if (!Arrays.equals(binder, computedBinder)) {
+            shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+            "Incorect PSK binder value");
+        }
+    }
+
+    // Class that produces partial messages used to compute binder hash
+    static final class PartialClientHelloMessage extends HandshakeMessage {
+
+        private final ClientHello.ClientHelloMessage msg;
+        private final CHPreSharedKeySpec psk;
+
+        PartialClientHelloMessage(HandshakeContext ctx,
+                                  ClientHello.ClientHelloMessage msg,
+                                  CHPreSharedKeySpec psk) {
+            super(ctx);
+
+            this.msg = msg;
+            this.psk = psk;
+        }
+
+        @Override
+        SSLHandshake handshakeType() {
+            return msg.handshakeType();
+        }
+
+        private int pskTotalLength() {
+            return psk.getIdsEncodedLength() +
+                psk.getBindersEncodedLength() + 8;
+        }
+
+        @Override
+        int messageLength() {
+
+            if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) != null) {
+                return msg.messageLength();
+            } else {
+                return msg.messageLength() + pskTotalLength();
+            }
+        }
+
+        @Override
+        void send(HandshakeOutStream hos) throws IOException {
+            msg.sendCore(hos);
+
+            // complete extensions
+            int extsLen = msg.extensions.length();
+            if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) == null) {
+                extsLen += pskTotalLength();
+            }
+            hos.putInt16(extsLen - 2);
+            // write the complete extensions
+            for (SSLExtension ext : SSLExtension.values()) {
+                byte[] extData = msg.extensions.get(ext);
+                if (extData == null) {
+                    continue;
+                }
+                // the PSK could be there from an earlier round
+                if (ext == SSLExtension.CH_PRE_SHARED_KEY) {
+                    continue;
+                }
+                int extID = ext.id;
+                hos.putInt16(extID);
+                hos.putBytes16(extData);
+            }
+
+            // partial PSK extension
+            int extID = SSLExtension.CH_PRE_SHARED_KEY.id;
+            hos.putInt16(extID);
+            byte[] encodedPsk = psk.getEncoded();
+            hos.putInt16(encodedPsk.length);
+            hos.write(encodedPsk, 0, psk.getIdsEncodedLength() + 2);
+        }
+    }
+
+    private static final
+            class CHPreSharedKeyProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHPreSharedKeyProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            if (!chc.isResumption || chc.resumingSession == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("No session to resume.");
+                }
+                return null;
+            }
+
+            Optional<SecretKey> pskOpt = chc.resumingSession.getPreSharedKey();
+            if (!pskOpt.isPresent()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Existing session has no PSK.");
+                }
+                return null;
+            }
+            SecretKey psk = pskOpt.get();
+            Optional<byte[]> pskIdOpt = chc.resumingSession.getPskIdentity();
+            if (!pskIdOpt.isPresent()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "PSK has no identity, or identity was already used");
+                }
+                return null;
+            }
+            byte[] pskId = pskIdOpt.get();
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Found resumable session. Preparing PSK message.");
+            }
+
+            List<PskIdentity> identities = new ArrayList<>();
+            int ageMillis = (int)(System.currentTimeMillis() -
+                    chc.resumingSession.getTicketCreationTime());
+            int obfuscatedAge =
+                    ageMillis + chc.resumingSession.getTicketAgeAdd();
+            identities.add(new PskIdentity(pskId, obfuscatedAge));
+
+            SecretKey binderKey = deriveBinderKey(psk, chc.resumingSession);
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+            CHPreSharedKeySpec pskPrototype = createPskPrototype(
+                chc.resumingSession.getSuite().hashAlg.hashLength, identities);
+            HandshakeHash pskBinderHash = chc.handshakeHash.copy();
+
+            byte[] binder = computeBinder(binderKey, pskBinderHash,
+                    chc.resumingSession, chc, clientHello, pskPrototype);
+
+            List<byte[]> binders = new ArrayList<>();
+            binders.add(binder);
+
+            CHPreSharedKeySpec pskMessage =
+                    new CHPreSharedKeySpec(identities, binders);
+            chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
+            return pskMessage.getEncoded();
+        }
+
+        private CHPreSharedKeySpec createPskPrototype(
+                int hashLength, List<PskIdentity> identities) {
+            List<byte[]> binders = new ArrayList<>();
+            byte[] binderProto = new byte[hashLength];
+            for (PskIdentity curId : identities) {
+                binders.add(binderProto);
+            }
+
+            return new CHPreSharedKeySpec(identities, binders);
+        }
+    }
+
+    private static byte[] computeBinder(SecretKey binderKey,
+            SSLSessionImpl session,
+            HandshakeHash pskBinderHash) throws IOException {
+
+        pskBinderHash.determine(
+                session.getProtocolVersion(), session.getSuite());
+        pskBinderHash.update();
+        byte[] digest = pskBinderHash.digest();
+
+        return computeBinder(binderKey, session, digest);
+    }
+
+    private static byte[] computeBinder(SecretKey binderKey,
+            HandshakeHash hash, SSLSessionImpl session,
+            HandshakeContext ctx, ClientHello.ClientHelloMessage hello,
+            CHPreSharedKeySpec pskPrototype) throws IOException {
+
+        PartialClientHelloMessage partialMsg =
+                new PartialClientHelloMessage(ctx, hello, pskPrototype);
+
+        SSLEngineOutputRecord record = new SSLEngineOutputRecord(hash);
+        HandshakeOutStream hos = new HandshakeOutStream(record);
+        partialMsg.write(hos);
+
+        hash.determine(session.getProtocolVersion(), session.getSuite());
+        hash.update();
+        byte[] digest = hash.digest();
+
+        return computeBinder(binderKey, session, digest);
+    }
+
+    private static byte[] computeBinder(SecretKey binderKey,
+            SSLSessionImpl session, byte[] digest) throws IOException {
+        try {
+            CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
+            HKDF hkdf = new HKDF(hashAlg.name);
+            byte[] label = ("tls13 finished").getBytes();
+            byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
+                    label, new byte[0], hashAlg.hashLength);
+            SecretKey finishedKey = hkdf.expand(
+                    binderKey, hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
+
+            String hmacAlg =
+                "Hmac" + hashAlg.name.replace("-", "");
+            try {
+                Mac hmac = JsseJce.getMac(hmacAlg);
+                hmac.init(finishedKey);
+                return hmac.doFinal(digest);
+            } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
+                throw new IOException(ex);
+            }
+        } catch(GeneralSecurityException ex) {
+            throw new IOException(ex);
+        }
+    }
+
+    private static SecretKey deriveBinderKey(SecretKey psk,
+            SSLSessionImpl session) throws IOException {
+        try {
+            CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
+            HKDF hkdf = new HKDF(hashAlg.name);
+            byte[] zeros = new byte[hashAlg.hashLength];
+            SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
+
+            byte[] label = ("tls13 res binder").getBytes();
+            MessageDigest md = MessageDigest.getInstance(hashAlg.toString());;
+            byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
+                    label, md.digest(new byte[0]), hashAlg.hashLength);
+            return hkdf.expand(earlySecret,
+                    hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
+        } catch (GeneralSecurityException ex) {
+            throw new IOException(ex);
+        }
+    }
+
+    private static final
+            class CHPreSharedKeyAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                           HandshakeMessage message) throws IOException {
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                "Handling pre_shared_key absence.");
+            }
+
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Resumption is only determined by PSK, when enabled
+            shc.resumingSession = null;
+            shc.isResumption = false;
+        }
+    }
+
+    private static final
+            class SHPreSharedKeyConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHPreSharedKeyConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a response of the specific request?
+            if (!chc.handshakeExtensions.containsKey(
+                    SSLExtension.CH_PRE_SHARED_KEY)) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Server sent unexpected pre_shared_key extension");
+            }
+
+            SHPreSharedKeySpec shPsk = new SHPreSharedKeySpec(chc, buffer);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Received pre_shared_key extension: ", shPsk);
+            }
+
+            // The PSK identity should not be reused, even if it is
+            // not selected.
+            chc.resumingSession.consumePskIdentity();
+
+            if (shPsk.selectedIdentity != 0) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Selected identity index is not in correct range.");
+            }
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                "Resuming session: ", chc.resumingSession);
+            }
+
+            // remove the session from the cache
+            SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
+                    chc.sslContext.engineGetClientSessionContext();
+            sessionCache.remove(chc.resumingSession.getSessionId());
+        }
+    }
+
+    private static final
+            class SHPreSharedKeyAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Handling pre_shared_key absence.");
+            }
+
+            if (chc.handshakeExtensions.containsKey(
+                    SSLExtension.CH_PRE_SHARED_KEY)) {
+                // The PSK identity should not be reused, even if it is
+                // not selected.
+                chc.resumingSession.consumePskIdentity();
+            }
+
+            // The server refused to resume, or the client did not
+            // request 1.3 resumption.
+            chc.resumingSession = null;
+            chc.isResumption = false;
+        }
+    }
+
+    private static final
+            class SHPreSharedKeyProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHPreSharedKeyProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            SHPreSharedKeySpec psk = (SHPreSharedKeySpec)
+                    shc.handshakeExtensions.get(SH_PRE_SHARED_KEY);
+            if (psk == null) {
+                return null;
+            }
+
+            return psk.getEncoded();
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,21 +25,19 @@
 
 package sun.security.ssl;
 
+import java.math.BigInteger;
 import java.security.*;
-import java.math.BigInteger;
-import java.util.regex.Pattern;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.regex.Matcher;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Collections;
+import java.util.regex.Pattern;
 import javax.crypto.spec.DHParameterSpec;
 
 /**
  * Predefined default DH ephemeral parameters.
  */
 final class PredefinedDHParameterSpecs {
-    private final static boolean debugIsOn =
-            (Debug.getInstance("ssl") != null) && Debug.isOn("sslctx");
 
     //
     // Default DH ephemeral parameters
@@ -209,15 +207,15 @@
     // a measure of the uncertainty that prime modulus p is not a prime
     //
     // see BigInteger.isProbablePrime(int certainty)
-    private final static int PRIME_CERTAINTY = 120;
+    private static final int PRIME_CERTAINTY = 120;
 
     // the known security property, jdk.tls.server.defaultDHEParameters
-    private final static String PROPERTY_NAME =
+    private static final String PROPERTY_NAME =
             "jdk.tls.server.defaultDHEParameters";
 
     private static final Pattern spacesPattern = Pattern.compile("\\s+");
 
-    private final static Pattern syntaxPattern = Pattern.compile(
+    private static final Pattern syntaxPattern = Pattern.compile(
             "(\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})" +
             "(,\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})*");
 
@@ -225,10 +223,10 @@
             "\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}");
 
     // cache of predefined default DH ephemeral parameters
-    final static Map<Integer, DHParameterSpec> definedParams;
+    static final Map<Integer, DHParameterSpec> definedParams;
 
     // cache of Finite Field DH Ephemeral parameters (RFC 7919/FFDHE)
-    final static Map<Integer, DHParameterSpec> ffdheParams;
+    static final Map<Integer, DHParameterSpec> ffdheParams;
 
     static {
         String property = AccessController.doPrivileged(
@@ -252,8 +250,9 @@
             Matcher spacesMatcher = spacesPattern.matcher(property);
             property = spacesMatcher.replaceAll("");
 
-            if (debugIsOn) {
-                System.out.println("The Security Property " +
+            if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
+                SSLLogger.fine(
+                        "The Security Property " +
                         PROPERTY_NAME + ": " + property);
             }
         }
@@ -267,8 +266,8 @@
                     String primeModulus = paramsFinder.group(1);
                     BigInteger p = new BigInteger(primeModulus, 16);
                     if (!p.isProbablePrime(PRIME_CERTAINTY)) {
-                        if (debugIsOn) {
-                            System.out.println(
+                        if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
+                            SSLLogger.fine(
                                 "Prime modulus p in Security Property, " +
                                 PROPERTY_NAME + ", is not a prime: " +
                                 primeModulus);
@@ -284,8 +283,8 @@
                     int primeLen = p.bitLength();
                     defaultParams.put(primeLen, spec);
                 }
-            } else if (debugIsOn) {
-                System.out.println("Invalid Security Property, " +
+            } else if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
+                SSLLogger.fine("Invalid Security Property, " +
                         PROPERTY_NAME + ", definition");
             }
         }
--- a/src/java.base/share/classes/sun/security/ssl/ProtocolList.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2002, 2015, 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.util.*;
-
-/**
- * A list of ProtocolVersions. Also maintains the list of supported protocols.
- * Instances of this class are immutable. Some member variables are final
- * and can be accessed directly without method accessors.
- *
- * @author  Andreas Sterbenz
- * @since   1.4.1
- */
-final class ProtocolList {
-
-    // the sorted protocol version list
-    private final ArrayList<ProtocolVersion> protocols;
-
-    private String[] protocolNames;
-
-    // the minimum and maximum ProtocolVersions in this list
-    final ProtocolVersion min, max;
-
-    // the format for the hello version to use
-    final ProtocolVersion helloVersion;
-
-    ProtocolList(String[] names) {
-        this(convert(names));
-    }
-
-    ProtocolList(ArrayList<ProtocolVersion> versions) {
-        this.protocols = versions;
-
-        if ((protocols.size() == 1) &&
-                protocols.contains(ProtocolVersion.SSL20Hello)) {
-            throw new IllegalArgumentException("SSLv2Hello cannot be " +
-                "enabled unless at least one other supported version " +
-                "is also enabled.");
-        }
-
-        if (protocols.size() != 0) {
-            Collections.sort(protocols);
-            min = protocols.get(0);
-            max = protocols.get(protocols.size() - 1);
-            helloVersion = protocols.get(0);
-        } else {
-            min = ProtocolVersion.NONE;
-            max = ProtocolVersion.NONE;
-            helloVersion = ProtocolVersion.NONE;
-        }
-    }
-
-    private static ArrayList<ProtocolVersion> convert(String[] names) {
-        if (names == null) {
-            throw new IllegalArgumentException("Protocols may not be null");
-        }
-
-        ArrayList<ProtocolVersion> versions = new ArrayList<>(names.length);
-        for (int i = 0; i < names.length; i++ ) {
-            ProtocolVersion version = ProtocolVersion.valueOf(names[i]);
-            if (versions.contains(version) == false) {
-                versions.add(version);
-            }
-        }
-
-        return versions;
-    }
-
-    /**
-     * Return whether this list contains the specified protocol version.
-     * SSLv2Hello is not a real protocol version we support, we always
-     * return false for it.
-     */
-    boolean contains(ProtocolVersion protocolVersion) {
-        if (protocolVersion == ProtocolVersion.SSL20Hello) {
-            return false;
-        }
-        return protocols.contains(protocolVersion);
-    }
-
-    /**
-     * Return a reference to the internal Collection of CipherSuites.
-     * The Collection MUST NOT be modified.
-     */
-    Collection<ProtocolVersion> collection() {
-        return protocols;
-    }
-
-    /**
-     * Select a protocol version from the list.
-     *
-     * Return the lower of the protocol version of that suggested by
-     * the <code>protocolVersion</code> and the highest version of this
-     * protocol list, or null if no protocol version is available.
-     *
-     * The method is used by TLS server to negotiated the protocol
-     * version between client suggested protocol version in the
-     * client hello and protocol versions supported by the server.
-     */
-    ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
-        ProtocolVersion selectedVersion = null;
-        for (ProtocolVersion pv : protocols) {
-            if (pv.compareTo(protocolVersion) > 0) {
-                break;      // Safe to break here as this.protocols is sorted,
-                            // and DTLS and SSL/TLS protocols are not mixed.
-            }
-            selectedVersion = pv;
-        }
-
-        return selectedVersion;
-    }
-
-    /**
-     * Return an array with the names of the ProtocolVersions in this list.
-     */
-    synchronized String[] toStringArray() {
-        if (protocolNames == null) {
-            protocolNames = new String[protocols.size()];
-            int i = 0;
-            for (ProtocolVersion version : protocols) {
-                protocolNames[i++] = version.name;
-            }
-        }
-        return protocolNames.clone();
-    }
-
-    @Override
-    public String toString() {
-        return protocols.toString();
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,33 +25,40 @@
 
 package sun.security.ssl;
 
-import java.util.*;
 import java.security.CryptoPrimitive;
-import sun.security.ssl.CipherSuite.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
 
 /**
- * Type safe enum for an SSL/TLS protocol version. Instances are obtained
- * using the static factory methods or by referencing the static members
- * in this class. Member variables are final and can be accessed without
- * accessor methods.
- *
- * There is only ever one instance per supported protocol version, this
- * means == can be used for comparision instead of equals() if desired.
- *
- * Checks for a particular version number should generally take this form:
- *
- * <pre>{@code
- * if (protocolVersion.v >= ProtocolVersion.TLS10) {
- *   // TLS 1.0 code goes here
- * } else {
- *   // SSL 3.0 code here
- * }
- * }</pre>
+ * Enum for an SSL/TLS/DTLS protocol version.
  *
  * @author  Andreas Sterbenz
  * @since   1.4.1
  */
-public final class ProtocolVersion implements Comparable<ProtocolVersion> {
+enum ProtocolVersion {
+//    TLS13           (0x0304,    "TLSv1.3",      false),
+    TLS13           (SSLConfiguration.tls13VN,    "TLSv1.3",      false),
+    TLS12           (0x0303,    "TLSv1.2",      false),
+    TLS11           (0x0302,    "TLSv1.1",      false),
+    TLS10           (0x0301,    "TLSv1",        false),
+    SSL30           (0x0300,    "SSLv3",        false),
+    SSL20Hello      (0x0002,    "SSLv2Hello",   false),
+
+    DTLS12          (0xFEFD,    "DTLSv1.2",     true),
+    DTLS10          (0xFEFF,    "DTLSv1.0",     true),
+
+    // Dummy protocol version value for invalid SSLSession
+    NONE            (-1,        "NONE",         false);
+
+
+    final int id;
+    final String name;
+    final boolean isDTLS;
+    final byte major;
+    final byte minor;
+    final boolean isAvailable;
 
     // The limit of maximum protocol version
     static final int LIMIT_MAX_VALUE = 0xFFFF;
@@ -59,257 +66,348 @@
     // The limit of minimum protocol version
     static final int LIMIT_MIN_VALUE = 0x0000;
 
-    // Dummy protocol version value for invalid SSLSession
-    static final ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
+    // (D)TLS ProtocolVersion array for TLS 1.0 and previous versions.
+    static final ProtocolVersion[] PROTOCOLS_TO_10 = new ProtocolVersion[] {
+            TLS10, SSL30
+        };
 
-    // If enabled, send/accept SSLv2 hello messages
-    static final ProtocolVersion SSL20Hello =
-                                new ProtocolVersion(0x0002, "SSLv2Hello");
+    // (D)TLS ProtocolVersion array for TLS 1.1/DTLS 1.0 and previous versions.
+    static final ProtocolVersion[] PROTOCOLS_TO_11 = new ProtocolVersion[] {
+            TLS11, TLS10, SSL30, DTLS10
+        };
 
-    // SSL 3.0
-    static final ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3");
+    // (D)TLS ProtocolVersion array for (D)TLS 1.2 and previous versions.
+    static final ProtocolVersion[] PROTOCOLS_TO_12 = new ProtocolVersion[] {
+            TLS12, TLS11, TLS10, SSL30, DTLS12, DTLS10
+    };
 
-    // TLS 1.0
-    static final ProtocolVersion TLS10 = new ProtocolVersion(0x0301, "TLSv1");
+    // (D)TLS ProtocolVersion array for (D)TLS 1.3 and previous versions.
+    static final ProtocolVersion[] PROTOCOLS_TO_13 = new ProtocolVersion[] {
+            TLS13, TLS12, TLS11, TLS10, SSL30, DTLS12, DTLS10
+        };
 
-    // TLS 1.1
-    static final ProtocolVersion TLS11 = new ProtocolVersion(0x0302, "TLSv1.1");
+    // No protocol version specified.
+    static final ProtocolVersion[] PROTOCOLS_OF_NONE = new ProtocolVersion[] {
+            NONE
+        };
 
-    // TLS 1.2
-    static final ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2");
+    // (D)TLS ProtocolVersion array for SSL 3.0.
+    static final ProtocolVersion[] PROTOCOLS_OF_30 = new ProtocolVersion[] {
+            SSL30
+        };
 
-    // DTLS 1.0
-    // {254, 255}, the version value of DTLS 1.0.
-    static final ProtocolVersion DTLS10 =
-                                new ProtocolVersion(0xFEFF, "DTLSv1.0");
+    // (D)TLS ProtocolVersion array for TLS 1.1/DTSL 1.0.
+    static final ProtocolVersion[] PROTOCOLS_OF_11 = new ProtocolVersion[] {
+            TLS11, DTLS10
+        };
 
-    // No DTLS 1.1, that version number was skipped in order to harmonize
-    // version numbers with TLS.
+    // (D)TLS ProtocolVersion array for (D)TLS 1.2.
+    static final ProtocolVersion[] PROTOCOLS_OF_12 = new ProtocolVersion[] {
+            TLS12, DTLS12
+        };
 
-    // DTLS 1.2
-    // {254, 253}, the version value of DTLS 1.2.
-    static final ProtocolVersion DTLS12 =
-                                new ProtocolVersion(0xFEFD, "DTLSv1.2");
+    // (D)TLS ProtocolVersion array for (D)TLS 1.3.
+    static final ProtocolVersion[] PROTOCOLS_OF_13 = new ProtocolVersion[] {
+            TLS13
+        };
 
-    private static final boolean FIPS = SunJSSE.isFIPS();
-
-    // minimum version we implement (SSL 3.0)
-    static final ProtocolVersion MIN = FIPS ? TLS10 : SSL30;
+    // (D)TLS ProtocolVersion array for TSL 1.0/1.1 and DTLS 1.0.
+    static final ProtocolVersion[] PROTOCOLS_10_11 = new ProtocolVersion[] {
+            TLS11, TLS10, DTLS10
+        };
 
-    // maximum version we implement (TLS 1.2)
-    static final ProtocolVersion MAX = TLS12;
+    // (D)TLS ProtocolVersion array for TSL 1.1/1.2 and DTLS 1.0/1.2.
+    static final ProtocolVersion[] PROTOCOLS_11_12 = new ProtocolVersion[] {
+            TLS12, TLS11, DTLS12, DTLS10
+        };
 
-    // SSL/TLS ProtocolVersion to use by default (TLS 1.2)
-    static final ProtocolVersion DEFAULT_TLS = TLS12;
+    // (D)TLS ProtocolVersion array for TSL 1.2/1.3 and DTLS 1.2/1.3.
+    static final ProtocolVersion[] PROTOCOLS_12_13 = new ProtocolVersion[] {
+            TLS13, TLS12, DTLS12
+        };
 
-    // DTLS ProtocolVersion to use by default (TLS 1.2)
-    static final ProtocolVersion DEFAULT_DTLS = DTLS12;
+    // (D)TLS ProtocolVersion array for TSL 1.0/1.1/1.2 and DTLS 1.0/1.2.
+    static final ProtocolVersion[] PROTOCOLS_10_12 = new ProtocolVersion[] {
+            TLS12, TLS11, TLS10, DTLS12, DTLS10
+        };
 
-    // Default version for hello messages (SSLv2Hello)
-    static final ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30;
+    // TLS ProtocolVersion array for TLS 1.2 and previous versions.
+    static final ProtocolVersion[] PROTOCOLS_TO_TLS12 = new ProtocolVersion[] {
+            TLS12, TLS11, TLS10, SSL30
+    };
 
-    // Available protocols
-    //
-    // Including all supported protocols except the disabled ones.
-    static final Set<ProtocolVersion> availableProtocols;
+    // TLS ProtocolVersion array for TLS 1.1 and previous versions.
+    static final ProtocolVersion[] PROTOCOLS_TO_TLS11 = new ProtocolVersion[] {
+            TLS11, TLS10, SSL30
+    };
 
-    // version in 16 bit MSB format as it appears in records and
-    // messages, i.e. 0x0301 for TLS 1.0
-    public final int v;
+    // TLS ProtocolVersion array for TLS 1.0 and previous versions.
+    static final ProtocolVersion[] PROTOCOLS_TO_TLS10 = new ProtocolVersion[] {
+            TLS10, SSL30
+    };
 
-    // major and minor version
-    public final byte major, minor;
-
-    // name used in JSSE (e.g. TLSv1 for TLS 1.0)
-    final String name;
+    // Empty ProtocolVersion array
+    static final ProtocolVersion[] PROTOCOLS_EMPTY = new ProtocolVersion[0];
 
-    // Initialize the available protocols.
-    static {
-        Set<ProtocolVersion> protocols = new HashSet<>(7);
+    private ProtocolVersion(int id, String name, boolean isDTLS) {
+        this.id = id;
+        this.name = name;
+        this.isDTLS = isDTLS;
+        this.major = (byte)((id >>> 8) & 0xFF);
+        this.minor = (byte)(id & 0xFF);
 
-        ProtocolVersion[] pvs = new ProtocolVersion[] {
-                SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12};
-        EnumSet<CryptoPrimitive> cryptoPrimitives =
-            EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT);
-        for (ProtocolVersion p : pvs) {
-            if (SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits(
-                    cryptoPrimitives, p.name, null)) {
-                protocols.add(p);
+        this.isAvailable = SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits(
+                EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT),
+                name, null);
+    }
+
+    /**
+     * Return a ProtocolVersion with the specified major and minor
+     * version numbers.
+     */
+    static ProtocolVersion valueOf(byte major, byte minor) {
+        for (ProtocolVersion pv : ProtocolVersion.values()) {
+            if ((pv.major == major) && (pv.minor == minor)) {
+                return pv;
             }
         }
 
-        availableProtocols =
-                Collections.<ProtocolVersion>unmodifiableSet(protocols);
+        return null;
     }
 
-    // private
-    private ProtocolVersion(int v, String name) {
-        this.v = v;
-        this.name = name;
-        major = (byte)(v >>> 8);
-        minor = (byte)(v & 0xFF);
+    /**
+     * Return a ProtocolVersion with the specified version number.
+     */
+    static ProtocolVersion valueOf(int id) {
+        for (ProtocolVersion pv : ProtocolVersion.values()) {
+            if (pv.id == id) {
+                return pv;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Return name of a (D)TLS protocol specified by major and
+     * minor version numbers.
+     */
+    static String nameOf(byte major, byte minor) {
+        for (ProtocolVersion pv : ProtocolVersion.values()) {
+            if ((pv.major == major) && (pv.minor == minor)) {
+                return pv.name;
+            }
+        }
+
+        return "(D)TLS-" + major + "." + minor;
     }
 
-    // private
-    private static ProtocolVersion valueOf(int v) {
-        if (v == SSL30.v) {
-            return SSL30;
-        } else if (v == TLS10.v) {
-            return TLS10;
-        } else if (v == TLS11.v) {
-            return TLS11;
-        } else if (v == TLS12.v) {
-            return TLS12;
-        } else if (v == SSL20Hello.v) {
-            return SSL20Hello;
-        } else if (v == DTLS10.v) {
-            return DTLS10;
-        } else if (v == DTLS12.v) {
-            return DTLS12;
+    /**
+     * Return name of a (D)TLS protocol specified by a protocol number.
+     */
+    static String nameOf(int id) {
+        return nameOf((byte)((id >>> 8) & 0xFF), (byte)(id & 0xFF));
+    }
+
+    /**
+     * Return a ProtocolVersion for the given (D)TLS protocol name.
+     */
+    static ProtocolVersion nameOf(String name) {
+        for (ProtocolVersion pv : ProtocolVersion.values()) {
+            if (pv.name.equals(name)) {
+                return pv;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Return true if the specific (D)TLS protocol is negotiable.
+     *
+     * Used to filter out SSLv2Hello and protocol numbers less than the
+     * minimal supported protocol versions.
+     */
+    static boolean isNegotiable(
+            byte major, byte minor, boolean isDTLS, boolean allowSSL20Hello) {
+        int v = ((major & 0xFF) << 8) | (minor & 0xFF);
+        if (isDTLS) {
+            return v <= DTLS10.id;
         } else {
-            int major = (v >>> 8) & 0xFF;
-            int minor = v & 0xFF;
-            return new ProtocolVersion(v, "Unknown-" + major + "." + minor);
+            if (v < SSL30.id) {
+               if (!allowSSL20Hello || (v != SSL20Hello.id)) {
+                   return false;
+               }
+            }
+            return true;
         }
     }
 
     /**
-     * Return a ProtocolVersion with the specified major and minor version
-     * numbers. Never throws exceptions.
+     * Get names of a list of ProtocolVersion objects.
      */
-    public static ProtocolVersion valueOf(int major, int minor) {
-        return valueOf(((major & 0xFF) << 8) | (minor & 0xFF));
+    static String[] toStringArray(List<ProtocolVersion> protocolVersions) {
+        if ((protocolVersions != null) && !protocolVersions.isEmpty()) {
+            String[] protocolNames = new String[protocolVersions.size()];
+            int i = 0;
+            for (ProtocolVersion pv : protocolVersions) {
+                protocolNames[i++] = pv.name;
+            }
+
+            return protocolNames;
+        }
+
+        return new String[0];
+    }
+
+    /**
+     * Get names of a list of protocol version identifiers.
+     */
+    static String[] toStringArray(int[] protocolVersions) {
+        if ((protocolVersions != null) && protocolVersions.length != 0) {
+            String[] protocolNames = new String[protocolVersions.length];
+            int i = 0;
+            for (int pv : protocolVersions) {
+                protocolNames[i++] = ProtocolVersion.nameOf(pv);
+            }
+
+            return protocolNames;
+        }
+
+        return new String[0];
     }
 
     /**
-     * Return a ProtocolVersion for the given name.
-     *
-     * @exception IllegalArgumentException if name is null or does not
-     * identify a supported protocol
+     * Get a list of ProtocolVersion objects of an array protocol
+     * version names.
      */
-    static ProtocolVersion valueOf(String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("Protocol cannot be null");
-        }
-
-        if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) {
-            throw new IllegalArgumentException(
-                    "Only TLS 1.0 or later allowed in FIPS mode");
+    static List<ProtocolVersion> namesOf(String[] protocolNames) {
+        if (protocolNames == null || protocolNames.length == 0) {
+            return Collections.<ProtocolVersion>emptyList();
         }
 
-        if (name.equals(SSL30.name)) {
-            return SSL30;
-        } else if (name.equals(TLS10.name)) {
-            return TLS10;
-        } else if (name.equals(TLS11.name)) {
-            return TLS11;
-        } else if (name.equals(TLS12.name)) {
-            return TLS12;
-        } else if (name.equals(SSL20Hello.name)) {
-            return SSL20Hello;
-        } else if (name.equals(DTLS10.name)) {
-            return DTLS10;
-        } else if (name.equals(DTLS12.name)) {
-            return DTLS12;
-        } else {
-            throw new IllegalArgumentException(name);
+        List<ProtocolVersion> pvs = new ArrayList<>(protocolNames.length);
+        for (String pn : protocolNames) {
+            ProtocolVersion pv = ProtocolVersion.nameOf(pn);
+            if (pv == null) {
+                throw new IllegalArgumentException(
+                        "Unsupported protocol" + pn);
+            }
+
+            pvs.add(pv);
         }
-    }
 
-    @Override
-    public String toString() {
-        return name;
+        return Collections.unmodifiableList(pvs);
     }
 
     /**
-     * Compares this object with the specified object for order.
+     * Return true if the specific protocol version name is
+     * of (D)TLS 1.2 or newer version.
      */
-    @Override
-    public int compareTo(ProtocolVersion protocolVersion) {
-        if (maybeDTLSProtocol()) {
-            if (!protocolVersion.maybeDTLSProtocol()) {
-                throw new IllegalArgumentException("Not DTLS protocol");
-            }
+    static boolean useTLS12PlusSpec(String name) {
+        ProtocolVersion pv = ProtocolVersion.nameOf(name);
+        if (pv != null && pv != NONE) {
+            return pv.isDTLS ? (pv.id <= DTLS12.id) : (pv.id >= TLS12.id);
+        }
+
+        return false;
+    }
 
-            return protocolVersion.v - this.v;
+    /**
+     * Compares this object with the specified ProtocolVersion.
+     *
+     * @see java.lang.Comparable
+     */
+    int compare(ProtocolVersion that) {
+        if (this == that) {
+            return 0;
+        }
+
+        if (this == ProtocolVersion.NONE) {
+            return -1;
+        } else if (that == ProtocolVersion.NONE) {
+            return 1;
+        }
+
+        if (isDTLS) {
+            return that.id - this.id;
         } else {
-            if (protocolVersion.maybeDTLSProtocol()) {
-                throw new IllegalArgumentException("Not TLS protocol");
-            }
-
-            return this.v - protocolVersion.v;
+            return this.id - that.id;
         }
     }
 
     /**
-     * Returns true if a ProtocolVersion represents a DTLS protocol.
+     * Return true if this ProtocolVersion object is of (D)TLS 1.3 or
+     * newer version.
+     */
+    boolean useTLS13PlusSpec() {
+        return isDTLS ? (this.id < DTLS12.id) : (this.id >= TLS13.id);
+    }
+
+    /**
+     * Return true if this ProtocolVersion object is of (D)TLS 1.2 or
+     * newer version.
      */
-    boolean isDTLSProtocol() {
-        return this.v == DTLS12.v || this.v == DTLS10.v;
+    boolean useTLS12PlusSpec() {
+        return isDTLS ? (this.id <= DTLS12.id) : (this.id >= TLS12.id);
+    }
+
+    /**
+     * Return true if this ProtocolVersion object is of
+     * TLS 1.1/DTLS 1.0 or newer version.
+     */
+    boolean useTLS11PlusSpec() {
+        return isDTLS ? true : (this.id >= TLS11.id);
+    }
+
+    /**
+     * Return true if this ProtocolVersion object is of TLS 1.0 or
+     * newer version.
+     */
+    boolean useTLS10PlusSpec() {
+        return isDTLS ? true : (this.id >= TLS10.id);
     }
 
     /**
-     * Returns true if a ProtocolVersion may represent a DTLS protocol.
+     * Return true if this ProtocolVersion object is of TLS 1.0 or
+     * newer version.
      */
-    boolean maybeDTLSProtocol() {
-        return (this.major & 0x80) != 0;
+    static boolean useTLS10PlusSpec(int id, boolean isDTLS) {
+        return isDTLS ? true : (id >= TLS10.id);
     }
 
-    boolean useTLS12PlusSpec() {
-        return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v);
-    }
-
-    boolean useTLS11PlusSpec() {
-        return maybeDTLSProtocol() ? true : (this.v >= TLS11.v);
-    }
-
-    boolean useTLS10PlusSpec() {
-        return maybeDTLSProtocol() ? true : (this.v >= TLS10.v);
+    /**
+     * Return true if this ProtocolVersion object is of (D)TLS 1.3 or
+     * newer version.
+     */
+    static boolean useTLS13PlusSpec(int id, boolean isDTLS) {
+        return isDTLS ? (id < DTLS12.id) : (id >= TLS13.id);
     }
 
-    boolean obsoletes(CipherSuite suite) {
-        ProtocolVersion proto = this;
-        if (proto.isDTLSProtocol()) {
-            // DTLS bans stream ciphers.
-            if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
-                return true;
-            }
-
-            proto = mapToTLSProtocol(this);
-        }
-
-        return (proto.v >= suite.obsoleted);
-    }
-
-    boolean supports(CipherSuite suite) {
-        ProtocolVersion proto = this;
-        if (proto.isDTLSProtocol()) {
-            // DTLS bans stream ciphers.
-            if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
-                return false;
-            }
-
-            proto = mapToTLSProtocol(this);
-        }
-
-        return (proto.v >= suite.supported);
-    }
-
-    // Map a specified protocol to the corresponding TLS version.
-    //
-    // DTLS 1.2 -> TLS 1.2
-    // DTLS 1.0 -> TLS 1.1
-    private static ProtocolVersion mapToTLSProtocol(
-            ProtocolVersion protocolVersion) {
-
-        if (protocolVersion.isDTLSProtocol()) {
-            if (protocolVersion.v == DTLS10.v) {
-                protocolVersion = TLS11;
-            } else {    // DTLS12
-                protocolVersion = TLS12;
+    /**
+     * Select the lower of that suggested protocol version and
+     * the highest of the listed protocol versions.
+     *
+     * @param listedVersions    the listed protocol version
+     * @param suggestedVersion  the suggested protocol version
+     */
+    static ProtocolVersion selectedFrom(
+            List<ProtocolVersion> listedVersions, int suggestedVersion) {
+        ProtocolVersion selectedVersion = ProtocolVersion.NONE;
+        for (ProtocolVersion pv : listedVersions) {
+            if (pv.id == suggestedVersion) {
+                return pv;
+            } else if (pv.isDTLS) {
+                if (pv.id > suggestedVersion && pv.id < selectedVersion.id) {
+                    selectedVersion = pv;
+                }
+            } else {
+                if (pv.id < suggestedVersion && pv.id > selectedVersion.id) {
+                    selectedVersion = pv;
+                }
             }
         }
 
-        return protocolVersion;
+        return selectedVersion;
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.net.ssl.SSLProtocolException;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "psk_key_exchange_modes" extensions.
+ */
+final class PskKeyExchangeModesExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new PskKeyExchangeModesProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new PskKeyExchangeModesConsumer();
+    static final HandshakeAbsence chOnLoadAbsence =
+            new PskKeyExchangeModesOnLoadAbsence();
+    static final HandshakeAbsence chOnTradeAbsence =
+            new PskKeyExchangeModesOnTradeAbsence();
+
+    static final SSLStringizer pkemStringizer =
+            new PskKeyExchangeModesStringizer();
+
+    enum PskKeyExchangeMode {
+        PSK_KE          ((byte)0, "psk_ke"),
+        PSK_DHE_KE      ((byte)1, "psk_dhe_ke");
+
+        final byte id;
+        final String name;
+
+        PskKeyExchangeMode(byte id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        static PskKeyExchangeMode valueOf(byte id) {
+            for(PskKeyExchangeMode pkem : values()) {
+                if (pkem.id == id) {
+                    return pkem;
+                }
+            }
+
+            return null;
+        }
+
+        static String nameOf(byte id) {
+            for (PskKeyExchangeMode pkem : PskKeyExchangeMode.values()) {
+                if (pkem.id == id) {
+                    return pkem.name;
+                }
+            }
+
+            return "<UNKNOWN PskKeyExchangeMode TYPE: " + (id & 0x0FF) + ">";
+        }
+    }
+
+    static final
+            class PskKeyExchangeModesSpec implements SSLExtensionSpec {
+        private static final PskKeyExchangeModesSpec DEFAULT =
+                new PskKeyExchangeModesSpec(new byte[] {
+                        PskKeyExchangeMode.PSK_DHE_KE.id});
+
+        final byte[] modes;
+
+        PskKeyExchangeModesSpec(byte[] modes) {
+            this.modes = modes;
+        }
+
+        PskKeyExchangeModesSpec(ByteBuffer m) throws IOException {
+            if (m.remaining() < 2) {
+                throw new SSLProtocolException(
+                    "Invalid psk_key_exchange_modes extension: " +
+                    "insufficient data");
+            }
+
+            this.modes = Record.getBytes8(m);
+        }
+
+        boolean contains(PskKeyExchangeMode mode) {
+            if (modes != null) {
+                for (byte m : modes) {
+                    if (mode.id == m) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"ke_modes\": '['{0}']'", Locale.ENGLISH);
+            if (modes == null || modes.length ==  0) {
+                Object[] messageFields = {
+                        "<no PSK key exchange modes specified>"
+                    };
+                return messageFormat.format(messageFields);
+            } else {
+                StringBuilder builder = new StringBuilder(64);
+                boolean isFirst = true;
+                for (byte mode : modes) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        builder.append(", ");
+                    }
+
+                    builder.append(PskKeyExchangeMode.nameOf(mode));
+                }
+
+                Object[] messageFields = {
+                        builder.toString()
+                    };
+
+                return messageFormat.format(messageFields);
+            }
+        }
+    }
+
+    private static final
+            class PskKeyExchangeModesStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new PskKeyExchangeModesSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data consumer of a "psk_key_exchange_modes" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class PskKeyExchangeModesConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private PskKeyExchangeModesConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(
+                    SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable psk_key_exchange_modes extension");
+                }
+
+                // No session resumption is allowed.
+                if (shc.isResumption && shc.resumingSession != null) {
+                    shc.isResumption = false;
+                    shc.resumingSession = null;
+                }
+
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            PskKeyExchangeModesSpec spec;
+            try {
+                spec = new PskKeyExchangeModesSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SSLExtension.PSK_KEY_EXCHANGE_MODES, spec);
+
+            // Impact on session resumption.
+            //
+            // Do the requested modes support session resumption?
+            if (shc.isResumption) {     // resumingSession may not be set
+                // Note: psk_dhe_ke is the only supported mode now.  If the
+                // psk_ke mode is supported in the future, may need an update
+                // here.
+                if (!spec.contains(PskKeyExchangeMode.PSK_DHE_KE)) {
+                    shc.isResumption = false;
+                    shc.resumingSession = null;
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                            "abort session resumption, " +
+                            "no supported psk_dhe_ke PSK key exchange mode");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "psk_key_exchange_modes" extension in the
+     * ClientHello handshake message.
+     */
+    private static final
+            class PskKeyExchangeModesProducer implements HandshakeProducer {
+
+        // Prevent instantiation of this class.
+        private PskKeyExchangeModesProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(
+                    SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                        "Ignore unavailable psk_key_exchange_modes extension");
+                }
+
+                return null;
+            }
+
+            byte[] extData = new byte[] {0x01, 0x01};   // psk_dhe_ke
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SSLExtension.PSK_KEY_EXCHANGE_MODES,
+                    PskKeyExchangeModesSpec.DEFAULT);
+
+            return extData;
+        }
+    }
+    /**
+     * The absence processing if a "psk_key_exchange_modes" extension is
+     * not present in the ClientHello handshake message.
+     */
+    private static final
+        class PskKeyExchangeModesOnLoadAbsence implements HandshakeAbsence {
+
+        // Prevent instantiation of this class.
+        private PskKeyExchangeModesOnLoadAbsence() {
+            // blank
+        }
+
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // No session resumptio is allowed.
+            if (shc.isResumption) {     // resumingSession may not be set
+                shc.isResumption = false;
+                shc.resumingSession = null;
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "abort session resumption, " +
+                            "no supported psk_dhe_ke PSK key exchange mode");
+                }
+            }
+        }
+    }
+
+    /**
+     * The absence processing if a "signature_algorithms" extension is
+     * not present in the ClientHello handshake message.
+     */
+    private static final
+        class PskKeyExchangeModesOnTradeAbsence implements HandshakeAbsence {
+
+        // Prevent instantiation of this class.
+        private PskKeyExchangeModesOnTradeAbsence() {
+            // blank
+        }
+
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // A client MUST provide a "psk_key_exchange_modes" extension if
+            // it offers a "pre_shared_key" extension.  If clients offer
+            // "pre_shared_key" without a "psk_key_exchange_modes" extension,
+            // servers MUST abort the handshake.
+            SSLExtensionSpec spec =
+                shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY);
+            if (spec == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "pre_shared_key key extension is offered " +
+                        "without a psk_key_exchange_modes extension");
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,247 +23,299 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
-import java.io.*;
-import java.security.*;
-
-import javax.crypto.*;
-
-import javax.net.ssl.*;
-
-import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
-import sun.security.util.KeyUtil;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.crypto.SecretKey;
+import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
+import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
+import sun.security.ssl.RSAKeyExchange.RSAPremasterSecret;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.HexDumpEncoder;
 
 /**
- * This is the client key exchange message (CLIENT --> SERVER) used with
- * all RSA key exchanges; it holds the RSA-encrypted pre-master secret.
- *
- * The message is encrypted using PKCS #1 block type 02 encryption with the
- * server's public key.  The padding and resulting message size is a function
- * of this server's public key modulus size, but the pre-master secret is
- * always exactly 48 bytes.
- *
+ * Pack of the "ClientKeyExchange" handshake message.
  */
-final class RSAClientKeyExchange extends HandshakeMessage {
+final class RSAClientKeyExchange {
+    static final SSLConsumer rsaHandshakeConsumer =
+        new RSAClientKeyExchangeConsumer();
+    static final HandshakeProducer rsaHandshakeProducer =
+        new RSAClientKeyExchangeProducer();
 
-    /*
-     * The following field values were encrypted with the server's public
-     * key (or temp key from server key exchange msg) and are presented
-     * here in DECRYPTED form.
-     */
-    private ProtocolVersion protocolVersion; // preMaster [0,1]
-    SecretKey preMaster;
-    private byte[] encrypted;           // same size as public modulus
-
-    /*
-     * Client randomly creates a pre-master secret and encrypts it
-     * using the server's RSA public key; only the server can decrypt
-     * it, using its RSA private key.  Result is the same size as the
-     * server's public key, and uses PKCS #1 block format 02.
+    /**
+     * The RSA ClientKeyExchange handshake message.
      */
-    @SuppressWarnings("deprecation")
-    RSAClientKeyExchange(ProtocolVersion protocolVersion,
-            ProtocolVersion maxVersion,
-            SecureRandom generator, PublicKey publicKey) throws IOException {
-        if (publicKey.getAlgorithm().equals("RSA") == false) {
-            throw new SSLKeyException("Public key not of type RSA: " +
-                publicKey.getAlgorithm());
+    private static final
+            class RSAClientKeyExchangeMessage extends HandshakeMessage {
+        final int protocolVersion;
+        final boolean useTLS10PlusSpec;
+        final byte[] encrypted;
+
+        RSAClientKeyExchangeMessage(HandshakeContext context,
+                RSAPremasterSecret premaster,
+                PublicKey publicKey) throws GeneralSecurityException {
+            super(context);
+            this.protocolVersion = context.clientHelloVersion;
+            this.encrypted = premaster.getEncoded(
+                    publicKey, context.sslContext.getSecureRandom());
+            this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
+                    protocolVersion, context.sslContext.isDTLS());
         }
-        this.protocolVersion = protocolVersion;
-
-        try {
-            String s = protocolVersion.useTLS12PlusSpec() ?
-                "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
-            KeyGenerator kg = JsseJce.getKeyGenerator(s);
-            kg.init(new TlsRsaPremasterSecretParameterSpec(
-                    maxVersion.v, protocolVersion.v), generator);
-            preMaster = kg.generateKey();
 
-            Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
-            cipher.init(Cipher.WRAP_MODE, publicKey, generator);
-            encrypted = cipher.wrap(preMaster);
-        } catch (GeneralSecurityException e) {
-            throw (SSLKeyException)new SSLKeyException
-                                ("RSA premaster secret error").initCause(e);
-        }
-    }
+        RSAClientKeyExchangeMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+
+            if (m.remaining() < 2) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "Invalid RSA ClientKeyExchange message: insufficient data");
+            }
 
-    /*
-     * Retrieving the cipher's provider name for the debug purposes
-     * can throw an exception by itself.
-     */
-    private static String safeProviderName(Cipher cipher) {
-        try {
-            return cipher.getProvider().toString();
-        } catch (Exception e) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Retrieving The Cipher provider name" +
-                        " caused exception " + e.getMessage());
+            this.protocolVersion = context.clientHelloVersion;
+            this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
+                    protocolVersion, context.sslContext.isDTLS());
+            if (useTLS10PlusSpec) {
+                this.encrypted = Record.getBytes16(m);
+            } else {    //  SSL 3.0
+                this.encrypted = new byte[m.remaining()];
+                m.get(encrypted);
             }
         }
-        try {
-            return cipher.toString() + " (provider name not available)";
-        } catch (Exception e) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Retrieving The Cipher name" +
-                        " caused exception " + e.getMessage());
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.CLIENT_KEY_EXCHANGE;
+        }
+
+        @Override
+        public int messageLength() {
+            if (useTLS10PlusSpec) {
+                return encrypted.length + 2;
+            } else {
+                return encrypted.length;
             }
         }
-        return "(cipher/provider names not available)";
-    }
 
-    /*
-     * Server gets the PKCS #1 (block format 02) data, decrypts
-     * it with its private key.
-     */
-    @SuppressWarnings("deprecation")
-    RSAClientKeyExchange(ProtocolVersion currentVersion,
-            ProtocolVersion maxVersion,
-            SecureRandom generator, HandshakeInStream input,
-            int messageSize, PrivateKey privateKey) throws IOException {
-
-        if (privateKey.getAlgorithm().equals("RSA") == false) {
-            throw new SSLKeyException("Private key not of type RSA: " +
-                 privateKey.getAlgorithm());
-        }
-
-        if (currentVersion.useTLS10PlusSpec()) {
-            encrypted = input.getBytes16();
-        } else {
-            encrypted = new byte [messageSize];
-            if (input.read(encrypted) != messageSize) {
-                throw new SSLProtocolException(
-                        "SSL: read PreMasterSecret: short read");
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            if (useTLS10PlusSpec) {
+                hos.putBytes16(encrypted);
+            } else {
+                hos.write(encrypted);
             }
         }
 
-        byte[] encoded = null;
-        try {
-            boolean needFailover = false;
-            Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
-            try {
-                // Try UNWRAP_MODE mode firstly.
-                cipher.init(Cipher.UNWRAP_MODE, privateKey,
-                        new TlsRsaPremasterSecretParameterSpec(
-                                maxVersion.v, currentVersion.v),
-                        generator);
-
-                // The provider selection can be delayed, please don't call
-                // any Cipher method before the call to Cipher.init().
-                needFailover = !KeyUtil.isOracleJCEProvider(
-                        cipher.getProvider().getName());
-            } catch (InvalidKeyException | UnsupportedOperationException iue) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("The Cipher provider "
-                            + safeProviderName(cipher)
-                            + " caused exception: " + iue.getMessage());
-                }
-
-                needFailover = true;
-            }
-
-            if (needFailover) {
-                // The cipher might be spoiled by unsuccessful call to init(),
-                // so request a fresh instance
-                cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"RSA ClientKeyExchange\": '{'\n" +
+                "  \"client_version\":  {0}\n" +
+                "  \"encncrypted\": '{'\n" +
+                "{1}\n" +
+                "  '}'\n" +
+                "'}'",
+                Locale.ENGLISH);
 
-                // Use DECRYPT_MODE and dispose the previous initialization.
-                cipher.init(Cipher.DECRYPT_MODE, privateKey);
-                boolean failed = false;
-                try {
-                    encoded = cipher.doFinal(encrypted);
-                } catch (BadPaddingException bpe) {
-                    // Note: encoded == null
-                    failed = true;
-                }
-                encoded = KeyUtil.checkTlsPreMasterSecretKey(
-                                maxVersion.v, currentVersion.v,
-                                generator, encoded, failed);
-                preMaster = generatePreMasterSecret(
-                                maxVersion.v, currentVersion.v,
-                                encoded, generator);
-            } else {
-                // the cipher should have been initialized
-                preMaster = (SecretKey)cipher.unwrap(encrypted,
-                        "TlsRsaPremasterSecret", Cipher.SECRET_KEY);
-            }
-        } catch (InvalidKeyException ibk) {
-            // the message is too big to process with RSA
-            throw new SSLException(
-                "Unable to process PreMasterSecret", ibk);
-        } catch (Exception e) {
-            // unlikely to happen, otherwise, must be a provider exception
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("RSA premaster secret decryption error:");
-                e.printStackTrace(System.out);
-            }
-            throw new RuntimeException("Could not generate dummy secret", e);
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                ProtocolVersion.nameOf(protocolVersion),
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(encrypted), "    "),
+            };
+            return messageFormat.format(messageFields);
         }
     }
 
-    // generate a premaster secret with the specified version number
-    @SuppressWarnings("deprecation")
-    private static SecretKey generatePreMasterSecret(
-            int clientVersion, int serverVersion,
-            byte[] encodedSecret, SecureRandom generator) {
-
-        if (debug != null && Debug.isOn("handshake")) {
-            System.out.println("Generating a premaster secret");
+    /**
+     * The RSA "ClientKeyExchange" handshake message producer.
+     */
+    private static final
+            class RSAClientKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private RSAClientKeyExchangeProducer() {
+            // blank
         }
 
-        try {
-            String s = ((clientVersion >= ProtocolVersion.TLS12.v) ?
-                "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
-            KeyGenerator kg = JsseJce.getKeyGenerator(s);
-            kg.init(new TlsRsaPremasterSecretParameterSpec(
-                    clientVersion, serverVersion, encodedSecret),
-                    generator);
-            return kg.generateKey();
-        } catch (InvalidAlgorithmParameterException |
-                NoSuchAlgorithmException iae) {
-            // unlikely to happen, otherwise, must be a provider exception
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("RSA premaster secret generation error:");
-                iae.printStackTrace(System.out);
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // This happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            EphemeralRSACredentials rsaCredentials = null;
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials credential : chc.handshakeCredentials) {
+                if (credential instanceof EphemeralRSACredentials) {
+                    rsaCredentials = (EphemeralRSACredentials)credential;
+                    if (x509Credentials != null) {
+                        break;
+                    }
+                } else if (credential instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)credential;
+                    if (rsaCredentials != null) {
+                        break;
+                    }
+                }
+            }
+
+            if (rsaCredentials == null && x509Credentials == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No RSA credentials negotiated for client key exchange");
+            }
+
+            PublicKey publicKey = (rsaCredentials != null) ?
+                    rsaCredentials.popPublicKey : x509Credentials.popPublicKey;
+            if (!publicKey.getAlgorithm().equals("RSA")) {      // unlikely
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Not RSA public key for client key exchange");
             }
-            throw new RuntimeException("Could not generate premaster secret", iae);
+
+            RSAPremasterSecret premaster;
+            RSAClientKeyExchangeMessage ckem;
+            try {
+                premaster = RSAPremasterSecret.createPremasterSecret(chc);
+                chc.handshakePossessions.add(premaster);
+                ckem = new RSAClientKeyExchangeMessage(
+                        chc, premaster, publicKey);
+            } catch (GeneralSecurityException gse) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Cannot generate RSA premaster secret", gse);
+
+                return null;    // make the compiler happy
+            }
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced RSA ClientKeyExchange handshake message", ckem);
+            }
+
+            // Output the handshake message.
+            ckem.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // update the states
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    chc.negotiatedCipherSuite.keyExchange,
+                    chc.negotiatedProtocol);
+            if (ke == null) {   // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            } else {
+                SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
+                SecretKey masterSecret =
+                        masterKD.deriveKey("MasterSecret", null);
+
+                // update the states
+                chc.handshakeSession.setMasterSecret(masterSecret);
+                SSLTrafficKeyDerivation kd =
+                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+                if (kd == null) {   // unlikely
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                            chc.negotiatedProtocol);
+                } else {
+                    chc.handshakeKeyDerivation =
+                        kd.createKeyDerivation(chc, masterSecret);
+                }
+            }
+
+            // The handshake message has been delivered.
+            return null;
         }
     }
 
-    @Override
-    int messageType() {
-        return ht_client_key_exchange;
-    }
+    /**
+     * The RSA "ClientKeyExchange" handshake message consumer.
+     */
+    private static final
+            class RSAClientKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private RSAClientKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            EphemeralRSAPossession rsaPossession = null;
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof EphemeralRSAPossession) {
+                    rsaPossession = (EphemeralRSAPossession)possession;
+                    break;
+                } else if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    if (rsaPossession != null) {
+                        break;
+                    }
+                }
+            }
+
+            if (rsaPossession == null && x509Possession == null) {  // unlikely
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No RSA possessions negotiated for client key exchange");
+            }
+
+            PrivateKey privateKey = (rsaPossession != null) ?
+                    rsaPossession.popPrivateKey : x509Possession.popPrivateKey;
+            if (!privateKey.getAlgorithm().equals("RSA")) {     // unlikely
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Not RSA private key for client key exchange");
+            }
 
-    @Override
-    int messageLength() {
-        if (protocolVersion.useTLS10PlusSpec()) {
-            return encrypted.length + 2;
-        } else {
-            return encrypted.length;
+            RSAClientKeyExchangeMessage ckem =
+                    new RSAClientKeyExchangeMessage(shc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming RSA ClientKeyExchange handshake message", ckem);
+            }
+
+            // create the credentials
+            RSAPremasterSecret premaster;
+            try {
+                premaster =
+                    RSAPremasterSecret.decode(shc, privateKey, ckem.encrypted);
+                shc.handshakeCredentials.add(premaster);
+            } catch (GeneralSecurityException gse) {
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Cannot decode RSA premaster secret", gse);
+            }
+
+            // update the states
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    shc.negotiatedCipherSuite.keyExchange,
+                    shc.negotiatedProtocol);
+            if (ke == null) {   // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            } else {
+                SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
+                SecretKey masterSecret =
+                        masterKD.deriveKey("MasterSecret", null);
+
+                // update the states
+                shc.handshakeSession.setMasterSecret(masterSecret);
+                SSLTrafficKeyDerivation kd =
+                        SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+                if (kd == null) {       // unlikely
+                    shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                            shc.negotiatedProtocol);
+                } else {
+                    shc.handshakeKeyDerivation =
+                        kd.createKeyDerivation(shc, masterSecret);
+                }
+            }
         }
     }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        if (protocolVersion.useTLS10PlusSpec()) {
-            s.putBytes16(encrypted);
-        } else {
-            s.write(encrypted);
-        }
-    }
-
-    @Override
-    void print(PrintStream s) throws IOException {
-        String version = "version not available/extractable";
-
-        byte[] ba = preMaster.getEncoded();
-        if (ba != null && ba.length >= 2) {
-            version = ProtocolVersion.valueOf(ba[0], ba[1]).name;
-        }
-
-        s.println("*** ClientKeyExchange, RSA PreMasterSecret, " + version);
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
+import sun.security.util.KeyUtil;
+
+final class RSAKeyExchange {
+    static final SSLPossessionGenerator poGenerator =
+            new EphemeralRSAPossessionGenerator();
+    static final SSLKeyAgreementGenerator kaGenerator =
+            new RSAKAGenerator();
+
+    static final class EphemeralRSAPossession implements SSLPossession {
+        // Proof of possession of the private key corresponding to the public
+        // key for which a certificate is being provided for authentication.
+        final RSAPublicKey        popPublicKey;
+        final PrivateKey          popPrivateKey;
+
+        EphemeralRSAPossession(PrivateKey popPrivateKey,
+                RSAPublicKey popPublicKey) {
+            this.popPublicKey = popPublicKey;
+            this.popPrivateKey = popPrivateKey;
+        }
+    }
+
+    static final class EphemeralRSACredentials implements SSLCredentials {
+        final RSAPublicKey popPublicKey;
+
+        EphemeralRSACredentials(RSAPublicKey popPublicKey) {
+            this.popPublicKey = popPublicKey;
+        }
+    }
+
+    private static final class EphemeralRSAPossessionGenerator
+            implements SSLPossessionGenerator {
+        // Prevent instantiation of this class.
+        private EphemeralRSAPossessionGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLPossession createPossession(HandshakeContext context) {
+            try {
+                EphemeralKeyManager ekm =
+                        context.sslContext.getEphemeralKeyManager();
+                KeyPair kp = ekm.getRSAKeyPair(
+                        true, context.sslContext.getSecureRandom());
+                if (kp != null) {
+                    return new EphemeralRSAPossession(
+                            kp.getPrivate(), (RSAPublicKey)kp.getPublic());
+                } else {
+                    // Could not generate the ephemeral key, ignore.
+                    return null;
+                }
+            } catch (RuntimeException rte) {
+                // Could not determine keylength, ignore.
+                return null;
+            }
+        }
+    }
+
+    static final
+            class RSAPremasterSecret implements SSLPossession, SSLCredentials {
+        final SecretKey premasterSecret;
+
+        RSAPremasterSecret(SecretKey premasterSecret) {
+            this.premasterSecret = premasterSecret;
+        }
+
+        byte[] getEncoded(PublicKey publicKey,
+                SecureRandom secureRandom) throws GeneralSecurityException {
+            Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
+            cipher.init(Cipher.WRAP_MODE, publicKey, secureRandom);
+            return cipher.wrap(premasterSecret);
+        }
+
+        @SuppressWarnings("deprecation")
+        static RSAPremasterSecret createPremasterSecret(
+                ClientHandshakeContext chc) throws GeneralSecurityException {
+            String algorithm = chc.negotiatedProtocol.useTLS12PlusSpec() ?
+                    "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
+            KeyGenerator kg = JsseJce.getKeyGenerator(algorithm);
+            TlsRsaPremasterSecretParameterSpec spec =
+                    new TlsRsaPremasterSecretParameterSpec(
+                            chc.clientHelloVersion,
+                            chc.negotiatedProtocol.id);
+            kg.init(spec, chc.sslContext.getSecureRandom());
+
+            return new RSAPremasterSecret(kg.generateKey());
+        }
+
+        @SuppressWarnings("deprecation")
+        static RSAPremasterSecret decode(ServerHandshakeContext shc,
+                PrivateKey privateKey,
+                byte[] encrypted) throws GeneralSecurityException {
+
+            byte[] encoded = null;
+            boolean needFailover = false;
+            Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
+            try {
+                // Try UNWRAP_MODE mode firstly.
+                cipher.init(Cipher.UNWRAP_MODE, privateKey,
+                        new TlsRsaPremasterSecretParameterSpec(
+                                shc.clientHelloVersion,
+                                shc.negotiatedProtocol.id),
+                                shc.sslContext.getSecureRandom());
+
+                // The provider selection can be delayed, please don't call
+                // any Cipher method before the call to Cipher.init().
+                needFailover = !KeyUtil.isOracleJCEProvider(
+                        cipher.getProvider().getName());
+            } catch (InvalidKeyException | UnsupportedOperationException iue) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("The Cipher provider "
+                            + safeProviderName(cipher)
+                            + " caused exception: " + iue.getMessage());
+                }
+
+                needFailover = true;
+            }
+
+            SecretKey preMaster;
+            if (needFailover) {
+                // The cipher might be spoiled by unsuccessful call to init(),
+                // so request a fresh instance
+                cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
+
+                // Use DECRYPT_MODE and dispose the previous initialization.
+                cipher.init(Cipher.DECRYPT_MODE, privateKey);
+                boolean failed = false;
+                try {
+                    encoded = cipher.doFinal(encrypted);
+                } catch (BadPaddingException bpe) {
+                    // Note: encoded == null
+                    failed = true;
+                }
+                encoded = KeyUtil.checkTlsPreMasterSecretKey(
+                        shc.clientHelloVersion, shc.negotiatedProtocol.id,
+                        shc.sslContext.getSecureRandom(), encoded, failed);
+                preMaster = generatePremasterSecret(
+                        shc.clientHelloVersion, shc.negotiatedProtocol.id,
+                        encoded, shc.sslContext.getSecureRandom());
+            } else {
+                // the cipher should have been initialized
+                preMaster = (SecretKey)cipher.unwrap(encrypted,
+                        "TlsRsaPremasterSecret", Cipher.SECRET_KEY);
+            }
+
+            return new RSAPremasterSecret(preMaster);
+        }
+
+        /*
+         * Retrieving the cipher's provider name for the debug purposes
+         * can throw an exception by itself.
+         */
+        private static String safeProviderName(Cipher cipher) {
+            try {
+                return cipher.getProvider().toString();
+            } catch (Exception e) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Retrieving The Cipher provider name" +
+                            " caused exception ", e);
+                }
+            }
+            try {
+                return cipher.toString() + " (provider name not available)";
+            } catch (Exception e) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Retrieving The Cipher name" +
+                            " caused exception ", e);
+                }
+            }
+
+            return "(cipher/provider names not available)";
+        }
+
+        // generate a premaster secret with the specified version number
+        @SuppressWarnings("deprecation")
+        private static SecretKey generatePremasterSecret(
+                int clientVersion, int serverVersion, byte[] encodedSecret,
+                SecureRandom generator) throws GeneralSecurityException {
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Generating a premaster secret");
+            }
+
+            try {
+                String s = ((clientVersion >= ProtocolVersion.TLS12.id) ?
+                    "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
+                KeyGenerator kg = JsseJce.getKeyGenerator(s);
+                kg.init(new TlsRsaPremasterSecretParameterSpec(
+                        clientVersion, serverVersion, encodedSecret),
+                        generator);
+                return kg.generateKey();
+            } catch (InvalidAlgorithmParameterException |
+                    NoSuchAlgorithmException iae) {
+                // unlikely to happen, otherwise, must be a provider exception
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("RSA premaster secret generation error:");
+                    iae.printStackTrace(System.out);
+                }
+
+                throw new GeneralSecurityException(
+                        "Could not generate premaster secret", iae);
+            }
+        }
+    }
+
+    private static final
+            class RSAKAGenerator implements SSLKeyAgreementGenerator {
+        // Prevent instantiation of this class.
+        private RSAKAGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+            RSAPremasterSecret premaster = null;
+            if (context instanceof ClientHandshakeContext) {
+                for (SSLPossession possession : context.handshakePossessions) {
+                    if (possession instanceof RSAPremasterSecret) {
+                        premaster = (RSAPremasterSecret)possession;
+                        break;
+                    }
+                }
+            } else {
+                for (SSLCredentials credential : context.handshakeCredentials) {
+                    if (credential instanceof RSAPremasterSecret) {
+                        premaster = (RSAPremasterSecret)credential;
+                        break;
+                    }
+                }
+            }
+
+            if (premaster == null) {
+                context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No sufficient RSA key agreement parameters negotiated");
+            }
+
+            return new RSAKAKeyDerivation(context, premaster.premasterSecret);
+        }
+
+        private static final
+                class RSAKAKeyDerivation implements SSLKeyDerivation {
+            private final HandshakeContext context;
+            private final SecretKey preMasterSecret;
+
+            RSAKAKeyDerivation(
+                    HandshakeContext context, SecretKey preMasterSecret) {
+                this.context = context;
+                this.preMasterSecret = preMasterSecret;
+            }
+
+            @Override
+            public SecretKey deriveKey(String algorithm,
+                    AlgorithmParameterSpec params) throws IOException {
+                SSLMasterKeyDerivation mskd =
+                        SSLMasterKeyDerivation.valueOf(
+                                context.negotiatedProtocol);
+                if (mskd == null) {
+                    // unlikely
+                    throw new SSLHandshakeException(
+                            "No expected master key derivation for protocol: " +
+                            context.negotiatedProtocol.name);
+                }
+                SSLKeyDerivation kd = mskd.createKeyDerivation(
+                        context, preMasterSecret);
+                return kd.deriveKey("MasterSecret", params);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.CryptoPrimitive;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPublicKeySpec;
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Locale;
+import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
+import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.X509Authentication.X509Credentials;
+import sun.security.ssl.X509Authentication.X509Possession;
+import sun.security.util.HexDumpEncoder;
+
+/**
+ * Pack of the ServerKeyExchange handshake message.
+ */
+final class RSAServerKeyExchange {
+    static final SSLConsumer rsaHandshakeConsumer =
+        new RSAServerKeyExchangeConsumer();
+    static final HandshakeProducer rsaHandshakeProducer =
+        new RSAServerKeyExchangeProducer();
+
+    /**
+     * The ephemeral RSA ServerKeyExchange handshake message.
+     *
+     * Used for RSA_EXPORT, SSL 3.0 and TLS 1.0 only.
+     */
+    private static final
+            class RSAServerKeyExchangeMessage extends HandshakeMessage {
+        // public key encapsulated in this message
+        private final byte[] modulus;     // 1 to 2^16 - 1 bytes
+        private final byte[] exponent;    // 1 to 2^16 - 1 bytes
+
+        // signature bytes, none-null as no anonymous RSA key exchange.
+        private final byte[] paramsSignature;
+
+        private RSAServerKeyExchangeMessage(HandshakeContext handshakeContext,
+                X509Possession x509Possession,
+                EphemeralRSAPossession rsaPossession) throws IOException {
+            super(handshakeContext);
+
+            // This happens in server side only.
+            ServerHandshakeContext shc =
+                    (ServerHandshakeContext)handshakeContext;
+
+            RSAPublicKey publicKey = rsaPossession.popPublicKey;
+            RSAPublicKeySpec spec = JsseJce.getRSAPublicKeySpec(publicKey);
+            this.modulus = Utilities.toByteArray(spec.getModulus());
+            this.exponent = Utilities.toByteArray(spec.getPublicExponent());
+            byte[] signature = null;
+            try {
+                Signature signer = RSASignature.getInstance();
+                signer.initSign(x509Possession.popPrivateKey,
+                        shc.sslContext.getSecureRandom());
+                updateSignature(signer,
+                          shc.clientHelloRandom.randomBytes,
+                          shc.serverHelloRandom.randomBytes);
+                signature = signer.sign();
+            } catch (NoSuchAlgorithmException |
+                    InvalidKeyException | SignatureException ex) {
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Failed to sign ephemeral RSA parameters", ex);
+            }
+
+            this.paramsSignature = signature;
+        }
+
+        RSAServerKeyExchangeMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+
+            // This happens in client side only.
+            ClientHandshakeContext chc =
+                    (ClientHandshakeContext)handshakeContext;
+
+            this.modulus = Record.getBytes16(m);
+            this.exponent = Record.getBytes16(m);
+            this.paramsSignature = Record.getBytes16(m);
+
+            X509Credentials x509Credentials = null;
+            for (SSLCredentials cd : chc.handshakeCredentials) {
+                if (cd instanceof X509Credentials) {
+                    x509Credentials = (X509Credentials)cd;
+                    break;
+                }
+            }
+
+            if (x509Credentials == null) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No RSA credentials negotiated for server key exchange");
+            }
+
+            try {
+                Signature signer = RSASignature.getInstance();
+                signer.initVerify(x509Credentials.popPublicKey);
+                updateSignature(signer,
+                          chc.clientHelloRandom.randomBytes,
+                          chc.serverHelloRandom.randomBytes);
+                if (!signer.verify(paramsSignature)) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid signature of RSA ServerKeyExchange message");
+                }
+            } catch (NoSuchAlgorithmException |
+                    InvalidKeyException | SignatureException ex) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "Failed to sign ephemeral RSA parameters", ex);
+            }
+        }
+
+        @Override
+        SSLHandshake handshakeType() {
+            return SSLHandshake.SERVER_KEY_EXCHANGE;
+        }
+
+        @Override
+        int messageLength() {
+            return 6 + modulus.length + exponent.length
+                   + paramsSignature.length;
+        }
+
+        @Override
+        void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes16(modulus);
+            hos.putBytes16(exponent);
+            hos.putBytes16(paramsSignature);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"RSA ServerKeyExchange\": '{'\n" +
+                "  \"parameters\": '{'\n" +
+                "    \"rsa_modulus\": '{'\n" +
+                "{0}\n" +
+                "    '}',\n" +
+                "    \"rsa_exponent\": '{'\n" +
+                "{1}\n" +
+                "    '}'\n" +
+                "  '}',\n" +
+                "  \"digital signature\":  '{'\n" +
+                "    \"signature\": '{'\n" +
+                "{2}\n" +
+                "    '}',\n" +
+                "  '}'\n" +
+                "'}'",
+                Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(modulus), "      "),
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(exponent), "      "),
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(paramsSignature), "      ")
+            };
+            return messageFormat.format(messageFields);
+        }
+
+        /*
+         * Hash the nonces and the ephemeral RSA public key.
+         */
+        private void updateSignature(Signature signature,
+                byte[] clntNonce, byte[] svrNonce) throws SignatureException {
+            signature.update(clntNonce);
+            signature.update(svrNonce);
+
+            signature.update((byte)(modulus.length >> 8));
+            signature.update((byte)(modulus.length & 0x0ff));
+            signature.update(modulus);
+
+            signature.update((byte)(exponent.length >> 8));
+            signature.update((byte)(exponent.length & 0x0ff));
+            signature.update(exponent);
+        }
+    }
+
+    /**
+     * The RSA "ServerKeyExchange" handshake message producer.
+     */
+    private static final
+            class RSAServerKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private RSAServerKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            EphemeralRSAPossession rsaPossession = null;
+            X509Possession x509Possession = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof EphemeralRSAPossession) {
+                    rsaPossession = (EphemeralRSAPossession)possession;
+                    if (x509Possession != null) {
+                        break;
+                    }
+                } else if (possession instanceof X509Possession) {
+                    x509Possession = (X509Possession)possession;
+                    if (rsaPossession != null) {
+                        break;
+                    }
+                }
+            }
+
+            if (rsaPossession == null) {
+                // The X.509 certificate itself should be used for RSA_EXPORT
+                // key exchange.  The ServerKeyExchange handshake message is
+                // not needed.
+                return null;
+            } else if (x509Possession == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No RSA certificate negotiated for server key exchange");
+            } else if (!"RSA".equals(
+                    x509Possession.popPrivateKey.getAlgorithm())) {
+                // unlikely
+                shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "No X.509 possession can be used for " +
+                        "ephemeral RSA ServerKeyExchange");
+            }
+
+            RSAServerKeyExchangeMessage skem =
+                    new RSAServerKeyExchangeMessage(
+                            shc, x509Possession, rsaPossession);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced RSA ServerKeyExchange handshake message", skem);
+            }
+
+            // Output the handshake message.
+            skem.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The RSA "ServerKeyExchange" handshake message consumer.
+     */
+    private static final
+            class RSAServerKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private RSAServerKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            RSAServerKeyExchangeMessage skem =
+                    new RSAServerKeyExchangeMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming RSA ServerKeyExchange handshake message", skem);
+            }
+
+            //
+            // validate
+            //
+            // check constraints of RSA PublicKey
+            RSAPublicKey publicKey;
+            try {
+                KeyFactory kf = JsseJce.getKeyFactory("RSA");
+                RSAPublicKeySpec spec = new RSAPublicKeySpec(
+                    new BigInteger(1, skem.modulus),
+                    new BigInteger(1, skem.exponent));
+                publicKey = (RSAPublicKey)kf.generatePublic(spec);
+            } catch (GeneralSecurityException gse) {
+                chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                        "Could not generate RSAPublicKey", gse);
+
+                return;     // make the compiler happy
+            }
+
+            if (!chc.algorithmConstraints.permits(
+                    EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
+                chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
+                        "RSA ServerKeyExchange does not comply to " +
+                        "algorithm constraints");
+            }
+
+            //
+            // update
+            //
+            chc.handshakeCredentials.add(new EphemeralRSACredentials(publicKey));
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/RSASignature.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/RSASignature.java	Mon Jun 25 13:41:39 2018 -0700
@@ -23,7 +23,6 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.security.*;
@@ -46,71 +45,38 @@
  * getInternalInstance() method.
  *
  * This class is not thread safe.
- *
  */
 public final class RSASignature extends SignatureSpi {
-
     private final Signature rawRsa;
-    private MessageDigest md5, sha;
-
-    // flag indicating if the MessageDigests are in reset state
-    private boolean isReset;
+    private final MessageDigest mdMD5;
+    private final MessageDigest mdSHA;
 
     public RSASignature() throws NoSuchAlgorithmException {
         super();
         rawRsa = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA);
-        isReset = true;
+        this.mdMD5 = JsseJce.getMessageDigest("MD5");
+        this.mdSHA = JsseJce.getMessageDigest("SHA");
     }
 
     /**
-     * Get an implementation for the RSA signature. Follows the standard
-     * JCA getInstance() model, so it return the implementation from the
-     * provider with the highest precedence, which may be this class.
+     * Get an implementation for the RSA signature.
+     *
+     * Follows the standard JCA getInstance() model, so it return the
+     * implementation from the  provider with the highest precedence,
+     * which may be this class.
      */
     static Signature getInstance() throws NoSuchAlgorithmException {
         return JsseJce.getSignature(JsseJce.SIGNATURE_SSLRSA);
     }
 
-    /**
-     * Get an internal implementation for the RSA signature. Used for RSA
-     * client authentication, which needs the ability to set the digests
-     * to externally provided values via the setHashes() method.
-     */
-    static Signature getInternalInstance()
-            throws NoSuchAlgorithmException, NoSuchProviderException {
-        return Signature.getInstance(JsseJce.SIGNATURE_SSLRSA, "SunJSSE");
-    }
-
-    /**
-     * Set the MD5 and SHA hashes to the provided objects.
-     */
-    @SuppressWarnings("deprecation")
-    static void setHashes(Signature sig, MessageDigest md5, MessageDigest sha) {
-        sig.setParameter("hashes", new MessageDigest[] {md5, sha});
-    }
-
-    /**
-     * Reset the MessageDigests unless they are already reset.
-     */
-    private void reset() {
-        if (isReset == false) {
-            md5.reset();
-            sha.reset();
-            isReset = true;
-        }
-    }
-
-    private static void checkNull(Key key) throws InvalidKeyException {
-        if (key == null) {
-            throw new InvalidKeyException("Key must not be null");
-        }
-    }
-
     @Override
     protected void engineInitVerify(PublicKey publicKey)
             throws InvalidKeyException {
-        checkNull(publicKey);
-        reset();
+        if (publicKey == null) {
+            throw new InvalidKeyException("Public key must not be null");
+        }
+        mdMD5.reset();
+        mdSHA.reset();
         rawRsa.initVerify(publicKey);
     }
 
@@ -123,42 +89,31 @@
     @Override
     protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
             throws InvalidKeyException {
-        checkNull(privateKey);
-        reset();
+        if (privateKey == null) {
+            throw new InvalidKeyException("Private key must not be null");
+        }
+        mdMD5.reset();
+        mdSHA.reset();
         rawRsa.initSign(privateKey, random);
     }
 
-    // lazily initialize the MessageDigests
-    private void initDigests() {
-        if (md5 == null) {
-            md5 = JsseJce.getMD5();
-            sha = JsseJce.getSHA();
-        }
-    }
-
     @Override
     protected void engineUpdate(byte b) {
-        initDigests();
-        isReset = false;
-        md5.update(b);
-        sha.update(b);
+        mdMD5.update(b);
+        mdSHA.update(b);
     }
 
     @Override
     protected void engineUpdate(byte[] b, int off, int len) {
-        initDigests();
-        isReset = false;
-        md5.update(b, off, len);
-        sha.update(b, off, len);
+        mdMD5.update(b, off, len);
+        mdSHA.update(b, off, len);
     }
 
     private byte[] getDigest() throws SignatureException {
         try {
-            initDigests();
             byte[] data = new byte[36];
-            md5.digest(data, 0, 16);
-            sha.digest(data, 16, 20);
-            isReset = true;
+            mdMD5.digest(data, 0, 16);
+            mdSHA.digest(data, 16, 20);
             return data;
         } catch (DigestException e) {
             // should never occur
@@ -186,19 +141,9 @@
 
     @Override
     @SuppressWarnings("deprecation")
-    protected void engineSetParameter(String param, Object value)
-            throws InvalidParameterException {
-        if (param.equals("hashes") == false) {
-            throw new InvalidParameterException
-                ("Parameter not supported: " + param);
-        }
-        if (value instanceof MessageDigest[] == false) {
-            throw new InvalidParameterException
-                ("value must be MessageDigest[]");
-        }
-        MessageDigest[] digests = (MessageDigest[])value;
-        md5 = digests[0];
-        sha = digests[1];
+    protected void engineSetParameter(String param,
+            Object value) throws InvalidParameterException {
+        throw new InvalidParameterException("Parameters not supported");
     }
 
     @Override
@@ -211,8 +156,8 @@
 
     @Override
     @SuppressWarnings("deprecation")
-    protected Object engineGetParameter(String param)
-            throws InvalidParameterException {
+    protected Object engineGetParameter(
+            String param) throws InvalidParameterException {
         throw new InvalidParameterException("Parameters not supported");
     }
 
--- a/src/java.base/share/classes/sun/security/ssl/RandomCookie.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/RandomCookie.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,11 +23,12 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.*;
+import java.nio.ByteBuffer;
 import java.security.SecureRandom;
+import java.util.Arrays;
 
 /*
  * RandomCookie ... SSL hands standard format random cookies (nonces)
@@ -37,33 +38,102 @@
  * @author David Brownell
  */
 final class RandomCookie {
+    final byte[] randomBytes = new byte[32];   // exactly 32 bytes
 
-    byte[] random_bytes;  // exactly 32 bytes
+    private static final byte[] hrrRandomBytes = new byte[] {
+            (byte)0xCF, (byte)0x21, (byte)0xAD, (byte)0x74,
+            (byte)0xE5, (byte)0x9A, (byte)0x61, (byte)0x11,
+            (byte)0xBE, (byte)0x1D, (byte)0x8C, (byte)0x02,
+            (byte)0x1E, (byte)0x65, (byte)0xB8, (byte)0x91,
+            (byte)0xC2, (byte)0xA2, (byte)0x11, (byte)0x16,
+            (byte)0x7A, (byte)0xBB, (byte)0x8C, (byte)0x5E,
+            (byte)0x07, (byte)0x9E, (byte)0x09, (byte)0xE2,
+            (byte)0xC8, (byte)0xA8, (byte)0x33, (byte)0x9C
+        };
+
+    private static final byte[] t12Protection = new byte[] {
+            (byte)0x44, (byte)0x4F, (byte)0x57, (byte)0x4E,
+            (byte)0x47, (byte)0x52, (byte)0x44, (byte)0x01
+        };
+
+    private static final byte[] t11Protection = new byte[] {
+            (byte)0x44, (byte)0x4F, (byte)0x57, (byte)0x4E,
+            (byte)0x47, (byte)0x52, (byte)0x44, (byte)0x00
+        };
+
+    static final RandomCookie hrrRandom = new RandomCookie(hrrRandomBytes);
 
     RandomCookie(SecureRandom generator) {
-        random_bytes = new byte[32];
-        generator.nextBytes(random_bytes);
-    }
-
-    RandomCookie(HandshakeInStream m) throws IOException {
-        random_bytes = new byte[32];
-        m.read(random_bytes, 0, 32);
+        generator.nextBytes(randomBytes);
     }
 
-    void send(HandshakeOutStream out) throws IOException {
-        out.write(random_bytes, 0, 32);
+    // Used for server random generation with version downgrade protection.
+    RandomCookie(HandshakeContext context) {
+        SecureRandom generator = context.sslContext.getSecureRandom();
+        generator.nextBytes(randomBytes);
+
+        // TLS 1.3 has a downgrade protection mechanism embedded in the
+        // server's random value.  TLS 1.3 servers which negotiate TLS 1.2
+        // or below in response to a ClientHello MUST set the last eight
+        // bytes of their Random value specially.
+        byte[] protection = null;
+        if (context.maximumActiveProtocol.useTLS13PlusSpec()) {
+            if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
+                if (context.negotiatedProtocol.useTLS12PlusSpec()) {
+                    protection = t12Protection;
+                } else {
+                    protection = t11Protection;
+                }
+            }
+        } else if (context.maximumActiveProtocol.useTLS12PlusSpec()) {
+            if (!context.negotiatedProtocol.useTLS12PlusSpec()) {
+                protection = t11Protection;
+            }
+        }
+
+        if (protection != null) {
+            System.arraycopy(protection, 0, randomBytes,
+                    randomBytes.length - protection.length, protection.length);
+        }
+    }
+
+    RandomCookie(ByteBuffer m) throws IOException {
+        m.get(randomBytes);
     }
 
-    void print(PrintStream s) {
-        s.print("random_bytes = {");
-        for (int i = 0; i < 32; i++) {
-            int k = random_bytes[i] & 0xFF;
-            if (i != 0) {
-                s.print(' ');
+    private RandomCookie(byte[] randomBytes) {
+        System.arraycopy(randomBytes, 0, this.randomBytes, 0, 32);
+    }
+
+    @Override
+    public String toString() {
+        return "random_bytes = {" + Utilities.toHexString(randomBytes) + "}";
+    }
+
+    boolean isHelloRetryRequest() {
+        return Arrays.equals(hrrRandomBytes, randomBytes);
+    }
+
+    // Used for client random validation of version downgrade protection.
+    boolean isVersionDowngrade(HandshakeContext context) {
+        if (context.maximumActiveProtocol.useTLS13PlusSpec()) {
+            if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
+                return isT12Downgrade() || isT11Downgrade();
             }
-            s.print(Utilities.hexDigits[k >>> 4]);
-            s.print(Utilities.hexDigits[k & 0xf]);
+        } else if (context.maximumActiveProtocol.useTLS12PlusSpec()) {
+            if (!context.negotiatedProtocol.useTLS12PlusSpec()) {
+                return isT11Downgrade();
+            }
         }
-        s.println("}");
+
+        return false;
+    }
+
+    private boolean isT12Downgrade() {
+        return Arrays.equals(randomBytes, 24, 32, t12Protection, 0, 8);
+    }
+
+    private boolean isT11Downgrade() {
+        return Arrays.equals(randomBytes, 24, 32, t11Protection, 0, 8);
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/Record.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/Record.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,27 +25,19 @@
 
 package sun.security.ssl;
 
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLException;
+
 /**
- * SSL/TLS/DTLS records, as pulled off (and put onto) a TCP stream.  This is
- * the base interface, which defines common information and interfaces
+ * SSL/(D)TLS record.
+ *
+ * This is the base interface, which defines common information and interfaces
  * used by both Input and Output records.
  *
  * @author David Brownell
  */
 interface Record {
-
-    /*
-     * There are four record types, which are part of the interface
-     * to this level (along with the maximum record size).
-     *
-     * enum { change_cipher_spec(20), alert(21), handshake(22),
-     *      application_data(23), (255) } ContentType;
-     */
-    static final byte   ct_change_cipher_spec = 20;
-    static final byte   ct_alert = 21;
-    static final byte   ct_handshake = 22;
-    static final byte   ct_application_data = 23;
-
     static final int    maxMacSize = 48;        // the max supported MAC or
                                                 // AEAD tag size
     static final int    maxDataSize = 16384;    // 2^14 bytes of data
@@ -59,35 +51,146 @@
      * System property to enable/disable CBC protection in SSL3/TLS1.
      */
     static final boolean enableCBCProtection =
-            Debug.getBooleanProperty("jsse.enableCBCProtection", true);
+            Utilities.getBooleanProperty("jsse.enableCBCProtection", true);
 
     /*
      * The overflow values of integers of 8, 16 and 24 bits.
      */
-    static final int OVERFLOW_OF_INT08 = (1 << 8);
-    static final int OVERFLOW_OF_INT16 = (1 << 16);
-    static final int OVERFLOW_OF_INT24 = (1 << 24);
+    static final int OVERFLOW_OF_INT08 = (0x01 << 8);
+    static final int OVERFLOW_OF_INT16 = (0x01 << 16);
+    static final int OVERFLOW_OF_INT24 = (0x01 << 24);
+
+    /*
+     * Read 8, 16, 24, and 32 bit integer data types, encoded
+     * in standard big-endian form.
+     */
+    static int getInt8(ByteBuffer m) throws IOException {
+        verifyLength(m, 1);
+        return (m.get() & 0xFF);
+    }
 
-    /**
-     * Return a description for the given content type.
+    static int getInt16(ByteBuffer m) throws IOException {
+        verifyLength(m, 2);
+        return ((m.get() & 0xFF) << 8) |
+                (m.get() & 0xFF);
+    }
+
+    static int getInt24(ByteBuffer m) throws IOException {
+        verifyLength(m, 3);
+        return ((m.get() & 0xFF) << 16) |
+               ((m.get() & 0xFF) <<  8) |
+                (m.get() & 0xFF);
+    }
+
+    static int getInt32(ByteBuffer m) throws IOException {
+        verifyLength(m, 4);
+        return ((m.get() & 0xFF) << 24) |
+               ((m.get() & 0xFF) << 16) |
+               ((m.get() & 0xFF) <<  8) |
+                (m.get() & 0xFF);
+    }
+
+    /*
+     * Read byte vectors with 8, 16, and 24 bit length encodings.
      */
-    static String contentName(byte contentType) {
-        switch (contentType) {
-        case ct_change_cipher_spec:
-            return "Change Cipher Spec";
-        case ct_alert:
-            return "Alert";
-        case ct_handshake:
-            return "Handshake";
-        case ct_application_data:
-            return "Application Data";
-        default:
-            return "contentType = " + contentType;
+    static byte[] getBytes8(ByteBuffer m) throws IOException {
+        int len = Record.getInt8(m);
+        verifyLength(m, len);
+        byte[] b = new byte[len];
+
+        m.get(b);
+        return b;
+    }
+
+    static byte[] getBytes16(ByteBuffer m) throws IOException {
+        int len = Record.getInt16(m);
+        verifyLength(m, len);
+        byte[] b = new byte[len];
+
+        m.get(b);
+        return b;
+    }
+
+    static byte[] getBytes24(ByteBuffer m) throws IOException {
+        int len = Record.getInt24(m);
+        verifyLength(m, len);
+        byte[] b = new byte[len];
+
+        m.get(b);
+        return b;
+    }
+
+    /*
+     * Write 8, 16, 24, and 32 bit integer data types, encoded
+     * in standard big-endian form.
+     */
+    static void putInt8(ByteBuffer m, int i) throws IOException {
+        verifyLength(m, 1);
+        m.put((byte)(i & 0xFF));
+    }
+
+    static void putInt16(ByteBuffer m, int i) throws IOException {
+        verifyLength(m, 2);
+        m.put((byte)((i >> 8) & 0xFF));
+        m.put((byte)(i & 0xFF));
+    }
+
+    static void putInt24(ByteBuffer m, int i) throws IOException {
+        verifyLength(m, 3);
+        m.put((byte)((i >> 16) & 0xFF));
+        m.put((byte)((i >> 8) & 0xFF));
+        m.put((byte)(i & 0xFF));
+    }
+
+    static void putInt32(ByteBuffer m, int i) throws IOException {
+        m.put((byte)((i >> 24) & 0xFF));
+        m.put((byte)((i >> 16) & 0xFF));
+        m.put((byte)((i >> 8) & 0xFF));
+        m.put((byte)(i & 0xFF));
+    }
+
+    /*
+     * Write byte vectors with 8, 16, and 24 bit length encodings.
+     */
+    static void putBytes8(ByteBuffer m, byte[] s) throws IOException {
+        if (s == null || s.length == 0) {
+            verifyLength(m, 1);
+            putInt8(m, 0);
+        } else {
+            verifyLength(m, 1 + s.length);
+            putInt8(m, s.length);
+            m.put(s);
         }
     }
 
-    static boolean isValidContentType(byte contentType) {
-        return (contentType == 20) || (contentType == 21) ||
-               (contentType == 22) || (contentType == 23);
+    static void putBytes16(ByteBuffer m, byte[] s) throws IOException {
+        if (s == null || s.length == 0) {
+            verifyLength(m, 2);
+            putInt16(m, 0);
+        } else {
+            verifyLength(m, 2 + s.length);
+            putInt16(m, s.length);
+            m.put(s);
+        }
+    }
+
+    static void putBytes24(ByteBuffer m, byte[] s) throws IOException {
+        if (s == null || s.length == 0) {
+            verifyLength(m, 3);
+            putInt24(m, 0);
+        } else {
+            verifyLength(m, 3 + s.length);
+            putInt24(m, s.length);
+            m.put(s);
+        }
+    }
+
+    // Verify that the buffer has sufficient remaining.
+    static void verifyLength(
+            ByteBuffer m, int len) throws SSLException {
+        if (len > m.remaining()) {
+            throw new SSLException("Insufficient space in the buffer, " +
+                    "may be cause by an unexpected end of handshake data.");
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+import sun.security.ssl.ClientHello.ClientHelloMessage;
+import static sun.security.ssl.SSLExtension.CH_RENEGOTIATION_INFO;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import static sun.security.ssl.SSLExtension.SH_RENEGOTIATION_INFO;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "renegotiation_info" extensions [RFC 5746].
+ */
+final class RenegoInfoExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHRenegotiationInfoProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHRenegotiationInfoConsumer();
+    static final HandshakeAbsence chOnLoadAbsence =
+            new CHRenegotiationInfoAbsence();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHRenegotiationInfoProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHRenegotiationInfoConsumer();
+    static final HandshakeAbsence shOnLoadAbsence =
+            new SHRenegotiationInfoAbsence();
+
+    static final SSLStringizer rniStringizer =
+            new RenegotiationInfoStringizer();
+
+    /**
+     * The "renegotiation_info" extension.
+     */
+    static final class RenegotiationInfoSpec implements SSLExtensionSpec {
+        // A nominal object that does not holding any real renegotiation info.
+        static final RenegotiationInfoSpec NOMINAL =
+                new RenegotiationInfoSpec(new byte[0]);
+
+        private final byte[] renegotiatedConnection;
+
+        private RenegotiationInfoSpec(byte[] renegotiatedConnection) {
+            this.renegotiatedConnection = Arrays.copyOf(
+                    renegotiatedConnection, renegotiatedConnection.length);
+        }
+
+        private RenegotiationInfoSpec(ByteBuffer m) throws IOException {
+            // Parse the extension.
+            if (!m.hasRemaining() || m.remaining() < 1) {
+                throw new SSLProtocolException(
+                    "Invalid renegotiation_info extension data: " +
+                    "insufficient data");
+            }
+            this.renegotiatedConnection = Record.getBytes8(m);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"renegotiated connection\": '['{0}']'", Locale.ENGLISH);
+            if (renegotiatedConnection.length == 0) {
+                Object[] messageFields = {
+                        "<no renegotiated connection>"
+                    };
+                return messageFormat.format(messageFields);
+            } else {
+                Object[] messageFields = {
+                        Utilities.toHexString(renegotiatedConnection)
+                    };
+                return messageFormat.format(messageFields);
+            }
+        }
+    }
+
+    private static final
+            class RenegotiationInfoStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new RenegotiationInfoSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "renegotiation_info" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHRenegotiationInfoProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHRenegotiationInfoProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore unavailable renegotiation_info extension");
+                }
+
+                return null;
+            }
+
+            if (!chc.conContext.isNegotiated) {
+                if (chc.activeCipherSuites.contains(
+                        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
+                    // Using the the TLS_EMPTY_RENEGOTIATION_INFO_SCSV instead.
+                    return null;
+                }
+
+                // initial handshaking.
+                //
+                // If this is the initial handshake for a connection, then the
+                // "renegotiated_connection" field is of zero length in both
+                // the ClientHello and the ServerHello. [RFC 5746]
+                byte[] extData = new byte[] { 0x00 };
+                chc.handshakeExtensions.put(
+                        CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
+
+                return extData;
+            } else if (chc.conContext.secureRenegotiation) {
+                // secure renegotiation
+                //
+                // For ClientHello handshake message in renegotiation, this
+                // field contains the "client_verify_data".
+                byte[] extData =
+                        new byte[chc.conContext.clientVerifyData.length + 1];
+                ByteBuffer m = ByteBuffer.wrap(extData);
+                Record.putBytes8(m, chc.conContext.clientVerifyData);
+
+                // The conContext.clientVerifyData will be used for further
+                // processing, so it does not matter to save whatever in the
+                // RenegotiationInfoSpec object.
+                chc.handshakeExtensions.put(
+                        CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
+
+                return extData;
+            } else {    // not secure renegotiation
+                if (HandshakeContext.allowUnsafeRenegotiation) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning("Using insecure renegotiation");
+                    }
+
+                    return null;
+                } else {
+                    // terminate the session.
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "insecure renegotiation is not allowed");
+                }
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Network data producer of a "renegotiation_info" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class CHRenegotiationInfoConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHRenegotiationInfoConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Ignore unavailable extension: " +
+                            CH_RENEGOTIATION_INFO.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            RenegotiationInfoSpec spec;
+            try {
+                spec = new RenegotiationInfoSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            if (!shc.conContext.isNegotiated) {
+                // initial handshaking.
+                if (spec.renegotiatedConnection.length != 0) {
+                    shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Invalid renegotiation_info extension data: not empty");
+                }
+                shc.conContext.secureRenegotiation = true;
+            } else {
+                if (!shc.conContext.secureRenegotiation) {
+                    // Unexpected RI extension for insecure renegotiation,
+                    // abort the handshake with a fatal handshake_failure alert.
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "The renegotiation_info is present in a insecure " +
+                            "renegotiation");
+                } else {
+                    // verify the client_verify_data value
+                    if (!Arrays.equals(shc.conContext.clientVerifyData,
+                            spec.renegotiatedConnection)) {
+                        shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                            "Invalid renegotiation_info extension data: " +
+                            "incorrect verify data in ClientHello");
+                    }
+                }
+            }
+
+            // Update the context.
+            //
+            // The conContext.clientVerifyData will be used for further
+            // processing, so it does not matter to save whatever in the
+            // RenegotiationInfoSpec object.
+            shc.handshakeExtensions.put(
+                    CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * The absence processing if a "renegotiation_info" extension is
+     * not present in the ClientHello handshake message.
+     */
+    private static final
+            class CHRenegotiationInfoAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            if (!shc.conContext.isNegotiated) {
+                // initial handshaking.
+                for (int id : clientHello.cipherSuiteIds) {
+                    if (id ==
+                            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.id) {
+                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                            SSLLogger.finest(
+                                "Safe renegotiation, using the SCSV signgling");
+                        }
+                        shc.conContext.secureRenegotiation = true;
+                        return;
+                    }
+                }
+
+                if (!HandshakeContext.allowLegacyHelloMessages) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Failed to negotiate the use of secure renegotiation");
+                }   // otherwise, allow legacy hello message
+
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("Warning: No renegotiation " +
+                        "indication in ClientHello, allow legacy ClientHello");
+                }
+
+                shc.conContext.secureRenegotiation = false;
+            } else if (shc.conContext.secureRenegotiation) {
+                // Require secure renegotiation, terminate the connection.
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Inconsistent secure renegotiation indication");
+            } else {    // renegotiation, not secure
+                if (HandshakeContext.allowUnsafeRenegotiation) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning("Using insecure renegotiation");
+                    }
+                } else {
+                    // Unsafe renegotiation should have been aborted in
+                    // ealier processes.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine("Terminate insecure renegotiation");
+                    }
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Unsafe renegotiation is not allowed");
+                }
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "renegotiation_info" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class SHRenegotiationInfoProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHRenegotiationInfoProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to "renegotiation_info" extension request only.
+            RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
+                    shc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
+            if (requestedSpec == null && !shc.conContext.secureRenegotiation) {
+                // Ignore, no renegotiation_info extension or SCSV signgling
+                // requested.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable renegotiation_info extension");
+                }
+                return null;        // ignore the extension
+            }
+
+            if (!shc.conContext.secureRenegotiation) {
+                // Ignore, no secure renegotiation is negotiated.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "No secure renegotiation has been negotiated");
+                }
+                return null;        // ignore the extension
+            }
+
+            if (!shc.conContext.isNegotiated) {
+                // initial handshaking.
+                //
+                // If this is the initial handshake for a connection, then the
+                // "renegotiated_connection" field is of zero length in both
+                // the ClientHello and the ServerHello. [RFC 5746]
+                byte[] extData = new byte[] { 0x00 };
+
+                // The conContext.client/serverVerifyData will be used for
+                // further processing, so it does not matter to save whatever
+                // in the RenegotiationInfoSpec object.
+                shc.handshakeExtensions.put(
+                        SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
+
+                return extData;
+            } else {
+                // secure renegotiation
+                //
+                // For secure renegotiation, the server MUST include a
+                // "renegotiation_info" extension containing the saved
+                // client_verify_data and server_verify_data in the ServerHello.
+                int infoLen = shc.conContext.clientVerifyData.length +
+                              shc.conContext.serverVerifyData.length;
+                byte[] extData = new byte[infoLen + 1];
+                ByteBuffer m = ByteBuffer.wrap(extData);
+                Record.putInt8(m, infoLen);
+                m.put(shc.conContext.clientVerifyData);
+                m.put(shc.conContext.serverVerifyData);
+
+                // The conContext.client/serverVerifyData will be used for
+                // further processing, so it does not matter to save whatever
+                // in the RenegotiationInfoSpec object.
+                shc.handshakeExtensions.put(
+                        SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
+
+                return extData;
+            }
+        }
+    }
+
+    /**
+     * Network data consumer of a "renegotiation_info" extension in
+     * the ServerHello handshake message.
+     */
+    private static final
+            class SHRenegotiationInfoConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHRenegotiationInfoConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to the client renegotiation_info extension request
+            // or SCSV signling, which is mandatory for ClientHello message.
+            RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
+                    chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
+            if (requestedSpec == null &&
+                    !chc.activeCipherSuites.contains(
+                            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "Missing renegotiation_info and SCSV detected in " +
+                    "ClientHello");
+            }
+
+            // Parse the extension.
+            RenegotiationInfoSpec spec;
+            try {
+                spec = new RenegotiationInfoSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+
+            if (!chc.conContext.isNegotiated) {     // initial handshake
+                // If the extension is present, set the secure_renegotiation
+                // flag to TRUE.  The client MUST then verify that the
+                // length of the "renegotiated_connection" field is zero,
+                // and if it is not, MUST abort the handshake (by sending
+                // a fatal handshake_failure alert). [RFC 5746]
+                if (spec.renegotiatedConnection.length != 0) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid renegotiation_info in ServerHello: " +
+                        "not empty renegotiated_connection");
+                }
+
+                chc.conContext.secureRenegotiation = true;
+            } else {        // renegotiation
+                // The client MUST then verify that the first half of the
+                // "renegotiated_connection" field is equal to the saved
+                // client_verify_data value, and the second half is equal to the
+                // saved server_verify_data value.  If they are not, the client
+                // MUST abort the handshake. [RFC 5746]
+                int infoLen = chc.conContext.clientVerifyData.length +
+                              chc.conContext.serverVerifyData.length;
+                if (spec.renegotiatedConnection.length != infoLen) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid renegotiation_info in ServerHello: " +
+                        "invalid renegotiated_connection length (" +
+                        spec.renegotiatedConnection.length + ")");
+                }
+
+                byte[] cvd = chc.conContext.clientVerifyData;
+                if (!Arrays.equals(spec.renegotiatedConnection,
+                        0, cvd.length, cvd, 0, cvd.length)) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid renegotiation_info in ServerHello: " +
+                        "unmatched client_verify_data value");
+                }
+                byte[] svd = chc.conContext.serverVerifyData;
+                if (!Arrays.equals(spec.renegotiatedConnection,
+                        cvd.length, infoLen, svd, 0, svd.length)) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Invalid renegotiation_info in ServerHello: " +
+                        "unmatched server_verify_data value");
+                }
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * The absence processing if a "renegotiation_info" extension is
+     * not present in the ServerHello handshake message.
+     */
+    private static final
+            class SHRenegotiationInfoAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to the client renegotiation_info extension request
+            // or SCSV signling, which is mandatory for ClientHello message.
+            RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
+                    chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
+            if (requestedSpec == null &&
+                    !chc.activeCipherSuites.contains(
+                            CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "Missing renegotiation_info and SCSV detected in " +
+                    "ClientHello");
+            }
+
+            if (!chc.conContext.isNegotiated) {
+                // initial handshaking.
+                if (!HandshakeContext.allowLegacyHelloMessages) {
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Failed to negotiate the use of secure renegotiation");
+                }   // otherwise, allow legacy hello message
+
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("Warning: No renegotiation " +
+                        "indication in ServerHello, allow legacy ServerHello");
+                }
+
+                chc.conContext.secureRenegotiation = false;
+            } else if (chc.conContext.secureRenegotiation) {
+                // Require secure renegotiation, terminate the connection.
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Inconsistent secure renegotiation indication");
+            } else {    // renegotiation, not secure
+                if (HandshakeContext.allowUnsafeRenegotiation) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.warning("Using insecure renegotiation");
+                    }
+                } else {
+                    // Unsafe renegotiation should have been aborted in
+                    // ealier processes.
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine("Terminate insecure renegotiation");
+                    }
+                    chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Unsafe renegotiation is not allowed");
+                }
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/RenegotiationInfoExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2006, 2012, 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.IOException;
-
-import javax.net.ssl.SSLProtocolException;
-
-/*
- * For secure renegotiation, RFC5746 defines a new TLS extension,
- * "renegotiation_info" (with extension type 0xff01), which contains a
- * cryptographic binding to the enclosing TLS connection (if any) for
- * which the renegotiation is being performed.  The "extension data"
- * field of this extension contains a "RenegotiationInfo" structure:
- *
- *      struct {
- *          opaque renegotiated_connection<0..255>;
- *      } RenegotiationInfo;
- */
-final class RenegotiationInfoExtension extends HelloExtension {
-    private final byte[] renegotiated_connection;
-
-    RenegotiationInfoExtension(byte[] clientVerifyData,
-                byte[] serverVerifyData) {
-        super(ExtensionType.EXT_RENEGOTIATION_INFO);
-
-        if (clientVerifyData.length != 0) {
-            renegotiated_connection =
-                    new byte[clientVerifyData.length + serverVerifyData.length];
-            System.arraycopy(clientVerifyData, 0, renegotiated_connection,
-                    0, clientVerifyData.length);
-
-            if (serverVerifyData.length != 0) {
-                System.arraycopy(serverVerifyData, 0, renegotiated_connection,
-                        clientVerifyData.length, serverVerifyData.length);
-            }
-        } else {
-            // ignore both the client and server verify data.
-            renegotiated_connection = new byte[0];
-        }
-    }
-
-    RenegotiationInfoExtension(HandshakeInStream s, int len)
-                throws IOException {
-        super(ExtensionType.EXT_RENEGOTIATION_INFO);
-
-        // check the extension length
-        if (len < 1) {
-            throw new SSLProtocolException("Invalid " + type + " extension");
-        }
-
-        int renegoInfoDataLen = s.getInt8();
-        if (renegoInfoDataLen + 1 != len) {  // + 1 = the byte we just read
-            throw new SSLProtocolException("Invalid " + type + " extension");
-        }
-
-        renegotiated_connection = new byte[renegoInfoDataLen];
-        if (renegoInfoDataLen != 0) {
-            s.read(renegotiated_connection, 0, renegoInfoDataLen);
-        }
-    }
-
-
-    // Length of the encoded extension, including the type and length fields
-    @Override
-    int length() {
-        return 5 + renegotiated_connection.length;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(renegotiated_connection.length + 1);
-        s.putBytes8(renegotiated_connection);
-    }
-
-    boolean isEmpty() {
-        return renegotiated_connection.length == 0;
-    }
-
-    byte[] getRenegotiatedConnection() {
-        return renegotiated_connection;
-    }
-
-    @Override
-    public String toString() {
-        return "Extension " + type + ", renegotiated_connection: " +
-                    (renegotiated_connection.length == 0 ? "<empty>" :
-                    Debug.toString(renegotiated_connection));
-    }
-
-}
--- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,18 +26,13 @@
 package sun.security.ssl;
 
 import java.security.AlgorithmConstraints;
+import java.security.AlgorithmParameters;
 import java.security.CryptoPrimitive;
-import java.security.AlgorithmParameters;
-
+import java.security.Key;
+import java.util.Set;
 import javax.net.ssl.*;
-
-import java.security.Key;
-
-import java.util.Set;
-
 import sun.security.util.DisabledAlgorithmConstraints;
 import static sun.security.util.DisabledAlgorithmConstraints.*;
-import sun.security.ssl.CipherSuite.*;
 
 /**
  * Algorithm constraints for disabled algorithms property
@@ -55,10 +50,10 @@
             new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS,
                     new SSLAlgorithmDecomposer(true));
 
-    private AlgorithmConstraints userAlgConstraints = null;
-    private AlgorithmConstraints peerAlgConstraints = null;
+    private final AlgorithmConstraints userSpecifiedConstraints;
+    private final AlgorithmConstraints peerSpecifiedConstraints;
 
-    private boolean enabledX509DisabledAlgConstraints = true;
+    private final boolean enabledX509DisabledAlgConstraints;
 
     // the default algorithm constraints
     static final AlgorithmConstraints DEFAULT =
@@ -68,60 +63,86 @@
     static final AlgorithmConstraints DEFAULT_SSL_ONLY =
                         new SSLAlgorithmConstraints((SSLSocket)null, false);
 
-    SSLAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) {
-        userAlgConstraints = algorithmConstraints;
+    SSLAlgorithmConstraints(AlgorithmConstraints userSpecifiedConstraints) {
+        this.userSpecifiedConstraints = userSpecifiedConstraints;
+        this.peerSpecifiedConstraints = null;
+        this.enabledX509DisabledAlgConstraints = true;
     }
 
     SSLAlgorithmConstraints(SSLSocket socket,
             boolean withDefaultCertPathConstraints) {
+        AlgorithmConstraints configuredConstraints = null;
         if (socket != null) {
-            userAlgConstraints =
-                socket.getSSLParameters().getAlgorithmConstraints();
+            HandshakeContext hc =
+                    ((SSLSocketImpl)socket).conContext.handshakeContext;
+            if (hc != null) {
+                configuredConstraints = hc.sslConfig.algorithmConstraints;
+            } else {
+                configuredConstraints = null;
+            }
         }
-
-        if (!withDefaultCertPathConstraints) {
-            enabledX509DisabledAlgConstraints = false;
-        }
+        this.userSpecifiedConstraints = configuredConstraints;
+        this.peerSpecifiedConstraints = null;
+        this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
     }
 
     SSLAlgorithmConstraints(SSLEngine engine,
             boolean withDefaultCertPathConstraints) {
+        AlgorithmConstraints configuredConstraints = null;
         if (engine != null) {
-            userAlgConstraints =
-                engine.getSSLParameters().getAlgorithmConstraints();
+            HandshakeContext hc =
+                    ((SSLEngineImpl)engine).conContext.handshakeContext;
+            if (hc != null) {
+                configuredConstraints = hc.sslConfig.algorithmConstraints;
+            } else {
+                configuredConstraints = null;
+            }
         }
-
-        if (!withDefaultCertPathConstraints) {
-            enabledX509DisabledAlgConstraints = false;
-        }
+        this.userSpecifiedConstraints = configuredConstraints;
+        this.peerSpecifiedConstraints = null;
+        this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
     }
 
     SSLAlgorithmConstraints(SSLSocket socket, String[] supportedAlgorithms,
             boolean withDefaultCertPathConstraints) {
+        AlgorithmConstraints configuredConstraints = null;
+        AlgorithmConstraints negotiatedConstraints = null;
         if (socket != null) {
-            userAlgConstraints =
-                socket.getSSLParameters().getAlgorithmConstraints();
-            peerAlgConstraints =
+            HandshakeContext hc =
+                    ((SSLSocketImpl)socket).conContext.handshakeContext;
+            if (hc != null) {
+                configuredConstraints = hc.sslConfig.algorithmConstraints;
+            } else {
+                configuredConstraints = null;
+            }
+
+            negotiatedConstraints =
                 new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
         }
-
-        if (!withDefaultCertPathConstraints) {
-            enabledX509DisabledAlgConstraints = false;
-        }
+        this.userSpecifiedConstraints = configuredConstraints;
+        this.peerSpecifiedConstraints = negotiatedConstraints;
+        this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
     }
 
     SSLAlgorithmConstraints(SSLEngine engine, String[] supportedAlgorithms,
             boolean withDefaultCertPathConstraints) {
+        AlgorithmConstraints configuredConstraints = null;
+        AlgorithmConstraints negotiatedConstraints = null;
         if (engine != null) {
-            userAlgConstraints =
-                engine.getSSLParameters().getAlgorithmConstraints();
-            peerAlgConstraints =
+            HandshakeContext hc =
+                    ((SSLEngineImpl)engine).conContext.handshakeContext;
+            if (hc != null) {
+                configuredConstraints = hc.sslConfig.algorithmConstraints;
+            } else {
+                configuredConstraints = null;
+            }
+
+            negotiatedConstraints =
                 new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
         }
-
-        if (!withDefaultCertPathConstraints) {
-            enabledX509DisabledAlgConstraints = false;
-        }
+        this.userSpecifiedConstraints = configuredConstraints;
+        this.peerSpecifiedConstraints = negotiatedConstraints;
+        this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
     }
 
     @Override
@@ -130,13 +151,13 @@
 
         boolean permitted = true;
 
-        if (peerAlgConstraints != null) {
-            permitted = peerAlgConstraints.permits(
+        if (peerSpecifiedConstraints != null) {
+            permitted = peerSpecifiedConstraints.permits(
                                     primitives, algorithm, parameters);
         }
 
-        if (permitted && userAlgConstraints != null) {
-            permitted = userAlgConstraints.permits(
+        if (permitted && userSpecifiedConstraints != null) {
+            permitted = userSpecifiedConstraints.permits(
                                     primitives, algorithm, parameters);
         }
 
@@ -158,12 +179,12 @@
 
         boolean permitted = true;
 
-        if (peerAlgConstraints != null) {
-            permitted = peerAlgConstraints.permits(primitives, key);
+        if (peerSpecifiedConstraints != null) {
+            permitted = peerSpecifiedConstraints.permits(primitives, key);
         }
 
-        if (permitted && userAlgConstraints != null) {
-            permitted = userAlgConstraints.permits(primitives, key);
+        if (permitted && userSpecifiedConstraints != null) {
+            permitted = userSpecifiedConstraints.permits(primitives, key);
         }
 
         if (permitted) {
@@ -183,13 +204,13 @@
 
         boolean permitted = true;
 
-        if (peerAlgConstraints != null) {
-            permitted = peerAlgConstraints.permits(
+        if (peerSpecifiedConstraints != null) {
+            permitted = peerSpecifiedConstraints.permits(
                                     primitives, algorithm, key, parameters);
         }
 
-        if (permitted && userAlgConstraints != null) {
-            permitted = userAlgConstraints.permits(
+        if (permitted && userSpecifiedConstraints != null) {
+            permitted = userSpecifiedConstraints.permits(
                                     primitives, algorithm, key, parameters);
         }
 
--- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,9 +27,12 @@
 
 import java.util.HashSet;
 import java.util.Set;
+import sun.security.ssl.CipherSuite.HashAlg;
+import sun.security.ssl.CipherSuite.KeyExchange;
+import static sun.security.ssl.CipherSuite.KeyExchange.*;
+import sun.security.ssl.CipherSuite.MacAlg;
+import static sun.security.ssl.SSLCipher.*;
 import sun.security.util.AlgorithmDecomposer;
-import static sun.security.ssl.CipherSuite.*;
-import static sun.security.ssl.CipherSuite.KeyExchange.*;
 
 /**
  * The class decomposes standard SSL/TLS cipher suites into sub-elements.
@@ -126,18 +129,13 @@
                 }
                 break;
             default:
-                if (ClientKeyExchangeService.find(keyExchange.name) != null) {
-                    if (!onlyX509) {
-                        components.add(keyExchange.name);
-                    }
-                }
                 // otherwise ignore
             }
 
         return components;
     }
 
-    private Set<String> decomposes(CipherSuite.BulkCipher bulkCipher) {
+    private Set<String> decomposes(SSLCipher bulkCipher) {
         Set<String> components = new HashSet<>();
 
         if (bulkCipher.transformation != null) {
@@ -185,7 +183,7 @@
     }
 
     private Set<String> decomposes(CipherSuite.MacAlg macAlg,
-            BulkCipher cipher) {
+            SSLCipher cipher) {
         Set<String> components = new HashSet<>();
 
         if (macAlg == CipherSuite.MacAlg.M_NULL
@@ -211,8 +209,26 @@
         return components;
     }
 
-    private Set<String> decompose(KeyExchange keyExchange, BulkCipher cipher,
-            MacAlg macAlg) {
+    private Set<String> decomposes(CipherSuite.HashAlg hashAlg) {
+        Set<String> components = new HashSet<>();
+
+        if (hashAlg == CipherSuite.HashAlg.H_SHA256) {
+            components.add("SHA256");
+            components.add("SHA-256");
+            components.add("HmacSHA256");
+        } else if (hashAlg == CipherSuite.HashAlg.H_SHA384) {
+            components.add("SHA384");
+            components.add("SHA-384");
+            components.add("HmacSHA384");
+        }
+
+        return components;
+    }
+
+    private Set<String> decompose(KeyExchange keyExchange,
+            SSLCipher cipher,
+            MacAlg macAlg,
+            HashAlg hashAlg) {
         Set<String> components = new HashSet<>();
 
         if (keyExchange != null) {
@@ -233,6 +249,10 @@
             components.addAll(decomposes(macAlg, cipher));
         }
 
+        if (hashAlg != null) {
+            components.addAll(decomposes(hashAlg));
+        }
+
         return components;
     }
 
@@ -241,18 +261,19 @@
         if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) {
             CipherSuite cipherSuite = null;
             try {
-                cipherSuite = CipherSuite.valueOf(algorithm);
+                cipherSuite = CipherSuite.nameOf(algorithm);
             } catch (IllegalArgumentException iae) {
                 // ignore: unknown or unsupported ciphersuite
             }
 
             if (cipherSuite != null) {
-                return decompose(cipherSuite.keyExchange, cipherSuite.cipher,
-                        cipherSuite.macAlg);
+                return decompose(cipherSuite.keyExchange,
+                        cipherSuite.bulkCipher,
+                        cipherSuite.macAlg,
+                        cipherSuite.hashAlg);
             }
         }
 
         return super.decompose(algorithm);
     }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLAuthentication.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+interface SSLAuthentication
+        extends SSLPossessionGenerator, SSLHandshakeBinding {
+    // blank
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLHandshakeException;
+
+final class SSLBasicKeyDerivation implements SSLKeyDerivation {
+    private final String hashAlg;
+    private final SecretKey secret;
+    private final byte[] hkdfInfo;
+
+    SSLBasicKeyDerivation(SecretKey secret, String hashAlg,
+            byte[] label, byte[] context, int length) {
+        this.hashAlg = hashAlg.replace("-", "");
+        this.secret = secret;
+        this.hkdfInfo = createHkdfInfo(label, context, length);
+    }
+
+    @Override
+    public SecretKey deriveKey(String algorithm,
+            AlgorithmParameterSpec keySpec) throws IOException {
+        try {
+            HKDF hkdf = new HKDF(hashAlg);
+            return hkdf.expand(secret, hkdfInfo,
+                    ((SecretSizeSpec)keySpec).length, algorithm);
+        } catch (GeneralSecurityException gse) {
+            throw (SSLHandshakeException) new SSLHandshakeException(
+                "Could not generate secret").initCause(gse);
+        }
+    }
+
+    private static byte[] createHkdfInfo(
+            byte[] label, byte[] context, int length) {
+        byte[] info = new byte[4 + label.length + context.length];
+        ByteBuffer m = ByteBuffer.wrap(info);
+        try {
+            Record.putInt16(m, length);
+            Record.putBytes8(m, label);
+            Record.putBytes8(m, context);
+        } catch (IOException ioe) {
+            // unlikely
+        }
+        return info;
+    }
+
+    static class SecretSizeSpec implements AlgorithmParameterSpec {
+        final int length;
+
+        SecretSizeSpec(int length) {
+            this.length = length;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,2369 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import sun.security.ssl.Authenticator.MAC;
+import static sun.security.ssl.CipherType.*;
+import static sun.security.ssl.JsseJce.*;
+
+enum SSLCipher {
+    // exportable ciphers
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true, true,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new NullReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_NONE
+            ),
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new NullReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_13
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new NullWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_NONE
+            ),
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new NullWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true, true,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new StreamReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new StreamWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false, true,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new StreamReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new StreamWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_DES_40(CIPHER_DES,  BLOCK_CIPHER, 5, 8, 8, 0, true, true,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T10BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T10BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            )
+        })),
+
+    // domestic strength ciphers
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_RC4_128(CIPHER_RC4, STREAM_CIPHER, 16, 16, 0, 0, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new StreamReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new StreamWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_DES(CIPHER_DES, BLOCK_CIPHER, 8, 8, 8, 0, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T10BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T11BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_11
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T10BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T11BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_11
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_3DES(CIPHER_3DES, BLOCK_CIPHER, 24, 24, 8, 0, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T10BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T11BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_11_12
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T10BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T11BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_11_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_IDEA("IDEA", BLOCK_CIPHER, 16, 16, 8, 0, false, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                null,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                null,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_AES_128(CIPHER_AES, BLOCK_CIPHER, 16, 16, 16, 0, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T10BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T11BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_11_12
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T10BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T11BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_11_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_AES_256(CIPHER_AES, BLOCK_CIPHER, 32, 32, 16, 0, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T10BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T11BlockReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_11_12
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T10BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_TO_10
+            ),
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T11BlockWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_11_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 16, 12, 4, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T12GcmReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_12
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T12GcmWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 32, 12, 4, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T12GcmReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_12
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T12GcmWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_AES_128_GCM_IV(CIPHER_AES_GCM, AEAD_CIPHER, 16, 16, 12, 0, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T13GcmReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T13GcmWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    B_AES_256_GCM_IV(CIPHER_AES_GCM, AEAD_CIPHER, 32, 32, 12, 0, true, false,
+        (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<ReadCipherGenerator, ProtocolVersion[]>(
+                new T13GcmReadCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }),
+        (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<WriteCipherGenerator, ProtocolVersion[]>(
+                new T13GcmWriteCipherGenerator(),
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }));
+
+    // descriptive name including key size, e.g. AES/128
+    final String description;
+
+    // JCE cipher transformation string, e.g. AES/CBC/NoPadding
+    final String transformation;
+
+    // algorithm name, e.g. AES
+    final String algorithm;
+
+    // supported and compile time enabled. Also see isAvailable()
+    final boolean allowed;
+
+    // number of bytes of entropy in the key
+    final int keySize;
+
+    // length of the actual cipher key in bytes.
+    // for non-exportable ciphers, this is the same as keySize
+    final int expandedKeySize;
+
+    // size of the IV
+    final int ivSize;
+
+    // size of fixed IV
+    //
+    // record_iv_length = ivSize - fixedIvSize
+    final int fixedIvSize;
+
+    // exportable under 512/40 bit rules
+    final boolean exportable;
+
+    // Is the cipher algorithm of Cipher Block Chaining (CBC) mode?
+    final CipherType cipherType;
+
+    // size of the authentication tag, only applicable to cipher suites in
+    // Galois Counter Mode (GCM)
+    //
+    // As far as we know, all supported GCM cipher suites use 128-bits
+    // authentication tags.
+    final int tagSize = 16;
+
+    // runtime availability
+    private final boolean isAvailable;
+
+    private final Map.Entry<ReadCipherGenerator,
+            ProtocolVersion[]>[] readCipherGenerators;
+    private final Map.Entry<WriteCipherGenerator,
+            ProtocolVersion[]>[] writeCipherGenerators;
+
+    // Map of Ciphers listed in jdk.tls.KeyLimit
+    private static final HashMap<String, Long> cipherLimits = new HashMap<>();
+
+    // Keywords found on the jdk.tls.KeyLimit security property.
+    final static String tag[] = {"KEYUPDATE"};
+
+    static  {
+        final long max = 4611686018427387904L; // 2^62
+        String prop = AccessController.doPrivileged(
+                new PrivilegedAction<String>() {
+            @Override
+            public String run() {
+                return Security.getProperty("jdk.tls.keyLimits");
+            }
+        });
+
+        if (prop != null) {
+            String propvalue[] = prop.split(",");
+
+            for (String entry : propvalue) {
+                int index;
+                // If this is not a UsageLimit, goto to next entry.
+                String values[] = entry.trim().toUpperCase().split(" ");
+
+                if (values[1].contains(tag[0])) {
+                    index = 0;
+                } else {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                        SSLLogger.fine("jdk.net.keyLimits:  Unknown action:  " +
+                                entry);
+                    }
+                    continue;
+                }
+
+                long size;
+                int i = values[2].indexOf("^");
+                try {
+                    if (i >= 0) {
+                        size = (long) Math.pow(2,
+                                Integer.parseInt(values[2].substring(i + 1)));
+                    } else {
+                        size = Long.parseLong(values[2]);
+                    }
+                    if (size < 1 || size > max) {
+                        throw new NumberFormatException("Length exceeded limits");
+                    }
+                } catch (NumberFormatException e) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                        SSLLogger.fine("jdk.net.keyLimits:  " + e.getMessage() +
+                                ":  " +  entry);
+                    }
+                    continue;
+                }
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine("jdk.net.keyLimits:  entry = " + entry +
+                            ". " + values[0] + ":" + tag[index] + " = " + size);
+                }
+                cipherLimits.put(values[0] + ":" + tag[index], size);
+            }
+        }
+    }
+
+    private SSLCipher(String transformation,
+            CipherType cipherType, int keySize,
+            int expandedKeySize, int ivSize,
+            int fixedIvSize, boolean allowed, boolean exportable,
+            Map.Entry<ReadCipherGenerator,
+                    ProtocolVersion[]>[] readCipherGenerators,
+            Map.Entry<WriteCipherGenerator,
+                    ProtocolVersion[]>[] writeCipherGenerators) {
+        this.transformation = transformation;
+        String[] splits = transformation.split("/");
+        this.algorithm = splits[0];
+        this.cipherType = cipherType;
+        this.description = this.algorithm + "/" + (keySize << 3);
+        this.keySize = keySize;
+        this.ivSize = ivSize;
+        this.fixedIvSize = fixedIvSize;
+        this.allowed = allowed;
+
+        this.expandedKeySize = expandedKeySize;
+        this.exportable = exportable;
+
+        // availability of this bulk cipher
+        //
+        // We assume all supported ciphers are always available since they are
+        // shipped with the SunJCE  provider.  However, AES/256 is unavailable
+        // when the default JCE policy jurisdiction files are installed because
+        // of key length restrictions.
+        this.isAvailable = allowed && isUnlimited(keySize, transformation);
+
+        this.readCipherGenerators = readCipherGenerators;
+        this.writeCipherGenerators = writeCipherGenerators;
+    }
+
+    SSLReadCipher createReadCipher(Authenticator authenticator,
+            ProtocolVersion protocolVersion,
+            SecretKey key, IvParameterSpec iv,
+            SecureRandom random) throws GeneralSecurityException {
+        if (readCipherGenerators.length == 0) {
+            return null;
+        }
+
+        ReadCipherGenerator rcg = null;
+        for (Map.Entry<ReadCipherGenerator,
+                ProtocolVersion[]> me : readCipherGenerators) {
+            for (ProtocolVersion pv : me.getValue()) {
+                if (protocolVersion == pv) {
+                    rcg = me.getKey();
+                }
+            }
+        }
+
+        if (rcg != null) {
+            return rcg.createCipher(this, authenticator,
+                    protocolVersion, transformation, key, iv, random);
+        }
+        return null;
+    }
+
+    SSLWriteCipher createWriteCipher(Authenticator authenticator,
+            ProtocolVersion protocolVersion,
+            SecretKey key, IvParameterSpec iv,
+            SecureRandom random) throws GeneralSecurityException {
+        if (readCipherGenerators.length == 0) {
+            return null;
+        }
+
+        WriteCipherGenerator rcg = null;
+        for (Map.Entry<WriteCipherGenerator,
+                ProtocolVersion[]> me : writeCipherGenerators) {
+            for (ProtocolVersion pv : me.getValue()) {
+                if (protocolVersion == pv) {
+                    rcg = me.getKey();
+                }
+            }
+        }
+
+        if (rcg != null) {
+            return rcg.createCipher(this, authenticator,
+                    protocolVersion, transformation, key, iv, random);
+        }
+        return null;
+    }
+
+    /**
+     * Test if this bulk cipher is available. For use by CipherSuite.
+     */
+    boolean isAvailable() {
+        return this.isAvailable;
+    }
+
+    private static boolean isUnlimited(int keySize, String transformation) {
+        int keySizeInBits = keySize * 8;
+        if (keySizeInBits > 128) {    // need the JCE unlimited
+                                      // strength jurisdiction policy
+            try {
+                if (Cipher.getMaxAllowedKeyLength(
+                        transformation) < keySizeInBits) {
+                    return false;
+                }
+            } catch (Exception e) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return description;
+    }
+
+    interface ReadCipherGenerator {
+        SSLReadCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException;
+    }
+
+    abstract static class SSLReadCipher {
+        final Authenticator authenticator;
+        final ProtocolVersion protocolVersion;
+        boolean keyLimitEnabled = false;
+        long keyLimitCountdown = 0;
+        SecretKey baseSecret;
+
+        SSLReadCipher(Authenticator authenticator,
+                ProtocolVersion protocolVersion) {
+            this.authenticator = authenticator;
+            this.protocolVersion = protocolVersion;
+        }
+
+        static final SSLReadCipher nullTlsReadCipher() {
+            try {
+                return B_NULL.createReadCipher(
+                        Authenticator.nullTlsMac(),
+                        ProtocolVersion.NONE, null, null, null);
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                throw new RuntimeException("Cannot create NULL SSLCipher", gse);
+            }
+        }
+
+        static final SSLReadCipher nullDTlsReadCipher() {
+            try {
+                return B_NULL.createReadCipher(
+                        Authenticator.nullDtlsMac(),
+                        ProtocolVersion.NONE, null, null, null);
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                throw new RuntimeException("Cannot create NULL SSLCipher", gse);
+            }
+        }
+
+        abstract Plaintext decrypt(byte contentType, ByteBuffer bb,
+                    byte[] sequence) throws GeneralSecurityException;
+
+        void dispose() {
+            // blank
+        }
+
+        abstract int estimateFragmentSize(int packetSize, int headerSize);
+
+        boolean isNullCipher() {
+            return false;
+        }
+
+        /**
+         * Check if processed bytes have reached the key usage limit.
+         * If key usage limit is not be monitored, return false.
+         */
+        public boolean atKeyLimit() {
+            if (keyLimitCountdown >= 0) {
+                return false;
+            }
+
+            // Turn off limit checking as KeyUpdate will be occurring
+            keyLimitEnabled = false;
+            return true;
+        }
+    }
+
+    interface WriteCipherGenerator {
+        SSLWriteCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException;
+    }
+
+    abstract static class SSLWriteCipher {
+        final Authenticator authenticator;
+        final ProtocolVersion protocolVersion;
+        boolean keyLimitEnabled = false;
+        long keyLimitCountdown = 0;
+        SecretKey baseSecret;
+
+        SSLWriteCipher(Authenticator authenticator,
+                ProtocolVersion protocolVersion) {
+            this.authenticator = authenticator;
+            this.protocolVersion = protocolVersion;
+        }
+
+        abstract int encrypt(byte contentType, ByteBuffer bb);
+
+        static final SSLWriteCipher nullTlsWriteCipher() {
+            try {
+                return B_NULL.createWriteCipher(
+                        Authenticator.nullTlsMac(),
+                        ProtocolVersion.NONE, null, null, null);
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                throw new RuntimeException(
+                        "Cannot create NULL SSL write Cipher", gse);
+            }
+        }
+
+        static final SSLWriteCipher nullDTlsWriteCipher() {
+            try {
+                return B_NULL.createWriteCipher(
+                        Authenticator.nullDtlsMac(),
+                        ProtocolVersion.NONE, null, null, null);
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                throw new RuntimeException(
+                        "Cannot create NULL SSL write Cipher", gse);
+            }
+        }
+
+        void dispose() {
+            // blank
+        }
+
+        abstract int getExplicitNonceSize();
+        abstract int calculateFragmentSize(int packetLimit, int headerSize);
+        abstract int calculatePacketSize(int fragmentSize, int headerSize);
+
+        boolean isCBCMode() {
+            return false;
+        }
+
+        boolean isNullCipher() {
+            return false;
+        }
+
+        /**
+         * Check if processed bytes have reached the key usage limit.
+         * If key usage limit is not be monitored, return false.
+         */
+        public boolean atKeyLimit() {
+            if (keyLimitCountdown >= 0) {
+                return false;
+            }
+
+            // Turn off limit checking as KeyUpdate will be occurring
+            keyLimitEnabled = false;
+            return true;
+        }
+    }
+
+    private static final
+            class NullReadCipherGenerator implements ReadCipherGenerator {
+        @Override
+        public SSLReadCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new NullReadCipher(authenticator, protocolVersion);
+        }
+
+        static final class NullReadCipher extends SSLReadCipher {
+            NullReadCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion) {
+                super(authenticator, protocolVersion);
+            }
+
+            @Override
+            public Plaintext decrypt(byte contentType, ByteBuffer bb,
+                    byte[] sequence) throws GeneralSecurityException {
+                MAC signer = (MAC)authenticator;
+                if (signer.macAlg().size != 0) {
+                    checkStreamMac(signer, bb, contentType, sequence);
+                } else {
+                    authenticator.increaseSequenceNumber();
+                }
+
+                return new Plaintext(contentType,
+                        ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
+                        -1, -1L, bb.slice());
+            }
+
+            @Override
+            int estimateFragmentSize(int packetSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                return packetSize - headerSize - macLen;
+            }
+
+            @Override
+            boolean isNullCipher() {
+                return true;
+            }
+        }
+    }
+
+    private static final
+            class NullWriteCipherGenerator implements WriteCipherGenerator {
+        @Override
+        public SSLWriteCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new NullWriteCipher(authenticator, protocolVersion);
+        }
+
+        static final class NullWriteCipher extends SSLWriteCipher {
+            NullWriteCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion) {
+                super(authenticator, protocolVersion);
+            }
+
+            @Override
+            public int encrypt(byte contentType, ByteBuffer bb) {
+                // add message authentication code
+                MAC signer = (MAC)authenticator;
+                if (signer.macAlg().size != 0) {
+                    addMac(signer, bb, contentType);
+                } else {
+                    authenticator.increaseSequenceNumber();
+                }
+
+                int len = bb.remaining();
+                bb.position(bb.limit());
+                return len;
+            }
+
+
+            @Override
+            int getExplicitNonceSize() {
+                return 0;
+            }
+
+            @Override
+            int calculateFragmentSize(int packetLimit, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                return packetLimit - headerSize - macLen;
+            }
+
+            @Override
+            int calculatePacketSize(int fragmentSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                return fragmentSize + headerSize + macLen;
+            }
+
+            @Override
+            boolean isNullCipher() {
+                return true;
+            }
+        }
+    }
+
+    private static final
+            class StreamReadCipherGenerator implements ReadCipherGenerator {
+        @Override
+        public SSLReadCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new StreamReadCipher(authenticator, protocolVersion,
+                    algorithm, key, params, random);
+        }
+
+        static final class StreamReadCipher extends SSLReadCipher {
+            private final Cipher cipher;
+
+            StreamReadCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                cipher.init(Cipher.DECRYPT_MODE, key, params, random);
+            }
+
+            @Override
+            public Plaintext decrypt(byte contentType, ByteBuffer bb,
+                    byte[] sequence) throws GeneralSecurityException {
+                int len = bb.remaining();
+                int pos = bb.position();
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    if (len != cipher.update(dup, bb)) {
+                        // catch BouncyCastle buffering error
+                        throw new RuntimeException(
+                                "Unexpected number of plaintext bytes");
+                    }
+                    if (bb.position() != dup.position()) {
+                        throw new RuntimeException(
+                                "Unexpected ByteBuffer position");
+                    }
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+                bb.position(pos);
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Plaintext after DECRYPTION", bb.duplicate());
+                }
+
+                MAC signer = (MAC)authenticator;
+                if (signer.macAlg().size != 0) {
+                    checkStreamMac(signer, bb, contentType, sequence);
+                } else {
+                    authenticator.increaseSequenceNumber();
+                }
+
+                return new Plaintext(contentType,
+                        ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
+                        -1, -1L, bb.slice());
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int estimateFragmentSize(int packetSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                return packetSize - headerSize - macLen;
+            }
+        }
+    }
+
+    private static final
+            class StreamWriteCipherGenerator implements WriteCipherGenerator {
+        @Override
+        public SSLWriteCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new StreamWriteCipher(authenticator,
+                    protocolVersion, algorithm, key, params, random);
+        }
+
+        static final class StreamWriteCipher extends SSLWriteCipher {
+            private final Cipher cipher;
+
+            StreamWriteCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                cipher.init(Cipher.ENCRYPT_MODE, key, params, random);
+            }
+
+            @Override
+            public int encrypt(byte contentType, ByteBuffer bb) {
+                // add message authentication code
+                MAC signer = (MAC)authenticator;
+                if (signer.macAlg().size != 0) {
+                    addMac(signer, bb, contentType);
+                } else {
+                    authenticator.increaseSequenceNumber();
+                }
+
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.finest(
+                        "Padded plaintext before ENCRYPTION", bb.duplicate());
+                }
+
+                int len = bb.remaining();
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    if (len != cipher.update(dup, bb)) {
+                        // catch BouncyCastle buffering error
+                        throw new RuntimeException(
+                                "Unexpected number of plaintext bytes");
+                    }
+                    if (bb.position() != dup.position()) {
+                        throw new RuntimeException(
+                                "Unexpected ByteBuffer position");
+                    }
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+
+                return len;
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int getExplicitNonceSize() {
+                return 0;
+            }
+
+            @Override
+            int calculateFragmentSize(int packetLimit, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                return packetLimit - headerSize - macLen;
+            }
+
+            @Override
+            int calculatePacketSize(int fragmentSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                return fragmentSize + headerSize + macLen;
+            }
+        }
+    }
+
+    private static final
+            class T10BlockReadCipherGenerator implements ReadCipherGenerator {
+        @Override
+        public SSLReadCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new BlockReadCipher(authenticator,
+                    protocolVersion, algorithm, key, params, random);
+        }
+
+        static final class BlockReadCipher extends SSLReadCipher {
+            private final Cipher cipher;
+
+            BlockReadCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                cipher.init(Cipher.DECRYPT_MODE, key, params, random);
+            }
+
+            @Override
+            public Plaintext decrypt(byte contentType, ByteBuffer bb,
+                    byte[] sequence) throws GeneralSecurityException {
+                BadPaddingException reservedBPE = null;
+
+                // sanity check length of the ciphertext
+                MAC signer = (MAC)authenticator;
+                int cipheredLength = bb.remaining();
+                int tagLen = signer.macAlg().size;
+                if (tagLen != 0) {
+                    if (!sanityCheck(tagLen, bb.remaining())) {
+                        reservedBPE = new BadPaddingException(
+                                "ciphertext sanity check failed");
+                    }
+                }
+                // decryption
+                int len = bb.remaining();
+                int pos = bb.position();
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    if (len != cipher.update(dup, bb)) {
+                        // catch BouncyCastle buffering error
+                        throw new RuntimeException(
+                                "Unexpected number of plaintext bytes");
+                    }
+
+                    if (bb.position() != dup.position()) {
+                        throw new RuntimeException(
+                                "Unexpected ByteBuffer position");
+                    }
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Padded plaintext after DECRYPTION",
+                            bb.duplicate().position(pos));
+                }
+
+                // remove the block padding
+                int blockSize = cipher.getBlockSize();
+                bb.position(pos);
+                try {
+                    removePadding(bb, tagLen, blockSize, protocolVersion);
+                } catch (BadPaddingException bpe) {
+                    if (reservedBPE == null) {
+                        reservedBPE = bpe;
+                    }
+                }
+
+                // Requires message authentication code for null, stream and
+                // block cipher suites.
+                try {
+                    if (tagLen != 0) {
+                        checkCBCMac(signer, bb,
+                                contentType, cipheredLength, sequence);
+                    } else {
+                        authenticator.increaseSequenceNumber();
+                    }
+                } catch (BadPaddingException bpe) {
+                    if (reservedBPE == null) {
+                        reservedBPE = bpe;
+                    }
+                }
+
+                // Is it a failover?
+                if (reservedBPE != null) {
+                    throw reservedBPE;
+                }
+
+                return new Plaintext(contentType,
+                        ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
+                        -1, -1L, bb.slice());
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int estimateFragmentSize(int packetSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+
+                // No padding for a maximum fragment.
+                //
+                // 1 byte padding length field: 0x00
+                return packetSize - headerSize - macLen - 1;
+            }
+
+            /**
+             * Sanity check the length of a fragment before decryption.
+             *
+             * In CBC mode, check that the fragment length is one or multiple
+             * times of the block size of the cipher suite, and is at least
+             * one (one is the smallest size of padding in CBC mode) bigger
+             * than the tag size of the MAC algorithm except the explicit IV
+             * size for TLS 1.1 or later.
+             *
+             * In non-CBC mode, check that the fragment length is not less than
+             * the tag size of the MAC algorithm.
+             *
+             * @return true if the length of a fragment matches above
+             *         requirements
+             */
+            private boolean sanityCheck(int tagLen, int fragmentLen) {
+                int blockSize = cipher.getBlockSize();
+                if ((fragmentLen % blockSize) == 0) {
+                    int minimal = tagLen + 1;
+                    minimal = (minimal >= blockSize) ? minimal : blockSize;
+
+                    return (fragmentLen >= minimal);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    private static final
+            class T10BlockWriteCipherGenerator implements WriteCipherGenerator {
+        @Override
+        public SSLWriteCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new BlockWriteCipher(authenticator,
+                    protocolVersion, algorithm, key, params, random);
+        }
+
+        static final class BlockWriteCipher extends SSLWriteCipher {
+            private final Cipher cipher;
+
+            BlockWriteCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                cipher.init(Cipher.ENCRYPT_MODE, key, params, random);
+            }
+
+            @Override
+            public int encrypt(byte contentType, ByteBuffer bb) {
+                int pos = bb.position();
+
+                // add message authentication code
+                MAC signer = (MAC)authenticator;
+                if (signer.macAlg().size != 0) {
+                    addMac(signer, bb, contentType);
+                } else {
+                    authenticator.increaseSequenceNumber();
+                }
+
+                int blockSize = cipher.getBlockSize();
+                int len = addPadding(bb, blockSize);
+                bb.position(pos);
+
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Padded plaintext before ENCRYPTION",
+                            bb.duplicate());
+                }
+
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    if (len != cipher.update(dup, bb)) {
+                        // catch BouncyCastle buffering error
+                        throw new RuntimeException(
+                                "Unexpected number of plaintext bytes");
+                    }
+
+                    if (bb.position() != dup.position()) {
+                        throw new RuntimeException(
+                                "Unexpected ByteBuffer position");
+                    }
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+
+                return len;
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int getExplicitNonceSize() {
+                return 0;
+            }
+
+            @Override
+            int calculateFragmentSize(int packetLimit, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                int blockSize = cipher.getBlockSize();
+                int fragLen = packetLimit - headerSize;
+                fragLen -= (fragLen % blockSize);   // cannot hold a block
+                // No padding for a maximum fragment.
+                fragLen -= 1;       // 1 byte padding length field: 0x00
+                fragLen -= macLen;
+                return fragLen;
+            }
+
+            @Override
+            int calculatePacketSize(int fragmentSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                int blockSize = cipher.getBlockSize();
+                int paddedLen = fragmentSize + macLen + 1;
+                if ((paddedLen % blockSize)  != 0) {
+                    paddedLen += blockSize - 1;
+                    paddedLen -= paddedLen % blockSize;
+                }
+
+                return headerSize + paddedLen;
+            }
+
+            @Override
+            boolean isCBCMode() {
+                return true;
+            }
+        }
+    }
+
+    // For TLS 1.1 and 1.2
+    private static final
+            class T11BlockReadCipherGenerator implements ReadCipherGenerator {
+        @Override
+        public SSLReadCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator, ProtocolVersion protocolVersion,
+                String algorithm, Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new BlockReadCipher(authenticator, protocolVersion,
+                    sslCipher, algorithm, key, params, random);
+        }
+
+        static final class BlockReadCipher extends SSLReadCipher {
+            private final Cipher cipher;
+
+            BlockReadCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion,
+                    SSLCipher sslCipher, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                if (params == null) {
+                    params = new IvParameterSpec(new byte[sslCipher.ivSize]);
+                }
+                cipher.init(Cipher.DECRYPT_MODE, key, params, random);
+            }
+
+            @Override
+            public Plaintext decrypt(byte contentType, ByteBuffer bb,
+                    byte[] sequence) throws GeneralSecurityException {
+                BadPaddingException reservedBPE = null;
+
+                // sanity check length of the ciphertext
+                MAC signer = (MAC)authenticator;
+                int cipheredLength = bb.remaining();
+                int tagLen = signer.macAlg().size;
+                if (tagLen != 0) {
+                    if (!sanityCheck(tagLen, bb.remaining())) {
+                        reservedBPE = new BadPaddingException(
+                                "ciphertext sanity check failed");
+                    }
+                }
+
+                // decryption
+                int len = bb.remaining();
+                int pos = bb.position();
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    if (len != cipher.update(dup, bb)) {
+                        // catch BouncyCastle buffering error
+                        throw new RuntimeException(
+                                "Unexpected number of plaintext bytes");
+                    }
+
+                    if (bb.position() != dup.position()) {
+                        throw new RuntimeException(
+                                "Unexpected ByteBuffer position");
+                    }
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Padded plaintext after DECRYPTION",
+                            bb.duplicate().position(pos));
+                }
+
+                // Ignore the explicit nonce.
+                bb.position(pos + cipher.getBlockSize());
+                pos = bb.position();
+
+                // remove the block padding
+                int blockSize = cipher.getBlockSize();
+                bb.position(pos);
+                try {
+                    removePadding(bb, tagLen, blockSize, protocolVersion);
+                } catch (BadPaddingException bpe) {
+                    if (reservedBPE == null) {
+                        reservedBPE = bpe;
+                    }
+                }
+
+                // Requires message authentication code for null, stream and
+                // block cipher suites.
+                try {
+                    if (tagLen != 0) {
+                        checkCBCMac(signer, bb,
+                                contentType, cipheredLength, sequence);
+                    } else {
+                        authenticator.increaseSequenceNumber();
+                    }
+                } catch (BadPaddingException bpe) {
+                    if (reservedBPE == null) {
+                        reservedBPE = bpe;
+                    }
+                }
+
+                // Is it a failover?
+                if (reservedBPE != null) {
+                    throw reservedBPE;
+                }
+
+                return new Plaintext(contentType,
+                        ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
+                        -1, -1L, bb.slice());
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int estimateFragmentSize(int packetSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+
+                // No padding for a maximum fragment.
+                //
+                // 1 byte padding length field: 0x00
+                int nonceSize = cipher.getBlockSize();
+                return packetSize - headerSize - nonceSize - macLen - 1;
+            }
+
+            /**
+             * Sanity check the length of a fragment before decryption.
+             *
+             * In CBC mode, check that the fragment length is one or multiple
+             * times of the block size of the cipher suite, and is at least
+             * one (one is the smallest size of padding in CBC mode) bigger
+             * than the tag size of the MAC algorithm except the explicit IV
+             * size for TLS 1.1 or later.
+             *
+             * In non-CBC mode, check that the fragment length is not less than
+             * the tag size of the MAC algorithm.
+             *
+             * @return true if the length of a fragment matches above
+             *         requirements
+             */
+            private boolean sanityCheck(int tagLen, int fragmentLen) {
+                int blockSize = cipher.getBlockSize();
+                if ((fragmentLen % blockSize) == 0) {
+                    int minimal = tagLen + 1;
+                    minimal = (minimal >= blockSize) ? minimal : blockSize;
+                    minimal += blockSize;
+
+                    return (fragmentLen >= minimal);
+                }
+
+                return false;
+            }
+        }
+    }
+
+    // For TLS 1.1 and 1.2
+    private static final
+            class T11BlockWriteCipherGenerator implements WriteCipherGenerator {
+        @Override
+        public SSLWriteCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator, ProtocolVersion protocolVersion,
+                String algorithm, Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new BlockWriteCipher(authenticator, protocolVersion,
+                    sslCipher, algorithm, key, params, random);
+        }
+
+        static final class BlockWriteCipher extends SSLWriteCipher {
+            private final Cipher cipher;
+            private final SecureRandom random;
+
+            BlockWriteCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion,
+                    SSLCipher sslCipher, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                this.random = random;
+                if (params == null) {
+                    params = new IvParameterSpec(new byte[sslCipher.ivSize]);
+                }
+                cipher.init(Cipher.ENCRYPT_MODE, key, params, random);
+            }
+
+            @Override
+            public int encrypt(byte contentType, ByteBuffer bb) {
+                // To be unique and aware of overflow-wrap, sequence number
+                // is used as the nonce_explicit of block cipher suites.
+                int pos = bb.position();
+
+                // add message authentication code
+                MAC signer = (MAC)authenticator;
+                if (signer.macAlg().size != 0) {
+                    addMac(signer, bb, contentType);
+                } else {
+                    authenticator.increaseSequenceNumber();
+                }
+
+                // DON'T WORRY, the nonce spaces are considered already.
+                byte[] nonce = new byte[cipher.getBlockSize()];
+                random.nextBytes(nonce);
+                pos = pos - nonce.length;
+                bb.position(pos);
+                bb.put(nonce);
+                bb.position(pos);
+
+                int blockSize = cipher.getBlockSize();
+                int len = addPadding(bb, blockSize);
+                bb.position(pos);
+
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Padded plaintext before ENCRYPTION",
+                            bb.duplicate());
+                }
+
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    if (len != cipher.update(dup, bb)) {
+                        // catch BouncyCastle buffering error
+                        throw new RuntimeException(
+                                "Unexpected number of plaintext bytes");
+                    }
+
+                    if (bb.position() != dup.position()) {
+                        throw new RuntimeException(
+                                "Unexpected ByteBuffer position");
+                    }
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+
+                return len;
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int getExplicitNonceSize() {
+                return cipher.getBlockSize();
+            }
+
+            @Override
+            int calculateFragmentSize(int packetLimit, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                int blockSize = cipher.getBlockSize();
+                int fragLen = packetLimit - headerSize - blockSize;
+                fragLen -= (fragLen % blockSize);   // cannot hold a block
+                // No padding for a maximum fragment.
+                fragLen -= 1;       // 1 byte padding length field: 0x00
+                fragLen -= macLen;
+                return fragLen;
+            }
+
+            @Override
+            int calculatePacketSize(int fragmentSize, int headerSize) {
+                int macLen = ((MAC)authenticator).macAlg().size;
+                int blockSize = cipher.getBlockSize();
+                int paddedLen = fragmentSize + macLen + 1;
+                if ((paddedLen % blockSize)  != 0) {
+                    paddedLen += blockSize - 1;
+                    paddedLen -= paddedLen % blockSize;
+                }
+
+                return headerSize + blockSize + paddedLen;
+            }
+
+            @Override
+            boolean isCBCMode() {
+                return true;
+            }
+        }
+    }
+
+    private static final
+            class T12GcmReadCipherGenerator implements ReadCipherGenerator {
+        @Override
+        public SSLReadCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new GcmReadCipher(authenticator, protocolVersion, sslCipher,
+                    algorithm, key, params, random);
+        }
+
+        static final class GcmReadCipher extends SSLReadCipher {
+            private final Cipher cipher;
+            private final int tagSize;
+            private final Key key;
+            private final byte[] fixedIv;
+            private final int recordIvSize;
+            private final SecureRandom random;
+
+            GcmReadCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion,
+                    SSLCipher sslCipher, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                this.tagSize = sslCipher.tagSize;
+                this.key = key;
+                this.fixedIv = ((IvParameterSpec)params).getIV();
+                this.recordIvSize = sslCipher.ivSize - sslCipher.fixedIvSize;
+                this.random = random;
+
+                // DON'T initialize the cipher for AEAD!
+            }
+
+            @Override
+            public Plaintext decrypt(byte contentType, ByteBuffer bb,
+                    byte[] sequence) throws GeneralSecurityException {
+                if (bb.remaining() < (recordIvSize + tagSize)) {
+                    throw new BadPaddingException(
+                        "Insufficient buffer remaining for AEAD cipher " +
+                        "fragment (" + bb.remaining() + "). Needs to be " +
+                        "more than or equal to IV size (" + recordIvSize +
+                         ") + tag size (" + tagSize + ")");
+                }
+
+                // initialize the AEAD cipher for the unique IV
+                byte[] iv = Arrays.copyOf(fixedIv,
+                                    fixedIv.length + recordIvSize);
+                bb.get(iv, fixedIv.length, recordIvSize);
+                GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
+                try {
+                    cipher.init(Cipher.DECRYPT_MODE, key, spec, random);
+                } catch (InvalidKeyException |
+                            InvalidAlgorithmParameterException ikae) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                                "invalid key or spec in GCM mode", ikae);
+                }
+
+                // update the additional authentication data
+                byte[] aad = authenticator.acquireAuthenticationBytes(
+                        contentType, bb.remaining() - tagSize,
+                        sequence);
+                cipher.updateAAD(aad);
+
+                // DON'T decrypt the nonce_explicit for AEAD mode. The buffer
+                // position has moved out of the nonce_explicit range.
+                int len, pos = bb.position();
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    len = cipher.doFinal(dup, bb);
+                } catch (IllegalBlockSizeException ibse) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                        "Cipher error in AEAD mode \"" + ibse.getMessage() +
+                        " \"in JCE provider " + cipher.getProvider().getName());
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+                // reset the limit to the end of the decrypted data
+                bb.position(pos);
+                bb.limit(pos + len);
+
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Plaintext after DECRYPTION", bb.duplicate());
+                }
+
+                return new Plaintext(contentType,
+                        ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
+                        -1, -1L, bb.slice());
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int estimateFragmentSize(int packetSize, int headerSize) {
+                return packetSize - headerSize - recordIvSize - tagSize;
+            }
+        }
+    }
+
+    private static final
+            class T12GcmWriteCipherGenerator implements WriteCipherGenerator {
+        @Override
+        public SSLWriteCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator,
+                ProtocolVersion protocolVersion, String algorithm,
+                Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new GcmWriteCipher(authenticator, protocolVersion, sslCipher,
+                    algorithm, key, params, random);
+        }
+
+        private static final class GcmWriteCipher extends SSLWriteCipher {
+            private final Cipher cipher;
+            private final int tagSize;
+            private final Key key;
+            private final byte[] fixedIv;
+            private final int recordIvSize;
+            private final SecureRandom random;
+
+            GcmWriteCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion,
+                    SSLCipher sslCipher, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                this.tagSize = sslCipher.tagSize;
+                this.key = key;
+                this.fixedIv = ((IvParameterSpec)params).getIV();
+                this.recordIvSize = sslCipher.ivSize - sslCipher.fixedIvSize;
+                this.random = random;
+
+                // DON'T initialize the cipher for AEAD!
+            }
+
+            @Override
+            public int encrypt(byte contentType,
+                    ByteBuffer bb) {
+                // To be unique and aware of overflow-wrap, sequence number
+                // is used as the nonce_explicit of AEAD cipher suites.
+                byte[] nonce = authenticator.sequenceNumber();
+
+                // initialize the AEAD cipher for the unique IV
+                byte[] iv = Arrays.copyOf(fixedIv,
+                                            fixedIv.length + nonce.length);
+                System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length);
+
+                GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
+                try {
+                    cipher.init(Cipher.ENCRYPT_MODE, key, spec, random);
+                } catch (InvalidKeyException |
+                            InvalidAlgorithmParameterException ikae) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                                "invalid key or spec in GCM mode", ikae);
+                }
+
+                // Update the additional authentication data, using the
+                // implicit sequence number of the authenticator.
+                byte[] aad = authenticator.acquireAuthenticationBytes(
+                                        contentType, bb.remaining(), null);
+                cipher.updateAAD(aad);
+
+                // DON'T WORRY, the nonce spaces are considered already.
+                bb.position(bb.position() - nonce.length);
+                bb.put(nonce);
+
+                // DON'T encrypt the nonce for AEAD mode.
+                int len, pos = bb.position();
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Plaintext before ENCRYPTION",
+                            bb.duplicate());
+                }
+
+                ByteBuffer dup = bb.duplicate();
+                int outputSize = cipher.getOutputSize(dup.remaining());
+                if (outputSize > bb.remaining()) {
+                    // Need to expand the limit of the output buffer for
+                    // the authentication tag.
+                    //
+                    // DON'T worry about the buffer's capacity, we have
+                    // reserved space for the authentication tag.
+                    bb.limit(pos + outputSize);
+                }
+
+                try {
+                    len = cipher.doFinal(dup, bb);
+                } catch (IllegalBlockSizeException |
+                            BadPaddingException | ShortBufferException ibse) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                            "Cipher error in AEAD mode in JCE provider " +
+                            cipher.getProvider().getName(), ibse);
+                }
+
+                if (len != outputSize) {
+                    throw new RuntimeException(
+                            "Cipher buffering error in JCE provider " +
+                            cipher.getProvider().getName());
+                }
+
+                return len + nonce.length;
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int getExplicitNonceSize() {
+                return recordIvSize;
+            }
+
+            @Override
+            int calculateFragmentSize(int packetLimit, int headerSize) {
+                return packetLimit - headerSize - recordIvSize - tagSize;
+            }
+
+            @Override
+            int calculatePacketSize(int fragmentSize, int headerSize) {
+                return fragmentSize + headerSize + recordIvSize + tagSize;
+            }
+        }
+    }
+
+    private static final
+            class T13GcmReadCipherGenerator implements ReadCipherGenerator {
+
+        @Override
+        public SSLReadCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator, ProtocolVersion protocolVersion,
+                String algorithm, Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new GcmReadCipher(authenticator, protocolVersion, sslCipher,
+                    algorithm, key, params, random);
+        }
+
+        static final class GcmReadCipher extends SSLReadCipher {
+            private final Cipher cipher;
+            private final int tagSize;
+            private final Key key;
+            private final byte[] iv;
+            private final SecureRandom random;
+
+            GcmReadCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion,
+                    SSLCipher sslCipher, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                this.tagSize = sslCipher.tagSize;
+                this.key = key;
+                this.iv = ((IvParameterSpec)params).getIV();
+                this.random = random;
+
+                keyLimitCountdown = cipherLimits.getOrDefault(
+                        algorithm.toUpperCase() + ":" + tag[0], 0L);
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine("KeyLimit read side: algorithm = " +
+                            algorithm.toUpperCase() + ":" + tag[0] +
+                            "\ncountdown value = " + keyLimitCountdown);
+                }
+                if (keyLimitCountdown > 0) {
+                    keyLimitEnabled = true;
+                }
+                // DON'T initialize the cipher for AEAD!
+            }
+
+            @Override
+            public Plaintext decrypt(byte contentType, ByteBuffer bb,
+                    byte[] sequence) throws GeneralSecurityException {
+                // An implementation may receive an unencrypted record of type
+                // change_cipher_spec consisting of the single byte value 0x01
+                // at any time after the first ClientHello message has been
+                // sent or received and before the peer's Finished message has
+                // been received and MUST simply drop it without further
+                // processing.
+                if (contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
+                    return new Plaintext(contentType,
+                        ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
+                        -1, -1L, bb.slice());
+                }
+
+                if (bb.remaining() <= tagSize) {
+                    throw new BadPaddingException(
+                        "Insufficient buffer remaining for AEAD cipher " +
+                        "fragment (" + bb.remaining() + "). Needs to be " +
+                        "more than tag size (" + tagSize + ")");
+                }
+
+                byte[] sn = sequence;
+                if (sn == null) {
+                    sn = authenticator.sequenceNumber();
+                }
+                byte[] nonce = iv.clone();
+                int offset = nonce.length - sn.length;
+                for (int i = 0; i < sn.length; i++) {
+                    nonce[offset + i] ^= sn[i];
+                }
+
+                // initialize the AEAD cipher for the unique IV
+                GCMParameterSpec spec =
+                        new GCMParameterSpec(tagSize * 8, nonce);
+                try {
+                    cipher.init(Cipher.DECRYPT_MODE, key, spec, random);
+                } catch (InvalidKeyException |
+                            InvalidAlgorithmParameterException ikae) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                                "invalid key or spec in GCM mode", ikae);
+                }
+
+                // Update the additional authentication data, using the
+                // implicit sequence number of the authenticator.
+                byte[] aad = authenticator.acquireAuthenticationBytes(
+                                        contentType, bb.remaining(), sn);
+                cipher.updateAAD(aad);
+
+                int len, pos = bb.position();
+                ByteBuffer dup = bb.duplicate();
+                try {
+                    len = cipher.doFinal(dup, bb);
+                } catch (IllegalBlockSizeException ibse) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                        "Cipher error in AEAD mode \"" + ibse.getMessage() +
+                        " \"in JCE provider " + cipher.getProvider().getName());
+                } catch (ShortBufferException sbe) {
+                    // catch BouncyCastle buffering error
+                    throw new RuntimeException("Cipher buffering error in " +
+                        "JCE provider " + cipher.getProvider().getName(), sbe);
+                }
+                // reset the limit to the end of the decrypted data
+                bb.position(pos);
+                bb.limit(pos + len);
+
+                // remove inner plaintext padding
+                int i = bb.limit() - 1;
+                for (; i > 0 && bb.get(i) == 0; i--) {
+                    // blank
+                }
+                if (i < (pos + 1)) {
+                    throw new BadPaddingException(
+                            "Incorrect inner plaintext: no content type");
+                }
+                contentType = bb.get(i);
+                bb.limit(i);
+
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Plaintext after DECRYPTION", bb.duplicate());
+                }
+                if (keyLimitEnabled) {
+                    keyLimitCountdown -= len;
+                }
+
+                return new Plaintext(contentType,
+                        ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor,
+                        -1, -1L, bb.slice());
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int estimateFragmentSize(int packetSize, int headerSize) {
+                return packetSize - headerSize - tagSize;
+            }
+        }
+    }
+
+    private static final
+            class T13GcmWriteCipherGenerator implements WriteCipherGenerator {
+        @Override
+        public SSLWriteCipher createCipher(SSLCipher sslCipher,
+                Authenticator authenticator, ProtocolVersion protocolVersion,
+                String algorithm, Key key, AlgorithmParameterSpec params,
+                SecureRandom random) throws GeneralSecurityException {
+            return new GcmWriteCipher(authenticator, protocolVersion, sslCipher,
+                    algorithm, key, params, random);
+        }
+
+        private static final class GcmWriteCipher extends SSLWriteCipher {
+            private final Cipher cipher;
+            private final int tagSize;
+            private final Key key;
+            private final byte[] iv;
+            private final SecureRandom random;
+
+            GcmWriteCipher(Authenticator authenticator,
+                    ProtocolVersion protocolVersion,
+                    SSLCipher sslCipher, String algorithm,
+                    Key key, AlgorithmParameterSpec params,
+                    SecureRandom random) throws GeneralSecurityException {
+                super(authenticator, protocolVersion);
+                this.cipher = JsseJce.getCipher(algorithm);
+                this.tagSize = sslCipher.tagSize;
+                this.key = key;
+                this.iv = ((IvParameterSpec)params).getIV();
+                this.random = random;
+
+                keyLimitCountdown = cipherLimits.getOrDefault(
+                        algorithm.toUpperCase() + ":" + tag[0], 0L);
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine("KeyLimit write side: algorithm = "
+                            + algorithm.toUpperCase() + ":" + tag[0] +
+                            "\ncountdown value = " + keyLimitCountdown);
+                }
+                if (keyLimitCountdown > 0) {
+                    keyLimitEnabled = true;
+                }
+
+                // DON'T initialize the cipher for AEAD!
+            }
+
+            @Override
+            public int encrypt(byte contentType,
+                    ByteBuffer bb) {
+                byte[] sn = authenticator.sequenceNumber();
+                byte[] nonce = iv.clone();
+                int offset = nonce.length - sn.length;
+                for (int i = 0; i < sn.length; i++) {
+                    nonce[offset + i] ^= sn[i];
+                }
+
+                // initialize the AEAD cipher for the unique IV
+                GCMParameterSpec spec =
+                        new GCMParameterSpec(tagSize * 8, nonce);
+                try {
+                    cipher.init(Cipher.ENCRYPT_MODE, key, spec, random);
+                } catch (InvalidKeyException |
+                            InvalidAlgorithmParameterException ikae) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                                "invalid key or spec in GCM mode", ikae);
+                }
+
+                // Update the additional authentication data, using the
+                // implicit sequence number of the authenticator.
+                int outputSize = cipher.getOutputSize(bb.remaining());
+                byte[] aad = authenticator.acquireAuthenticationBytes(
+                                        contentType, outputSize, sn);
+                cipher.updateAAD(aad);
+
+                int len, pos = bb.position();
+                if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) {
+                    SSLLogger.fine(
+                            "Plaintext before ENCRYPTION",
+                            bb.duplicate());
+                }
+
+                ByteBuffer dup = bb.duplicate();
+                if (outputSize > bb.remaining()) {
+                    // Need to expand the limit of the output buffer for
+                    // the authentication tag.
+                    //
+                    // DON'T worry about the buffer's capacity, we have
+                    // reserved space for the authentication tag.
+                    bb.limit(pos + outputSize);
+                }
+
+                try {
+                    len = cipher.doFinal(dup, bb);
+                } catch (IllegalBlockSizeException |
+                            BadPaddingException | ShortBufferException ibse) {
+                    // unlikely to happen
+                    throw new RuntimeException(
+                            "Cipher error in AEAD mode in JCE provider " +
+                            cipher.getProvider().getName(), ibse);
+                }
+
+                if (len != outputSize) {
+                    throw new RuntimeException(
+                            "Cipher buffering error in JCE provider " +
+                            cipher.getProvider().getName());
+                }
+
+                if (keyLimitEnabled) {
+                    keyLimitCountdown -= len;
+                }
+                return len;
+            }
+
+            @Override
+            void dispose() {
+                if (cipher != null) {
+                    try {
+                        cipher.doFinal();
+                    } catch (Exception e) {
+                        // swallow all types of exceptions.
+                    }
+                }
+            }
+
+            @Override
+            int getExplicitNonceSize() {
+                return 0;
+            }
+
+            @Override
+            int calculateFragmentSize(int packetLimit, int headerSize) {
+                return packetLimit - headerSize - tagSize;
+            }
+
+            @Override
+            int calculatePacketSize(int fragmentSize, int headerSize) {
+                return fragmentSize + headerSize + tagSize;
+            }
+        }
+    }
+
+    private static void addMac(MAC signer,
+            ByteBuffer destination, byte contentType) {
+        if (signer.macAlg().size != 0) {
+            int dstContent = destination.position();
+            byte[] hash = signer.compute(contentType, destination, false);
+
+            /*
+             * position was advanced to limit in MAC compute above.
+             *
+             * Mark next area as writable (above layers should have
+             * established that we have plenty of room), then write
+             * out the hash.
+             */
+            destination.limit(destination.limit() + hash.length);
+            destination.put(hash);
+
+            // reset the position and limit
+            destination.position(dstContent);
+        }
+    }
+
+    // for null and stream cipher
+    private static void checkStreamMac(MAC signer, ByteBuffer bb,
+            byte contentType,  byte[] sequence) throws BadPaddingException {
+        int tagLen = signer.macAlg().size;
+
+        // Requires message authentication code for null, stream and
+        // block cipher suites.
+        if (tagLen != 0) {
+            int contentLen = bb.remaining() - tagLen;
+            if (contentLen < 0) {
+                throw new BadPaddingException("bad record");
+            }
+
+            // Run MAC computation and comparison on the payload.
+            //
+            // MAC data would be stripped off during the check.
+            if (checkMacTags(contentType, bb, signer, sequence, false)) {
+                throw new BadPaddingException("bad record MAC");
+            }
+        }
+    }
+
+    // for CBC cipher
+    private static void checkCBCMac(MAC signer, ByteBuffer bb,
+            byte contentType, int cipheredLength,
+            byte[] sequence) throws BadPaddingException {
+        BadPaddingException reservedBPE = null;
+        int tagLen = signer.macAlg().size;
+        int pos = bb.position();
+
+        if (tagLen != 0) {
+            int contentLen = bb.remaining() - tagLen;
+            if (contentLen < 0) {
+                reservedBPE = new BadPaddingException("bad record");
+
+                // set offset of the dummy MAC
+                contentLen = cipheredLength - tagLen;
+                bb.limit(pos + cipheredLength);
+            }
+
+            // Run MAC computation and comparison on the payload.
+            //
+            // MAC data would be stripped off during the check.
+            if (checkMacTags(contentType, bb, signer, sequence, false)) {
+                if (reservedBPE == null) {
+                    reservedBPE =
+                            new BadPaddingException("bad record MAC");
+                }
+            }
+
+            // Run MAC computation and comparison on the remainder.
+            int remainingLen = calculateRemainingLen(
+                    signer, cipheredLength, contentLen);
+
+            // NOTE: remainingLen may be bigger (less than 1 block of the
+            // hash algorithm of the MAC) than the cipheredLength.
+            //
+            // Is it possible to use a static buffer, rather than allocate
+            // it dynamically?
+            remainingLen += signer.macAlg().size;
+            ByteBuffer temporary = ByteBuffer.allocate(remainingLen);
+
+            // Won't need to worry about the result on the remainder. And
+            // then we won't need to worry about what's actual data to
+            // check MAC tag on.  We start the check from the header of the
+            // buffer so that we don't need to construct a new byte buffer.
+            checkMacTags(contentType, temporary, signer, sequence, true);
+        }
+
+        // Is it a failover?
+        if (reservedBPE != null) {
+            throw reservedBPE;
+        }
+    }
+
+    /*
+     * Run MAC computation and comparison
+     */
+    private static boolean checkMacTags(byte contentType, ByteBuffer bb,
+            MAC signer, byte[] sequence, boolean isSimulated) {
+        int tagLen = signer.macAlg().size;
+        int position = bb.position();
+        int lim = bb.limit();
+        int macOffset = lim - tagLen;
+
+        bb.limit(macOffset);
+        byte[] hash = signer.compute(contentType, bb, sequence, isSimulated);
+        if (hash == null || tagLen != hash.length) {
+            // Something is wrong with MAC implementation.
+            throw new RuntimeException("Internal MAC error");
+        }
+
+        bb.position(macOffset);
+        bb.limit(lim);
+        try {
+            int[] results = compareMacTags(bb, hash);
+            return (results[0] != 0);
+        } finally {
+            // reset to the data
+            bb.position(position);
+            bb.limit(macOffset);
+        }
+    }
+
+    /*
+     * A constant-time comparison of the MAC tags.
+     *
+     * Please DON'T change the content of the ByteBuffer parameter!
+     */
+    private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
+        // An array of hits is used to prevent Hotspot optimization for
+        // the purpose of a constant-time check.
+        int[] results = {0, 0};     // {missed #, matched #}
+
+        // The caller ensures there are enough bytes available in the buffer.
+        // So we won't need to check the remaining of the buffer.
+        for (byte t : tag) {
+            if (bb.get() != t) {
+                results[0]++;       // mismatched bytes
+            } else {
+                results[1]++;       // matched bytes
+            }
+        }
+
+        return results;
+    }
+
+    /*
+     * Calculate the length of a dummy buffer to run MAC computation
+     * and comparison on the remainder.
+     *
+     * The caller MUST ensure that the fullLen is not less than usedLen.
+     */
+    private static int calculateRemainingLen(
+            MAC signer, int fullLen, int usedLen) {
+
+        int blockLen = signer.macAlg().hashBlockSize;
+        int minimalPaddingLen = signer.macAlg().minimalPaddingSize;
+
+        // (blockLen - minimalPaddingLen) is the maximum message size of
+        // the last block of hash function operation. See FIPS 180-4, or
+        // MD5 specification.
+        fullLen += 13 - (blockLen - minimalPaddingLen);
+        usedLen += 13 - (blockLen - minimalPaddingLen);
+
+        // Note: fullLen is always not less than usedLen, and blockLen
+        // is always bigger than minimalPaddingLen, so we don't worry
+        // about negative values. 0x01 is added to the result to ensure
+        // that the return value is positive.  The extra one byte does
+        // not impact the overall MAC compression function evaluations.
+        return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) -
+                Math.ceil(usedLen/(1.0d * blockLen))) * blockLen;
+    }
+
+    private static int addPadding(ByteBuffer bb, int blockSize) {
+
+        int     len = bb.remaining();
+        int     offset = bb.position();
+
+        int     newlen = len + 1;
+        byte    pad;
+        int     i;
+
+        if ((newlen % blockSize) != 0) {
+            newlen += blockSize - 1;
+            newlen -= newlen % blockSize;
+        }
+        pad = (byte) (newlen - len);
+
+        /*
+         * Update the limit to what will be padded.
+         */
+        bb.limit(newlen + offset);
+
+        /*
+         * TLS version of the padding works for both SSLv3 and TLSv1
+         */
+        for (i = 0, offset += len; i < pad; i++) {
+            bb.put(offset++, (byte) (pad - 1));
+        }
+
+        bb.position(offset);
+        bb.limit(offset);
+
+        return newlen;
+    }
+
+    private static int removePadding(ByteBuffer bb,
+            int tagLen, int blockSize,
+            ProtocolVersion protocolVersion) throws BadPaddingException {
+        int len = bb.remaining();
+        int offset = bb.position();
+
+        // last byte is length byte (i.e. actual padding length - 1)
+        int padOffset = offset + len - 1;
+        int padLen = bb.get(padOffset) & 0xFF;
+
+        int newLen = len - (padLen + 1);
+        if ((newLen - tagLen) < 0) {
+            // If the buffer is not long enough to contain the padding plus
+            // a MAC tag, do a dummy constant-time padding check.
+            //
+            // Note that it is a dummy check, so we won't care about what is
+            // the actual padding data.
+            checkPadding(bb.duplicate(), (byte)(padLen & 0xFF));
+
+            throw new BadPaddingException("Invalid Padding length: " + padLen);
+        }
+
+        // The padding data should be filled with the padding length value.
+        int[] results = checkPadding(
+                bb.duplicate().position(offset + newLen),
+                (byte)(padLen & 0xFF));
+        if (protocolVersion.useTLS10PlusSpec()) {
+            if (results[0] != 0) {          // padding data has invalid bytes
+                throw new BadPaddingException("Invalid TLS padding data");
+            }
+        } else { // SSLv3
+            // SSLv3 requires 0 <= length byte < block size
+            // some implementations do 1 <= length byte <= block size,
+            // so accept that as well
+            // v3 does not require any particular value for the other bytes
+            if (padLen > blockSize) {
+                throw new BadPaddingException("Padding length (" +
+                padLen + ") of SSLv3 message should not be bigger " +
+                "than the block size (" + blockSize + ")");
+            }
+        }
+
+        // Reset buffer limit to remove padding.
+        bb.limit(offset + newLen);
+
+        return newLen;
+    }
+
+    /*
+     * A constant-time check of the padding.
+     *
+     * NOTE that we are checking both the padding and the padLen bytes here.
+     *
+     * The caller MUST ensure that the bb parameter has remaining.
+     */
+    private static int[] checkPadding(ByteBuffer bb, byte pad) {
+        if (!bb.hasRemaining()) {
+            throw new RuntimeException("hasRemaining() must be positive");
+        }
+
+        // An array of hits is used to prevent Hotspot optimization for
+        // the purpose of a constant-time check.
+        int[] results = {0, 0};    // {missed #, matched #}
+        bb.mark();
+        for (int i = 0; i <= 256; bb.reset()) {
+            for (; bb.hasRemaining() && i <= 256; i++) {
+                if (bb.get() != pad) {
+                    results[0]++;       // mismatched padding data
+                } else {
+                    results[1]++;       // matched padding data
+                }
+            }
+        }
+
+        return results;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.AlgorithmConstraints;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.BiFunction;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SNIMatcher;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import sun.security.ssl.SSLExtension.ClientExtensions;
+import sun.security.ssl.SSLExtension.ServerExtensions;
+
+/**
+ * SSL/(D)TLS configuration.
+ */
+final class SSLConfiguration implements Cloneable {
+    // configurations with SSLParameters
+    AlgorithmConstraints        algorithmConstraints;
+    List<ProtocolVersion>       enabledProtocols;
+    List<CipherSuite>           enabledCipherSuites;
+    ClientAuthType              clientAuthType;
+    String                      identificationProtocol;
+    List<SNIServerName>         serverNames;
+    Collection<SNIMatcher>      sniMatchers;
+    String[]                    applicationProtocols;
+    boolean                     preferLocalCipherSuites;
+    boolean                     enableRetransmissions;
+    int                         maximumPacketSize;
+
+    // the maximum protocol version of enabled protocols
+    ProtocolVersion             maximumProtocolVersion;
+
+    // Configurations per SSLSocket or SSLEngine instance.
+    boolean                     isClientMode;
+    boolean                     enableSessionCreation;
+
+    // the application layer protocol negotiation configuration
+    BiFunction<SSLSocket, List<String>, String> socketAPSelector;
+    BiFunction<SSLEngine, List<String>, String> engineAPSelector;
+
+    HashMap<HandshakeCompletedListener, AccessControlContext>
+                                handshakeListeners;
+
+    boolean                     noSniExtension;
+    boolean                     noSniMatcher;
+
+    // To switch off the extended_master_secret extension.
+    static final boolean useExtendedMasterSecret;
+
+    // Allow session resumption without Extended Master Secret extension.
+    static final boolean allowLegacyResumption =
+        Utilities.getBooleanProperty("jdk.tls.allowLegacyResumption", true);
+
+    // Allow full handshake without Extended Master Secret extension.
+    static final boolean allowLegacyMasterSecret =
+        Utilities.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true);
+
+    // Allow full handshake without Extended Master Secret extension.
+    static final boolean useCompatibilityMode = Utilities.getBooleanProperty(
+            "jdk.tls.client.useCompatibilityMode", true);
+
+// TODO: Please remove after TLS 1.3 draft interop testing
+// delete me
+static int tls13VN;
+
+    // Is the extended_master_secret extension supported?
+    static {
+        boolean supportExtendedMasterSecret = Utilities.getBooleanProperty(
+                    "jdk.tls.useExtendedMasterSecret", true);
+        if (supportExtendedMasterSecret) {
+            try {
+                JsseJce.getKeyGenerator("SunTlsExtendedMasterSecret");
+            } catch (NoSuchAlgorithmException nae) {
+                supportExtendedMasterSecret = false;
+            }
+        }
+        useExtendedMasterSecret = supportExtendedMasterSecret;
+
+// delete me
+try {
+    tls13VN =
+        AccessController.doPrivileged(
+            new PrivilegedExceptionAction<Integer>() {
+                @Override
+                public Integer run() throws Exception {
+                    return Integer.parseInt(
+                        System.getProperty("jdk.tls13.version", "0304"), 16);
+                }
+            });
+} catch (PrivilegedActionException ex) {
+    // blank
+}
+    }
+
+    SSLConfiguration(SSLContextImpl sslContext, boolean isClientMode) {
+
+        // Configurations with SSLParameters, default values.
+        this.algorithmConstraints = SSLAlgorithmConstraints.DEFAULT;
+        this.enabledProtocols =
+                sslContext.getDefaultProtocolVersions(!isClientMode);
+        this.enabledCipherSuites =
+                sslContext.getDefaultCipherSuites(!isClientMode);
+        this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
+
+        this.identificationProtocol = null;
+        this.serverNames = Collections.<SNIServerName>emptyList();
+        this.sniMatchers = Collections.<SNIMatcher>emptyList();
+        this.preferLocalCipherSuites = false;
+
+        this.applicationProtocols = new String[0];
+        this.enableRetransmissions = sslContext.isDTLS();
+        this.maximumPacketSize = 0;         // please reset it explicitly later
+
+        this.maximumProtocolVersion = ProtocolVersion.NONE;
+        for (ProtocolVersion pv : enabledProtocols) {
+            if (pv.compareTo(maximumProtocolVersion) > 0) {
+                this.maximumProtocolVersion = pv;
+            }
+        }
+
+        // Configurations per SSLSocket or SSLEngine instance.
+        this.isClientMode = isClientMode;
+        this.enableSessionCreation = true;
+        this.socketAPSelector = null;
+        this.engineAPSelector = null;
+
+        this.handshakeListeners = null;
+        this.noSniExtension = false;
+        this.noSniMatcher = false;
+    }
+
+    SSLParameters getSSLParameters() {
+        SSLParameters params = new SSLParameters();
+
+        params.setAlgorithmConstraints(this.algorithmConstraints);
+        params.setProtocols(ProtocolVersion.toStringArray(enabledProtocols));
+        params.setCipherSuites(CipherSuite.namesOf(enabledCipherSuites));
+        switch (this.clientAuthType) {
+            case CLIENT_AUTH_REQUIRED:
+                params.setNeedClientAuth(true);
+                break;
+            case CLIENT_AUTH_REQUESTED:
+                params.setWantClientAuth(true);
+                break;
+            default:
+                params.setWantClientAuth(false);
+        }
+        params.setEndpointIdentificationAlgorithm(this.identificationProtocol);
+
+        if (serverNames.isEmpty() && !noSniExtension) {
+            // 'null' indicates none has been set
+            params.setServerNames(null);
+        } else {
+            params.setServerNames(this.serverNames);
+        }
+
+        if (sniMatchers.isEmpty() && !noSniMatcher) {
+            // 'null' indicates none has been set
+            params.setSNIMatchers(null);
+        } else {
+            params.setSNIMatchers(this.sniMatchers);
+        }
+
+        params.setApplicationProtocols(this.applicationProtocols);
+        params.setUseCipherSuitesOrder(this.preferLocalCipherSuites);
+        params.setEnableRetransmissions(this.enableRetransmissions);
+        params.setMaximumPacketSize(this.maximumPacketSize);
+
+        return params;
+    }
+
+    void setSSLParameters(SSLParameters params) {
+        AlgorithmConstraints ac = params.getAlgorithmConstraints();
+        if (ac != null) {
+            this.algorithmConstraints = ac;
+        }   // otherwise, use the default value
+
+        String[] sa = params.getCipherSuites();
+        if (sa != null) {
+            this.enabledCipherSuites = CipherSuite.validValuesOf(sa);
+        }   // otherwise, use the default values
+
+        sa = params.getProtocols();
+        if (sa != null) {
+            this.enabledProtocols = ProtocolVersion.namesOf(sa);
+
+            this.maximumProtocolVersion = ProtocolVersion.NONE;
+            for (ProtocolVersion pv : enabledProtocols) {
+                if (pv.compareTo(maximumProtocolVersion) > 0) {
+                    this.maximumProtocolVersion = pv;
+                }
+            }
+        }   // otherwise, use the default values
+
+        if (params.getNeedClientAuth()) {
+            this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUIRED;
+        } else if (params.getWantClientAuth()) {
+            this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUESTED;
+        } else {
+            this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
+        }
+
+        String s = params.getEndpointIdentificationAlgorithm();
+        if (s != null) {
+            this.identificationProtocol = s;
+        }   // otherwise, use the default value
+
+        List<SNIServerName> sniNames = params.getServerNames();
+        if (sniNames != null) {
+            this.noSniExtension = sniNames.isEmpty();
+            this.serverNames = sniNames;
+        }   // null if none has been set
+
+        Collection<SNIMatcher> matchers = params.getSNIMatchers();
+        if (matchers != null) {
+            this.noSniMatcher = matchers.isEmpty();
+            this.sniMatchers = matchers;
+        }   // null if none has been set
+
+        sa = params.getApplicationProtocols();
+        if (sa != null) {
+            this.applicationProtocols = sa;
+        }   // otherwise, use the default values
+
+        this.preferLocalCipherSuites = params.getUseCipherSuitesOrder();
+        this.enableRetransmissions = params.getEnableRetransmissions();
+        this.maximumPacketSize = params.getMaximumPacketSize();
+    }
+
+    // SSLSocket only
+    void addHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+
+        if (handshakeListeners == null) {
+            handshakeListeners = new HashMap<>(4);
+        }
+
+        handshakeListeners.put(listener, AccessController.getContext());
+    }
+
+    // SSLSocket only
+    void removeHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+
+        if (handshakeListeners == null) {
+            throw new IllegalArgumentException("no listeners");
+        }
+
+        if (handshakeListeners.remove(listener) == null) {
+            throw new IllegalArgumentException("listener not registered");
+        }
+
+        if (handshakeListeners.isEmpty()) {
+            handshakeListeners = null;
+        }
+    }
+
+    /**
+     * Return true if the extension is available.
+     */
+    boolean isAvailable(SSLExtension extension) {
+        for (ProtocolVersion protocolVersion : enabledProtocols) {
+            if (extension.isAvailable(protocolVersion)) {
+                if (isClientMode ?
+                        ClientExtensions.defaults.contains(extension) :
+                        ServerExtensions.defaults.contains(extension)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Return true if the extension is available for the specific protocol.
+     */
+    boolean isAvailable(SSLExtension extension,
+            ProtocolVersion protocolVersion) {
+        return extension.isAvailable(protocolVersion) &&
+                (isClientMode ? ClientExtensions.defaults.contains(extension) :
+                                ServerExtensions.defaults.contains(extension));
+    }
+
+    /**
+     * Get the enabled extensions for the specific handshake message.
+     *
+     * Used to consume handshake extensions.
+     */
+    SSLExtension[] getEnabledExtensions(SSLHandshake handshakeType) {
+        List<SSLExtension> extensions = new ArrayList<>();
+        for (SSLExtension extension : SSLExtension.values()) {
+            if (extension.handshakeType == handshakeType) {
+                if (isAvailable(extension)) {
+                    extensions.add(extension);
+                }
+            }
+        }
+
+        return extensions.toArray(new SSLExtension[0]);
+    }
+
+    /**
+     * Get the enabled extensions for the specific handshake message, excluding
+     * the specified extensions.
+     *
+     * Used to consume handshake extensions.
+     */
+    SSLExtension[] getExclusiveExtensions(SSLHandshake handshakeType,
+            List<SSLExtension> excluded) {
+        List<SSLExtension> extensions = new ArrayList<>();
+        for (SSLExtension extension : SSLExtension.values()) {
+            if (extension.handshakeType == handshakeType) {
+                if (isAvailable(extension) && !excluded.contains(extension)) {
+                    extensions.add(extension);
+                }
+            }
+        }
+
+        return extensions.toArray(new SSLExtension[0]);
+    }
+
+    /**
+     * Get the enabled extensions for the specific handshake message
+     * and the specific protocol version.
+     *
+     * Used to produce handshake extensions after handshake protocol
+     * version negotiation.
+     */
+    SSLExtension[] getEnabledExtensions(
+            SSLHandshake handshakeType, ProtocolVersion protocolVersion) {
+        return getEnabledExtensions(
+            handshakeType, Arrays.asList(protocolVersion));
+    }
+
+    /**
+     * Get the enabled extensions for the specific handshake message
+     * and the specific protocol versions.
+     *
+     * Used to produce ClientHello extensions before handshake protocol
+     * version negotiation.
+     */
+    SSLExtension[] getEnabledExtensions(
+            SSLHandshake handshakeType, List<ProtocolVersion> activeProtocols) {
+        List<SSLExtension> extensions = new ArrayList<>();
+        for (SSLExtension extension : SSLExtension.values()) {
+            if (extension.handshakeType == handshakeType) {
+                if (!isAvailable(extension)) {
+                    continue;
+                }
+
+                for (ProtocolVersion protocolVersion : activeProtocols) {
+                    if (extension.isAvailable(protocolVersion)) {
+                        extensions.add(extension);
+                        break;
+                    }
+                }
+            }
+        }
+
+        return extensions.toArray(new SSLExtension[0]);
+    }
+
+    @Override
+    @SuppressWarnings({"unchecked", "CloneDeclaresCloneNotSupported"})
+    public Object clone() {
+        // Note that only references to the configurations are copied.
+        try {
+            SSLConfiguration config = (SSLConfiguration)super.clone();
+            if (handshakeListeners != null) {
+                config.handshakeListeners =
+                    (HashMap<HandshakeCompletedListener, AccessControlContext>)
+                            handshakeListeners.clone();
+            }
+
+            return config;
+        } catch (CloneNotSupportedException cnse) {
+            // unlikely
+        }
+
+        return null;    // unlikely
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLConsumer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+
+interface SSLConsumer {
+    void consume(ConnectionContext context,
+            ByteBuffer message) throws IOException;
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,23 +25,25 @@
 
 package sun.security.ssl;
 
+import java.io.*;
 import java.net.Socket;
-
-import java.io.*;
-import java.util.*;
 import java.security.*;
 import java.security.cert.*;
-import java.security.cert.Certificate;
-
+import java.util.*;
 import javax.net.ssl.*;
-
+import sun.security.action.GetPropertyAction;
 import sun.security.provider.certpath.AlgorithmChecker;
-import sun.security.action.GetPropertyAction;
 import sun.security.validator.Validator;
 
-public abstract class SSLContextImpl extends SSLContextSpi {
+/**
+ * Implementation of an SSLContext.
+ *
+ * Implementation note: Instances of this class and the child classes are
+ * immutable, except that the context initialization (SSLContext.init()) may
+ * reset the key, trust managers and source of secure random.
+ */
 
-    private static final Debug debug = Debug.getInstance("ssl");
+public abstract class SSLContextImpl extends SSLContextSpi {
 
     private final EphemeralKeyManager ephemeralKeyManager;
     private final SSLSessionContextImpl clientCache;
@@ -54,15 +56,15 @@
     private SecureRandom secureRandom;
 
     // DTLS cookie exchange manager
-    private volatile HelloCookieManager helloCookieManager;
+    private volatile HelloCookieManager.Builder helloCookieManagerBuilder;
 
-    private final boolean clientEnableStapling = Debug.getBooleanProperty(
+    private final boolean clientEnableStapling = Utilities.getBooleanProperty(
             "jdk.tls.client.enableStatusRequestExtension", true);
-    private final boolean serverEnableStapling = Debug.getBooleanProperty(
+    private final boolean serverEnableStapling = Utilities.getBooleanProperty(
             "jdk.tls.server.enableStatusRequestExtension", false);
-    private final static Collection<CipherSuite> clientCustomizedCipherSuites =
+    private static final Collection<CipherSuite> clientCustomizedCipherSuites =
             getCustomizedCipherSuites("jdk.tls.client.cipherSuites");
-    private final static Collection<CipherSuite> serverCustomizedCipherSuites =
+    private static final Collection<CipherSuite> serverCustomizedCipherSuites =
             getCustomizedCipherSuites("jdk.tls.server.cipherSuites");
 
     private volatile StatusResponseManager statusResponseManager;
@@ -109,12 +111,12 @@
          * first connection to timeout and fail. Make sure it is
          * primed and ready by getting some initial output from it.
          */
-        if (debug != null && Debug.isOn("sslctx")) {
-            System.out.println("trigger seeding of SecureRandom");
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
+            SSLLogger.finest("trigger seeding of SecureRandom");
         }
         secureRandom.nextInt();
-        if (debug != null && Debug.isOn("sslctx")) {
-            System.out.println("done seeding SecureRandom");
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
+            SSLLogger.finest("done seeding of SecureRandom");
         }
 
         isInitialized = true;
@@ -168,10 +170,10 @@
             if (km instanceof X509ExtendedKeyManager) {
                 return (X509ExtendedKeyManager)km;
             }
-            if (debug != null && Debug.isOn("sslctx")) {
-                System.out.println(
-                    "X509KeyManager passed to " +
-                    "SSLContext.init():  need an " +
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
+                SSLLogger.warning(
+                    "X509KeyManager passed to SSLContext.init():  need an " +
                     "X509ExtendedKeyManager for SSLEngine use");
             }
             return new AbstractKeyManagerWrapper((X509KeyManager)km);
@@ -242,36 +244,26 @@
         return ephemeralKeyManager;
     }
 
-    // Used for DTLS in server mode only, see ServerHandshaker.
-    HelloCookieManager getHelloCookieManager() {
-        if (!isInitialized) {
-            throw new IllegalStateException("SSLContext is not initialized");
-        }
-
-        if (helloCookieManager != null) {
-            return helloCookieManager;
-        }
-
-        synchronized (this) {
-            if (helloCookieManager == null) {
-                helloCookieManager = getHelloCookieManager(secureRandom);
+    // Used for DTLS in server mode only.
+    HelloCookieManager getHelloCookieManager(ProtocolVersion protocolVersion) {
+        if (helloCookieManagerBuilder == null) {
+            synchronized (this) {
+                if (helloCookieManagerBuilder == null) {
+                    helloCookieManagerBuilder =
+                            new HelloCookieManager.Builder(secureRandom);
+                }
             }
         }
 
-        return helloCookieManager;
-    }
-
-    HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
-        throw new UnsupportedOperationException(
-                "Cookie exchange applies to DTLS only");
+        return helloCookieManagerBuilder.valueOf(protocolVersion);
     }
 
     StatusResponseManager getStatusResponseManager() {
         if (serverEnableStapling && statusResponseManager == null) {
             synchronized (this) {
                 if (statusResponseManager == null) {
-                    if (debug != null && Debug.isOn("sslctx")) {
-                        System.out.println(
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
+                        SSLLogger.finest(
                                 "Initializing StatusResponseManager");
                     }
                     statusResponseManager = new StatusResponseManager();
@@ -282,52 +274,55 @@
         return statusResponseManager;
     }
 
-    // Get supported ProtocolList.
-    abstract ProtocolList getSuportedProtocolList();
+    // Get supported protocols.
+    abstract List<ProtocolVersion> getSupportedProtocolVersions();
+
+    // Get default protocols for server mode.
+    abstract List<ProtocolVersion> getServerDefaultProtocolVersions();
 
-    // Get default ProtocolList for server mode.
-    abstract ProtocolList getServerDefaultProtocolList();
+    // Get default protocols for client mode.
+    abstract List<ProtocolVersion> getClientDefaultProtocolVersions();
 
-    // Get default ProtocolList for client mode.
-    abstract ProtocolList getClientDefaultProtocolList();
+    // Get supported CipherSuite list.
+    abstract List<CipherSuite> getSupportedCipherSuites();
 
-    // Get supported CipherSuiteList.
-    abstract CipherSuiteList getSupportedCipherSuiteList();
+    // Get default CipherSuite list for server mode.
+    abstract List<CipherSuite> getServerDefaultCipherSuites();
 
-    // Get default CipherSuiteList for server mode.
-    abstract CipherSuiteList getServerDefaultCipherSuiteList();
+    // Get default CipherSuite list for client mode.
+    abstract List<CipherSuite> getClientDefaultCipherSuites();
 
-    // Get default CipherSuiteList for client mode.
-    abstract CipherSuiteList getClientDefaultCipherSuiteList();
+    // Is the context for DTLS protocols?
+    abstract boolean isDTLS();
 
-    // Get default ProtocolList.
-    ProtocolList getDefaultProtocolList(boolean roleIsServer) {
-        return roleIsServer ? getServerDefaultProtocolList()
-                            : getClientDefaultProtocolList();
+    // Get default protocols.
+    List<ProtocolVersion> getDefaultProtocolVersions(boolean roleIsServer) {
+        return roleIsServer ? getServerDefaultProtocolVersions()
+                            : getClientDefaultProtocolVersions();
     }
 
-    // Get default CipherSuiteList.
-    CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) {
-        return roleIsServer ? getServerDefaultCipherSuiteList()
-                            : getClientDefaultCipherSuiteList();
+    // Get default CipherSuite list.
+    List<CipherSuite> getDefaultCipherSuites(boolean roleIsServer) {
+        return roleIsServer ? getServerDefaultCipherSuites()
+                            : getClientDefaultCipherSuites();
     }
 
     /**
      * Return whether a protocol list is the original default enabled
      * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
      */
-    boolean isDefaultProtocolList(ProtocolList protocols) {
-        return (protocols == getServerDefaultProtocolList()) ||
-               (protocols == getClientDefaultProtocolList());
+    boolean isDefaultProtocolVesions(List<ProtocolVersion> protocols) {
+        return (protocols == getServerDefaultProtocolVersions()) ||
+               (protocols == getClientDefaultProtocolVersions());
     }
 
     /**
      * Return whether a protocol list is the original default enabled
      * protocols.  See: SSLSocket/SSLEngine.setEnabledProtocols()
      */
-    boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
-        return (cipherSuites == getServerDefaultCipherSuiteList()) ||
-               (cipherSuites == getClientDefaultCipherSuiteList());
+    boolean isDefaultCipherSuiteList(List<CipherSuite> cipherSuites) {
+        return (cipherSuites == getServerDefaultCipherSuites()) ||
+               (cipherSuites == getClientDefaultCipherSuites());
     }
 
     /**
@@ -342,93 +337,83 @@
         return isClient ? clientEnableStapling : serverEnableStapling;
     }
 
-
     /*
      * Return the list of all available CipherSuites that are supported
      * using currently installed providers.
      */
-    private static CipherSuiteList getApplicableSupportedCipherSuiteList(
-            ProtocolList protocols) {
+    private static List<CipherSuite> getApplicableSupportedCipherSuites(
+            List<ProtocolVersion> protocols) {
 
-        return getApplicableCipherSuiteList(
-                CipherSuite.allowedCipherSuites(),
-                protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY);
+        return getApplicableCipherSuites(
+                CipherSuite.allowedCipherSuites(), protocols);
     }
 
     /*
      * Return the list of all available CipherSuites that are default enabled
      * in client or server side.
      */
-    private static CipherSuiteList getApplicableEnabledCipherSuiteList(
-            ProtocolList protocols, boolean isClient) {
+    private static List<CipherSuite> getApplicableEnabledCipherSuites(
+            List<ProtocolVersion> protocols, boolean isClient) {
 
         if (isClient) {
             if (!clientCustomizedCipherSuites.isEmpty()) {
-                return getApplicableCipherSuiteList(
-                        clientCustomizedCipherSuites,
-                        protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY);
+                return getApplicableCipherSuites(
+                        clientCustomizedCipherSuites, protocols);
             }
         } else {
             if (!serverCustomizedCipherSuites.isEmpty()) {
-                return getApplicableCipherSuiteList(
-                        serverCustomizedCipherSuites,
-                        protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY);
+                return getApplicableCipherSuites(
+                        serverCustomizedCipherSuites, protocols);
             }
         }
 
-        return getApplicableCipherSuiteList(
-                CipherSuite.allowedCipherSuites(),
-                protocols, CipherSuite.DEFAULT_SUITES_PRIORITY);
+        return getApplicableCipherSuites(
+                CipherSuite.defaultCipherSuites(), protocols);
     }
 
     /*
      * Return the list of available CipherSuites which are applicable to
      * the specified protocols.
      */
-    private static CipherSuiteList getApplicableCipherSuiteList(
+    private static List<CipherSuite> getApplicableCipherSuites(
             Collection<CipherSuite> allowedCipherSuites,
-            ProtocolList protocols, int minPriority) {
-
+            List<ProtocolVersion> protocols) {
         TreeSet<CipherSuite> suites = new TreeSet<>();
-        if (!(protocols.collection().isEmpty()) &&
-                protocols.min.v != ProtocolVersion.NONE.v) {
+        if (protocols != null && (!protocols.isEmpty())) {
             for (CipherSuite suite : allowedCipherSuites) {
-                if (!suite.allowed || suite.priority < minPriority) {
+                if (!suite.isAvailable()) {
                     continue;
                 }
 
-                if (suite.isAvailable() &&
-                        !protocols.min.obsoletes(suite) &&
-                        protocols.max.supports(suite)) {
+                boolean isSupported = false;
+                for (ProtocolVersion protocol : protocols) {
+                    if (!suite.supports(protocol)) {
+                        continue;
+                    }
+
                     if (SSLAlgorithmConstraints.DEFAULT.permits(
                             EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
                             suite.name, null)) {
                         suites.add(suite);
-                    } else {
-                        if (debug != null && Debug.isOn("sslctx") &&
-                                Debug.isOn("verbose")) {
-                            System.out.println(
-                                    "Ignoring disabled cipher suite: " +
-                                            suite.name);
-                        }
+                        isSupported = true;
+                    } else if (SSLLogger.isOn &&
+                            SSLLogger.isOn("ssl,sslctx,verbose")) {
+                        SSLLogger.fine(
+                                "Ignore disabled cipher suite: " + suite.name);
                     }
-                } else if (debug != null &&
-                        Debug.isOn("sslctx") && Debug.isOn("verbose")) {
-                    if (protocols.min.obsoletes(suite)) {
-                        System.out.println(
-                            "Ignoring obsoleted cipher suite: " + suite);
-                    } else if (!protocols.max.supports(suite)) {
-                        System.out.println(
-                            "Ignoring unsupported cipher suite: " + suite);
-                    } else {
-                        System.out.println(
-                            "Ignoring unavailable cipher suite: " + suite);
-                    }
+
+                    break;
+                }
+
+                if (!isSupported && SSLLogger.isOn &&
+                        SSLLogger.isOn("ssl,sslctx,verbose")) {
+                    SSLLogger.finest(
+                            "Ignore unsupported cipher suite: " + suite);
                 }
             }
         }
 
-        return new CipherSuiteList(suites);
+        return new ArrayList<>(suites);
     }
 
     /*
@@ -438,8 +423,8 @@
             String propertyName) {
 
         String property = GetPropertyAction.privilegedGetProperty(propertyName);
-        if (debug != null && Debug.isOn("sslctx")) {
-            System.out.println(
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
+            SSLLogger.fine(
                     "System property " + propertyName + " is set to '" +
                     property + "'");
         }
@@ -463,10 +448,10 @@
 
                 CipherSuite suite;
                 try {
-                    suite = CipherSuite.valueOf(cipherSuiteNames[i]);
+                    suite = CipherSuite.nameOf(cipherSuiteNames[i]);
                 } catch (IllegalArgumentException iae) {
-                    if (debug != null && Debug.isOn("sslctx")) {
-                        System.out.println(
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
+                        SSLLogger.fine(
                                 "Unknown or unsupported cipher suite name: " +
                                 cipherSuiteNames[i]);
                     }
@@ -474,11 +459,11 @@
                     continue;
                 }
 
-                if (suite.isAvailable()) {
+                if (suite != null && suite.isAvailable()) {
                     cipherSuites.add(suite);
                 } else {
-                    if (debug != null && Debug.isOn("sslctx")) {
-                        System.out.println(
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
+                        SSLLogger.fine(
                                 "The current installed providers do not " +
                                 "support cipher suite: " + cipherSuiteNames[i]);
                     }
@@ -492,23 +477,23 @@
     }
 
 
-    private static String[] getAvailableProtocols(
+    private static List<ProtocolVersion> getAvailableProtocols(
             ProtocolVersion[] protocolCandidates) {
 
-        List<String> availableProtocols = Collections.<String>emptyList();
-        if (protocolCandidates !=  null && protocolCandidates.length != 0) {
+        List<ProtocolVersion> availableProtocols =
+                Collections.<ProtocolVersion>emptyList();
+        if (protocolCandidates != null && protocolCandidates.length != 0) {
             availableProtocols = new ArrayList<>(protocolCandidates.length);
             for (ProtocolVersion p : protocolCandidates) {
-                if (ProtocolVersion.availableProtocols.contains(p)) {
-                    availableProtocols.add(p.name);
+                if (p.isAvailable) {
+                    availableProtocols.add(p);
                 }
             }
         }
 
-        return availableProtocols.toArray(new String[0]);
+        return availableProtocols;
     }
 
-
     /*
      * The SSLContext implementation for SSL/(D)TLS algorithm
      *
@@ -548,79 +533,108 @@
      * @see SSLContext
      */
     private abstract static class AbstractTLSContext extends SSLContextImpl {
-        private static final ProtocolList supportedProtocolList;
-        private static final ProtocolList serverDefaultProtocolList;
+        private static final List<ProtocolVersion> supportedProtocols;
+        private static final List<ProtocolVersion> serverDefaultProtocols;
 
-        private static final CipherSuiteList supportedCipherSuiteList;
-        private static final CipherSuiteList serverDefaultCipherSuiteList;
+        private static final List<CipherSuite> supportedCipherSuites;
+        private static final List<CipherSuite> serverDefaultCipherSuites;
 
         static {
             if (SunJSSE.isFIPS()) {
-                supportedProtocolList = new ProtocolList(new String[] {
-                    ProtocolVersion.TLS10.name,
-                    ProtocolVersion.TLS11.name,
-                    ProtocolVersion.TLS12.name
-                });
+                supportedProtocols = Arrays.asList(
+                    ProtocolVersion.TLS13,
+                    ProtocolVersion.TLS12,
+                    ProtocolVersion.TLS11,
+                    ProtocolVersion.TLS10
+                );
 
-                serverDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
-                    ProtocolVersion.TLS10,
+                serverDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS13,
+                    ProtocolVersion.TLS12,
                     ProtocolVersion.TLS11,
-                    ProtocolVersion.TLS12
-                }));
+                    ProtocolVersion.TLS10
+                });
             } else {
-                supportedProtocolList = new ProtocolList(new String[] {
-                    ProtocolVersion.SSL20Hello.name,
-                    ProtocolVersion.SSL30.name,
-                    ProtocolVersion.TLS10.name,
-                    ProtocolVersion.TLS11.name,
-                    ProtocolVersion.TLS12.name
-                });
+                supportedProtocols = Arrays.asList(
+                    ProtocolVersion.TLS13,
+                    ProtocolVersion.TLS12,
+                    ProtocolVersion.TLS11,
+                    ProtocolVersion.TLS10,
+                    ProtocolVersion.SSL30,
+                    ProtocolVersion.SSL20Hello
+                );
 
-                serverDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
-                    ProtocolVersion.SSL20Hello,
+                serverDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS13,
+                    ProtocolVersion.TLS12,
+                    ProtocolVersion.TLS11,
+                    ProtocolVersion.TLS10,
                     ProtocolVersion.SSL30,
-                    ProtocolVersion.TLS10,
-                    ProtocolVersion.TLS11,
-                    ProtocolVersion.TLS12
-                }));
+                    ProtocolVersion.SSL20Hello
+                });
             }
 
-            supportedCipherSuiteList = getApplicableSupportedCipherSuiteList(
-                    supportedProtocolList);
-            serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
-                    serverDefaultProtocolList, false);
+            supportedCipherSuites = getApplicableSupportedCipherSuites(
+                    supportedProtocols);
+            serverDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    serverDefaultProtocols, false);
         }
 
         @Override
-        ProtocolList getSuportedProtocolList() {
-            return supportedProtocolList;
+        List<ProtocolVersion> getSupportedProtocolVersions() {
+            return supportedProtocols;
         }
 
         @Override
-        CipherSuiteList getSupportedCipherSuiteList() {
-            return supportedCipherSuiteList;
+        List<CipherSuite> getSupportedCipherSuites() {
+            return supportedCipherSuites;
         }
 
         @Override
-        ProtocolList getServerDefaultProtocolList() {
-            return serverDefaultProtocolList;
+        List<ProtocolVersion> getServerDefaultProtocolVersions() {
+            return serverDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getServerDefaultCipherSuiteList() {
-            return serverDefaultCipherSuiteList;
+        List<CipherSuite> getServerDefaultCipherSuites() {
+            return serverDefaultCipherSuites;
         }
 
         @Override
         SSLEngine createSSLEngineImpl() {
-            return new SSLEngineImpl(this, false);
+            return new SSLEngineImpl(this);
         }
 
         @Override
         SSLEngine createSSLEngineImpl(String host, int port) {
-            return new SSLEngineImpl(this, host, port, false);
+            return new SSLEngineImpl(this, host, port);
+        }
+
+        @Override
+        boolean isDTLS() {
+            return false;
+        }
+
+        static ProtocolVersion[] getSupportedProtocols() {
+            if (SunJSSE.isFIPS()) {
+                return new ProtocolVersion[] {
+                        ProtocolVersion.TLS13,
+                        ProtocolVersion.TLS12,
+                        ProtocolVersion.TLS11,
+                        ProtocolVersion.TLS10
+                };
+            } else {
+                return new ProtocolVersion[]{
+                        ProtocolVersion.TLS13,
+                        ProtocolVersion.TLS12,
+                        ProtocolVersion.TLS11,
+                        ProtocolVersion.TLS10,
+                        ProtocolVersion.SSL30,
+                        ProtocolVersion.SSL20Hello
+                };
+            }
         }
     }
 
@@ -630,35 +644,35 @@
      * @see SSLContext
      */
     public static final class TLS10Context extends AbstractTLSContext {
-        private static final ProtocolList clientDefaultProtocolList;
-        private static final CipherSuiteList clientDefaultCipherSuiteList;
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
 
         static {
             if (SunJSSE.isFIPS()) {
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
                     ProtocolVersion.TLS10
-                }));
+                });
             } else {
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
-                    ProtocolVersion.SSL30,
-                    ProtocolVersion.TLS10
-                }));
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS10,
+                    ProtocolVersion.SSL30
+                });
             }
 
-            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
-                    clientDefaultProtocolList, true);
+            clientDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    clientDefaultProtocols, true);
         }
 
         @Override
-        ProtocolList getClientDefaultProtocolList() {
-            return clientDefaultProtocolList;
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getClientDefaultCipherSuiteList() {
-            return clientDefaultCipherSuiteList;
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
         }
     }
 
@@ -668,38 +682,38 @@
      * @see SSLContext
      */
     public static final class TLS11Context extends AbstractTLSContext {
-        private static final ProtocolList clientDefaultProtocolList;
-        private static final CipherSuiteList clientDefaultCipherSuiteList;
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
 
         static {
             if (SunJSSE.isFIPS()) {
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
-                    ProtocolVersion.TLS10,
-                    ProtocolVersion.TLS11
-                }));
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS11,
+                    ProtocolVersion.TLS10
+                });
             } else {
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
-                    ProtocolVersion.SSL30,
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS11,
                     ProtocolVersion.TLS10,
-                    ProtocolVersion.TLS11
-                }));
+                    ProtocolVersion.SSL30
+                });
             }
 
-            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
-                    clientDefaultProtocolList, true);
+            clientDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    clientDefaultProtocols, true);
 
         }
 
         @Override
-        ProtocolList getClientDefaultProtocolList() {
-            return clientDefaultProtocolList;
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getClientDefaultCipherSuiteList() {
-            return clientDefaultCipherSuiteList;
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
         }
     }
 
@@ -709,39 +723,83 @@
      * @see SSLContext
      */
     public static final class TLS12Context extends AbstractTLSContext {
-        private static final ProtocolList clientDefaultProtocolList;
-        private static final CipherSuiteList clientDefaultCipherSuiteList;
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
 
         static {
             if (SunJSSE.isFIPS()) {
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
-                    ProtocolVersion.TLS10,
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS12,
                     ProtocolVersion.TLS11,
-                    ProtocolVersion.TLS12
-                }));
+                    ProtocolVersion.TLS10
+                });
             } else {
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(new ProtocolVersion[] {
-                    ProtocolVersion.SSL30,
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS12,
+                    ProtocolVersion.TLS11,
                     ProtocolVersion.TLS10,
-                    ProtocolVersion.TLS11,
-                    ProtocolVersion.TLS12
-                }));
+                    ProtocolVersion.SSL30
+                });
             }
 
-            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
-                    clientDefaultProtocolList, true);
+            clientDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    clientDefaultProtocols, true);
+        }
+
+        @Override
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
         }
 
         @Override
-        ProtocolList getClientDefaultProtocolList() {
-            return clientDefaultProtocolList;
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
+        }
+    }
+
+    /*
+     * The SSLContext implementation for TLS1.3 algorithm
+     *
+     * @see SSLContext
+     */
+    public static final class TLS13Context extends AbstractTLSContext {
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
+
+        static {
+            if (SunJSSE.isFIPS()) {
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS13,
+                    ProtocolVersion.TLS12,
+                    ProtocolVersion.TLS11,
+                    ProtocolVersion.TLS10
+                });
+            } else {
+                clientDefaultProtocols = getAvailableProtocols(
+                        new ProtocolVersion[] {
+                    ProtocolVersion.TLS13,
+                    ProtocolVersion.TLS12,
+                    ProtocolVersion.TLS11,
+                    ProtocolVersion.TLS10,
+                    ProtocolVersion.SSL30
+                });
+            }
+
+            clientDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    clientDefaultProtocols, true);
         }
 
         @Override
-        CipherSuiteList getClientDefaultCipherSuiteList() {
-            return clientDefaultCipherSuiteList;
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
+        }
+
+        @Override
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
         }
     }
 
@@ -751,10 +809,15 @@
      * @see SSLContext
      */
     private static class CustomizedSSLProtocols {
-        private static final String PROPERTY_NAME = "jdk.tls.client.protocols";
+        private static final String JDK_TLS_CLIENT_PROTOCOLS =
+                "jdk.tls.client.protocols";
+        private static final String JDK_TLS_SERVER_PROTOCOLS =
+                "jdk.tls.server.protocols";
         static IllegalArgumentException reservedException = null;
-        static ArrayList<ProtocolVersion>
-                                customizedProtocols = new ArrayList<>();
+        static final ArrayList<ProtocolVersion> customizedClientProtocols =
+                new ArrayList<>();
+        static final ArrayList<ProtocolVersion> customizedServerProtocols =
+                new ArrayList<>();
 
         // Don't want a java.lang.LinkageError for illegal system property.
         //
@@ -763,9 +826,18 @@
         // the provider service. Instead, please handle the initialization
         // exception in the caller's constructor.
         static {
-            String property = GetPropertyAction
-                    .privilegedGetProperty(PROPERTY_NAME);
-            if (property != null && property.length() != 0) {
+            populate(JDK_TLS_CLIENT_PROTOCOLS, customizedClientProtocols);
+            populate(JDK_TLS_SERVER_PROTOCOLS, customizedServerProtocols);
+        }
+
+        private static void populate(String propname,
+                ArrayList<ProtocolVersion> arrayList) {
+            String property = GetPropertyAction.privilegedGetProperty(propname);
+            if (property == null) {
+                return;
+            }
+
+            if (property.length() != 0) {
                 // remove double quote marks from beginning/end of the property
                 if (property.length() > 1 && property.charAt(0) == '"' &&
                         property.charAt(property.length() - 1) == '"') {
@@ -773,33 +845,32 @@
                 }
             }
 
-            if (property != null && property.length() != 0) {
+            if (property.length() != 0) {
                 String[] protocols = property.split(",");
                 for (int i = 0; i < protocols.length; i++) {
                     protocols[i] = protocols[i].trim();
                     // Is it a supported protocol name?
-                    try {
-                        ProtocolVersion pro =
-                                ProtocolVersion.valueOf(protocols[i]);
-
-                        if (SunJSSE.isFIPS() &&
-                                ((pro.v == ProtocolVersion.SSL30.v) ||
-                                 (pro.v == ProtocolVersion.SSL20Hello.v))) {
-                            reservedException = new IllegalArgumentException(
-                                    PROPERTY_NAME + ": " + pro +
-                                    " is not FIPS compliant");
+                    ProtocolVersion pv =
+                            ProtocolVersion.nameOf(protocols[i]);
+                    if (pv == null) {
+                        reservedException = new IllegalArgumentException(
+                            propname + ": " + protocols[i] +
+                            " is not a supported SSL protocol name");
+                    }
 
-                            break;
-                        }
+                    if (SunJSSE.isFIPS() &&
+                            ((pv == ProtocolVersion.SSL30) ||
+                             (pv == ProtocolVersion.SSL20Hello))) {
+                        reservedException = new IllegalArgumentException(
+                                propname + ": " + pv +
+                                " is not FIPS compliant");
 
-                        // ignore duplicated protocols
-                        if (!customizedProtocols.contains(pro)) {
-                            customizedProtocols.add(pro);
-                        }
-                    } catch (IllegalArgumentException iae) {
-                        reservedException = new IllegalArgumentException(
-                                PROPERTY_NAME + ": " + protocols[i] +
-                                " is not a standard SSL protocol name", iae);
+                        break;
+                    }
+
+                    // ignore duplicated protocols
+                    if (!arrayList.contains(pv)) {
+                        arrayList.add(pv);
                     }
                 }
             }
@@ -813,10 +884,11 @@
      */
     private static class CustomizedTLSContext extends AbstractTLSContext {
 
-        private static final ProtocolList clientDefaultProtocolList;
-        private static final CipherSuiteList clientDefaultCipherSuiteList;
-
-        private static IllegalArgumentException reservedException = null;
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<ProtocolVersion> serverDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
+        private static final List<CipherSuite> serverDefaultCipherSuites;
+        private static final IllegalArgumentException reservedException;
 
         // Don't want a java.lang.LinkageError for illegal system property.
         //
@@ -827,49 +899,69 @@
         static {
             reservedException = CustomizedSSLProtocols.reservedException;
             if (reservedException == null) {
-                ArrayList<ProtocolVersion>
-                        customizedTLSProtocols = new ArrayList<>();
-                for (ProtocolVersion protocol :
-                        CustomizedSSLProtocols.customizedProtocols) {
-                    if (!protocol.isDTLSProtocol()) {
-                        customizedTLSProtocols.add(protocol);
-                    }
+                clientDefaultProtocols = customizedProtocols(true,
+                        CustomizedSSLProtocols.customizedClientProtocols);
+                serverDefaultProtocols = customizedProtocols(false,
+                        CustomizedSSLProtocols.customizedServerProtocols);
+
+                clientDefaultCipherSuites =
+                        getApplicableEnabledCipherSuites(
+                                clientDefaultProtocols, true);
+                serverDefaultCipherSuites =
+                        getApplicableEnabledCipherSuites(
+                                serverDefaultProtocols, false);
+
+            } else {
+                // unlikely to be used
+                clientDefaultProtocols = null;
+                serverDefaultProtocols = null;
+                clientDefaultCipherSuites = null;
+                serverDefaultCipherSuites = null;
+            }
+        }
+
+        private static List<ProtocolVersion> customizedProtocols(
+                boolean client, List<ProtocolVersion> customized) {
+            List<ProtocolVersion> refactored = new ArrayList<>();
+            for (ProtocolVersion pv : customized) {
+                if (!pv.isDTLS) {
+                    refactored.add(pv);
                 }
+            }
 
-                // candidates for available protocols
-                ProtocolVersion[] candidates;
-                if (customizedTLSProtocols.isEmpty()) {
-                    // Use the default enabled client protocols if no
-                    // customized TLS protocols.
-                    if (SunJSSE.isFIPS()) {
-                        candidates = new ProtocolVersion[] {
-                            ProtocolVersion.TLS10,
-                            ProtocolVersion.TLS11,
-                            ProtocolVersion.TLS12
-                        };
-                    } else {
-                        candidates = new ProtocolVersion[] {
-                            ProtocolVersion.SSL30,
-                            ProtocolVersion.TLS10,
-                            ProtocolVersion.TLS11,
-                            ProtocolVersion.TLS12
-                        };
-                    }
+            // Use the default enabled protocols if no customization
+            ProtocolVersion[] candidates;
+            if (refactored.isEmpty()) {
+                if (client) {
+                    candidates = getProtocols();
                 } else {
-                    // Use the customized TLS protocols.
-                    candidates =
-                            new ProtocolVersion[customizedTLSProtocols.size()];
-                    candidates = customizedTLSProtocols.toArray(candidates);
+                    candidates = getSupportedProtocols();
                 }
+            } else {
+                // Use the customized TLS protocols.
+                candidates =
+                    refactored.toArray(new ProtocolVersion[refactored.size()]);
+            }
+
+            return getAvailableProtocols(candidates);
+        }
 
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(candidates));
-                clientDefaultCipherSuiteList =
-                        getApplicableEnabledCipherSuiteList(
-                                clientDefaultProtocolList, true);
+        static ProtocolVersion[] getProtocols() {
+            if (SunJSSE.isFIPS()) {
+                return new ProtocolVersion[]{
+                        ProtocolVersion.TLS13,
+                        ProtocolVersion.TLS12,
+                        ProtocolVersion.TLS11,
+                        ProtocolVersion.TLS10
+                };
             } else {
-                clientDefaultProtocolList = null;       // unlikely to be used
-                clientDefaultCipherSuiteList = null;    // unlikely to be used
+                return new ProtocolVersion[]{
+                        ProtocolVersion.TLS13,
+                        ProtocolVersion.TLS12,
+                        ProtocolVersion.TLS11,
+                        ProtocolVersion.TLS10,
+                        ProtocolVersion.SSL30
+                };
             }
         }
 
@@ -880,14 +972,26 @@
         }
 
         @Override
-        ProtocolList getClientDefaultProtocolList() {
-            return clientDefaultProtocolList;
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
+        }
+
+        @Override
+        List<ProtocolVersion> getServerDefaultProtocolVersions() {
+            return serverDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getClientDefaultCipherSuiteList() {
-            return clientDefaultCipherSuiteList;
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
         }
+
+        @Override
+        List<CipherSuite> getServerDefaultCipherSuites() {
+            return serverDefaultCipherSuites;
+        }
+
+
     }
 
     /*
@@ -909,30 +1013,33 @@
         private static final TrustManager[] trustManagers;
         private static final KeyManager[] keyManagers;
 
-        static Exception reservedException = null;
+        private static final Exception reservedException;
 
         static {
+            Exception reserved = null;
             TrustManager[] tmMediator;
             try {
                 tmMediator = getTrustManagers();
             } catch (Exception e) {
-                reservedException = e;
+                reserved = e;
                 tmMediator = new TrustManager[0];
             }
             trustManagers = tmMediator;
 
-            if (reservedException == null) {
+            if (reserved == null) {
                 KeyManager[] kmMediator;
                 try {
                     kmMediator = getKeyManagers();
                 } catch (Exception e) {
-                    reservedException = e;
+                    reserved = e;
                     kmMediator = new KeyManager[0];
                 }
                 keyManagers = kmMediator;
             } else {
                 keyManagers = new KeyManager[0];
             }
+
+            reservedException = reserved;
         }
 
         private static TrustManager[] getTrustManagers() throws Exception {
@@ -976,11 +1083,11 @@
             final String defaultKeyStore = props.get("keyStore");
             String defaultKeyStoreType = props.get("keyStoreType");
             String defaultKeyStoreProvider = props.get("keyStoreProvider");
-            if (debug != null && Debug.isOn("defaultctx")) {
-                System.out.println("keyStore is : " + defaultKeyStore);
-                System.out.println("keyStore type is : " +
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) {
+                SSLLogger.fine("keyStore is : " + defaultKeyStore);
+                SSLLogger.fine("keyStore type is : " +
                                         defaultKeyStoreType);
-                System.out.println("keyStore provider is : " +
+                SSLLogger.fine("keyStore provider is : " +
                                         defaultKeyStoreProvider);
             }
 
@@ -1014,8 +1121,8 @@
                  * Try to initialize key store.
                  */
                 if ((defaultKeyStoreType.length()) != 0) {
-                    if (debug != null && Debug.isOn("defaultctx")) {
-                        System.out.println("init keystore");
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) {
+                        SSLLogger.finest("init keystore");
                     }
                     if (defaultKeyStoreProvider.length() == 0) {
                         ks = KeyStore.getInstance(defaultKeyStoreType);
@@ -1037,8 +1144,8 @@
             /*
              * Try to initialize key manager.
              */
-            if (debug != null && Debug.isOn("defaultctx")) {
-                System.out.println("init keymanager of type " +
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) {
+                SSLLogger.fine("init keymanager of type " +
                     KeyManagerFactory.getDefaultAlgorithm());
             }
             KeyManagerFactory kmf = KeyManagerFactory.getInstance(
@@ -1095,8 +1202,8 @@
                 super.engineInit(DefaultManagersHolder.keyManagers,
                         DefaultManagersHolder.trustManagers, null);
             } catch (Exception e) {
-                if (debug != null && Debug.isOn("defaultctx")) {
-                    System.out.println("default context init failed: " + e);
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) {
+                    SSLLogger.fine("default context init failed: ", e);
                 }
                 throw e;
             }
@@ -1128,65 +1235,65 @@
      * @see SSLContext
      */
     private abstract static class AbstractDTLSContext extends SSLContextImpl {
-        private static final ProtocolList supportedProtocolList;
-        private static final ProtocolList serverDefaultProtocolList;
+        private static final List<ProtocolVersion> supportedProtocols;
+        private static final List<ProtocolVersion> serverDefaultProtocols;
 
-        private static final CipherSuiteList supportedCipherSuiteList;
-        private static final CipherSuiteList serverDefaultCipherSuiteList;
+        private static final List<CipherSuite> supportedCipherSuites;
+        private static final List<CipherSuite> serverDefaultCipherSuites;
 
         static {
             // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
-            supportedProtocolList = new ProtocolList(new String[] {
-                ProtocolVersion.DTLS10.name,
-                ProtocolVersion.DTLS12.name
-            });
+            supportedProtocols = Arrays.asList(
+                ProtocolVersion.DTLS12,
+                ProtocolVersion.DTLS10
+            );
 
             // available protocols for server mode
-            serverDefaultProtocolList = new ProtocolList(
-                    getAvailableProtocols(new ProtocolVersion[] {
-                ProtocolVersion.DTLS10,
-                ProtocolVersion.DTLS12
-            }));
+            serverDefaultProtocols = getAvailableProtocols(
+                    new ProtocolVersion[] {
+                ProtocolVersion.DTLS12,
+                ProtocolVersion.DTLS10
+            });
 
-            supportedCipherSuiteList = getApplicableSupportedCipherSuiteList(
-                    supportedProtocolList);
-            serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
-                    serverDefaultProtocolList, false);
+            supportedCipherSuites = getApplicableSupportedCipherSuites(
+                    supportedProtocols);
+            serverDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    serverDefaultProtocols, false);
         }
 
         @Override
-        ProtocolList getSuportedProtocolList() {
-            return supportedProtocolList;
+        List<ProtocolVersion> getSupportedProtocolVersions() {
+            return supportedProtocols;
         }
 
         @Override
-        CipherSuiteList getSupportedCipherSuiteList() {
-            return supportedCipherSuiteList;
+        List<CipherSuite> getSupportedCipherSuites() {
+            return supportedCipherSuites;
         }
 
         @Override
-        ProtocolList getServerDefaultProtocolList() {
-            return serverDefaultProtocolList;
+        List<ProtocolVersion> getServerDefaultProtocolVersions() {
+            return serverDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getServerDefaultCipherSuiteList() {
-            return serverDefaultCipherSuiteList;
+        List<CipherSuite> getServerDefaultCipherSuites() {
+            return serverDefaultCipherSuites;
         }
 
         @Override
         SSLEngine createSSLEngineImpl() {
-            return new SSLEngineImpl(this, true);
+            return new SSLEngineImpl(this);
         }
 
         @Override
         SSLEngine createSSLEngineImpl(String host, int port) {
-            return new SSLEngineImpl(this, host, port, true);
+            return new SSLEngineImpl(this, host, port);
         }
 
         @Override
-        HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
-            return new HelloCookieManager(secureRandom);
+        boolean isDTLS() {
+            return true;
         }
     }
 
@@ -1196,28 +1303,28 @@
      * @see SSLContext
      */
     public static final class DTLS10Context extends AbstractDTLSContext {
-        private static final ProtocolList clientDefaultProtocolList;
-        private static final CipherSuiteList clientDefaultCipherSuiteList;
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
 
         static {
             // available protocols for client mode
-            clientDefaultProtocolList = new ProtocolList(
-                    getAvailableProtocols(new ProtocolVersion[] {
+            clientDefaultProtocols = getAvailableProtocols(
+                    new ProtocolVersion[] {
                 ProtocolVersion.DTLS10
-            }));
+            });
 
-            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
-                    clientDefaultProtocolList, true);
+            clientDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    clientDefaultProtocols, true);
         }
 
         @Override
-        ProtocolList getClientDefaultProtocolList() {
-            return clientDefaultProtocolList;
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getClientDefaultCipherSuiteList() {
-            return clientDefaultCipherSuiteList;
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
         }
     }
 
@@ -1227,29 +1334,29 @@
      * @see SSLContext
      */
     public static final class DTLS12Context extends AbstractDTLSContext {
-        private static final ProtocolList clientDefaultProtocolList;
-        private static final CipherSuiteList clientDefaultCipherSuiteList;
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
 
         static {
             // available protocols for client mode
-            clientDefaultProtocolList = new ProtocolList(
-                    getAvailableProtocols(new ProtocolVersion[] {
-                ProtocolVersion.DTLS10,
-                ProtocolVersion.DTLS12
-            }));
+            clientDefaultProtocols = getAvailableProtocols(
+                    new ProtocolVersion[] {
+                ProtocolVersion.DTLS12,
+                ProtocolVersion.DTLS10
+            });
 
-            clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList(
-                    clientDefaultProtocolList, true);
+            clientDefaultCipherSuites = getApplicableEnabledCipherSuites(
+                    clientDefaultProtocols, true);
         }
 
         @Override
-        ProtocolList getClientDefaultProtocolList() {
-            return clientDefaultProtocolList;
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getClientDefaultCipherSuiteList() {
-            return clientDefaultCipherSuiteList;
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
         }
     }
 
@@ -1259,8 +1366,10 @@
      * @see SSLContext
      */
     private static class CustomizedDTLSContext extends AbstractDTLSContext {
-        private static final ProtocolList clientDefaultProtocolList;
-        private static final CipherSuiteList clientDefaultCipherSuiteList;
+        private static final List<ProtocolVersion> clientDefaultProtocols;
+        private static final List<ProtocolVersion> serverDefaultProtocols;
+        private static final List<CipherSuite> clientDefaultCipherSuites;
+        private static final List<CipherSuite> serverDefaultCipherSuites;
 
         private static IllegalArgumentException reservedException = null;
 
@@ -1273,43 +1382,53 @@
         static {
             reservedException = CustomizedSSLProtocols.reservedException;
             if (reservedException == null) {
-                ArrayList<ProtocolVersion>
-                        customizedDTLSProtocols = new ArrayList<>();
-                for (ProtocolVersion protocol :
-                        CustomizedSSLProtocols.customizedProtocols) {
-                    if (protocol.isDTLSProtocol()) {
-                        customizedDTLSProtocols.add(protocol);
-                    }
-                }
+                clientDefaultProtocols = customizedProtocols(true,
+                        CustomizedSSLProtocols.customizedClientProtocols);
+                serverDefaultProtocols = customizedProtocols(false,
+                        CustomizedSSLProtocols.customizedServerProtocols);
+
+                clientDefaultCipherSuites =
+                        getApplicableEnabledCipherSuites(
+                                clientDefaultProtocols, true);
+                serverDefaultCipherSuites =
+                        getApplicableEnabledCipherSuites(
+                                serverDefaultProtocols, false);
+
+            } else {
+                // unlikely to be used
+                clientDefaultProtocols = null;
+                serverDefaultProtocols = null;
+                clientDefaultCipherSuites = null;
+                serverDefaultCipherSuites = null;
+            }
+        }
 
-                // candidates for available protocols
-                ProtocolVersion[] candidates;
-                if (customizedDTLSProtocols.isEmpty()) {
-                    // Use the default enabled client protocols if no
-                    // customized TLS protocols.
-                    //
-                    // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
-                    candidates = new ProtocolVersion[] {
-                        ProtocolVersion.DTLS10,
-                        ProtocolVersion.DTLS12
-                    };
+        private static List<ProtocolVersion> customizedProtocols(boolean client,
+                List<ProtocolVersion> customized) {
+            List<ProtocolVersion> refactored = new ArrayList<>();
+            for (ProtocolVersion pv : customized) {
+                if (pv.isDTLS) {
+                    refactored.add(pv);
+                }
+            }
 
-                } else {
-                    // Use the customized TLS protocols.
-                    candidates =
-                            new ProtocolVersion[customizedDTLSProtocols.size()];
-                    candidates = customizedDTLSProtocols.toArray(candidates);
-                }
+            ProtocolVersion[] candidates;
+            // Use the default enabled protocols if no customization
+            if (refactored.isEmpty()) {
+                candidates = new ProtocolVersion[]{
+                        ProtocolVersion.DTLS12,
+                        ProtocolVersion.DTLS10
+                };
+                if (!client)
+                    return Arrays.asList(candidates);
+            } else {
+                // Use the customized TLS protocols.
+                candidates =
+                        new ProtocolVersion[customized.size()];
+                candidates = customized.toArray(candidates);
+            }
 
-                clientDefaultProtocolList = new ProtocolList(
-                        getAvailableProtocols(candidates));
-                clientDefaultCipherSuiteList =
-                        getApplicableEnabledCipherSuiteList(
-                                clientDefaultProtocolList, true);
-            } else {
-                clientDefaultProtocolList = null;       // unlikely to be used
-                clientDefaultCipherSuiteList = null;    // unlikely to be used
-            }
+            return getAvailableProtocols(candidates);
         }
 
         protected CustomizedDTLSContext() {
@@ -1319,13 +1438,23 @@
         }
 
         @Override
-        ProtocolList getClientDefaultProtocolList() {
-            return clientDefaultProtocolList;
+        List<ProtocolVersion> getClientDefaultProtocolVersions() {
+            return clientDefaultProtocols;
         }
 
         @Override
-        CipherSuiteList getClientDefaultCipherSuiteList() {
-            return clientDefaultCipherSuiteList;
+        List<ProtocolVersion> getServerDefaultProtocolVersions() {
+            return serverDefaultProtocols;
+        }
+
+        @Override
+        List<CipherSuite> getClientDefaultCipherSuites() {
+            return clientDefaultCipherSuites;
+        }
+
+        @Override
+        List<CipherSuite> getServerDefaultCipherSuites() {
+            return serverDefaultCipherSuites;
         }
     }
 
@@ -1340,7 +1469,6 @@
 
 }
 
-
 final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager
             implements X509TrustManager {
 
@@ -1417,10 +1545,8 @@
             }
 
             // try the best to check the algorithm constraints
-            ProtocolVersion protocolVersion =
-                ProtocolVersion.valueOf(session.getProtocol());
-            AlgorithmConstraints constraints = null;
-            if (protocolVersion.useTLS12PlusSpec()) {
+            AlgorithmConstraints constraints;
+            if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
@@ -1459,10 +1585,8 @@
             }
 
             // try the best to check the algorithm constraints
-            ProtocolVersion protocolVersion =
-                ProtocolVersion.valueOf(session.getProtocol());
-            AlgorithmConstraints constraints = null;
-            if (protocolVersion.useTLS12PlusSpec()) {
+            AlgorithmConstraints constraints;
+            if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
                 if (session instanceof ExtendedSSLSession) {
                     ExtendedSSLSession extSession =
                                     (ExtendedSSLSession)session;
@@ -1484,8 +1608,8 @@
     }
 
     private void checkAlgorithmConstraints(X509Certificate[] chain,
-            AlgorithmConstraints constraints, boolean isClient) throws CertificateException {
-
+            AlgorithmConstraints constraints,
+            boolean isClient) throws CertificateException {
         try {
             // Does the certificate chain end with a trusted certificate?
             int checkedLength = chain.length - 1;
@@ -1503,11 +1627,12 @@
             // A forward checker, need to check from trust to target
             if (checkedLength >= 0) {
                 AlgorithmChecker checker =
-                        new AlgorithmChecker(constraints, null,
-                                (isClient ? Validator.VAR_TLS_CLIENT : Validator.VAR_TLS_SERVER));
+                    new AlgorithmChecker(constraints, null,
+                            (isClient ? Validator.VAR_TLS_CLIENT :
+                                        Validator.VAR_TLS_SERVER));
                 checker.init(false);
                 for (int i = checkedLength; i >= 0; i--) {
-                    Certificate cert = chain[i];
+                    X509Certificate cert = chain[i];
                     // We don't care about the unresolved critical extensions.
                     checker.check(cert, Collections.<String>emptySet());
                 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLCredentials.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+interface SSLCredentials {
+}
--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,761 +25,468 @@
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.security.*;
-import java.util.*;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.List;
+import java.util.Map;
 import java.util.function.BiFunction;
-
-import javax.crypto.BadPaddingException;
-
-import javax.net.ssl.*;
-import javax.net.ssl.SSLEngineResult.*;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLKeyException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLSession;
 
 /**
  * Implementation of an non-blocking SSLEngine.
  *
- * *Currently*, the SSLEngine code exists in parallel with the current
- * SSLSocket.  As such, the current implementation is using legacy code
- * with many of the same abstractions.  However, it varies in many
- * areas, most dramatically in the IO handling.
- *
- * There are three main I/O threads that can be existing in parallel:
- * wrap(), unwrap(), and beginHandshake().  We are encouraging users to
- * not call multiple instances of wrap or unwrap, because the data could
- * appear to flow out of the SSLEngine in a non-sequential order.  We
- * take all steps we can to at least make sure the ordering remains
- * consistent, but once the calls returns, anything can happen.  For
- * example, thread1 and thread2 both call wrap, thread1 gets the first
- * packet, thread2 gets the second packet, but thread2 gets control back
- * before thread1, and sends the data.  The receiving side would see an
- * out-of-order error.
- *
  * @author Brad Wetmore
  */
-public final class SSLEngineImpl extends SSLEngine {
-
-    //
-    // Fields and global comments
-    //
-
-    /*
-     * There's a state machine associated with each connection, which
-     * among other roles serves to negotiate session changes.
-     *
-     * - START with constructor, until the TCP connection's around.
-     * - HANDSHAKE picks session parameters before allowing traffic.
-     *          There are many substates due to sequencing requirements
-     *          for handshake messages.
-     * - DATA may be transmitted.
-     * - RENEGOTIATE state allows concurrent data and handshaking
-     *          traffic ("same" substates as HANDSHAKE), and terminates
-     *          in selection of new session (and connection) parameters
-     * - ERROR state immediately precedes abortive disconnect.
-     * - CLOSED when one side closes down, used to start the shutdown
-     *          process.  SSL connection objects are not reused.
-     *
-     * State affects what SSL record types may legally be sent:
-     *
-     * - Handshake ... only in HANDSHAKE and RENEGOTIATE states
-     * - App Data ... only in DATA and RENEGOTIATE states
-     * - Alert ... in HANDSHAKE, DATA, RENEGOTIATE
-     *
-     * Re what may be received:  same as what may be sent, except that
-     * HandshakeRequest handshaking messages can come from servers even
-     * in the application data state, to request entry to RENEGOTIATE.
-     *
-     * The state machine within HANDSHAKE and RENEGOTIATE states controls
-     * the pending session, not the connection state, until the change
-     * cipher spec and "Finished" handshake messages are processed and
-     * make the "new" session become the current one.
-     *
-     * NOTE: details of the SMs always need to be nailed down better.
-     * The text above illustrates the core ideas.
-     *
-     *                +---->-------+------>--------->-------+
-     *                |            |                        |
-     *     <-----<    ^            ^  <-----<               |
-     *START>----->HANDSHAKE>----->DATA>----->RENEGOTIATE    |
-     *                v            v               v        |
-     *                |            |               |        |
-     *                +------------+---------------+        |
-     *                |                                     |
-     *                v                                     |
-     *               ERROR>------>----->CLOSED<--------<----+
-     *
-     * ALSO, note that the purpose of handshaking (renegotiation is
-     * included) is to assign a different, and perhaps new, session to
-     * the connection.  The SSLv3 spec is a bit confusing on that new
-     * protocol feature.
-     */
-    private int                 connectionState;
-
-    private static final int    cs_START = 0;
-    private static final int    cs_HANDSHAKE = 1;
-    private static final int    cs_DATA = 2;
-    private static final int    cs_RENEGOTIATE = 3;
-    private static final int    cs_ERROR = 4;
-    private static final int    cs_CLOSED = 6;
-
-    /*
-     * Once we're in state cs_CLOSED, we can continue to
-     * wrap/unwrap until we finish sending/receiving the messages
-     * for close_notify.
-     */
-    private boolean             inboundDone = false;
-    private boolean             outboundDone = false;
-
-    /*
-     * The authentication context holds all information used to establish
-     * who this end of the connection is (certificate chains, private keys,
-     * etc) and who is trusted (e.g. as CAs or websites).
-     */
-    private SSLContextImpl      sslContext;
-
-    /*
-     * This connection is one of (potentially) many associated with
-     * any given session.  The output of the handshake protocol is a
-     * new session ... although all the protocol description talks
-     * about changing the cipher spec (and it does change), in fact
-     * that's incidental since it's done by changing everything that
-     * is associated with a session at the same time.  (TLS/IETF may
-     * change that to add client authentication w/o new key exchg.)
-     */
-    private Handshaker                  handshaker;
-    private SSLSessionImpl              sess;
-    private volatile SSLSessionImpl     handshakeSession;
-
-    /*
-     * Flag indicating if the next record we receive MUST be a Finished
-     * message. Temporarily set during the handshake to ensure that
-     * a change cipher spec message is followed by a finished message.
-     */
-    private boolean             expectingFinished;
-
-
-    /*
-     * If someone tries to closeInbound() (say at End-Of-Stream)
-     * our engine having received a close_notify, we need to
-     * notify the app that we may have a truncation attack underway.
-     */
-    private boolean             recvCN;
-
-    /*
-     * For improved diagnostics, we detail connection closure
-     * If the engine is closed (connectionState >= cs_ERROR),
-     * closeReason != null indicates if the engine was closed
-     * because of an error or because or normal shutdown.
-     */
-    private SSLException        closeReason;
-
-    /*
-     * Per-connection private state that doesn't change when the
-     * session is changed.
-     */
-    private ClientAuthType          doClientAuth =
-                                            ClientAuthType.CLIENT_AUTH_NONE;
-    private boolean                 enableSessionCreation = true;
-    InputRecord                     inputRecord;
-    OutputRecord                    outputRecord;
-    private AccessControlContext    acc;
-
-    // The cipher suites enabled for use on this connection.
-    private CipherSuiteList             enabledCipherSuites;
-
-    // the endpoint identification protocol
-    private String                      identificationProtocol = null;
-
-    // The cryptographic algorithm constraints
-    private AlgorithmConstraints        algorithmConstraints = null;
-
-    // The server name indication and matchers
-    List<SNIServerName>         serverNames =
-                                    Collections.<SNIServerName>emptyList();
-    Collection<SNIMatcher>      sniMatchers =
-                                    Collections.<SNIMatcher>emptyList();
-
-    // Configured application protocol values
-    String[] applicationProtocols = new String[0];
-
-    // Negotiated application protocol value.
-    //
-    // The value under negotiation will be obtained from handshaker.
-    String applicationProtocol = null;
-
-    // Callback function that selects the application protocol value during
-    // the SSL/TLS handshake.
-    BiFunction<SSLEngine, List<String>, String> applicationProtocolSelector;
-
-    // Have we been told whether we're client or server?
-    private boolean                     serverModeSet = false;
-    private boolean                     roleIsServer;
-
-    /*
-     * The protocol versions enabled for use on this connection.
-     *
-     * Note: we support a pseudo protocol called SSLv2Hello which when
-     * set will result in an SSL v2 Hello being sent with SSL (version 3.0)
-     * or TLS (version 3.1, 3.2, etc.) version info.
-     */
-    private ProtocolList        enabledProtocols;
-
-    /*
-     * The SSL version associated with this connection.
-     */
-    private ProtocolVersion     protocolVersion;
-
-    /*
-     * security parameters for secure renegotiation.
-     */
-    private boolean             secureRenegotiation;
-    private byte[]              clientVerifyData;
-    private byte[]              serverVerifyData;
-
-    /*
-     * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
-     * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES.
-     * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
-     *
-     * There are several locks here.
-     *
-     * The primary lock is the per-instance lock used by
-     * synchronized(this) and the synchronized methods.  It controls all
-     * access to things such as the connection state and variables which
-     * affect handshaking.  If we are inside a synchronized method, we
-     * can access the state directly, otherwise, we must use the
-     * synchronized equivalents.
-     *
-     * Note that we must never acquire the <code>this</code> lock after
-     * <code>writeLock</code> or run the risk of deadlock.
-     *
-     * Grab some coffee, and be careful with any code changes.
-     */
-    private Object              wrapLock;
-    private Object              unwrapLock;
-    Object                      writeLock;
-
-    /*
-     * Whether local cipher suites preference in server side should be
-     * honored during handshaking?
-     */
-    private boolean preferLocalCipherSuites = false;
-
-    /*
-     * whether DTLS handshake retransmissions should be enabled?
-     */
-    private boolean enableRetransmissions = false;
-
-    /*
-     * The maximum expected network packet size for SSL/TLS/DTLS records.
-     */
-    private int maximumPacketSize = 0;
-
-    /*
-     * Is this an instance for Datagram Transport Layer Security (DTLS)?
-     */
-    private final boolean isDTLS;
-
-    /*
-     * Class and subclass dynamic debugging support
-     */
-    private static final Debug debug = Debug.getInstance("ssl");
-
-    //
-    // Initialization/Constructors
-    //
+final class SSLEngineImpl extends SSLEngine implements SSLTransport {
+    private final SSLContextImpl        sslContext;
+    final TransportContext              conContext;
 
     /**
      * Constructor for an SSLEngine from SSLContext, without
-     * host/port hints.  This Engine will not be able to cache
-     * sessions, but must renegotiate everything by hand.
+     * host/port hints.
+     *
+     * This Engine will not be able to cache sessions, but must renegotiate
+     * everything by hand.
      */
-    SSLEngineImpl(SSLContextImpl ctx, boolean isDTLS) {
-        super();
-        this.isDTLS = isDTLS;
-        init(ctx, isDTLS);
+    SSLEngineImpl(SSLContextImpl sslContext) {
+        this(sslContext, null, -1);
     }
 
     /**
      * Constructor for an SSLEngine from SSLContext.
      */
-    SSLEngineImpl(SSLContextImpl ctx, String host, int port, boolean isDTLS) {
+    SSLEngineImpl(SSLContextImpl sslContext,
+            String host, int port) {
         super(host, port);
-        this.isDTLS = isDTLS;
-        init(ctx, isDTLS);
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        if (sslContext.isDTLS()) {
+            this.conContext = new TransportContext(sslContext, this,
+                    new DTLSInputRecord(handshakeHash),
+                    new DTLSOutputRecord(handshakeHash));
+        } else {
+            this.conContext = new TransportContext(sslContext, this,
+                    new SSLEngineInputRecord(handshakeHash),
+                    new SSLEngineOutputRecord(handshakeHash));
+        }
+
+        // Server name indication is a connection scope extension.
+        if (host != null) {
+            this.conContext.sslConfig.serverNames =
+                    Utilities.addToSNIServerNameList(
+                            conContext.sslConfig.serverNames, host);
+        }
+    }
+
+    @Override
+    public synchronized void beginHandshake() throws SSLException {
+        if (conContext.isUnsureMode) {
+            throw new IllegalStateException(
+                    "Client/Server mode has not yet been set.");
+        }
+
+        try {
+            conContext.kickstart();
+        } catch (IOException ioe) {
+            conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                "Couldn't kickstart handshaking", ioe);
+        } catch (Exception ex) {     // including RuntimeException
+            conContext.fatal(Alert.INTERNAL_ERROR,
+                "Fail to begin handshake", ex);
+        }
+    }
+
+    @Override
+    public synchronized SSLEngineResult wrap(ByteBuffer[] appData,
+            int offset, int length, ByteBuffer netData) throws SSLException {
+        return wrap(appData, offset, length, new ByteBuffer[]{ netData }, 0, 1);
     }
 
-    /**
-     * Initializes the Engine
-     */
-    private void init(SSLContextImpl ctx, boolean isDTLS) {
-        if (debug != null && Debug.isOn("ssl")) {
-            System.out.println("Using SSLEngineImpl.");
+    // @Override
+    public synchronized SSLEngineResult wrap(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
+
+        if (conContext.isUnsureMode) {
+            throw new IllegalStateException(
+                    "Client/Server mode has not yet been set.");
+        }
+
+        // See if the handshaker needs to report back some SSLException.
+        if (conContext.outputRecord.isEmpty()) {
+            checkTaskThrown();
+        }   // Otherwise, deliver cached records before throwing task exception.
+
+        // check parameters
+        checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
+
+        try {
+            return writeRecord(
+                srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
+        } catch (SSLProtocolException spe) {
+            // may be an unexpected handshake message
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE, spe);
+        } catch (IOException ioe) {
+            conContext.fatal(Alert.INTERNAL_ERROR,
+                "problem wrapping app data", ioe);
+        } catch (Exception ex) {     // including RuntimeException
+            conContext.fatal(Alert.INTERNAL_ERROR,
+                "Fail to wrap application data", ex);
         }
 
-        sslContext = ctx;
-        sess = SSLSessionImpl.nullSession;
-        handshakeSession = null;
-        protocolVersion = isDTLS ?
-                ProtocolVersion.DEFAULT_DTLS : ProtocolVersion.DEFAULT_TLS;
+        return null;    // make compiler happy
+    }
+
+    private SSLEngineResult writeRecord(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
+
+        if (isOutboundDone()) {
+            return new SSLEngineResult(
+                    Status.CLOSED, getHandshakeStatus(), 0, 0);
+        }
+
+        HandshakeContext hc = conContext.handshakeContext;
+        HandshakeStatus hsStatus = null;
+        if (!conContext.isNegotiated &&
+                !conContext.isClosed() && !conContext.isBroken) {
+            conContext.kickstart();
+
+            hsStatus = getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
+                /*
+                 * For DTLS, if the handshake state is
+                 * HandshakeStatus.NEED_UNWRAP, a call to SSLEngine.wrap()
+                 * means that the previous handshake packets (if delivered)
+                 * get lost, and need retransmit the handshake messages.
+                 */
+                if (!sslContext.isDTLS() || hc == null ||
+                        !hc.sslConfig.enableRetransmissions ||
+                        conContext.outputRecord.firstMessage) {
+
+                    return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+                }   // otherwise, need retransmission
+            }
+        }
+
+        if (hsStatus == null) {
+            hsStatus = getHandshakeStatus();
+        }
 
         /*
-         * State is cs_START until we initialize the handshaker.
-         *
-         * Apps using SSLEngine are probably going to be server.
-         * Somewhat arbitrary choice.
+         * If we have a task outstanding, this *MUST* be done before
+         * doing any more wrapping, because we could be in the middle
+         * of receiving a handshake message, for example, a finished
+         * message which would change the ciphers.
          */
-        roleIsServer = true;
-        connectionState = cs_START;
+        if (hsStatus == HandshakeStatus.NEED_TASK) {
+            return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
+        }
+
+        int dstsRemains = 0;
+        for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
+            dstsRemains += dsts[i].remaining();
+        }
 
-        // default server name indication
-        serverNames =
-            Utilities.addToSNIServerNameList(serverNames, getPeerHost());
+        // Check destination buffer size.
+        //
+        // We can be smarter about using smaller buffer sizes later.  For
+        // now, force it to be large enough to handle any valid record.
+        if (dstsRemains < conContext.conSession.getPacketBufferSize()) {
+            return new SSLEngineResult(
+                Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
+        }
 
-        // default security parameters for secure renegotiation
-        secureRenegotiation = false;
-        clientVerifyData = new byte[0];
-        serverVerifyData = new byte[0];
+        int srcsRemains = 0;
+        for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
+            srcsRemains += srcs[i].remaining();
+        }
 
-        enabledCipherSuites =
-                sslContext.getDefaultCipherSuiteList(roleIsServer);
-        enabledProtocols =
-                sslContext.getDefaultProtocolList(roleIsServer);
+        Ciphertext ciphertext = null;
+        try {
+            // Acquire the buffered to-be-delivered records or retransmissions.
+            //
+            // May have buffered records, or need retransmission if handshaking.
+            if (!conContext.outputRecord.isEmpty() || (hc != null &&
+                    hc.sslConfig.enableRetransmissions &&
+                    hc.sslContext.isDTLS() &&
+                    hsStatus == HandshakeStatus.NEED_UNWRAP)) {
+                ciphertext = encode(null, 0, 0,
+                        dsts, dstsOffset, dstsLength);
+            }
 
-        wrapLock = new Object();
-        unwrapLock = new Object();
-        writeLock = new Object();
+            if (ciphertext == null && srcsRemains != 0) {
+                ciphertext = encode(srcs, srcsOffset, srcsLength,
+                        dsts, dstsOffset, dstsLength);
+            }
+        } catch (IOException ioe) {
+            if (ioe instanceof SSLException) {
+                throw ioe;
+            } else {
+                throw new SSLException("Write problems", ioe);
+            }
+        }
 
         /*
-         * Save the Access Control Context.  This will be used later
-         * for a couple of things, including providing a context to
-         * run tasks in, and for determining which credentials
-         * to use for Subject based (JAAS) decisions
+         * Check for status.
          */
-        acc = AccessController.getContext();
+        Status status = (isOutboundDone() ? Status.CLOSED : Status.OK);
+        if (ciphertext != null && ciphertext.handshakeStatus != null) {
+            hsStatus = ciphertext.handshakeStatus;
+        } else {
+            hsStatus = getHandshakeStatus();
+        }
+
+        int deltaSrcs = srcsRemains;
+        for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
+            deltaSrcs -= srcs[i].remaining();
+        }
+
+        int deltaDsts = dstsRemains;
+        for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
+            deltaDsts -= dsts[i].remaining();
+        }
 
-        /*
-         * All outbound application data goes through this OutputRecord,
-         * other data goes through their respective records created
-         * elsewhere.  All inbound data goes through this one
-         * input record.
-         */
-        if (isDTLS) {
-            enableRetransmissions = true;
+        return new SSLEngineResult(status, hsStatus, deltaSrcs, deltaDsts,
+                ciphertext != null ? ciphertext.recordSN : -1L);
+    }
+
+    private Ciphertext encode(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
 
-            // SSLEngine needs no record local buffer
-            outputRecord = new DTLSOutputRecord();
-            inputRecord = new DTLSInputRecord();
+        Ciphertext ciphertext = null;
+        try {
+            ciphertext = conContext.outputRecord.encode(
+                srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
+        } catch (SSLHandshakeException she) {
+            // may be record sequence number overflow
+            conContext.fatal(Alert.HANDSHAKE_FAILURE, she);
+        } catch (IOException e) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
+        }
 
-        } else {
-            outputRecord = new SSLEngineOutputRecord();
-            inputRecord = new SSLEngineInputRecord();
+        if (ciphertext == null) {
+            return Ciphertext.CIPHERTEXT_NULL;
         }
 
-        maximumPacketSize = outputRecord.getMaxPacketSize();
+        // Is the handshake completed?
+        boolean needRetransmission =
+                conContext.sslContext.isDTLS() &&
+                conContext.handshakeContext != null &&
+                conContext.handshakeContext.sslConfig.enableRetransmissions;
+        HandshakeStatus hsStatus =
+                tryToFinishHandshake(ciphertext.contentType);
+        if (needRetransmission &&
+                hsStatus == HandshakeStatus.FINISHED &&
+                conContext.sslContext.isDTLS() &&
+                ciphertext.handshakeType == SSLHandshake.FINISHED.id) {
+            // Retransmit the last flight for DTLS.
+            //
+            // The application data transactions may begin immediately
+            // after the last flight.  If the last flight get lost, the
+            // application data may be discarded accordingly.  As could
+            // be an issue for some applications.  This impact can be
+            // mitigated by sending the last fligth twice.
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) {
+                SSLLogger.finest("retransmit the last flight messages");
+            }
+
+            conContext.outputRecord.launchRetransmission();
+            hsStatus = HandshakeStatus.NEED_WRAP;
+        }
+
+        if (hsStatus == null) {
+            hsStatus = conContext.getHandshakeStatus();
+        }
+
+        // Is the sequence number is nearly overflow?
+        if (conContext.outputRecord.seqNumIsHuge()) {
+            hsStatus = tryKeyUpdate(hsStatus);
+        }
+
+        // update context status
+        ciphertext.handshakeStatus = hsStatus;
+
+        return ciphertext;
+    }
+
+    private HandshakeStatus tryToFinishHandshake(byte contentType) {
+        HandshakeStatus hsStatus = null;
+        if ((contentType == ContentType.HANDSHAKE.id) &&
+                conContext.outputRecord.isEmpty()) {
+            if (conContext.handshakeContext == null) {
+                hsStatus = HandshakeStatus.FINISHED;
+            } else if (conContext.isPostHandshakeContext()) {
+                // unlikely, but just in case.
+                hsStatus = conContext.finishPostHandshake();
+            } else if (conContext.handshakeContext.handshakeFinished) {
+                hsStatus = conContext.finishHandshake();
+            }
+        }   // Otherwise, the followed call to getHSStatus() will help.
+
+        return hsStatus;
     }
 
     /**
-     * Initialize the handshaker object. This means:
-     *
-     *  . if a handshake is already in progress (state is cs_HANDSHAKE
-     *    or cs_RENEGOTIATE), do nothing and return
-     *
-     *  . if the engine is already closed, throw an Exception (internal error)
+     * Try renegotiation or key update for sequence number wrap.
      *
-     *  . otherwise (cs_START or cs_DATA), create the appropriate handshaker
-     *    object and advance the connection state (to cs_HANDSHAKE or
-     *    cs_RENEGOTIATE, respectively).
-     *
-     * This method is called right after a new engine is created, when
-     * starting renegotiation, or when changing client/server mode of the
-     * engine.
+     * Note that in order to maintain the handshake status properly, we check
+     * the sequence number after the last record reading/writing process.  As
+     * we request renegotiation or close the connection for wrapped sequence
+     * number when there is enough sequence number space left to handle a few
+     * more records, so the sequence number of the last record cannot be
+     * wrapped.
      */
-    private void initHandshaker() {
-        switch (connectionState) {
-
-        //
-        // Starting a new handshake.
-        //
-        case cs_START:
-        case cs_DATA:
-            break;
-
-        //
-        // We're already in the middle of a handshake.
-        //
-        case cs_HANDSHAKE:
-        case cs_RENEGOTIATE:
-            return;
-
-        //
-        // Anyone allowed to call this routine is required to
-        // do so ONLY if the connection state is reasonable...
-        //
-        default:
-            throw new IllegalStateException("Internal error");
-        }
-
-        // state is either cs_START or cs_DATA
-        if (connectionState == cs_START) {
-            connectionState = cs_HANDSHAKE;
-        } else { // cs_DATA
-            connectionState = cs_RENEGOTIATE;
+    private HandshakeStatus tryKeyUpdate(
+            HandshakeStatus currentHandshakeStatus) throws IOException {
+        // Don't bother to kickstart the renegotiation or key update when the
+        // local is asking for it.
+        if ((conContext.handshakeContext == null) &&
+                !conContext.isClosed() && !conContext.isBroken) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.finest("key update to wrap sequence number");
+            }
+            conContext.keyUpdate();
+            return conContext.getHandshakeStatus();
         }
 
-        if (roleIsServer) {
-            handshaker = new ServerHandshaker(this, sslContext,
-                    enabledProtocols, doClientAuth,
-                    protocolVersion, connectionState == cs_HANDSHAKE,
-                    secureRenegotiation, clientVerifyData, serverVerifyData,
-                    isDTLS);
-            handshaker.setSNIMatchers(sniMatchers);
-            handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
-        } else {
-            handshaker = new ClientHandshaker(this, sslContext,
-                    enabledProtocols,
-                    protocolVersion, connectionState == cs_HANDSHAKE,
-                    secureRenegotiation, clientVerifyData, serverVerifyData,
-                    isDTLS);
-            handshaker.setSNIServerNames(serverNames);
-        }
-        handshaker.setMaximumPacketSize(maximumPacketSize);
-        handshaker.setEnabledCipherSuites(enabledCipherSuites);
-        handshaker.setEnableSessionCreation(enableSessionCreation);
-        handshaker.setApplicationProtocols(applicationProtocols);
-        handshaker.setApplicationProtocolSelectorSSLEngine(
-            applicationProtocolSelector);
-
-        outputRecord.initHandshaker();
-    }
-
-    /*
-     * Report the current status of the Handshaker
-     */
-    private HandshakeStatus getHSStatus(HandshakeStatus hss) {
-
-        if (hss != null) {
-            return hss;
-        }
-
-        synchronized (this) {
-            if (!outputRecord.isEmpty()) {
-                // If no handshaking, special case to wrap alters.
-                return HandshakeStatus.NEED_WRAP;
-            } else if (handshaker != null) {
-                if (handshaker.taskOutstanding()) {
-                    return HandshakeStatus.NEED_TASK;
-                } else if (isDTLS && !inputRecord.isEmpty()) {
-                    return HandshakeStatus.NEED_UNWRAP_AGAIN;
-                } else {
-                    return HandshakeStatus.NEED_UNWRAP;
-                }
-            } else if (connectionState == cs_CLOSED) {
-                /*
-                 * Special case where we're closing, but
-                 * still need the close_notify before we
-                 * can officially be closed.
-                 *
-                 * Note isOutboundDone is taken care of by
-                 * hasOutboundData() above.
-                 */
-                if (!isInboundDone()) {
-                    return HandshakeStatus.NEED_UNWRAP;
-                } // else not handshaking
-            }
-
-            return HandshakeStatus.NOT_HANDSHAKING;
-        }
-    }
-
-    private synchronized void checkTaskThrown() throws SSLException {
-        if (handshaker != null) {
-            handshaker.checkThrown();
-        }
-    }
-
-    //
-    // Handshaking and connection state code
-    //
-
-    /*
-     * Provides "this" synchronization for connection state.
-     * Otherwise, you can access it directly.
-     */
-    private synchronized int getConnectionState() {
-        return connectionState;
+        return currentHandshakeStatus;
     }
 
-    private synchronized void setConnectionState(int state) {
-        connectionState = state;
-    }
-
-    /*
-     * Get the Access Control Context.
-     *
-     * Used for a known context to
-     * run tasks in, and for determining which credentials
-     * to use for Subject-based (JAAS) decisions.
-     */
-    AccessControlContext getAcc() {
-        return acc;
-    }
+    private static void checkParams(
+            ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+            ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
 
-    /*
-     * Is a handshake currently underway?
-     */
-    @Override
-    public SSLEngineResult.HandshakeStatus getHandshakeStatus() {
-        return getHSStatus(null);
-    }
+        if ((srcs == null) || (dsts == null)) {
+            throw new IllegalArgumentException(
+                    "source or destination buffer is null");
+        }
 
-    /*
-     * used by Handshaker to change the active write cipher, follows
-     * the output of the CCS message.
-     *
-     * Also synchronized on "this" from readRecord/delegatedTask.
-     */
-    void changeWriteCiphers() throws IOException {
-
-        Authenticator writeAuthenticator;
-        CipherBox writeCipher;
-        try {
-            writeCipher = handshaker.newWriteCipher();
-            writeAuthenticator = handshaker.newWriteAuthenticator();
-        } catch (GeneralSecurityException e) {
-            // "can't happen"
-            throw new SSLException("Algorithm missing:  ", e);
+        if ((dstsOffset < 0) || (dstsLength < 0) ||
+                (dstsOffset > dsts.length - dstsLength)) {
+            throw new IndexOutOfBoundsException(
+                    "index out of bound of the destination buffers");
         }
 
-        outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher);
-    }
-
-    /*
-     * Updates the SSL version associated with this connection.
-     * Called from Handshaker once it has determined the negotiated version.
-     */
-    synchronized void setVersion(ProtocolVersion protocolVersion) {
-        this.protocolVersion = protocolVersion;
-        outputRecord.setVersion(protocolVersion);
-    }
-
+        if ((srcsOffset < 0) || (srcsLength < 0) ||
+                (srcsOffset > srcs.length - srcsLength)) {
+            throw new IndexOutOfBoundsException(
+                    "index out of bound of the source buffers");
+        }
 
-    /**
-     * Kickstart the handshake if it is not already in progress.
-     * This means:
-     *
-     *  . if handshaking is already underway, do nothing and return
-     *
-     *  . if the engine is not connected or already closed, throw an
-     *    Exception.
-     *
-     *  . otherwise, call initHandshake() to initialize the handshaker
-     *    object and progress the state. Then, send the initial
-     *    handshaking message if appropriate (always on clients and
-     *    on servers when renegotiating).
-     */
-    private synchronized void kickstartHandshake() throws IOException {
-        switch (connectionState) {
-
-        case cs_START:
-            if (!serverModeSet) {
-                throw new IllegalStateException(
-                    "Client/Server mode not yet set.");
-            }
-            initHandshaker();
-            break;
-
-        case cs_HANDSHAKE:
-            // handshaker already setup, proceed
-            break;
-
-        case cs_DATA:
-            if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) {
-                throw new SSLHandshakeException(
-                        "Insecure renegotiation is not allowed");
+        for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
+            if (dsts[i] == null) {
+                throw new IllegalArgumentException(
+                        "destination buffer[" + i + "] == null");
             }
 
-            if (!secureRenegotiation) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println(
-                        "Warning: Using insecure renegotiation");
-                }
+            /*
+             * Make sure the destination bufffers are writable.
+             */
+            if (dsts[i].isReadOnly()) {
+                throw new ReadOnlyBufferException();
             }
-
-            // initialize the handshaker, move to cs_RENEGOTIATE
-            initHandshaker();
-            break;
-
-        case cs_RENEGOTIATE:
-            // handshaking already in progress, return
-            return;
-
-        default:
-            // cs_ERROR/cs_CLOSED
-            throw new SSLException("SSLEngine is closing/closed");
         }
 
-        //
-        // Kickstart handshake state machine if we need to ...
-        //
-        if (!handshaker.activated()) {
-             // prior to handshaking, activate the handshake
-            if (connectionState == cs_RENEGOTIATE) {
-                // don't use SSLv2Hello when renegotiating
-                handshaker.activate(protocolVersion);
-            } else {
-                handshaker.activate(null);
-            }
-
-            if (handshaker instanceof ClientHandshaker) {
-                // send client hello
-                handshaker.kickstart();
-            } else {    // instanceof ServerHandshaker
-                if (connectionState == cs_HANDSHAKE) {
-                    // initial handshake, no kickstart message to send
-                } else {
-                    // we want to renegotiate, send hello request
-                    handshaker.kickstart();
-                }
+        for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
+            if (srcs[i] == null) {
+                throw new IllegalArgumentException(
+                        "source buffer[" + i + "] == null");
             }
         }
     }
 
-    /*
-     * Start a SSLEngine handshake
-     */
     @Override
-    public void beginHandshake() throws SSLException {
-        try {
-            kickstartHandshake();
-        } catch (Exception e) {
-            fatal(Alerts.alert_handshake_failure,
-                "Couldn't kickstart handshaking", e);
-        }
+    public synchronized SSLEngineResult unwrap(ByteBuffer src,
+            ByteBuffer[] dsts, int offset, int length) throws SSLException {
+        return unwrap(
+                new ByteBuffer[]{src}, 0, 1, dsts, offset, length);
     }
 
-
-    //
-    // Read/unwrap side
-    //
-
+    // @Override
+    public synchronized SSLEngineResult unwrap(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
 
-    /**
-     * Unwraps a buffer.  Does a variety of checks before grabbing
-     * the unwrapLock, which blocks multiple unwraps from occurring.
-     */
-    @Override
-    public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer[] appData,
-            int offset, int length) throws SSLException {
+        if (conContext.isUnsureMode) {
+            throw new IllegalStateException(
+                    "Client/Server mode has not yet been set.");
+        }
 
-        // check engine parameters
-        checkEngineParas(netData, appData, offset, length, false);
+        // See if the handshaker needs to report back some SSLException.
+        checkTaskThrown();
+
+        // check parameters
+        checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
 
         try {
-            synchronized (unwrapLock) {
-                return readNetRecord(netData, appData, offset, length);
-            }
+            return readRecord(
+                srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
         } catch (SSLProtocolException spe) {
             // may be an unexpected handshake message
-            fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
-            return null;  // make compiler happy
-        } catch (Exception e) {
+            conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    spe.getMessage(), spe);
+        } catch (IOException ioe) {
             /*
              * Don't reset position so it looks like we didn't
              * consume anything.  We did consume something, and it
              * got us into this situation, so report that much back.
              * Our days of consuming are now over anyway.
              */
-            fatal(Alerts.alert_internal_error,
-                "problem unwrapping net record", e);
-            return null;  // make compiler happy
-        }
-    }
-
-    private static void checkEngineParas(ByteBuffer netData,
-            ByteBuffer[] appData, int offset, int len, boolean isForWrap) {
-
-        if ((netData == null) || (appData == null)) {
-            throw new IllegalArgumentException("src/dst is null");
-        }
-
-        if ((offset < 0) || (len < 0) || (offset > appData.length - len)) {
-            throw new IndexOutOfBoundsException();
-        }
-
-        /*
-         * If wrapping, make sure the destination bufffer is writable.
-         */
-        if (isForWrap && netData.isReadOnly()) {
-            throw new ReadOnlyBufferException();
+            conContext.fatal(Alert.INTERNAL_ERROR,
+                    "problem unwrapping net record", ioe);
+        } catch (Exception ex) {     // including RuntimeException
+            conContext.fatal(Alert.INTERNAL_ERROR,
+                "Fail to unwrap network record", ex);
         }
 
-        for (int i = offset; i < offset + len; i++) {
-            if (appData[i] == null) {
-                throw new IllegalArgumentException(
-                        "appData[" + i + "] == null");
-            }
-
-            /*
-             * If unwrapping, make sure the destination bufffers are writable.
-             */
-            if (!isForWrap && appData[i].isReadOnly()) {
-                throw new ReadOnlyBufferException();
-            }
-        }
+        return null;    // make compiler happy
     }
 
-    /*
-     * Makes additional checks for unwrap, but this time more
-     * specific to this packet and the current state of the machine.
-     */
-    private SSLEngineResult readNetRecord(ByteBuffer netData,
-            ByteBuffer[] appData, int offset, int length) throws IOException {
-
-        Status status = null;
-        HandshakeStatus hsStatus = null;
-
-        /*
-         * See if the handshaker needs to report back some SSLException.
-         */
-        checkTaskThrown();
+    private SSLEngineResult readRecord(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
 
         /*
          * Check if we are closing/closed.
          */
         if (isInboundDone()) {
-            return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0);
+            return new SSLEngineResult(
+                    Status.CLOSED, getHandshakeStatus(), 0, 0);
         }
 
-        /*
-         * If we're still in cs_HANDSHAKE, make sure it's been
-         * started.
-         */
-        synchronized (this) {
-            if ((connectionState == cs_HANDSHAKE) ||
-                    (connectionState == cs_START)) {
-                kickstartHandshake();
+        HandshakeStatus hsStatus = null;
+        if (!conContext.isNegotiated &&
+                !conContext.isClosed() && !conContext.isBroken) {
+            conContext.kickstart();
 
-                /*
-                 * If there's still outbound data to flush, we
-                 * can return without trying to unwrap anything.
-                 */
-                hsStatus = getHSStatus(null);
-
-                if (hsStatus == HandshakeStatus.NEED_WRAP) {
-                    return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
-                }
+            /*
+             * If there's still outbound data to flush, we
+             * can return without trying to unwrap anything.
+             */
+            hsStatus = getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_WRAP) {
+                return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
             }
         }
 
-        /*
-         * Grab a copy of this if it doesn't already exist,
-         * and we can use it several places before anything major
-         * happens on this side.  Races aren't critical
-         * here.
-         */
         if (hsStatus == null) {
-            hsStatus = getHSStatus(null);
+            hsStatus = getHandshakeStatus();
         }
 
         /*
@@ -795,42 +502,61 @@
         if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
             Plaintext plainText = null;
             try {
-                plainText = readRecord(null, null, 0, 0);
-            } catch (SSLException e) {
-                throw e;
-            } catch (IOException e) {
-                throw new SSLException("readRecord", e);
+                plainText = decode(null, 0, 0,
+                        dsts, dstsOffset, dstsLength);
+            } catch (IOException ioe) {
+                if (ioe instanceof SSLException) {
+                    throw ioe;
+                } else {
+                    throw new SSLException("readRecord", ioe);
+                }
             }
 
-            status = (isInboundDone() ? Status.CLOSED : Status.OK);
-            hsStatus = getHSStatus(plainText.handshakeStatus);
+            Status status = (isInboundDone() ? Status.CLOSED : Status.OK);
+            if (plainText.handshakeStatus != null) {
+                hsStatus = plainText.handshakeStatus;
+            } else {
+                hsStatus = getHandshakeStatus();
+            }
 
             return new SSLEngineResult(
                     status, hsStatus, 0, 0, plainText.recordSN);
         }
 
+        int srcsRemains = 0;
+        for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
+            srcsRemains += srcs[i].remaining();
+        }
+
+        if (srcsRemains == 0) {
+            return new SSLEngineResult(
+                Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
+        }
+
         /*
          * Check the packet to make sure enough is here.
          * This will also indirectly check for 0 len packets.
          */
         int packetLen = 0;
         try {
-            packetLen = inputRecord.bytesInCompletePacket(netData);
+            packetLen = conContext.inputRecord.bytesInCompletePacket(
+                    srcs, srcsOffset, srcsLength);
         } catch (SSLException ssle) {
             // Need to discard invalid records for DTLS protocols.
-            if (isDTLS) {
-                if (debug != null && Debug.isOn("ssl")) {
-                    System.out.println(
-                        Thread.currentThread().getName() +
-                        " discard invalid record: " + ssle);
+            if (sslContext.isDTLS()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) {
+                    SSLLogger.finest("Discard invalid DTLS records", ssle);
                 }
 
                 // invalid, discard the entire data [section 4.1.2.7, RFC 6347]
-                int deltaNet = netData.remaining();
-                netData.position(netData.limit());
+                int deltaNet = 0;
+                // int deltaNet = netData.remaining();
+                // netData.position(netData.limit());
 
-                status = (isInboundDone() ? Status.CLOSED : Status.OK);
-                hsStatus = getHSStatus(hsStatus);
+                Status status = (isInboundDone() ? Status.CLOSED : Status.OK);
+                if (hsStatus == null) {
+                    hsStatus = getHandshakeStatus();
+                }
 
                 return new SSLEngineResult(status, hsStatus, deltaNet, 0, -1L);
             } else {
@@ -839,10 +565,10 @@
         }
 
         // Is this packet bigger than SSL/TLS normally allows?
-        if (packetLen > sess.getPacketBufferSize()) {
-            int largestRecordSize = isDTLS ?
+        if (packetLen > conContext.conSession.getPacketBufferSize()) {
+            int largestRecordSize = sslContext.isDTLS() ?
                     DTLSRecord.maxRecordSize : SSLRecord.maxLargeRecordSize;
-            if ((packetLen <= largestRecordSize) && !isDTLS) {
+            if ((packetLen <= largestRecordSize) && !sslContext.isDTLS()) {
                 // Expand the expected maximum packet/application buffer
                 // sizes.
                 //
@@ -850,11 +576,11 @@
 
                 // Old behavior: shall we honor the System Property
                 // "jsse.SSLEngine.acceptLargeFragments" if it is "false"?
-                sess.expandBufferSizes();
+                conContext.conSession.expandBufferSizes();
             }
 
             // check the packet again
-            largestRecordSize = sess.getPacketBufferSize();
+            largestRecordSize = conContext.conSession.getPacketBufferSize();
             if (packetLen > largestRecordSize) {
                 throw new SSLProtocolException(
                         "Input record too big: max = " +
@@ -862,35 +588,28 @@
             }
         }
 
-        int netPos = netData.position();
-        int appRemains = 0;
-        for (int i = offset; i < offset + length; i++) {
-            if (appData[i] == null) {
-                throw new IllegalArgumentException(
-                        "appData[" + i + "] == null");
-            }
-            appRemains += appData[i].remaining();
-        }
-
         /*
          * Check for OVERFLOW.
          *
          * Delay enforcing the application buffer free space requirement
          * until after the initial handshaking.
          */
-        // synchronize connectionState?
-        if ((connectionState == cs_DATA) ||
-                (connectionState == cs_RENEGOTIATE)) {
+        int dstsRemains = 0;
+        for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
+            dstsRemains += dsts[i].remaining();
+        }
 
-            int FragLen = inputRecord.estimateFragmentSize(packetLen);
-            if (FragLen > appRemains) {
+        if (conContext.isNegotiated) {
+            int FragLen =
+                    conContext.inputRecord.estimateFragmentSize(packetLen);
+            if (FragLen > dstsRemains) {
                 return new SSLEngineResult(
                         Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
             }
         }
 
         // check for UNDERFLOW.
-        if ((packetLen == -1) || (netData.remaining() < packetLen)) {
+        if ((packetLen == -1) || (srcsRemains < packetLen)) {
             return new SSLEngineResult(Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
         }
 
@@ -899,11 +618,14 @@
          */
         Plaintext plainText = null;
         try {
-            plainText = readRecord(netData, appData, offset, length);
-        } catch (SSLException e) {
-            throw e;
-        } catch (IOException e) {
-            throw new SSLException("readRecord", e);
+            plainText = decode(srcs, srcsOffset, srcsLength,
+                            dsts, dstsOffset, dstsLength);
+        } catch (IOException ioe) {
+            if (ioe instanceof SSLException) {
+                throw ioe;
+            } else {
+                throw new SSLException("readRecord", ioe);
+            }
         }
 
         /*
@@ -916,1395 +638,348 @@
          *
          * status above should cover:  FINISHED, NEED_TASK
          */
-        status = (isInboundDone() ? Status.CLOSED : Status.OK);
-        hsStatus = getHSStatus(plainText.handshakeStatus);
+        Status status = (isInboundDone() ? Status.CLOSED : Status.OK);
+        if (plainText.handshakeStatus != null) {
+            hsStatus = plainText.handshakeStatus;
+        } else {
+            hsStatus = getHandshakeStatus();
+        }
 
-        int deltaNet = netData.position() - netPos;
-        int deltaApp = appRemains;
-        for (int i = offset; i < offset + length; i++) {
-            deltaApp -= appData[i].remaining();
+        int deltaNet = srcsRemains;
+        for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
+            deltaNet -= srcs[i].remaining();
+        }
+
+        int deltaApp = dstsRemains;
+        for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
+            deltaApp -= dsts[i].remaining();
         }
 
         return new SSLEngineResult(
                 status, hsStatus, deltaNet, deltaApp, plainText.recordSN);
     }
 
-    // the caller have synchronized readLock
-    void expectingFinishFlight() {
-        inputRecord.expectingFinishFlight();
-    }
+    private Plaintext decode(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
 
-    /*
-     * Actually do the read record processing.
-     *
-     * Returns a Status if it can make specific determinations
-     * of the engine state.  In particular, we need to signal
-     * that a handshake just completed.
-     *
-     * It would be nice to be symmetrical with the write side and move
-     * the majority of this to SSLInputRecord, but there's too much
-     * SSLEngine state to do that cleanly.  It must still live here.
-     */
-    private Plaintext readRecord(ByteBuffer netData,
-            ByteBuffer[] appData, int offset, int length) throws IOException {
+        Plaintext pt = SSLTransport.decode(conContext,
+                            srcs, srcsOffset, srcsLength,
+                            dsts, dstsOffset, dstsLength);
 
-        /*
-         * The various operations will return new sliced BB's,
-         * this will avoid having to worry about positions and
-         * limits in the netBB.
-         */
-        Plaintext plainText = null;
-
-        if (getConnectionState() == cs_ERROR) {
-            return Plaintext.PLAINTEXT_NULL;
-        }
-
-        /*
-         * Read a record ... maybe emitting an alert if we get a
-         * comprehensible but unsupported "hello" message during
-         * format checking (e.g. V2).
-         */
-        try {
-            if (isDTLS) {
-                // Don't process the incoming record until all of the
-                // buffered records get handled.
-                plainText = inputRecord.acquirePlaintext();
-            }
-
-            if ((!isDTLS || plainText == null) && netData != null) {
-                plainText = inputRecord.decode(netData);
-            }
-        } catch (UnsupportedOperationException unsoe) {         // SSLv2Hello
-            // Hack code to deliver SSLv2 error message for SSL/TLS connections.
-            if (!isDTLS) {
-                outputRecord.encodeV2NoCipher();
+        // Is the handshake completed?
+        if (pt != Plaintext.PLAINTEXT_NULL) {
+            HandshakeStatus hsStatus = tryToFinishHandshake(pt.contentType);
+            if (hsStatus == null) {
+                pt.handshakeStatus = conContext.getHandshakeStatus();
+            } else {
+                pt.handshakeStatus = hsStatus;
             }
 
-            fatal(Alerts.alert_unexpected_message, unsoe);
-        } catch (BadPaddingException e) {
-            /*
-             * The basic SSLv3 record protection involves (optional)
-             * encryption for privacy, and an integrity check ensuring
-             * data origin authentication.  We do them both here, and
-             * throw a fatal alert if the integrity check fails.
-             */
-            byte alertType = (connectionState != cs_DATA) ?
-                    Alerts.alert_handshake_failure :
-                    Alerts.alert_bad_record_mac;
-            fatal(alertType, e.getMessage(), e);
-        } catch (SSLHandshakeException she) {
-            // may be record sequence number overflow
-            fatal(Alerts.alert_handshake_failure, she);
-        } catch (IOException ioe) {
-            fatal(Alerts.alert_unexpected_message, ioe);
-        }
-
-        // plainText should never be null for TLS protocols
-        HandshakeStatus hsStatus = null;
-        if (plainText == Plaintext.PLAINTEXT_NULL) {
-            // Only happens for DTLS protocols.
-            //
-            // Received a retransmitted flight, and need to retransmit the
-            // previous delivered handshake flight messages.
-            if (enableRetransmissions) {
-                if (debug != null && Debug.isOn("verbose")) {
-                    Debug.log(
-                        "Retransmit the previous handshake flight messages.");
-                }
-
-                synchronized (this) {
-                    outputRecord.launchRetransmission();
-                }
-            }   // Otherwise, discard the retransmitted flight.
-        } else if (!isDTLS || plainText != null) {
-            hsStatus = processInputRecord(plainText, appData, offset, length);
-        }
-
-        if (hsStatus == null) {
-            hsStatus = getHSStatus(null);
-        }
-
-        if (plainText == null) {
-            plainText = Plaintext.PLAINTEXT_NULL;
-        }
-        plainText.handshakeStatus = hsStatus;
-
-        return plainText;
-    }
-
-    /*
-     * Process the record.
-     */
-    private synchronized HandshakeStatus processInputRecord(
-            Plaintext plainText,
-            ByteBuffer[] appData, int offset, int length) throws IOException {
-
-        HandshakeStatus hsStatus = null;
-        switch (plainText.contentType) {
-            case Record.ct_handshake:
-                /*
-                 * Handshake messages always go to a pending session
-                 * handshaker ... if there isn't one, create one.  This
-                 * must work asynchronously, for renegotiation.
-                 *
-                 * NOTE that handshaking will either resume a session
-                 * which was in the cache (and which might have other
-                 * connections in it already), or else will start a new
-                 * session (new keys exchanged) with just this connection
-                 * in it.
-                 */
-                initHandshaker();
-                if (!handshaker.activated()) {
-                    // prior to handshaking, activate the handshake
-                    if (connectionState == cs_RENEGOTIATE) {
-                        // don't use SSLv2Hello when renegotiating
-                        handshaker.activate(protocolVersion);
-                    } else {
-                        handshaker.activate(null);
-                    }
-                }
-
-                /*
-                 * process the handshake record ... may contain just
-                 * a partial handshake message or multiple messages.
-                 *
-                 * The handshaker state machine will ensure that it's
-                 * a finished message.
-                 */
-                handshaker.processRecord(plainText.fragment, expectingFinished);
-                expectingFinished = false;
-
-                if (handshaker.invalidated) {
-                    finishHandshake();
-
-                    // if state is cs_RENEGOTIATE, revert it to cs_DATA
-                    if (connectionState == cs_RENEGOTIATE) {
-                        connectionState = cs_DATA;
-                    }
-                } else if (handshaker.isDone()) {
-                    // reset the parameters for secure renegotiation.
-                    secureRenegotiation =
-                                handshaker.isSecureRenegotiation();
-                    clientVerifyData = handshaker.getClientVerifyData();
-                    serverVerifyData = handshaker.getServerVerifyData();
-                    // set connection ALPN value
-                    applicationProtocol =
-                        handshaker.getHandshakeApplicationProtocol();
-
-                    sess = handshaker.getSession();
-                    handshakeSession = null;
-                    if (outputRecord.isEmpty()) {
-                        hsStatus = finishHandshake();
-                        connectionState = cs_DATA;
-                    }
-
-                    // No handshakeListeners here.  That's a
-                    // SSLSocket thing.
-                } else if (handshaker.taskOutstanding()) {
-                    hsStatus = HandshakeStatus.NEED_TASK;
-                }
-                break;
-
-            case Record.ct_application_data:
-                // Pass this right back up to the application.
-                if ((connectionState != cs_DATA)
-                        && (connectionState != cs_RENEGOTIATE)
-                        && (connectionState != cs_CLOSED)) {
-                    throw new SSLProtocolException(
-                            "Data received in non-data state: " +
-                            connectionState);
-                }
-
-                if (expectingFinished) {
-                    throw new SSLProtocolException
-                            ("Expecting finished message, received data");
-                }
-
-                if (!inboundDone) {
-                    ByteBuffer fragment = plainText.fragment;
-                    int remains = fragment.remaining();
-
-                    // Should have enough room in appData.
-                    for (int i = offset;
-                            ((i < (offset + length)) && (remains > 0)); i++) {
-                        int amount = Math.min(appData[i].remaining(), remains);
-                        fragment.limit(fragment.position() + amount);
-                        appData[i].put(fragment);
-                        remains -= amount;
-                    }
-                }
-
-                break;
-
-            case Record.ct_alert:
-                recvAlert(plainText.fragment);
-                break;
-
-            case Record.ct_change_cipher_spec:
-                if ((connectionState != cs_HANDSHAKE
-                        && connectionState != cs_RENEGOTIATE)) {
-                    // For the CCS message arriving in the wrong state
-                    fatal(Alerts.alert_unexpected_message,
-                            "illegal change cipher spec msg, conn state = "
-                            + connectionState);
-                } else if (plainText.fragment.remaining() != 1
-                        || plainText.fragment.get() != 1) {
-                    // For structural/content issues with the CCS
-                    fatal(Alerts.alert_unexpected_message,
-                            "Malformed change cipher spec msg");
-                }
-
-                //
-                // The first message after a change_cipher_spec
-                // record MUST be a "Finished" handshake record,
-                // else it's a protocol violation.  We force this
-                // to be checked by a minor tweak to the state
-                // machine.
-                //
-                handshaker.receiveChangeCipherSpec();
-
-                CipherBox readCipher;
-                Authenticator readAuthenticator;
-                try {
-                    readCipher = handshaker.newReadCipher();
-                    readAuthenticator = handshaker.newReadAuthenticator();
-                } catch (GeneralSecurityException e) {
-                    // can't happen
-                    throw new SSLException("Algorithm missing:  ", e);
-                }
-                inputRecord.changeReadCiphers(readAuthenticator, readCipher);
-
-                // next message MUST be a finished message
-                expectingFinished = true;
-                break;
-
-            default:
-                //
-                // TLS requires that unrecognized records be ignored.
-                //
-                if (debug != null && Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                            ", Received record type: " + plainText.contentType);
-                }
-                break;
-        } // switch
-
-        /*
-         * We only need to check the sequence number state for
-         * non-handshaking record.
-         *
-         * Note that in order to maintain the handshake status
-         * properly, we check the sequence number after the last
-         * record reading process. As we request renegotiation
-         * or close the connection for wrapped sequence number
-         * when there is enough sequence number space left to
-         * handle a few more records, so the sequence number
-         * of the last record cannot be wrapped.
-         */
-        hsStatus = getHSStatus(hsStatus);
-        if (connectionState < cs_ERROR && !isInboundDone() &&
-                (hsStatus == HandshakeStatus.NOT_HANDSHAKING) &&
-                (inputRecord.seqNumIsHuge())) {
-            /*
-             * Ask for renegotiation when need to renew sequence number.
-             *
-             * Don't bother to kickstart the renegotiation when the local is
-             * asking for it.
-             */
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", request renegotiation " +
-                        "to avoid sequence number overflow");
-            }
-
-            beginHandshake();
-
-            hsStatus = getHSStatus(null);
-        }
-
-        return hsStatus;
-    }
-
-
-    //
-    // write/wrap side
-    //
-
-
-    /**
-     * Wraps a buffer.  Does a variety of checks before grabbing
-     * the wrapLock, which blocks multiple wraps from occurring.
-     */
-    @Override
-    public SSLEngineResult wrap(ByteBuffer[] appData,
-            int offset, int length, ByteBuffer netData) throws SSLException {
-
-        // check engine parameters
-        checkEngineParas(netData, appData, offset, length, true);
-
-        /*
-         * We can be smarter about using smaller buffer sizes later.
-         * For now, force it to be large enough to handle any valid record.
-         */
-        if (netData.remaining() < sess.getPacketBufferSize()) {
-            return new SSLEngineResult(
-                Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0);
-        }
-
-        try {
-            synchronized (wrapLock) {
-                return writeAppRecord(appData, offset, length, netData);
-            }
-        } catch (SSLProtocolException spe) {
-            // may be an unexpected handshake message
-            fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe);
-            return null;  // make compiler happy
-        } catch (Exception e) {
-            fatal(Alerts.alert_internal_error,
-                "problem wrapping app data", e);
-            return null;  // make compiler happy
-        }
-    }
-
-    /*
-     * Makes additional checks for unwrap, but this time more
-     * specific to this packet and the current state of the machine.
-     */
-    private SSLEngineResult writeAppRecord(ByteBuffer[] appData,
-            int offset, int length, ByteBuffer netData) throws IOException {
-
-        Status status = null;
-        HandshakeStatus hsStatus = null;
-
-        /*
-         * See if the handshaker needs to report back some SSLException.
-         */
-        checkTaskThrown();
-
-        /*
-         * short circuit if we're closed/closing.
-         */
-        if (isOutboundDone()) {
-            return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0);
-        }
-
-        /*
-         * If we're still in cs_HANDSHAKE, make sure it's been
-         * started.
-         */
-        synchronized (this) {
-            if ((connectionState == cs_HANDSHAKE) ||
-                (connectionState == cs_START)) {
-
-                kickstartHandshake();
-
-                /*
-                 * If there's no HS data available to write, we can return
-                 * without trying to wrap anything.
-                 */
-                hsStatus = getHSStatus(null);
-                if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
-                    /*
-                     * For DTLS, if the handshake state is
-                     * HandshakeStatus.NEED_UNWRAP, a call to SSLEngine.wrap()
-                     * means that the previous handshake packets (if delivered)
-                     * get lost, and need retransmit the handshake messages.
-                     */
-                    if (!isDTLS || !enableRetransmissions ||
-                            (handshaker == null) || outputRecord.firstMessage) {
-
-                        return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
-                    }   // otherwise, need retransmission
-                }
+            // Is the sequence number is nearly overflow?
+            if (conContext.inputRecord.seqNumIsHuge()) {
+                pt.handshakeStatus =
+                        tryKeyUpdate(pt.handshakeStatus);
             }
         }
 
-        /*
-         * Grab a copy of this if it doesn't already exist,
-         * and we can use it several places before anything major
-         * happens on this side.  Races aren't critical
-         * here.
-         */
-        if (hsStatus == null) {
-            hsStatus = getHSStatus(null);
-        }
-
-        /*
-         * If we have a task outstanding, this *MUST* be done before
-         * doing any more wrapping, because we could be in the middle
-         * of receiving a handshake message, for example, a finished
-         * message which would change the ciphers.
-         */
-        if (hsStatus == HandshakeStatus.NEED_TASK) {
-            return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
-        }
-
-        /*
-         * This will obtain any waiting outbound data, or will
-         * process the outbound appData.
-         */
-        int netPos = netData.position();
-        int appRemains = 0;
-        for (int i = offset; i < offset + length; i++) {
-            if (appData[i] == null) {
-                throw new IllegalArgumentException(
-                        "appData[" + i + "] == null");
-            }
-            appRemains += appData[i].remaining();
-        }
-
-        Ciphertext ciphertext = null;
-        try {
-            if (appRemains != 0) {
-                synchronized (writeLock) {
-                    ciphertext = writeRecord(appData, offset, length, netData);
-                }
-            } else {
-                synchronized (writeLock) {
-                    ciphertext = writeRecord(null, 0, 0, netData);
-                }
-            }
-        } catch (SSLException e) {
-            throw e;
-        } catch (IOException e) {
-            throw new SSLException("Write problems", e);
-        }
-
-        /*
-         * writeRecord might have reported some status.
-         * Now check for the remaining cases.
-         *
-         * status above should cover:  NEED_WRAP/FINISHED
-         */
-        status = (isOutboundDone() ? Status.CLOSED : Status.OK);
-        hsStatus = getHSStatus(ciphertext.handshakeStatus);
-
-        int deltaNet = netData.position() - netPos;
-        int deltaApp = appRemains;
-        for (int i = offset; i < offset + length; i++) {
-            deltaApp -= appData[i].remaining();
-        }
-
-        return new SSLEngineResult(
-                status, hsStatus, deltaApp, deltaNet, ciphertext.recordSN);
+        return pt;
     }
 
-    /*
-     * Central point to write/get all of the outgoing data.
-     */
-    private Ciphertext writeRecord(ByteBuffer[] appData,
-            int offset, int length, ByteBuffer netData) throws IOException {
-
-        Ciphertext ciphertext = null;
-        try {
-            // Acquire the buffered to-be-delivered records or retransmissions.
-            //
-            // May have buffered records, or need retransmission if handshaking.
-            if (!outputRecord.isEmpty() ||
-                    (enableRetransmissions && handshaker != null)) {
-                ciphertext = outputRecord.acquireCiphertext(netData);
-            }
-
-            if ((ciphertext == null) && (appData != null)) {
-                ciphertext = outputRecord.encode(
-                        appData, offset, length, netData);
-            }
-        } catch (SSLHandshakeException she) {
-            // may be record sequence number overflow
-            fatal(Alerts.alert_handshake_failure, she);
-
-            return Ciphertext.CIPHERTEXT_NULL;   // make the complier happy
-        } catch (IOException e) {
-            fatal(Alerts.alert_unexpected_message, e);
-
-            return Ciphertext.CIPHERTEXT_NULL;   // make the complier happy
-        }
-
-        if (ciphertext == null) {
-            return Ciphertext.CIPHERTEXT_NULL;
+    @Override
+    public synchronized Runnable getDelegatedTask() {
+        if (conContext.handshakeContext != null && // PRE or POST handshake
+                !conContext.handshakeContext.taskDelegated &&
+                !conContext.handshakeContext.delegatedActions.isEmpty()) {
+            conContext.handshakeContext.taskDelegated = true;
+            return new DelegatedTask(this);
         }
 
-        HandshakeStatus hsStatus = null;
-        Ciphertext.RecordType recordType = ciphertext.recordType;
-        if ((recordType.contentType == Record.ct_handshake) &&
-            (recordType.handshakeType == HandshakeMessage.ht_finished) &&
-            outputRecord.isEmpty()) {
-
-            if (handshaker == null) {
-                hsStatus = HandshakeStatus.FINISHED;
-            } else if (handshaker.isDone()) {
-                hsStatus = finishHandshake();
-                connectionState = cs_DATA;
-
-                // Retransmit the last flight twice.
-                //
-                // The application data transactions may begin immediately
-                // after the last flight.  If the last flight get lost, the
-                // application data may be discarded accordingly.  As could
-                // be an issue for some applications.  This impact can be
-                // mitigated by sending the last fligth twice.
-                if (isDTLS && enableRetransmissions) {
-                    if (debug != null && Debug.isOn("verbose")) {
-                        Debug.log(
-                            "Retransmit the last flight messages.");
-                    }
-
-                    synchronized (this) {
-                        outputRecord.launchRetransmission();
-                    }
-
-                    hsStatus = HandshakeStatus.NEED_WRAP;
-                }
-            }
-        }   // Otherwise, the followed call to getHSStatus() will help.
-
-        /*
-         * We only need to check the sequence number state for
-         * non-handshaking record.
-         *
-         * Note that in order to maintain the handshake status
-         * properly, we check the sequence number after the last
-         * record writing process. As we request renegotiation
-         * or close the connection for wrapped sequence number
-         * when there is enough sequence number space left to
-         * handle a few more records, so the sequence number
-         * of the last record cannot be wrapped.
-         */
-        hsStatus = getHSStatus(hsStatus);
-        if (connectionState < cs_ERROR && !isOutboundDone() &&
-                (hsStatus == HandshakeStatus.NOT_HANDSHAKING) &&
-                (outputRecord.seqNumIsHuge())) {
-            /*
-             * Ask for renegotiation when need to renew sequence number.
-             *
-             * Don't bother to kickstart the renegotiation when the local is
-             * asking for it.
-             */
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", request renegotiation " +
-                        "to avoid sequence number overflow");
-            }
-
-            beginHandshake();
-
-            hsStatus = getHSStatus(null);
-        }
-        ciphertext.handshakeStatus = hsStatus;
-
-        return ciphertext;
+        return null;
     }
 
-    private HandshakeStatus finishHandshake() {
-        handshaker = null;
-        inputRecord.setHandshakeHash(null);
-        outputRecord.setHandshakeHash(null);
-        connectionState = cs_DATA;
-
-       return HandshakeStatus.FINISHED;
-   }
-
-    //
-    // Close code
-    //
-
-    /**
-     * Signals that no more outbound application data will be sent
-     * on this <code>SSLEngine</code>.
-     */
-    private void closeOutboundInternal() {
-
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                                    ", closeOutboundInternal()");
-        }
-
-        /*
-         * Already closed, ignore
-         */
-        if (outboundDone) {
-            return;
-        }
-
-        switch (connectionState) {
+    @Override
+    public synchronized void closeInbound() throws SSLException {
+        conContext.closeInbound();
+    }
 
-        /*
-         * If we haven't even started yet, don't bother reading inbound.
-         */
-        case cs_START:
-            try {
-                outputRecord.close();
-            } catch (IOException ioe) {
-               // ignore
-            }
-            outboundDone = true;
-
-            try {
-                inputRecord.close();
-            } catch (IOException ioe) {
-               // ignore
-            }
-            inboundDone = true;
-            break;
-
-        case cs_ERROR:
-        case cs_CLOSED:
-            break;
-
-        /*
-         * Otherwise we indicate clean termination.
-         */
-        // case cs_HANDSHAKE:
-        // case cs_DATA:
-        // case cs_RENEGOTIATE:
-        default:
-            warning(Alerts.alert_close_notify);
-            try {
-                outputRecord.close();
-            } catch (IOException ioe) {
-               // ignore
-            }
-            outboundDone = true;
-            break;
-        }
-
-        connectionState = cs_CLOSED;
+    @Override
+    public synchronized boolean isInboundDone() {
+        return conContext.isInboundDone();
     }
 
     @Override
     public synchronized void closeOutbound() {
-        /*
-         * Dump out a close_notify to the remote side
-         */
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                                    ", called closeOutbound()");
-        }
-
-        closeOutboundInternal();
+        conContext.closeOutbound();
     }
 
-    /**
-     * Returns the outbound application data closure state
-     */
     @Override
-    public boolean isOutboundDone() {
-        return outboundDone && outputRecord.isEmpty();
+    public synchronized boolean isOutboundDone() {
+        return conContext.isOutboundDone();
     }
 
-    /**
-     * Signals that no more inbound network data will be sent
-     * to this <code>SSLEngine</code>.
-     */
-    private void closeInboundInternal() {
-
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                                    ", closeInboundInternal()");
-        }
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return CipherSuite.namesOf(sslContext.getSupportedCipherSuites());
+    }
 
-        /*
-         * Already closed, ignore
-         */
-        if (inboundDone) {
-            return;
-        }
-
-        closeOutboundInternal();
+    @Override
+    public synchronized String[] getEnabledCipherSuites() {
+        return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites);
+    }
 
-        try {
-            inputRecord.close();
-        } catch (IOException ioe) {
-           // ignore
-        }
-        inboundDone = true;
-
-        connectionState = cs_CLOSED;
+    @Override
+    public synchronized void setEnabledCipherSuites(String[] suites) {
+        conContext.sslConfig.enabledCipherSuites =
+                CipherSuite.validValuesOf(suites);
     }
 
-    /*
-     * Close the inbound side of the connection.  We grab the
-     * lock here, and do the real work in the internal verison.
-     * We do check for truncation attacks.
-     */
+    @Override
+    public String[] getSupportedProtocols() {
+        return ProtocolVersion.toStringArray(
+                sslContext.getSupportedProtocolVersions());
+    }
+
     @Override
-    public synchronized void closeInbound() throws SSLException {
-        /*
-         * Currently closes the outbound side as well.  The IETF TLS
-         * working group has expressed the opinion that 1/2 open
-         * connections are not allowed by the spec.  May change
-         * someday in the future.
-         */
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                                    ", called closeInbound()");
+    public synchronized String[] getEnabledProtocols() {
+        return ProtocolVersion.toStringArray(
+                conContext.sslConfig.enabledProtocols);
+    }
+
+    @Override
+    public synchronized void setEnabledProtocols(String[] protocols) {
+        if (protocols == null) {
+            throw new IllegalArgumentException("Protocols cannot be null");
         }
 
-        /*
-         * No need to throw an Exception if we haven't even started yet.
-         */
-        if ((connectionState != cs_START) && !recvCN) {
-            recvCN = true;  // Only receive the Exception once
-            fatal(Alerts.alert_internal_error,
-                "Inbound closed before receiving peer's close_notify: " +
-                "possible truncation attack?");
-        } else {
-            /*
-             * Currently, this is a no-op, but in case we change
-             * the close inbound code later.
-             */
-            closeInboundInternal();
-        }
+        conContext.sslConfig.enabledProtocols =
+                ProtocolVersion.namesOf(protocols);
     }
 
-    /**
-     * Returns the network inbound data closure state
-     */
-    @Override
-    public synchronized boolean isInboundDone() {
-        return inboundDone;
-    }
-
-
-    //
-    // Misc stuff
-    //
-
-
-    /**
-     * Returns the current <code>SSLSession</code> for this
-     * <code>SSLEngine</code>
-     * <P>
-     * These can be long lived, and frequently correspond to an
-     * entire login session for some user.
-     */
     @Override
     public synchronized SSLSession getSession() {
-        return sess;
+        return conContext.conSession;
     }
 
     @Override
     public synchronized SSLSession getHandshakeSession() {
-        return handshakeSession;
-    }
-
-    synchronized void setHandshakeSession(SSLSessionImpl session) {
-        // update the fragment size, which may be negotiated during handshaking
-        inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
-        outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
-
-        handshakeSession = session;
-    }
-
-    /**
-     * Returns a delegated <code>Runnable</code> task for
-     * this <code>SSLEngine</code>.
-     */
-    @Override
-    public synchronized Runnable getDelegatedTask() {
-        if (handshaker != null) {
-            return handshaker.getTask();
-        }
-        return null;
+        return conContext.handshakeContext == null ?
+                null : conContext.handshakeContext.handshakeSession;
     }
 
-
-    //
-    // EXCEPTION AND ALERT HANDLING
-    //
-
-    /*
-     * Send a warning alert.
-     */
-    void warning(byte description) {
-        sendAlert(Alerts.alert_warning, description);
-    }
-
-    synchronized void fatal(byte description, String diagnostic)
-            throws SSLException {
-        fatal(description, diagnostic, null, false);
-    }
-
-    synchronized void fatal(byte description, Throwable cause)
-            throws SSLException {
-        fatal(description, null, cause, false);
-    }
-
-    synchronized void fatal(byte description, String diagnostic,
-            Throwable cause) throws SSLException {
-        fatal(description, diagnostic, cause, false);
+    @Override
+    public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
+        return conContext.getHandshakeStatus();
     }
 
-    /*
-     * We've got a fatal error here, so start the shutdown process.
-     *
-     * Because of the way the code was written, we have some code
-     * calling fatal directly when the "description" is known
-     * and some throwing Exceptions which are then caught by higher
-     * levels which then call here.  This code needs to determine
-     * if one of the lower levels has already started the process.
-     *
-     * We won't worry about Errors, if we have one of those,
-     * we're in worse trouble.  Note:  the networking code doesn't
-     * deal with Errors either.
-     */
-    synchronized void fatal(byte description, String diagnostic,
-            Throwable cause, boolean recvFatalAlert) throws SSLException {
-
-        /*
-         * If we have no further information, make a general-purpose
-         * message for folks to see.  We generally have one or the other.
-         */
-        if (diagnostic == null) {
-            diagnostic = "General SSLEngine problem";
-        }
-        if (cause == null) {
-            cause = Alerts.getSSLException(description, cause, diagnostic);
-        }
-
-        /*
-         * If we've already shutdown because of an error,
-         * there is nothing we can do except rethrow the exception.
-         *
-         * Most exceptions seen here will be SSLExceptions.
-         * We may find the occasional Exception which hasn't been
-         * converted to a SSLException, so we'll do it here.
-         */
-        if (closeReason != null) {
-            if ((debug != null) && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", fatal: engine already closed.  Rethrowing " +
-                    cause.toString());
-            }
-            if (cause instanceof RuntimeException) {
-                throw (RuntimeException)cause;
-            } else if (cause instanceof SSLException) {
-                throw (SSLException)cause;
-            } else if (cause instanceof Exception) {
-                throw new SSLException("fatal SSLEngine condition", cause);
-            }
-        }
-
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName()
-                        + ", fatal error: " + description +
-                        ": " + diagnostic + "\n" + cause.toString());
-        }
-
-        /*
-         * Ok, this engine's going down.
-         */
-        int oldState = connectionState;
-        connectionState = cs_ERROR;
-
-        try {
-            inputRecord.close();
-        } catch (IOException ioe) {
-           // ignore
-        }
-        inboundDone = true;
-
-        sess.invalidate();
-        if (handshakeSession != null) {
-            handshakeSession.invalidate();
-        }
-
-        /*
-         * If we haven't even started handshaking yet, or we are the
-         * recipient of a fatal alert, no need to generate a fatal close
-         * alert.
-         */
-        if (oldState != cs_START && !recvFatalAlert) {
-            sendAlert(Alerts.alert_fatal, description);
-        }
-
-        if (cause instanceof SSLException) { // only true if != null
-            closeReason = (SSLException)cause;
-        } else {
-            /*
-             * Including RuntimeExceptions, but we'll throw those
-             * down below.  The closeReason isn't used again,
-             * except for null checks.
-             */
-            closeReason =
-                Alerts.getSSLException(description, cause, diagnostic);
-        }
-
-        try {
-            outputRecord.close();
-        } catch (IOException ioe) {
-           // ignore
-        }
-        outboundDone = true;
-
-        connectionState = cs_CLOSED;
-
-        if (cause instanceof RuntimeException) {
-            throw (RuntimeException)cause;
-        } else {
-            throw closeReason;
-        }
+    @Override
+    public synchronized void setUseClientMode(boolean mode) {
+        conContext.setUseClientMode(mode);
     }
 
-    /*
-     * Process an incoming alert ... caller must already have synchronized
-     * access to "this".
-     */
-    private void recvAlert(ByteBuffer fragment) throws IOException {
-        byte level = fragment.get();
-        byte description = fragment.get();
-
-        if (debug != null && (Debug.isOn("record") ||
-                Debug.isOn("handshake"))) {
-            synchronized (System.out) {
-                System.out.print(Thread.currentThread().getName());
-                System.out.print(", RECV " + protocolVersion + " ALERT:  ");
-                if (level == Alerts.alert_fatal) {
-                    System.out.print("fatal, ");
-                } else if (level == Alerts.alert_warning) {
-                    System.out.print("warning, ");
-                } else {
-                    System.out.print("<level " + (0x0ff & level) + ">, ");
-                }
-                System.out.println(Alerts.alertDescription(description));
-            }
-        }
-
-        if (level == Alerts.alert_warning) {
-            if (description == -1) {    // check for short message
-                fatal(Alerts.alert_illegal_parameter, "Short alert message");
-            } else if (description == Alerts.alert_close_notify) {
-                if (connectionState == cs_HANDSHAKE) {
-                    fatal(Alerts.alert_unexpected_message,
-                                "Received close_notify during handshake");
-                } else {
-                    recvCN = true;
-                    closeInboundInternal();  // reply to close
-                }
-            } else {
-
-                //
-                // The other legal warnings relate to certificates,
-                // e.g. no_certificate, bad_certificate, etc; these
-                // are important to the handshaking code, which can
-                // also handle illegal protocol alerts if needed.
-                //
-                if (handshaker != null) {
-                    handshaker.handshakeAlert(description);
-                }
-            }
-        } else { // fatal or unknown level
-            String reason = "Received fatal alert: "
-                + Alerts.alertDescription(description);
-
-            // The inbound and outbound queues will be closed as part of
-            // the call to fatal.  The handhaker to needs to be set to null
-            // so subsequent calls to getHandshakeStatus will return
-            // NOT_HANDSHAKING.
-            handshaker = null;
-            Throwable cause = Alerts.getSSLException(description, reason);
-            fatal(description, null, cause, true);
-        }
+    @Override
+    public synchronized boolean getUseClientMode() {
+        return conContext.sslConfig.isClientMode;
     }
 
-
-    /*
-     * Emit alerts.  Caller must have synchronized with "this".
-     */
-    private void sendAlert(byte level, byte description) {
-        // the connectionState cannot be cs_START
-        if (connectionState >= cs_CLOSED) {
-            return;
-        }
-
-        // For initial handshaking, don't send alert message to peer if
-        // handshaker has not started.
-        //
-        // Shall we send an fatal alter to terminate the connection gracefully?
-        if (connectionState <= cs_HANDSHAKE &&
-                (handshaker == null || !handshaker.started() ||
-                        !handshaker.activated())) {
-            return;
-        }
-
-        try {
-            outputRecord.encodeAlert(level, description);
-        } catch (IOException ioe) {
-            // ignore
-        }
-    }
-
-
-    //
-    // VARIOUS OTHER METHODS (COMMON TO SSLSocket)
-    //
-
-
-    /**
-     * Controls whether new connections may cause creation of new SSL
-     * sessions.
-     *
-     * As long as handshaking has not started, we can change
-     * whether we enable session creations.  Otherwise,
-     * we will need to wait for the next handshake.
-     */
     @Override
-    public synchronized void setEnableSessionCreation(boolean flag) {
-        enableSessionCreation = flag;
-
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setEnableSessionCreation(enableSessionCreation);
-        }
-    }
-
-    /**
-     * Returns true if new connections may cause creation of new SSL
-     * sessions.
-     */
-    @Override
-    public synchronized boolean getEnableSessionCreation() {
-        return enableSessionCreation;
-    }
-
-
-    /**
-     * Sets the flag controlling whether a server mode engine
-     * *REQUIRES* SSL client authentication.
-     *
-     * As long as handshaking has not started, we can change
-     * whether client authentication is needed.  Otherwise,
-     * we will need to wait for the next handshake.
-     */
-    @Override
-    public synchronized void setNeedClientAuth(boolean flag) {
-        doClientAuth = (flag ?
-                ClientAuthType.CLIENT_AUTH_REQUIRED :
-                ClientAuthType.CLIENT_AUTH_NONE);
-
-        if ((handshaker != null) &&
-                (handshaker instanceof ServerHandshaker) &&
-                !handshaker.activated()) {
-            ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
-        }
+    public synchronized void setNeedClientAuth(boolean need) {
+        conContext.sslConfig.clientAuthType =
+                (need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
+                        ClientAuthType.CLIENT_AUTH_NONE);
     }
 
     @Override
     public synchronized boolean getNeedClientAuth() {
-        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED);
+        return (conContext.sslConfig.clientAuthType ==
+                        ClientAuthType.CLIENT_AUTH_REQUIRED);
     }
 
-    /**
-     * Sets the flag controlling whether a server mode engine
-     * *REQUESTS* SSL client authentication.
-     *
-     * As long as handshaking has not started, we can change
-     * whether client authentication is requested.  Otherwise,
-     * we will need to wait for the next handshake.
-     */
     @Override
-    public synchronized void setWantClientAuth(boolean flag) {
-        doClientAuth = (flag ?
-                ClientAuthType.CLIENT_AUTH_REQUESTED :
-                ClientAuthType.CLIENT_AUTH_NONE);
-
-        if ((handshaker != null) &&
-                (handshaker instanceof ServerHandshaker) &&
-                !handshaker.activated()) {
-            ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
-        }
+    public synchronized void setWantClientAuth(boolean want) {
+        conContext.sslConfig.clientAuthType =
+                (want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
+                        ClientAuthType.CLIENT_AUTH_NONE);
     }
 
     @Override
     public synchronized boolean getWantClientAuth() {
-        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED);
+        return (conContext.sslConfig.clientAuthType ==
+                        ClientAuthType.CLIENT_AUTH_REQUESTED);
     }
 
-
-    /**
-     * Sets the flag controlling whether the engine is in SSL
-     * client or server mode.  Must be called before any SSL
-     * traffic has started.
-     */
     @Override
-    @SuppressWarnings("fallthrough")
-    public synchronized void setUseClientMode(boolean flag) {
-        switch (connectionState) {
-
-        case cs_START:
-            /*
-             * If we need to change the socket mode and the enabled
-             * protocols and cipher suites haven't specifically been
-             * set by the user, change them to the corresponding
-             * default ones.
-             */
-            if (roleIsServer != (!flag)) {
-                if (sslContext.isDefaultProtocolList(enabledProtocols)) {
-                    enabledProtocols =
-                            sslContext.getDefaultProtocolList(!flag);
-                }
-
-                if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
-                    enabledCipherSuites =
-                            sslContext.getDefaultCipherSuiteList(!flag);
-                }
-            }
-
-            roleIsServer = !flag;
-            serverModeSet = true;
-            break;
-
-        case cs_HANDSHAKE:
-            /*
-             * If we have a handshaker, but haven't started
-             * SSL traffic, we can throw away our current
-             * handshaker, and start from scratch.  Don't
-             * need to call doneConnect() again, we already
-             * have the streams.
-             */
-            assert(handshaker != null);
-            if (!handshaker.activated()) {
-                /*
-                 * If we need to change the socket mode and the enabled
-                 * protocols and cipher suites haven't specifically been
-                 * set by the user, change them to the corresponding
-                 * default ones.
-                 */
-                if (roleIsServer != (!flag)) {
-                    if (sslContext.isDefaultProtocolList(enabledProtocols)) {
-                        enabledProtocols =
-                                sslContext.getDefaultProtocolList(!flag);
-                    }
-
-                    if (sslContext.isDefaultCipherSuiteList(
-                                                    enabledCipherSuites)) {
-                        enabledCipherSuites =
-                            sslContext.getDefaultCipherSuiteList(!flag);
-                    }
-                }
-
-                roleIsServer = !flag;
-                connectionState = cs_START;
-                initHandshaker();
-                break;
-            }
-
-            // If handshake has started, that's an error.  Fall through...
-
-        default:
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", setUseClientMode() invoked in state = " +
-                    connectionState);
-            }
-
-            /*
-             * We can let them continue if they catch this correctly,
-             * we don't need to shut this down.
-             */
-            throw new IllegalArgumentException(
-                "Cannot change mode after SSL traffic has started");
-        }
+    public synchronized void setEnableSessionCreation(boolean flag) {
+        conContext.sslConfig.enableSessionCreation = flag;
     }
 
     @Override
-    public synchronized boolean getUseClientMode() {
-        return !roleIsServer;
-    }
-
-
-    /**
-     * Returns the names of the cipher suites which could be enabled for use
-     * on an SSL connection.  Normally, only a subset of these will actually
-     * be enabled by default, since this list may include cipher suites which
-     * do not support the mutual authentication of servers and clients, or
-     * which do not protect data confidentiality.  Servers may also need
-     * certain kinds of certificates to use certain cipher suites.
-     *
-     * @return an array of cipher suite names
-     */
-    @Override
-    public String[] getSupportedCipherSuites() {
-        return sslContext.getSupportedCipherSuiteList().toStringArray();
-    }
-
-    /**
-     * Controls which particular cipher suites are enabled for use on
-     * this connection.  The cipher suites must have been listed by
-     * getCipherSuites() as being supported.  Even if a suite has been
-     * enabled, it might never be used if no peer supports it or the
-     * requisite certificates (and private keys) are not available.
-     *
-     * @param suites Names of all the cipher suites to enable.
-     */
-    @Override
-    public synchronized void setEnabledCipherSuites(String[] suites) {
-        enabledCipherSuites = new CipherSuiteList(suites);
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setEnabledCipherSuites(enabledCipherSuites);
-        }
-    }
-
-    /**
-     * Returns the names of the SSL cipher suites which are currently enabled
-     * for use on this connection.  When an SSL engine is first created,
-     * all enabled cipher suites <em>(a)</em> protect data confidentiality,
-     * by traffic encryption, and <em>(b)</em> can mutually authenticate
-     * both clients and servers.  Thus, in some environments, this value
-     * might be empty.
-     *
-     * @return an array of cipher suite names
-     */
-    @Override
-    public synchronized String[] getEnabledCipherSuites() {
-        return enabledCipherSuites.toStringArray();
-    }
-
-
-    /**
-     * Returns the protocols that are supported by this implementation.
-     * A subset of the supported protocols may be enabled for this connection
-     * @return an array of protocol names.
-     */
-    @Override
-    public String[] getSupportedProtocols() {
-        return sslContext.getSuportedProtocolList().toStringArray();
-    }
-
-    /**
-     * Controls which protocols are enabled for use on
-     * this connection.  The protocols must have been listed by
-     * getSupportedProtocols() as being supported.
-     *
-     * @param protocols protocols to enable.
-     * @exception IllegalArgumentException when one of the protocols
-     *  named by the parameter is not supported.
-     */
-    @Override
-    public synchronized void setEnabledProtocols(String[] protocols) {
-        enabledProtocols = new ProtocolList(protocols);
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setEnabledProtocols(enabledProtocols);
-        }
+    public synchronized boolean getEnableSessionCreation() {
+        return conContext.sslConfig.enableSessionCreation;
     }
 
     @Override
-    public synchronized String[] getEnabledProtocols() {
-        return enabledProtocols.toStringArray();
+    public synchronized SSLParameters getSSLParameters() {
+        return conContext.sslConfig.getSSLParameters();
     }
 
-    /**
-     * Returns the SSLParameters in effect for this SSLEngine.
-     */
-    @Override
-    public synchronized SSLParameters getSSLParameters() {
-        SSLParameters params = super.getSSLParameters();
-
-        // the super implementation does not handle the following parameters
-        params.setEndpointIdentificationAlgorithm(identificationProtocol);
-        params.setAlgorithmConstraints(algorithmConstraints);
-        params.setSNIMatchers(sniMatchers);
-        params.setServerNames(serverNames);
-        params.setUseCipherSuitesOrder(preferLocalCipherSuites);
-        params.setEnableRetransmissions(enableRetransmissions);
-        params.setMaximumPacketSize(maximumPacketSize);
-        params.setApplicationProtocols(applicationProtocols);
-
-        return params;
-    }
-
-    /**
-     * Applies SSLParameters to this engine.
-     */
     @Override
     public synchronized void setSSLParameters(SSLParameters params) {
-        super.setSSLParameters(params);
-
-        // the super implementation does not handle the following parameters
-        identificationProtocol = params.getEndpointIdentificationAlgorithm();
-        algorithmConstraints = params.getAlgorithmConstraints();
-        preferLocalCipherSuites = params.getUseCipherSuitesOrder();
-        enableRetransmissions = params.getEnableRetransmissions();
-        maximumPacketSize = params.getMaximumPacketSize();
-
-        if (maximumPacketSize != 0) {
-            outputRecord.changePacketSize(maximumPacketSize);
-        } else {
-            // use the implicit maximum packet size.
-            maximumPacketSize = outputRecord.getMaxPacketSize();
-        }
+        conContext.sslConfig.setSSLParameters(params);
 
-        List<SNIServerName> sniNames = params.getServerNames();
-        if (sniNames != null) {
-            serverNames = sniNames;
-        }
-
-        Collection<SNIMatcher> matchers = params.getSNIMatchers();
-        if (matchers != null) {
-            sniMatchers = matchers;
-        }
-        applicationProtocols = params.getApplicationProtocols();
-
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setIdentificationProtocol(identificationProtocol);
-            handshaker.setAlgorithmConstraints(algorithmConstraints);
-            handshaker.setMaximumPacketSize(maximumPacketSize);
-            handshaker.setApplicationProtocols(applicationProtocols);
-            if (roleIsServer) {
-                handshaker.setSNIMatchers(sniMatchers);
-                handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
-            } else {
-                handshaker.setSNIServerNames(serverNames);
-            }
+        if (conContext.sslConfig.maximumPacketSize != 0) {
+            conContext.outputRecord.changePacketSize(
+                    conContext.sslConfig.maximumPacketSize);
         }
     }
 
     @Override
     public synchronized String getApplicationProtocol() {
-        return applicationProtocol;
+        return conContext.applicationProtocol;
     }
 
     @Override
     public synchronized String getHandshakeApplicationProtocol() {
-        if ((handshaker != null) && handshaker.started()) {
-            return handshaker.getHandshakeApplicationProtocol();
-        }
-        return null;
+        return conContext.handshakeContext == null ?
+                null : conContext.handshakeContext.applicationProtocol;
     }
 
     @Override
     public synchronized void setHandshakeApplicationProtocolSelector(
-        BiFunction<SSLEngine, List<String>, String> selector) {
-        applicationProtocolSelector = selector;
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setApplicationProtocolSelectorSSLEngine(selector);
-        }
+            BiFunction<SSLEngine, List<String>, String> selector) {
+        conContext.sslConfig.engineAPSelector = selector;
     }
 
     @Override
     public synchronized BiFunction<SSLEngine, List<String>, String>
-        getHandshakeApplicationProtocolSelector() {
-        return this.applicationProtocolSelector;
+            getHandshakeApplicationProtocolSelector() {
+        return conContext.sslConfig.engineAPSelector;
+    }
+
+    @Override
+    public boolean useDelegatedTask() {
+        return true;
+    }
+
+    private synchronized void checkTaskThrown() throws SSLException {
+        HandshakeContext hc = conContext.handshakeContext;
+        if (hc != null && hc.delegatedThrown != null) {
+            try {
+                throw getTaskThrown(hc.delegatedThrown);
+            } finally {
+                hc.delegatedThrown = null;
+            }
+        }
+
+        if (conContext.isBroken && conContext.closeReason != null) {
+            throw getTaskThrown(conContext.closeReason);
+        }
+    }
+
+    private static SSLException getTaskThrown(Exception taskThrown) {
+        String msg = taskThrown.getMessage();
+
+        if (msg == null) {
+            msg = "Delegated task threw Exception or Error";
+        }
+
+        if (taskThrown instanceof RuntimeException) {
+            throw new RuntimeException(msg, taskThrown);
+        } else if (taskThrown instanceof SSLHandshakeException) {
+            return (SSLHandshakeException)
+                new SSLHandshakeException(msg).initCause(taskThrown);
+        } else if (taskThrown instanceof SSLKeyException) {
+            return (SSLKeyException)
+                new SSLKeyException(msg).initCause(taskThrown);
+        } else if (taskThrown instanceof SSLPeerUnverifiedException) {
+            return (SSLPeerUnverifiedException)
+                new SSLPeerUnverifiedException(msg).initCause(taskThrown);
+        } else if (taskThrown instanceof SSLProtocolException) {
+            return (SSLProtocolException)
+                new SSLProtocolException(msg).initCause(taskThrown);
+        } else if (taskThrown instanceof SSLException) {
+            return (SSLException)taskThrown;
+        } else {
+            return new SSLException(msg, taskThrown);
+        }
     }
 
     /**
-     * Returns a printable representation of this end of the connection.
+     * Implement a simple task delegator.
      */
-    @Override
-    public String toString() {
-        StringBuilder retval = new StringBuilder(80);
+    private static class DelegatedTask implements Runnable {
+        private final SSLEngineImpl engine;
+
+        DelegatedTask(SSLEngineImpl engineInstance) {
+            this.engine = engineInstance;
+        }
+
+        @Override
+        public void run() {
+            synchronized (engine) {
+                HandshakeContext hc = engine.conContext.handshakeContext;
+                if (hc == null || hc.delegatedActions.isEmpty()) {
+                    return;
+                }
 
-        retval.append(Integer.toHexString(hashCode()));
-        retval.append("[");
-        retval.append("SSLEngine[hostname=");
-        String host = getPeerHost();
-        retval.append((host == null) ? "null" : host);
-        retval.append(" port=");
-        retval.append(Integer.toString(getPeerPort()));
-        retval.append(" role=" + (roleIsServer ? "Server" : "Client"));
-        retval.append("] ");
-        retval.append(getSession().getCipherSuite());
-        retval.append("]");
+                try {
+                    AccessController.doPrivileged(
+                            new DelegatedAction(hc), engine.conContext.acc);
+                } catch (PrivilegedActionException pae) {
+                    // Get the handshake context again in case the
+                    // handshaking has completed.
+                    hc = engine.conContext.handshakeContext;
+                    if (hc != null) {
+                        hc.delegatedThrown = pae.getException();
+                    } else if (engine.conContext.closeReason != null) {
+                        engine.conContext.closeReason =
+                                getTaskThrown(pae.getException());
+                    }
+                } catch (RuntimeException rte) {
+                    // Get the handshake context again in case the
+                    // handshaking has completed.
+                    hc = engine.conContext.handshakeContext;
+                    if (hc != null) {
+                        hc.delegatedThrown = rte;
+                    } else if (engine.conContext.closeReason != null) {
+                        engine.conContext.closeReason = rte;
+                    }
+                }
 
-        return retval.toString();
+                // Get the handshake context again in case the
+                // handshaking has completed.
+                hc = engine.conContext.handshakeContext;
+                if (hc != null) {
+                    hc.taskDelegated = false;
+                }
+            }
+        }
+
+        private static class DelegatedAction
+                implements PrivilegedExceptionAction<Void> {
+            final HandshakeContext context;
+            DelegatedAction(HandshakeContext context) {
+                this.context = context;
+            }
+
+            @Override
+            public Void run() throws Exception {
+                while (!context.delegatedActions.isEmpty()) {
+                    // Report back the task SSLException
+                    if (context.delegatedThrown != null) {
+                        Exception delegatedThrown = context.delegatedThrown;
+                        context.delegatedThrown = null;
+                        throw getTaskThrown(delegatedThrown);
+                    }
+
+                    Map.Entry<Byte, ByteBuffer> me =
+                            context.delegatedActions.poll();
+                    if (me != null) {
+                        context.dispatch(me.getKey(), me.getValue());
+                    }
+                }
+                return null;
+            }
+        }
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,48 +25,48 @@
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
 import javax.crypto.BadPaddingException;
-
-import javax.net.ssl.*;
-
-import sun.security.util.HexDumpEncoder;
-
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLProtocolException;
+import sun.security.ssl.SSLCipher.SSLReadCipher;
+import sun.security.ssl.KeyUpdate.KeyUpdateMessage;
+import sun.security.ssl.KeyUpdate.KeyUpdateRequest;
 
 /**
  * {@code InputRecord} implementation for {@code SSLEngine}.
  */
 final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
-    // used by handshake hash computation for handshake fragment
-    private byte prevType = -1;
-    private int hsMsgOff = 0;
-    private int hsMsgLen = 0;
-
     private boolean formatVerified = false;     // SSLv2 ruled out?
 
-    SSLEngineInputRecord() {
-        this.readAuthenticator = MAC.TLS_NULL;
+    // Cache for incomplete handshake messages.
+    private ByteBuffer handshakeBuffer = null;
+
+    SSLEngineInputRecord(HandshakeHash handshakeHash) {
+        super(handshakeHash, SSLReadCipher.nullTlsReadCipher());
     }
 
     @Override
     int estimateFragmentSize(int packetSize) {
-        int macLen = 0;
-        if (readAuthenticator instanceof MAC) {
-            macLen = ((MAC)readAuthenticator).MAClen();
-        }
-
         if (packetSize > 0) {
-            return readCipher.estimateFragmentSize(
-                    packetSize, macLen, headerSize);
+            return readCipher.estimateFragmentSize(packetSize, headerSize);
         } else {
             return Record.maxDataSize;
         }
     }
 
     @Override
-    int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
+    int bytesInCompletePacket(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
+
+        return bytesInCompletePacket(srcs[srcsOffset]);
+    }
+
+    private int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
         /*
          * SSLv2 length field is in bytes 0/1
          * SSLv3/TLS length field is in bytes 3/4
@@ -83,19 +83,23 @@
         /*
          * If we have already verified previous packets, we can
          * ignore the verifications steps, and jump right to the
-         * determination.  Otherwise, try one last hueristic to
+         * determination.  Otherwise, try one last heuristic to
          * see if it's SSL/TLS.
          */
         if (formatVerified ||
-                (byteZero == ct_handshake) || (byteZero == ct_alert)) {
+                (byteZero == ContentType.HANDSHAKE.id) ||
+                (byteZero == ContentType.ALERT.id)) {
             /*
              * Last sanity check that it's not a wild record
              */
-            ProtocolVersion recordVersion = ProtocolVersion.valueOf(
-                                    packet.get(pos + 1), packet.get(pos + 2));
-
-            // check the record version
-            checkRecordVersion(recordVersion, false);
+            byte majorVersion = packet.get(pos + 1);
+            byte minorVersion = packet.get(pos + 2);
+            if (!ProtocolVersion.isNegotiable(
+                    majorVersion, minorVersion, false, false)) {
+                throw new SSLException("Unrecognized record version " +
+                        ProtocolVersion.nameOf(majorVersion, minorVersion) +
+                        " , plaintext connection?");
+            }
 
             /*
              * Reasonably sure this is a V3, disable further checks.
@@ -123,11 +127,14 @@
             if (isShort &&
                     ((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) {
 
-                ProtocolVersion recordVersion = ProtocolVersion.valueOf(
-                                    packet.get(pos + 3), packet.get(pos + 4));
-
-                // check the record version
-                checkRecordVersion(recordVersion, true);
+                byte majorVersion = packet.get(pos + 3);
+                byte minorVersion = packet.get(pos + 4);
+                if (!ProtocolVersion.isNegotiable(
+                        majorVersion, minorVersion, false, false)) {
+                    throw new SSLException("Unrecognized record version " +
+                            ProtocolVersion.nameOf(majorVersion, minorVersion) +
+                            " , plaintext connection?");
+                }
 
                 /*
                  * Client or Server Hello
@@ -147,37 +154,29 @@
     }
 
     @Override
-    void checkRecordVersion(ProtocolVersion recordVersion,
-            boolean allowSSL20Hello) throws SSLException {
-
-        if (recordVersion.maybeDTLSProtocol()) {
-            throw new SSLException(
-                    "Unrecognized record version " + recordVersion +
-                    " , DTLS packet?");
-        }
+    Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
+            int srcsLength) throws IOException, BadPaddingException {
+        if (srcs == null || srcs.length == 0 || srcsLength == 0) {
+            return new Plaintext[0];
+        } else if (srcsLength == 1) {
+            return decode(srcs[srcsOffset]);
+        } else {
+            ByteBuffer packet = extract(srcs,
+                    srcsOffset, srcsLength, SSLRecord.headerSize);
 
-        // Check if the record version is too old.
-        if ((recordVersion.v < ProtocolVersion.MIN.v)) {
-            // if it's not SSLv2, we're out of here.
-            if (!allowSSL20Hello ||
-                    (recordVersion.v != ProtocolVersion.SSL20Hello.v)) {
-                throw new SSLException(
-                    "Unsupported record version " + recordVersion);
-            }
+            return decode(packet);
         }
     }
 
-    @Override
-    Plaintext decode(ByteBuffer packet)
+    private Plaintext[] decode(ByteBuffer packet)
             throws IOException, BadPaddingException {
 
         if (isClosed) {
             return null;
         }
 
-        if (debug != null && Debug.isOn("packet")) {
-             Debug.printHex(
-                    "[Raw read]: length = " + packet.remaining(), packet);
+        if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+            SSLLogger.fine("Raw read", packet);
         }
 
         // The caller should have validated the record.
@@ -191,7 +190,8 @@
              */
             int pos = packet.position();
             byte byteZero = packet.get(pos);
-            if (byteZero != ct_handshake && byteZero != ct_alert) {
+            if (byteZero != ContentType.HANDSHAKE.id &&
+                    byteZero != ContentType.ALERT.id) {
                 return handleUnknownRecord(packet);
             }
         }
@@ -199,27 +199,24 @@
         return decodeInputRecord(packet);
     }
 
-    private Plaintext decodeInputRecord(ByteBuffer packet)
+    private Plaintext[] decodeInputRecord(ByteBuffer packet)
             throws IOException, BadPaddingException {
-
         //
         // The packet should be a complete record, or more.
         //
-
         int srcPos = packet.position();
         int srcLim = packet.limit();
 
         byte contentType = packet.get();                   // pos: 0
         byte majorVersion = packet.get();                  // pos: 1
         byte minorVersion = packet.get();                  // pos: 2
-        int contentLen = ((packet.get() & 0xFF) << 8) +
-                          (packet.get() & 0xFF);           // pos: 3, 4
+        int contentLen = Record.getInt16(packet);          // pos: 3, 4
 
-        if (debug != null && Debug.isOn("record")) {
-             System.out.println(Thread.currentThread().getName() +
-                    ", READ: " +
-                    ProtocolVersion.valueOf(majorVersion, minorVersion) +
-                    " " + Record.contentName(contentType) + ", length = " +
+        if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+            SSLLogger.fine(
+                    "READ: " +
+                    ProtocolVersion.nameOf(majorVersion, minorVersion) +
+                    " " + ContentType.nameOf(contentType) + ", length = " +
                     contentLen);
         }
 
@@ -233,106 +230,131 @@
         }
 
         //
-        // check for handshake fragment
-        //
-        if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
-            throw new SSLProtocolException(
-                    "Expected to get a handshake fragment");
-        }
-
-        //
         // Decrypt the fragment
         //
         int recLim = srcPos + SSLRecord.headerSize + contentLen;
         packet.limit(recLim);
         packet.position(srcPos + SSLRecord.headerSize);
 
-        ByteBuffer plaintext;
+        ByteBuffer fragment;
         try {
-            plaintext =
-                decrypt(readAuthenticator, readCipher, contentType, packet);
+            Plaintext plaintext =
+                    readCipher.decrypt(contentType, packet, null);
+            fragment = plaintext.fragment;
+            contentType = plaintext.contentType;
+        } catch (BadPaddingException bpe) {
+            throw bpe;
+        } catch (GeneralSecurityException gse) {
+            throw (SSLProtocolException)(new SSLProtocolException(
+                    "Unexpected exception")).initCause(gse);
         } finally {
-            // comsume a complete record
+            // consume a complete record
             packet.limit(srcLim);
             packet.position(recLim);
         }
 
         //
-        // handshake hashing
+        // check for handshake fragment
         //
-        if (contentType == ct_handshake) {
-            int pltPos = plaintext.position();
-            int pltLim = plaintext.limit();
-            int frgPos = pltPos;
-            for (int remains = plaintext.remaining(); remains > 0;) {
-                int howmuch;
-                byte handshakeType;
-                if (hsMsgOff < hsMsgLen) {
-                    // a fragment of the handshake message
-                    howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
-                    handshakeType = prevType;
+        if (contentType != ContentType.HANDSHAKE.id &&
+                handshakeBuffer != null && handshakeBuffer.hasRemaining()) {
+            throw new SSLProtocolException(
+                    "Expecting a handshake fragment, but received " +
+                    ContentType.nameOf(contentType));
+        }
 
-                    hsMsgOff += howmuch;
-                    if (hsMsgOff == hsMsgLen) {
-                        // Now is a complete handshake message.
-                        hsMsgOff = 0;
-                        hsMsgLen = 0;
-                    }
-                } else {    // hsMsgOff == hsMsgLen, a new handshake message
-                    handshakeType = plaintext.get();
-                    int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
-                                       ((plaintext.get() & 0xFF) << 8) |
-                                        (plaintext.get() & 0xFF);
-                    plaintext.position(frgPos);
-                    if (remains < (handshakeLen + 4)) { // 4: handshake header
-                        // This handshake message is fragmented.
-                        prevType = handshakeType;
-                        hsMsgOff = remains - 4;         // 4: handshake header
-                        hsMsgLen = handshakeLen;
-                    }
+        //
+        // parse handshake messages
+        //
+        if (contentType == ContentType.HANDSHAKE.id) {
+            ByteBuffer handshakeFrag = fragment;
+            if ((handshakeBuffer != null) &&
+                    (handshakeBuffer.remaining() != 0)) {
+                ByteBuffer bb = ByteBuffer.wrap(new byte[
+                        handshakeBuffer.remaining() + fragment.remaining()]);
+                bb.put(handshakeBuffer);
+                bb.put(fragment);
+                handshakeFrag = bb.rewind();
+                handshakeBuffer = null;
+            }
 
-                    howmuch = Math.min(handshakeLen + 4, remains);
+            ArrayList<Plaintext> plaintexts = new ArrayList<>(5);
+            while (handshakeFrag.hasRemaining()) {
+                int remaining = handshakeFrag.remaining();
+                if (remaining < handshakeHeaderSize) {
+                    handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
+                    handshakeBuffer.put(handshakeFrag);
+                    handshakeBuffer.rewind();
+                    break;
                 }
 
-                plaintext.limit(frgPos + howmuch);
+                handshakeFrag.mark();
+                // skip the first byte: handshake type
+                byte handshakeType = handshakeFrag.get();
+                int handshakeBodyLen = Record.getInt24(handshakeFrag);
+                handshakeFrag.reset();
+                int handshakeMessageLen =
+                        handshakeHeaderSize + handshakeBodyLen;
+                if (remaining < handshakeMessageLen) {
+                    handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
+                    handshakeBuffer.put(handshakeFrag);
+                    handshakeBuffer.rewind();
+                    break;
+                } if (remaining == handshakeMessageLen) {
+                    if (handshakeHash.isHashable(handshakeType)) {
+                        handshakeHash.receive(handshakeFrag);
+                    }
 
-                if (handshakeType == HandshakeMessage.ht_hello_request) {
-                    // omitted from handshake hash computation
-                } else if ((handshakeType != HandshakeMessage.ht_finished) &&
-                    (handshakeType != HandshakeMessage.ht_certificate_verify)) {
-
-                    if (handshakeHash == null) {
-                        // used for cache only
-                        handshakeHash = new HandshakeHash(false);
-                    }
-                    handshakeHash.update(plaintext);
+                    plaintexts.add(
+                        new Plaintext(contentType,
+                            majorVersion, minorVersion, -1, -1L, handshakeFrag)
+                    );
+                    break;
                 } else {
-                    // Reserve until this handshake message has been processed.
-                    if (handshakeHash == null) {
-                        // used for cache only
-                        handshakeHash = new HandshakeHash(false);
+                    int fragPos = handshakeFrag.position();
+                    int fragLim = handshakeFrag.limit();
+                    int nextPos = fragPos + handshakeMessageLen;
+                    handshakeFrag.limit(nextPos);
+
+                    if (handshakeHash.isHashable(handshakeType)) {
+                        handshakeHash.receive(handshakeFrag);
                     }
-                    handshakeHash.reserve(plaintext);
+
+                    plaintexts.add(
+                        new Plaintext(contentType, majorVersion, minorVersion,
+                            -1, -1L, handshakeFrag.slice())
+                    );
+
+                    handshakeFrag.position(nextPos);
+                    handshakeFrag.limit(fragLim);
                 }
-
-                plaintext.position(frgPos + howmuch);
-                plaintext.limit(pltLim);
-
-                frgPos += howmuch;
-                remains -= howmuch;
             }
 
-            plaintext.position(pltPos);
+            return plaintexts.toArray(new Plaintext[0]);
         }
 
-        return new Plaintext(contentType,
-                majorVersion, minorVersion, -1, -1L, plaintext);
-                // recordEpoch, recordSeq, plaintext);
+        // KeyLimit check during application data.
+        // atKeyLimit() inactive when limits not checked, tc set when limits
+        // are active.
+
+        if (readCipher.atKeyLimit()) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("KeyUpdate: triggered, read side.");
+            }
+
+            PostHandshakeContext p = new PostHandshakeContext(tc);
+            KeyUpdate.handshakeProducer.produce(p,
+                    new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED));
+        }
+
+        return new Plaintext[] {
+            new Plaintext(contentType,
+                majorVersion, minorVersion, -1, -1L, fragment)
+        };
     }
 
-    private Plaintext handleUnknownRecord(ByteBuffer packet)
+    private Plaintext[] handleUnknownRecord(ByteBuffer packet)
             throws IOException, BadPaddingException {
-
         //
         // The packet should be a complete record.
         //
@@ -363,8 +385,8 @@
                  * error message, one that's treated as fatal by
                  * clients (Otherwise we'll hang.)
                  */
-                if (debug != null && Debug.isOn("record")) {
-                     System.out.println(Thread.currentThread().getName() +
+                if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                   SSLLogger.fine(
                             "Requested to negotiate unsupported SSLv2!");
                 }
 
@@ -380,23 +402,20 @@
              * V3 ClientHello message, and pass it up.
              */
             packet.position(srcPos + 2);        // exclude the header
-
-            if (handshakeHash == null) {
-                // used for cache only
-                handshakeHash = new HandshakeHash(false);
-            }
-            handshakeHash.update(packet);
+            handshakeHash.receive(packet);
             packet.position(srcPos);
 
             ByteBuffer converted = convertToClientHello(packet);
 
-            if (debug != null && Debug.isOn("packet")) {
-                 Debug.printHex(
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                SSLLogger.fine(
                         "[Converted] ClientHello", converted);
             }
 
-            return new Plaintext(ct_handshake,
-                majorVersion, minorVersion, -1, -1L, converted);
+            return new Plaintext[] {
+                    new Plaintext(ContentType.HANDSHAKE.id,
+                    majorVersion, minorVersion, -1, -1L, converted)
+                };
         } else {
             if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
                 throw new SSLException("SSL V2.0 servers are not supported.");
@@ -405,5 +424,4 @@
             throw new SSLException("Unsupported or unrecognized SSL message");
         }
     }
-
 }
--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,14 +25,14 @@
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.util.*;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import javax.net.ssl.SSLHandshakeException;
 
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import sun.security.util.HexDumpEncoder;
-import static sun.security.ssl.Ciphertext.RecordType;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
+import sun.security.ssl.KeyUpdate.KeyUpdateMessage;
+import sun.security.ssl.KeyUpdate.KeyUpdateRequest;
 
 /**
  * {@code OutputRecord} implementation for {@code SSLEngine}.
@@ -40,23 +40,22 @@
 final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
 
     private HandshakeFragment fragmenter = null;
-    private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
     private boolean isTalkingToV2 = false;      // SSLv2Hello
     private ByteBuffer v2ClientHello = null;    // SSLv2Hello
 
     private boolean isCloseWaiting = false;
 
-    SSLEngineOutputRecord() {
-        this.writeAuthenticator = MAC.TLS_NULL;
+    SSLEngineOutputRecord(HandshakeHash handshakeHash) {
+        super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher());
 
         this.packetSize = SSLRecord.maxRecordSize;
-        this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
+        this.protocolVersion = ProtocolVersion.NONE;
     }
 
     @Override
     public synchronized void close() throws IOException {
         if (!isClosed) {
-            if (alertMemos != null && !alertMemos.isEmpty()) {
+            if (fragmenter != null && fragmenter.hasAlert()) {
                 isCloseWaiting = true;
             } else {
                 super.close();
@@ -66,19 +65,11 @@
 
     @Override
     void encodeAlert(byte level, byte description) throws IOException {
-        RecordMemo memo = new RecordMemo();
+        if (fragmenter == null) {
+           fragmenter = new HandshakeFragment();
+        }
 
-        memo.contentType = Record.ct_alert;
-        memo.majorVersion = protocolVersion.major;
-        memo.minorVersion = protocolVersion.minor;
-        memo.encodeCipher = writeCipher;
-        memo.encodeAuthenticator = writeAuthenticator;
-
-        memo.fragment = new byte[2];
-        memo.fragment[0] = level;
-        memo.fragment[1] = description;
-
-        alertMemos.add(memo);
+        fragmenter.queueUpAlert(level, description);
     }
 
     @Override
@@ -93,7 +84,7 @@
             firstMessage = false;
 
             if ((helloVersion == ProtocolVersion.SSL20Hello) &&
-                (source[offset] == HandshakeMessage.ht_client_hello) &&
+                (source[offset] == SSLHandshake.CLIENT_HELLO.id) &&
                                             //  5: recode header size
                 (source[offset + 4 + 2 + 32] == 0)) {
                                             // V3 session ID is empty
@@ -106,7 +97,7 @@
                         source, (offset + 4), (length - 4));
 
                 v2ClientHello.position(2);     // exclude the header
-                handshakeHash.update(v2ClientHello);
+                handshakeHash.deliver(v2ClientHello);
                 v2ClientHello.position(0);
 
                 return;
@@ -114,8 +105,8 @@
         }
 
         byte handshakeType = source[offset];
-        if (handshakeType != HandshakeMessage.ht_hello_request) {
-            handshakeHash.update(source, offset, length);
+        if (handshakeHash.isHashable(handshakeType)) {
+            handshakeHash.deliver(source, offset, length);
         }
 
         fragmenter.queueUpFragment(source, offset, length);
@@ -135,22 +126,43 @@
     }
 
     @Override
-    Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+    Ciphertext encode(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
+        return encode(srcs, srcsOffset, srcsLength, dsts[0]);
+    }
+
+    private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
             ByteBuffer destination) throws IOException {
 
-        if (writeAuthenticator.seqNumOverflow()) {
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", sequence number extremely close to overflow " +
+        if (writeCipher.authenticator.seqNumOverflow()) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine(
+                    "sequence number extremely close to overflow " +
                     "(2^64-1 packets). Closing connection.");
             }
 
             throw new SSLHandshakeException("sequence number overflow");
         }
 
-        int macLen = 0;
-        if (writeAuthenticator instanceof MAC) {
-            macLen = ((MAC)writeAuthenticator).MAClen();
+        // Don't process the incoming record until all of the
+        // buffered records get handled.
+        Ciphertext ct = acquireCiphertext(destination);
+        if (ct != null) {
+            return ct;
+        }
+
+        if (sources == null || sources.length == 0) {
+            return null;
+        }
+
+        int srcsRemains = 0;
+        for (int i = offset; i < offset + length; i++) {
+            srcsRemains += sources[i].remaining();
+        }
+
+        if (srcsRemains == 0) {
+            return null;
         }
 
         int dstLim = destination.limit();
@@ -170,7 +182,7 @@
 
                 if (packetLeftSize > 0) {
                     fragLen = writeCipher.calculateFragmentSize(
-                            packetLeftSize, macLen, headerSize);
+                            packetLeftSize, headerSize);
 
                     fragLen = Math.min(fragLen, Record.maxDataSize);
                 } else {
@@ -208,26 +220,24 @@
             destination.limit(destination.position());
             destination.position(dstContent);
 
-            if ((debug != null) && Debug.isOn("record")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: " + protocolVersion + " " +
-                        Record.contentName(Record.ct_application_data) +
+            if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                SSLLogger.fine(
+                        "WRITE: " + protocolVersion + " " +
+                        ContentType.APPLICATION_DATA.name +
                         ", length = " + destination.remaining());
             }
 
             // Encrypt the fragment and wrap up a record.
-            recordSN = encrypt(writeAuthenticator, writeCipher,
-                    Record.ct_application_data, destination,
+            recordSN = encrypt(writeCipher,
+                    ContentType.APPLICATION_DATA.id, destination,
                     dstPos, dstLim, headerSize,
-                    protocolVersion, false);
+                    protocolVersion);
 
-            if ((debug != null) && Debug.isOn("packet")) {
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
                 ByteBuffer temporary = destination.duplicate();
                 temporary.limit(temporary.position());
                 temporary.position(dstPos);
-                Debug.printHex(
-                        "[Raw write]: length = " + temporary.remaining(),
-                        temporary);
+                SSLLogger.fine("Raw write", temporary);
             }
 
             packetLeftSize -= destination.position() - dstPos;
@@ -238,103 +248,62 @@
             if (isFirstAppOutputRecord) {
                 isFirstAppOutputRecord = false;
             }
+
+            // atKeyLimit() inactive when limits not checked, tc set when limits
+            // are active.
+            if (writeCipher.atKeyLimit()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine("KeyUpdate: triggered, write side.");
+                }
+
+                PostHandshakeContext p = new PostHandshakeContext(tc);
+                KeyUpdate.handshakeProducer.produce(p,
+                    new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED));
+            }
         }
 
-        return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
+        return new Ciphertext(ContentType.APPLICATION_DATA.id,
+                SSLHandshake.NOT_APPLICABLE.id, recordSN);
     }
 
-    @Override
-    Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+    private Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
         if (isTalkingToV2) {              // SSLv2Hello
             // We don't support SSLv2.  Send an SSLv2 error message
             // so that the connection can be closed gracefully.
             //
             // Please don't change the limit of the destination buffer.
             destination.put(SSLRecord.v2NoCipher);
-            if (debug != null && Debug.isOn("packet")) {
-                Debug.printHex(
-                        "[Raw write]: length = " + SSLRecord.v2NoCipher.length,
-                        SSLRecord.v2NoCipher);
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
             }
 
             isTalkingToV2 = false;
 
-            return new Ciphertext(RecordType.RECORD_ALERT, -1L);
+            return new Ciphertext(ContentType.ALERT.id,
+                    SSLHandshake.NOT_APPLICABLE.id, -1L);
         }
 
         if (v2ClientHello != null) {
             // deliver the SSLv2 format ClientHello message
             //
             // Please don't change the limit of the destination buffer.
-            if (debug != null) {
-                if (Debug.isOn("record")) {
-                     System.out.println(Thread.currentThread().getName() +
+            if (SSLLogger.isOn) {
+                if (SSLLogger.isOn("record")) {
+                     SSLLogger.fine(Thread.currentThread().getName() +
                             ", WRITE: SSLv2 ClientHello message" +
                             ", length = " + v2ClientHello.remaining());
                 }
 
-                if (Debug.isOn("packet")) {
-                    Debug.printHex(
-                        "[Raw write]: length = " + v2ClientHello.remaining(),
-                        v2ClientHello);
+                if (SSLLogger.isOn("packet")) {
+                    SSLLogger.fine("Raw write", v2ClientHello);
                 }
             }
 
             destination.put(v2ClientHello);
             v2ClientHello = null;
 
-            return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L);
-        }
-
-        if (alertMemos != null && !alertMemos.isEmpty()) {
-            RecordMemo memo = alertMemos.pop();
-
-            int macLen = 0;
-            if (memo.encodeAuthenticator instanceof MAC) {
-                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
-            }
-
-            int dstPos = destination.position();
-            int dstLim = destination.limit();
-            int dstContent = dstPos + headerSize +
-                                writeCipher.getExplicitNonceSize();
-            destination.position(dstContent);
-
-            destination.put(memo.fragment);
-
-            destination.limit(destination.position());
-            destination.position(dstContent);
-
-            if ((debug != null) && Debug.isOn("record")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: " + protocolVersion + " " +
-                        Record.contentName(Record.ct_alert) +
-                        ", length = " + destination.remaining());
-            }
-
-            // Encrypt the fragment and wrap up a record.
-            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
-                    Record.ct_alert, destination, dstPos, dstLim, headerSize,
-                    ProtocolVersion.valueOf(memo.majorVersion,
-                            memo.minorVersion), false);
-
-            if ((debug != null) && Debug.isOn("packet")) {
-                ByteBuffer temporary = destination.duplicate();
-                temporary.limit(temporary.position());
-                temporary.position(dstPos);
-                Debug.printHex(
-                        "[Raw write]: length = " + temporary.remaining(),
-                        temporary);
-            }
-
-            // remain the limit unchanged
-            destination.limit(dstLim);
-
-            if (isCloseWaiting && (memo.contentType == Record.ct_alert)) {
-                isCloseWaiting = true;
-                close();
-            }
-            return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
+            return new Ciphertext(ContentType.HANDSHAKE.id,
+                   SSLHandshake.CLIENT_HELLO.id, -1L);
         }
 
         if (fragmenter != null) {
@@ -347,8 +316,7 @@
     @Override
     boolean isEmpty() {
         return (!isTalkingToV2) && (v2ClientHello == null) &&
-                ((fragmenter == null) || fragmenter.isEmpty()) &&
-                ((alertMemos == null) || alertMemos.isEmpty());
+                ((fragmenter == null) || fragmenter.isEmpty());
     }
 
     // buffered record fragment
@@ -356,8 +324,7 @@
         byte            contentType;
         byte            majorVersion;
         byte            minorVersion;
-        CipherBox       encodeCipher;
-        Authenticator   encodeAuthenticator;
+        SSLWriteCipher  encodeCipher;
 
         byte[]          fragment;
     }
@@ -372,14 +339,12 @@
 
         void queueUpFragment(byte[] source,
                 int offset, int length) throws IOException {
-
             HandshakeMemo memo = new HandshakeMemo();
 
-            memo.contentType = Record.ct_handshake;
+            memo.contentType = ContentType.HANDSHAKE.id;
             memo.majorVersion = protocolVersion.major;  // kick start version?
             memo.minorVersion = protocolVersion.minor;
             memo.encodeCipher = writeCipher;
-            memo.encodeAuthenticator = writeAuthenticator;
 
             memo.handshakeType = source[offset];
             memo.acquireOffset = 0;
@@ -394,11 +359,10 @@
         void queueUpChangeCipherSpec() {
             RecordMemo memo = new RecordMemo();
 
-            memo.contentType = Record.ct_change_cipher_spec;
+            memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
             memo.majorVersion = protocolVersion.major;
             memo.minorVersion = protocolVersion.minor;
             memo.encodeCipher = writeCipher;
-            memo.encodeAuthenticator = writeAuthenticator;
 
             memo.fragment = new byte[1];
             memo.fragment[0] = 1;
@@ -406,6 +370,21 @@
             handshakeMemos.add(memo);
         }
 
+        void queueUpAlert(byte level, byte description) {
+            RecordMemo memo = new RecordMemo();
+
+            memo.contentType = ContentType.ALERT.id;
+            memo.majorVersion = protocolVersion.major;
+            memo.minorVersion = protocolVersion.minor;
+            memo.encodeCipher = writeCipher;
+
+            memo.fragment = new byte[2];
+            memo.fragment[0] = level;
+            memo.fragment[1] = description;
+
+            handshakeMemos.add(memo);
+        }
+
         Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
             if (isEmpty()) {
                 return null;
@@ -413,22 +392,17 @@
 
             RecordMemo memo = handshakeMemos.getFirst();
             HandshakeMemo hsMemo = null;
-            if (memo.contentType == Record.ct_handshake) {
+            if (memo.contentType == ContentType.HANDSHAKE.id) {
                 hsMemo = (HandshakeMemo)memo;
             }
 
-            int macLen = 0;
-            if (memo.encodeAuthenticator instanceof MAC) {
-                macLen = ((MAC)memo.encodeAuthenticator).MAClen();
-            }
-
             // ChangeCipherSpec message is pretty small.  Don't worry about
             // the fragmentation of ChangeCipherSpec record.
             int fragLen;
             if (packetSize > 0) {
                 fragLen = Math.min(maxRecordSize, packetSize);
                 fragLen = memo.encodeCipher.calculateFragmentSize(
-                        fragLen, macLen, headerSize);
+                        fragLen, headerSize);
             } else {
                 fragLen = Record.maxDataSize;
             }
@@ -474,11 +448,12 @@
                                  !handshakeMemos.isEmpty()) {
 
                             // look for the next buffered record fragment
-                            RecordMemo reMemo = handshakeMemos.getFirst();
-                            if (reMemo.contentType == Record.ct_handshake) {
-                                hsMemo = (HandshakeMemo)reMemo;
+                            RecordMemo rm = handshakeMemos.getFirst();
+                            if (rm.contentType == ContentType.HANDSHAKE.id &&
+                                    rm.encodeCipher == hsMemo.encodeCipher) {
+                                hsMemo = (HandshakeMemo)rm;
                             } else {
-                                // not handshake message, break the loop
+                                // not of the flight, break the loop
                                 break;
                             }
                         }
@@ -486,8 +461,6 @@
 
                     remainingFragLen -= chipLen;
                 }
-
-                fragLen -= remainingFragLen;
             } else {
                 fragLen = Math.min(fragLen, memo.fragment.length);
                 dstBuf.put(memo.fragment, 0, fragLen);
@@ -498,27 +471,26 @@
             dstBuf.limit(dstBuf.position());
             dstBuf.position(dstContent);
 
-            if ((debug != null) && Debug.isOn("record")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: " + protocolVersion + " " +
-                        Record.contentName(memo.contentType) +
+            if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                SSLLogger.fine(
+                        "WRITE: " + protocolVersion + " " +
+                        ContentType.nameOf(memo.contentType) +
                         ", length = " + dstBuf.remaining());
             }
 
             // Encrypt the fragment and wrap up a record.
-            long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+            long recordSN = encrypt(
+                    memo.encodeCipher,
                     memo.contentType, dstBuf,
                     dstPos, dstLim, headerSize,
                     ProtocolVersion.valueOf(memo.majorVersion,
-                            memo.minorVersion), false);
+                            memo.minorVersion));
 
-            if ((debug != null) && Debug.isOn("packet")) {
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
                 ByteBuffer temporary = dstBuf.duplicate();
                 temporary.limit(temporary.position());
                 temporary.position(dstPos);
-                Debug.printHex(
-                        "[Raw write]: length = " + temporary.remaining(),
-                        temporary);
+                SSLLogger.fine("Raw write", temporary);
             }
 
             // remain the limit unchanged
@@ -526,17 +498,32 @@
 
             // Reset the fragmentation offset.
             if (hsMemo != null) {
-                return new Ciphertext(RecordType.valueOf(
-                        hsMemo.contentType, hsMemo.handshakeType), recordSN);
+                return new Ciphertext(hsMemo.contentType,
+                        hsMemo.handshakeType, recordSN);
             } else {
-                return new Ciphertext(
-                        RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
+                if (isCloseWaiting &&
+                        memo.contentType == ContentType.ALERT.id) {
+                    close();
+                }
+
+                return new Ciphertext(memo.contentType,
+                        SSLHandshake.NOT_APPLICABLE.id, recordSN);
             }
         }
 
         boolean isEmpty() {
             return handshakeMemos.isEmpty();
         }
+
+        boolean hasAlert() {
+            for (RecordMemo memo : handshakeMemos) {
+                if (memo.contentType ==  ContentType.ALERT.id) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
     }
 
     /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Locale;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.util.HexDumpEncoder;
+
+enum SSLExtension implements SSLStringizer {
+    // Extensions defined in RFC 6066
+    CH_SERVER_NAME          (0x0000,  "server_name",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_13,
+                                ServerNameExtension.chNetworkProducer,
+                                ServerNameExtension.chOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                ServerNameExtension.chStringizer),
+    SH_SERVER_NAME          (0x0000, "server_name",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                ServerNameExtension.shNetworkProducer,
+                                ServerNameExtension.shOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                ServerNameExtension.shStringizer),
+    EE_SERVER_NAME          (0x0000, "server_name",
+                                SSLHandshake.ENCRYPTED_EXTENSIONS,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                ServerNameExtension.eeNetworkProducer,
+                                ServerNameExtension.eeOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                ServerNameExtension.shStringizer),
+    CH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_13,
+                                MaxFragExtension.chNetworkProducer,
+                                MaxFragExtension.chOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                MaxFragExtension.maxFragLenStringizer),
+    SH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                MaxFragExtension.shNetworkProducer,
+                                MaxFragExtension.shOnLoadConsumer,
+                                null,
+                                MaxFragExtension.shOnTradeConsumer,
+                                null,
+                                MaxFragExtension.maxFragLenStringizer),
+    EE_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
+                                SSLHandshake.ENCRYPTED_EXTENSIONS,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                MaxFragExtension.eeNetworkProducer,
+                                MaxFragExtension.eeOnLoadConsumer,
+                                null,
+                                MaxFragExtension.eeOnTradeConsumer,
+                                null,
+                                MaxFragExtension.maxFragLenStringizer),
+    CLIENT_CERTIFICATE_URL  (0x0002, "client_certificate_url"),
+    TRUSTED_CA_KEYS         (0x0003, "trusted_ca_keys"),
+    TRUNCATED_HMAC          (0x0004, "truncated_hmac"),
+
+    CH_STATUS_REQUEST       (0x0005, "status_request",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_13,
+                                CertStatusExtension.chNetworkProducer,
+                                CertStatusExtension.chOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                CertStatusExtension.certStatusReqStringizer),
+    SH_STATUS_REQUEST       (0x0005, "status_request",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                CertStatusExtension.shNetworkProducer,
+                                CertStatusExtension.shOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                CertStatusExtension.certStatusReqStringizer),
+
+    CR_STATUS_REQUEST       (0x0005, "status_request"),
+    CT_STATUS_REQUEST       (0x0005, "status_request",
+                                SSLHandshake.CERTIFICATE,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                CertStatusExtension.ctNetworkProducer,
+                                CertStatusExtension.ctOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                CertStatusExtension.certStatusRespStringizer),
+    // extensions defined in RFC 4681
+    USER_MAPPING            (0x0006, "user_mapping"),
+
+    // extensions defined in RFC 5878
+    CLIENT_AUTHZ            (0x0007, "client_authz"),
+    SERVER_AUTHZ            (0x0008, "server_authz"),
+
+    // extensions defined in RFC 5081
+    CERT_TYPE               (0x0009, "cert_type"),
+
+    // extensions defined in RFC 4492 (ECC)
+    CH_SUPPORTED_GROUPS     (0x000A, "supported_groups",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_13,
+                                SupportedGroupsExtension.chNetworkProducer,
+                                SupportedGroupsExtension.chOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                SupportedGroupsExtension.sgsStringizer),
+    EE_SUPPORTED_GROUPS     (0x000A, "supported_groups",
+                                SSLHandshake.ENCRYPTED_EXTENSIONS,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                SupportedGroupsExtension.eeNetworkProducer,
+                                SupportedGroupsExtension.eeOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                SupportedGroupsExtension.sgsStringizer),
+
+    CH_EC_POINT_FORMATS     (0x000B, "ec_point_formats",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                ECPointFormatsExtension.chNetworkProducer,
+                                ECPointFormatsExtension.chOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                ECPointFormatsExtension.epfStringizer),
+    SH_EC_POINT_FORMATS     (0x000B, "ec_point_formats",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                null,   // not use of the producer
+                                ECPointFormatsExtension.shOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                ECPointFormatsExtension.epfStringizer),
+
+    // extensions defined in RFC 5054
+    SRP                     (0x000C, "srp"),
+
+    // extensions defined in RFC 5246
+    CH_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_12_13,
+                                SignatureAlgorithmsExtension.chNetworkProducer,
+                                SignatureAlgorithmsExtension.chOnLoadConsumer,
+                                SignatureAlgorithmsExtension.chOnLoadAbsence,
+                                SignatureAlgorithmsExtension.chOnTradeConsumer,
+                                SignatureAlgorithmsExtension.chOnTradeAbsence,
+                                SignatureAlgorithmsExtension.ssStringizer),
+    CR_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms",
+                                SSLHandshake.CERTIFICATE_REQUEST,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                SignatureAlgorithmsExtension.crNetworkProducer,
+                                SignatureAlgorithmsExtension.crOnLoadConsumer,
+                                SignatureAlgorithmsExtension.crOnLoadAbsence,
+                                SignatureAlgorithmsExtension.crOnTradeConsumer,
+                                null,
+                                SignatureAlgorithmsExtension.ssStringizer),
+
+    CH_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_12_13,
+                                CertSignAlgsExtension.chNetworkProducer,
+                                CertSignAlgsExtension.chOnLoadConsumer,
+                                null,
+                                CertSignAlgsExtension.chOnTradeConsumer,
+                                null,
+                                CertSignAlgsExtension.ssStringizer),
+    CR_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert",
+                                SSLHandshake.CERTIFICATE_REQUEST,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                CertSignAlgsExtension.crNetworkProducer,
+                                CertSignAlgsExtension.crOnLoadConsumer,
+                                null,
+                                CertSignAlgsExtension.crOnTradeConsumer,
+                                null,
+                                CertSignAlgsExtension.ssStringizer),
+
+    // extensions defined in RFC 5764
+    USE_SRTP                (0x000E, "use_srtp"),
+
+    // extensions defined in RFC 6520
+    HEARTBEAT               (0x000E, "heartbeat"),
+
+    // extension defined in RFC 7301 (ALPN)
+    CH_ALPN                 (0x0010, "application_layer_protocol_negotiation",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_13,
+                                AlpnExtension.chNetworkProducer,
+                                AlpnExtension.chOnLoadConsumer,
+                                AlpnExtension.chOnLoadAbsence,
+                                null,
+                                null,
+                                AlpnExtension.alpnStringizer),
+    SH_ALPN                 (0x0010, "application_layer_protocol_negotiation",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                AlpnExtension.shNetworkProducer,
+                                AlpnExtension.shOnLoadConsumer,
+                                AlpnExtension.shOnLoadAbsence,
+                                null,
+                                null,
+                                AlpnExtension.alpnStringizer),
+    EE_ALPN                 (0x0010, "application_layer_protocol_negotiation",
+                                SSLHandshake.ENCRYPTED_EXTENSIONS,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                AlpnExtension.shNetworkProducer,
+                                AlpnExtension.shOnLoadConsumer,
+                                AlpnExtension.shOnLoadAbsence,
+                                null,
+                                null,
+                                AlpnExtension.alpnStringizer),
+
+    // extensions defined in RFC 6961
+    CH_STATUS_REQUEST_V2    (0x0011, "status_request_v2",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                CertStatusExtension.chV2NetworkProducer,
+                                CertStatusExtension.chV2OnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                CertStatusExtension.certStatusReqV2Stringizer),
+    SH_STATUS_REQUEST_V2    (0x0011, "status_request_v2",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                CertStatusExtension.shV2NetworkProducer,
+                                CertStatusExtension.shV2OnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                CertStatusExtension.certStatusReqV2Stringizer),
+
+    // extensions defined in RFC 6962
+    SIGNED_CERT_TIMESTAMP   (0x0012, "signed_certificate_timestamp"),
+
+    // extensions defined in RFC 7250
+    CLIENT_CERT_TYPE        (0x0013, "padding"),
+    SERVER_CERT_TYPE        (0x0014, "server_certificate_type"),
+
+    // extensions defined in RFC 7685
+    PADDING                 (0x0015, "client_certificate_type"),
+
+    // extensions defined in RFC 7366
+    ENCRYPT_THEN_MAC        (0x0016, "encrypt_then_mac"),
+
+    // extensions defined in RFC 7627
+    CH_EXTENDED_MASTER_SECRET  (0x0017, "extended_master_secret",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                ExtendedMasterSecretExtension.chNetworkProducer,
+                                ExtendedMasterSecretExtension.chOnLoadConsumer,
+                                ExtendedMasterSecretExtension.chOnLoadAbsence,
+                                null,
+                                null,
+                                ExtendedMasterSecretExtension.emsStringizer),
+    SH_EXTENDED_MASTER_SECRET  (0x0017, "extended_master_secret",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                ExtendedMasterSecretExtension.shNetworkProducer,
+                                ExtendedMasterSecretExtension.shOnLoadConsumer,
+                                ExtendedMasterSecretExtension.shOnLoadAbsence,
+                                null,
+                                null,
+                                ExtendedMasterSecretExtension.emsStringizer),
+
+    // extensions defined in RFC draft-ietf-tokbind-negotiation
+    TOKEN_BINDING           (0x0018, "token_binding "),
+
+    // extensions defined in RFC 7924
+    CACHED_INFO             (0x0019, "cached_info"),
+
+    // extensions defined in RFC 4507/5077
+    SESSION_TICKET          (0x0023, "session_ticket"),
+
+    // extensions defined in TLS 1.3
+    CH_EARLY_DATA           (0x002A, "early_data"),
+    EE_EARLY_DATA           (0x002A, "early_data"),
+    NST_EARLY_DATA          (0x002A, "early_data"),
+
+    CH_SUPPORTED_VERSIONS   (0x002B, "supported_versions",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_13,
+                                SupportedVersionsExtension.chNetworkProducer,
+                                SupportedVersionsExtension.chOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                SupportedVersionsExtension.chStringizer),
+    SH_SUPPORTED_VERSIONS   (0x002B, "supported_versions",
+                                SSLHandshake.SERVER_HELLO,
+                                        // and HelloRetryRequest
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                SupportedVersionsExtension.shNetworkProducer,
+                                SupportedVersionsExtension.shOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                SupportedVersionsExtension.shStringizer),
+    HRR_SUPPORTED_VERSIONS  (0x002B, "supported_versions",
+                                SSLHandshake.HELLO_RETRY_REQUEST,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                SupportedVersionsExtension.hrrNetworkProducer,
+                                SupportedVersionsExtension.hrrOnLoadConsumer,
+                                null,
+                                null,
+                                null,
+                                SupportedVersionsExtension.hrrStringizer),
+    MH_SUPPORTED_VERSIONS   (0x002B, "supported_versions",
+                                SSLHandshake.MESSAGE_HASH,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                SupportedVersionsExtension.hrrReproducer,
+                                null, null, null,
+                                null,
+                                SupportedVersionsExtension.hrrStringizer),
+
+    CH_COOKIE               (0x002C, "cookie",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                CookieExtension.chNetworkProducer,
+                                CookieExtension.chOnLoadConsumer,
+                                null,
+                                CookieExtension.chOnTradeConsumer,
+                                null,
+                                CookieExtension.cookieStringizer),
+    HRR_COOKIE              (0x002C, "cookie",
+                                SSLHandshake.HELLO_RETRY_REQUEST,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                CookieExtension.hrrNetworkProducer,
+                                CookieExtension.hrrOnLoadConsumer,
+                                null, null,
+                                null,
+                                CookieExtension.cookieStringizer),
+    MH_COOKIE               (0x002C, "cookie",
+                                SSLHandshake.MESSAGE_HASH,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                CookieExtension.hrrNetworkReproducer,
+                                null, null, null,
+                                null,
+                                CookieExtension.cookieStringizer),
+
+    PSK_KEY_EXCHANGE_MODES  (0x002D, "psk_key_exchange_modes",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                PskKeyExchangeModesExtension.chNetworkProducer,
+                                PskKeyExchangeModesExtension.chOnLoadConsumer,
+                                PskKeyExchangeModesExtension.chOnLoadAbsence,
+                                null,
+                                PskKeyExchangeModesExtension.chOnTradeAbsence,
+                                PskKeyExchangeModesExtension.pkemStringizer),
+    CERTIFICATE_AUTHORITIES (0x002F, "certificate_authorities"),
+    OID_FILTERS             (0x0030, "oid_filters"),
+    POST_HANDSHAKE_AUTH     (0x0030, "post_handshake_auth"),
+
+    CH_KEY_SHARE            (0x0033, "key_share",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                KeyShareExtension.chNetworkProducer,
+                                KeyShareExtension.chOnLoadConsumer,
+                                null, null, null,
+                                KeyShareExtension.chStringizer),
+    SH_KEY_SHARE            (0x0033, "key_share",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                KeyShareExtension.shNetworkProducer,
+                                KeyShareExtension.shOnLoadConsumer,
+                                KeyShareExtension.shOnLoadAbsence,
+                                null,
+                                null,
+                                KeyShareExtension.shStringizer),
+    HRR_KEY_SHARE           (0x0033, "key_share",
+                                SSLHandshake.HELLO_RETRY_REQUEST,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                KeyShareExtension.hrrNetworkProducer,
+                                KeyShareExtension.hrrOnLoadConsumer,
+                                null, null, null,
+                                KeyShareExtension.hrrStringizer),
+    MH_KEY_SHARE            (0x0033, "key_share",
+                                SSLHandshake.MESSAGE_HASH,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                KeyShareExtension.hrrNetworkReproducer,
+                                null, null, null, null,
+                                KeyShareExtension.hrrStringizer),
+
+    // Extensions defined in RFC 5746
+    CH_RENEGOTIATION_INFO   (0xff01, "renegotiation_info",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                RenegoInfoExtension.chNetworkProducer,
+                                RenegoInfoExtension.chOnLoadConsumer,
+                                RenegoInfoExtension.chOnLoadAbsence,
+                                null,
+                                null,
+                                RenegoInfoExtension.rniStringizer),
+    SH_RENEGOTIATION_INFO   (0xff01, "renegotiation_info",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_TO_12,
+                                RenegoInfoExtension.shNetworkProducer,
+                                RenegoInfoExtension.shOnLoadConsumer,
+                                RenegoInfoExtension.shOnLoadAbsence,
+                                null,
+                                null,
+                                RenegoInfoExtension.rniStringizer),
+
+    // TLS 1.3 PSK extension must be last
+    CH_PRE_SHARED_KEY       (0x0029, "pre_shared_key",
+                                SSLHandshake.CLIENT_HELLO,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                PreSharedKeyExtension.chNetworkProducer,
+                                PreSharedKeyExtension.chOnLoadConsumer,
+                                PreSharedKeyExtension.chOnLoadAbsence,
+                                PreSharedKeyExtension.chOnTradeConsumer,
+                                null,
+                                PreSharedKeyExtension.chStringizer),
+    SH_PRE_SHARED_KEY       (0x0029, "pre_shared_key",
+                                SSLHandshake.SERVER_HELLO,
+                                ProtocolVersion.PROTOCOLS_OF_13,
+                                PreSharedKeyExtension.shNetworkProducer,
+                                PreSharedKeyExtension.shOnLoadConsumer,
+                                PreSharedKeyExtension.shOnLoadAbsence,
+                                null, null,
+                                PreSharedKeyExtension.shStringizer);
+
+    final int id;
+    final SSLHandshake handshakeType;
+    final String name;
+    final ProtocolVersion[] supportedProtocols;
+    final HandshakeProducer networkProducer;
+    final ExtensionConsumer onLoadConsumer;
+    final HandshakeAbsence  onLoadAbsence;
+    final HandshakeConsumer onTradeConsumer;
+    final HandshakeAbsence  onTradeAbsence;
+    final SSLStringizer stringizer;
+
+    // known but unsupported extension
+    private SSLExtension(int id, String name) {
+        this.id = id;
+        this.handshakeType = SSLHandshake.NOT_APPLICABLE;
+        this.name = name;
+        this.supportedProtocols = new ProtocolVersion[0];
+        this.networkProducer = null;
+        this.onLoadConsumer = null;
+        this.onLoadAbsence = null;
+        this.onTradeConsumer = null;
+        this.onTradeAbsence = null;
+        this.stringizer = null;
+    }
+
+    // supported extension
+    private SSLExtension(int id, String name, SSLHandshake handshakeType,
+            ProtocolVersion[] supportedProtocols,
+            HandshakeProducer producer,
+            ExtensionConsumer onLoadConsumer, HandshakeAbsence onLoadAbsence,
+            HandshakeConsumer onTradeConsumer, HandshakeAbsence onTradeAbsence,
+            SSLStringizer stringize) {
+        this.id = id;
+        this.handshakeType = handshakeType;
+        this.name = name;
+        this.supportedProtocols = supportedProtocols;
+        this.networkProducer = producer;
+        this.onLoadConsumer = onLoadConsumer;
+        this.onLoadAbsence = onLoadAbsence;
+        this.onTradeConsumer = onTradeConsumer;
+        this.onTradeAbsence = onTradeAbsence;
+        this.stringizer = stringize;
+    }
+
+    static SSLExtension valueOf(SSLHandshake handshakeType, int extensionType) {
+        for (SSLExtension ext : SSLExtension.values()) {
+            if (ext.id == extensionType &&
+                    ext.handshakeType == handshakeType) {
+                return ext;
+            }
+        }
+
+        return null;
+    }
+
+    static boolean isConsumable(int extensionType) {
+        for (SSLExtension ext : SSLExtension.values()) {
+            if (ext.id == extensionType &&
+                    ext.onLoadConsumer != null) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public byte[] produce(ConnectionContext context,
+            HandshakeMessage message) throws IOException {
+        if (networkProducer != null) {
+            return networkProducer.produce(context, message);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Not yet supported extension producing.");
+        }
+    }
+
+    public void consumeOnLoad(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+        if (onLoadConsumer != null) {
+            onLoadConsumer.consume(context, message, buffer);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Not yet supported extension loading.");
+        }
+    }
+
+    public void consumeOnTrade(ConnectionContext context,
+            HandshakeMessage message) throws IOException {
+        if (onTradeConsumer != null) {
+            onTradeConsumer.consume(context, message);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Not yet supported extension processing.");
+        }
+    }
+
+    void absentOnLoad(ConnectionContext context,
+            HandshakeMessage message) throws IOException {
+        if (onLoadAbsence != null) {
+            onLoadAbsence.absent(context, message);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Not yet supported extension absence processing.");
+        }
+    }
+
+    void absentOnTrade(ConnectionContext context,
+            HandshakeMessage message) throws IOException {
+        if (onTradeAbsence != null) {
+            onTradeAbsence.absent(context, message);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Not yet supported extension absence processing.");
+        }
+    }
+
+    public boolean isAvailable(ProtocolVersion protocolVersion) {
+        for (int i = 0; i < supportedProtocols.length; i++) {
+            if (supportedProtocols[i] == protocolVersion) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    @Override
+    public String toString(ByteBuffer byteBuffer) {
+        MessageFormat messageFormat = new MessageFormat(
+            "\"{0} ({1})\": '{'\n" +
+            "{2}\n" +
+            "'}'",
+            Locale.ENGLISH);
+
+        String extData;
+        if (stringizer == null) {
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            String encoded = hexEncoder.encode(byteBuffer.duplicate());
+            extData = encoded;
+        } else {
+            extData = stringizer.toString(byteBuffer);
+        }
+
+        Object[] messageFields = {
+            this.name,
+            this.id,
+            Utilities.indent(extData)
+        };
+
+        return messageFormat.format(messageFields);
+    }
+
+    //////////////////////////////////////////////////////
+    // Nested extension, consumer and producer interfaces.
+
+    static interface ExtensionConsumer {
+        void consume(ConnectionContext context,
+                HandshakeMessage message, ByteBuffer buffer) throws IOException;
+    }
+
+    /**
+     * A (transparent) specification of extension data.
+     *
+     * This interface contains no methods or constants. Its only purpose is to
+     * group all extension data.  All extension data should implement this
+     * interface if the data is expected to handle in the following handshake
+     * processes.
+     */
+    static interface SSLExtensionSpec {
+        // blank
+    }
+
+    // Default enabled client extensions.
+    static final class ClientExtensions {
+        static final Collection<SSLExtension> defaults;
+
+        static {
+            Collection<SSLExtension> extensions = new LinkedList<>();
+            for (SSLExtension extension : SSLExtension.values()) {
+                if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) {
+                    extensions.add(extension);
+                }
+            }
+
+            // Switch off SNI extention?
+            boolean enableExtension =
+                Utilities.getBooleanProperty("jsse.enableSNIExtension", true);
+            if (!enableExtension) {
+                extensions.remove(CH_SERVER_NAME);
+            }
+
+            // To switch off the max_fragment_length extension.
+            enableExtension =
+                Utilities.getBooleanProperty("jsse.enableMFLExtension", false);
+            if (!enableExtension) {
+                extensions.remove(CH_MAX_FRAGMENT_LENGTH);
+            }
+
+            defaults = Collections.unmodifiableCollection(extensions);
+        }
+    }
+
+    // Default enabled server extensions.
+    static final class ServerExtensions {
+        static final Collection<SSLExtension> defaults;
+
+        static {
+            Collection<SSLExtension> extensions = new LinkedList<>();
+            for (SSLExtension extension : SSLExtension.values()) {
+                if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) {
+                    extensions.add(extension);
+                }
+            }
+
+            defaults = Collections.unmodifiableCollection(extensions);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.*;
+
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.util.HexDumpEncoder;
+
+/**
+ * SSL/(D)TLS extensions in a handshake message.
+ */
+final class SSLExtensions {
+    private final HandshakeMessage handshakeMessage;
+    private Map<SSLExtension, byte[]> extMap = new LinkedHashMap<>();
+    private int encodedLength;
+
+    // Extension map for debug logging
+    private final Map<Integer, byte[]> logMap =
+            SSLLogger.isOn ? null : new LinkedHashMap<>();
+
+    SSLExtensions(HandshakeMessage handshakeMessage) {
+        this.handshakeMessage = handshakeMessage;
+        this.encodedLength = 2;         // 2: the length of the extensions.
+    }
+
+    SSLExtensions(HandshakeMessage hm,
+            ByteBuffer m, SSLExtension[] extensions) throws IOException {
+        this.handshakeMessage = hm;
+
+        int len = Record.getInt16(m);
+        encodedLength = len + 2;        // 2: the length of the extensions.
+        while (len > 0) {
+            int extId = Record.getInt16(m);
+            int extLen = Record.getInt16(m);
+            if (extLen > m.remaining()) {
+                hm.handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Error parsing extension (" + extId +
+                        "): no sufficient data");
+            }
+
+            SSLHandshake handshakeType = hm.handshakeType();
+            if (SSLExtension.isConsumable(extId) &&
+                    SSLExtension.valueOf(handshakeType, extId) == null) {
+                hm.handshakeContext.conContext.fatal(
+                        Alert.UNSUPPORTED_EXTENSION,
+                        "extension (" + extId +
+                        ") should not be presented in " + handshakeType.name);
+            }
+
+            boolean isSupported = false;
+            for (SSLExtension extension : extensions) {
+                if ((extension.id != extId) ||
+                        (extension.onLoadConsumer == null)) {
+                    continue;
+                }
+
+                if (extension.handshakeType != handshakeType) {
+                    hm.handshakeContext.conContext.fatal(
+                            Alert.UNSUPPORTED_EXTENSION,
+                            "extension (" + extId + ") should not be " +
+                            "presented in " + handshakeType.name);
+                }
+
+                byte[] extData = new byte[extLen];
+                m.get(extData);
+                extMap.put(extension, extData);
+                if (logMap != null) {
+                    logMap.put(extId, extData);
+                }
+
+                isSupported = true;
+                break;
+            }
+
+            if (!isSupported) {
+                if (logMap != null) {
+                    // cache the extension for debug logging
+                    byte[] extData = new byte[extLen];
+                    m.get(extData);
+                    logMap.put(extId, extData);
+
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                                "Ignore unknown or unsupported extension",
+                                toString(extId, extData));
+                    }
+                } else {
+                    // ignore the extension
+                    int pos = m.position() + extLen;
+                    m.position(pos);
+                }
+            }
+
+            len -= extLen + 4;
+        }
+    }
+
+    byte[] get(SSLExtension ext) {
+        return extMap.get(ext);
+    }
+
+    /**
+     * Consume the specified extensions.
+     */
+    void consumeOnLoad(HandshakeContext context,
+            SSLExtension[] extensions) throws IOException {
+        for (SSLExtension extension : extensions) {
+            if (context.negotiatedProtocol != null &&
+                    !extension.isAvailable(context.negotiatedProtocol)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unsupported extension: " + extension.name);
+                }
+                continue;
+            }
+
+            if (!extMap.containsKey(extension)) {
+                if (extension.onLoadAbsence != null) {
+                    extension.absentOnLoad(context, handshakeMessage);
+                } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " + extension.name);
+                }
+                continue;
+            }
+
+
+            if (extension.onLoadConsumer == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                        "Ignore unsupported extension: " + extension.name);
+                }
+                continue;
+            }
+
+            ByteBuffer m = ByteBuffer.wrap(extMap.get(extension));
+            extension.consumeOnLoad(context, handshakeMessage, m);
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Consumed extension: " + extension.name);
+            }
+        }
+    }
+
+    /**
+     * Consider impact of the specified extensions.
+     */
+    void consumeOnTrade(HandshakeContext context,
+            SSLExtension[] extensions) throws IOException {
+        for (SSLExtension extension : extensions) {
+            if (!extMap.containsKey(extension)) {
+                if (extension.onTradeAbsence != null) {
+                    extension.absentOnTrade(context, handshakeMessage);
+                } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " + extension.name);
+                }
+                continue;
+            }
+
+            if (extension.onTradeConsumer == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Ignore impact of unsupported extension: " +
+                            extension.name);
+                }
+                continue;
+            }
+
+            extension.consumeOnTrade(context, handshakeMessage);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Populated with extension: " + extension.name);
+            }
+        }
+    }
+
+    /**
+     * Produce extension values for the specified extensions.
+     */
+    void produce(HandshakeContext context,
+            SSLExtension[] extensions) throws IOException {
+        for (SSLExtension extension : extensions) {
+            if (extMap.containsKey(extension)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Ignore, duplicated extension: " +
+                            extension.name);
+                }
+                continue;
+            }
+
+            if (extension.networkProducer == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Ignore, no extension producer defined: " +
+                            extension.name);
+                }
+                continue;
+            }
+
+            byte[] encoded = extension.produce(context, handshakeMessage);
+            if (encoded != null) {
+                extMap.put(extension, encoded);
+                encodedLength += encoded.length + 4; // extension_type (2)
+                                                     // extension_data length(2)
+            } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                // The extension is not available in the context.
+                SSLLogger.fine(
+                        "Ignore, context unavailable extension: " +
+                        extension.name);
+            }
+        }
+    }
+
+    /**
+     * Produce extension values for the specified extensions, replacing if
+     * there is an existing extension value for a specified extension.
+     */
+    void reproduce(HandshakeContext context,
+            SSLExtension[] extensions) throws IOException {
+        for (SSLExtension extension : extensions) {
+            if (extension.networkProducer == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Ignore, no extension producer defined: " +
+                            extension.name);
+                }
+                continue;
+            }
+
+            byte[] encoded = extension.produce(context, handshakeMessage);
+            if (encoded != null) {
+                if (extMap.containsKey(extension)) {
+                    byte[] old = extMap.replace(extension, encoded);
+                    if (old != null) {
+                        encodedLength -= old.length + 4;
+                    }
+                    encodedLength += encoded.length + 4;
+                } else {
+                    extMap.put(extension, encoded);
+                    encodedLength += encoded.length + 4;
+                                                    // extension_type (2)
+                                                    // extension_data length(2)
+                }
+            } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                // The extension is not available in the context.
+                SSLLogger.fine(
+                        "Ignore, context unavailable extension: " +
+                        extension.name);
+            }
+        }
+    }
+
+    // Note that TLS 1.3 may use empty extensions.  Please consider it while
+    // using this method.
+    int length() {
+        if (extMap.isEmpty()) {
+            return 0;
+        } else {
+            return encodedLength;
+        }
+    }
+
+    // Note that TLS 1.3 may use empty extensions.  Please consider it while
+    // using this method.
+    void send(HandshakeOutStream hos) throws IOException {
+        int extsLen = length();
+        if (extsLen == 0) {
+            return;
+        }
+        hos.putInt16(extsLen - 2);
+        // extensions must be sent in the order they appear in the enum
+        for (SSLExtension ext : SSLExtension.values()) {
+            byte[] extData = extMap.get(ext);
+            if (extData != null) {
+                hos.putInt16(ext.id);
+                hos.putBytes16(extData);
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (extMap.isEmpty() && (logMap == null || logMap.isEmpty())) {
+            return "<no extension>";
+        } else {
+            StringBuilder builder = new StringBuilder(512);
+            if (logMap != null) {
+                for (Map.Entry<Integer, byte[]> en : logMap.entrySet()) {
+                    SSLExtension ext = SSLExtension.valueOf(
+                            handshakeMessage.handshakeType(), en.getKey());
+                    if (builder.length() != 0) {
+                        builder.append(",\n");
+                    }
+                    if (ext != null) {
+                        builder.append(
+                                ext.toString(ByteBuffer.wrap(en.getValue())));
+                    } else {
+                        builder.append(toString(en.getKey(), en.getValue()));
+                    }
+                }
+
+                return builder.toString();
+            } else {
+                for (Map.Entry<SSLExtension, byte[]> en : extMap.entrySet()) {
+                    if (builder.length() != 0) {
+                        builder.append(",\n");
+                    }
+                    builder.append(
+                        en.getKey().toString(ByteBuffer.wrap(en.getValue())));
+                }
+
+                return builder.toString();
+            }
+        }
+    }
+
+    private static String toString(int extId, byte[] extData) {
+        MessageFormat messageFormat = new MessageFormat(
+            "\"unknown extension ({0})\": '{'\n" +
+            "{1}\n" +
+            "'}'",
+            Locale.ENGLISH);
+
+        HexDumpEncoder hexEncoder = new HexDumpEncoder();
+        String encoded = hexEncoder.encodeBuffer(extData);
+
+        Object[] messageFields = {
+            extId,
+            Utilities.indent(encoded)
+        };
+
+        return messageFormat.format(messageFields);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Map;
+import javax.net.ssl.SSLException;
+
+enum SSLHandshake implements SSLConsumer, HandshakeProducer {
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    HELLO_REQUEST ((byte)0x00, "hello_request",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                HelloRequest.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                HelloRequest.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    CLIENT_HELLO ((byte)0x01, "client_hello",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                ClientHello.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                ClientHello.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    SERVER_HELLO ((byte)0x02, "server_hello",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                ServerHello.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                ServerHello.t12HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                ServerHello.t13HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    HELLO_RETRY_REQUEST ((byte)0x02, "hello_retry_request",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                ServerHello.handshakeConsumer,      // Use ServerHello consumer
+                ProtocolVersion.PROTOCOLS_TO_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                ServerHello.hrrHandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    HELLO_VERIFY_REQUEST        ((byte)0x03, "hello_verify_request",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                HelloVerifyRequest.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                HelloVerifyRequest.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    NEW_SESSION_TICKET          ((byte)0x04, "new_session_ticket",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                NewSessionTicket.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_13
+        )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                NewSessionTicket.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+        )
+        })),
+    END_OF_EARLY_DATA           ((byte)0x05, "end_of_early_data"),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    ENCRYPTED_EXTENSIONS        ((byte)0x08, "encrypted_extensions",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                EncryptedExtensions.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                EncryptedExtensions.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    CERTIFICATE                 ((byte)0x0B, "certificate",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateMessage.t12HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            ),
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateMessage.t13HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateMessage.t12HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateMessage.t13HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    SERVER_KEY_EXCHANGE         ((byte)0x0C, "server_key_exchange",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                ServerKeyExchange.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                ServerKeyExchange.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    CERTIFICATE_REQUEST         ((byte)0x0D, "certificate_request",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateRequest.t10HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_11
+            ),
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateRequest.t12HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_12
+            ),
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateRequest.t13HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateRequest.t10HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_11
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateRequest.t12HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_12
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateRequest.t13HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    SERVER_HELLO_DONE           ((byte)0x0E, "server_hello_done",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                ServerHelloDone.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                ServerHelloDone.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    CERTIFICATE_VERIFY          ((byte)0x0F, "certificate_verify",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateVerify.s30HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_30
+            ),
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateVerify.t10HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_10_11
+            ),
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateVerify.t12HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_12
+            ),
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateVerify.t13HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateVerify.s30HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_30
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateVerify.t10HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_10_11
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateVerify.t12HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_12
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateVerify.t13HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    CLIENT_KEY_EXCHANGE         ((byte)0x10, "client_key_exchange",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                ClientKeyExchange.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                ClientKeyExchange.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    FINISHED                    ((byte)0x14, "finished",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                Finished.t12HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            ),
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                Finished.t13HandshakeConsumer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                Finished.t12HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            ),
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                Finished.t13HandshakeProducer,
+                ProtocolVersion.PROTOCOLS_OF_13
+            )
+        })),
+
+    CERTIFICATE_URL             ((byte)0x15, "certificate_url"),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    CERTIFICATE_STATUS          ((byte)0x16, "certificate_status",
+        (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                CertificateStatus.handshakeConsumer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                CertificateStatus.handshakeProducer,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        }),
+        (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(new Map.Entry[] {
+            new SimpleImmutableEntry<HandshakeAbsence, ProtocolVersion[]>(
+                CertificateStatus.handshakeAbsence,
+                ProtocolVersion.PROTOCOLS_TO_12
+            )
+        })),
+
+    SUPPLEMENTAL_DATA           ((byte)0x17, "supplemental_data"),
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    KEY_UPDATE                  ((byte)0x18, "key_update",
+            (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
+                    new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
+                            KeyUpdate.handshakeConsumer,
+                            ProtocolVersion.PROTOCOLS_OF_13
+                    )
+            }),
+            (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
+                    new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
+                            KeyUpdate.handshakeProducer,
+                            ProtocolVersion.PROTOCOLS_OF_13
+                    )
+            })),
+    MESSAGE_HASH                ((byte)0xFE, "message_hash"),
+    NOT_APPLICABLE              ((byte)0xFF, "not_applicable");
+
+    final byte id;
+    final String name;
+    final Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers;
+    final Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers;
+    final Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsences;
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    SSLHandshake(byte id, String name) {
+        this(id, name,
+                (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(
+                        new Map.Entry[0]),
+                (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(
+                        new Map.Entry[0]),
+                (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(
+                        new Map.Entry[0]));
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    SSLHandshake(byte id, String name,
+        Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers,
+        Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers) {
+        this(id, name, handshakeConsumers, handshakeProducers,
+                (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(
+                        new Map.Entry[0]));
+    }
+
+    SSLHandshake(byte id, String name,
+        Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers,
+        Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers,
+        Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsence) {
+        this.id = id;
+        this.name = name;
+        this.handshakeConsumers = handshakeConsumers;
+        this.handshakeProducers = handshakeProducers;
+        this.handshakeAbsences = handshakeAbsence;
+    }
+
+    @Override
+    public void consume(ConnectionContext context,
+            ByteBuffer message) throws IOException {
+        SSLConsumer hc = getHandshakeConsumer(context);
+        if (hc != null) {
+            hc.consume(context, message);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Unsupported handshake consumer: " + this.name);
+        }
+    }
+
+    private SSLConsumer getHandshakeConsumer(ConnectionContext context) {
+        if (handshakeConsumers.length == 0) {
+            return null;
+        }
+
+        // The consuming happens in handshake context only.
+        HandshakeContext hc = (HandshakeContext)context;
+        ProtocolVersion protocolVersion;
+        if ((hc.negotiatedProtocol == null) ||
+                (hc.negotiatedProtocol == ProtocolVersion.NONE)) {
+            protocolVersion = hc.maximumActiveProtocol;
+        } else {
+            protocolVersion = hc.negotiatedProtocol;
+        }
+
+        for (Map.Entry<SSLConsumer,
+                ProtocolVersion[]> phe : handshakeConsumers) {
+            for (ProtocolVersion pv : phe.getValue()) {
+                if (protocolVersion == pv) {
+                    return phe.getKey();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public byte[] produce(ConnectionContext context,
+            HandshakeMessage message) throws IOException {
+        HandshakeProducer hp = getHandshakeProducer(context);
+        if (hp != null) {
+            return hp.produce(context, message);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Unsupported handshake producer: " + this.name);
+        }
+    }
+
+    private HandshakeProducer getHandshakeProducer(
+            ConnectionContext context) {
+        if (handshakeConsumers.length == 0) {
+            return null;
+        }
+
+        // The consuming happens in handshake context only.
+        HandshakeContext hc = (HandshakeContext)context;
+        ProtocolVersion protocolVersion;
+        if ((hc.negotiatedProtocol == null) ||
+                (hc.negotiatedProtocol == ProtocolVersion.NONE)) {
+            protocolVersion = hc.maximumActiveProtocol;
+        } else {
+            protocolVersion = hc.negotiatedProtocol;
+        }
+
+        for (Map.Entry<HandshakeProducer,
+                ProtocolVersion[]> phe : handshakeProducers) {
+            for (ProtocolVersion pv : phe.getValue()) {
+                if (protocolVersion == pv) {
+                    return phe.getKey();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    static String nameOf(byte id) {
+        // If two handshake message share the same handshake type, returns
+        // the first handshake message name.
+        //
+        // It is not a big issue at present as only ServerHello and
+        // HellRetryRequest share a handshake type.
+        for (SSLHandshake hs : SSLHandshake.values()) {
+            if (hs.id == id) {
+                return hs.name;
+            }
+        }
+
+        return "UNKNOWN-HANDSHAKE-MESSAGE(" + id + ")";
+    }
+
+    static final void kickstart(HandshakeContext context) throws IOException {
+        if (context instanceof ClientHandshakeContext) {
+            // For initial handshaking, including session resumption,
+            // ClientHello message is used as the kickstart message.
+            //
+            // (D)TLS 1.2 and older protocols support renegotiation on existing
+            // connections.  A ClientHello messages is used to kickstart the
+            // renegotiation.
+            //
+            // (D)TLS 1.3 forbids renegotiation.  The post-handshake KeyUpdate
+            // message is used to update the sending cryptographic keys.
+            if (context.conContext.isNegotiated &&
+                    context.conContext.protocolVersion.useTLS13PlusSpec()) {
+                // Use KeyUpdate message for renegotiation.
+                KeyUpdate.kickstartProducer.produce(context);
+            } else {
+                // Using ClientHello message for the initial handshaking
+                // (including session resumption) or renegotiation.
+                // SSLHandshake.CLIENT_HELLO.produce(context);
+                ClientHello.kickstartProducer.produce(context);
+            }
+        } else {
+            // The server side can delivering kickstart message after the
+            // connection has established.
+            //
+            // (D)TLS 1.2 and older protocols use HelloRequest to begin a
+            // negotiation process anew.
+            //
+            // While (D)TLS 1.3 uses the post-handshake KeyUpdate message
+            // to update the sending cryptographic keys.
+            if (context.conContext.protocolVersion.useTLS13PlusSpec()) {
+                // Use KeyUpdate message for renegotiation.
+                KeyUpdate.kickstartProducer.produce(context);
+            } else {
+                // SSLHandshake.HELLO_REQUEST.produce(context);
+                HelloRequest.kickstartProducer.produce(context);
+            }
+        }
+    }
+
+    /**
+     * A (transparent) specification of handshake message.
+     */
+    static abstract class HandshakeMessage {
+        final HandshakeContext      handshakeContext;
+
+        HandshakeMessage(HandshakeContext handshakeContext) {
+            this.handshakeContext = handshakeContext;
+        }
+
+        abstract SSLHandshake handshakeType();
+        abstract int messageLength();
+        abstract void send(HandshakeOutStream hos) throws IOException;
+
+        void write(HandshakeOutStream hos) throws IOException {
+            int len = messageLength();
+            if (len >= Record.OVERFLOW_OF_INT24) {
+                throw new SSLException("Handshake message is overflow"
+                        + ", type = " + handshakeType() + ", len = " + len);
+            }
+            hos.write(handshakeType().id);
+            hos.putInt24(len);
+            send(hos);
+            hos.complete();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshakeBinding.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.util.Map;
+
+interface SSLHandshakeBinding {
+    default SSLHandshake[] getRelatedHandshakers(
+            HandshakeContext handshakeContext) {
+        return new SSLHandshake[0];
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    default Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
+            HandshakeContext handshakeContext) {
+        return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    default Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
+            HandshakeContext handshakeContext) {
+        return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyAgreement.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+interface SSLKeyAgreement
+        extends SSLPossessionGenerator, SSLKeyAgreementGenerator,
+        SSLHandshakeBinding {
+    // blank
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyAgreementGenerator.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+
+interface SSLKeyAgreementGenerator {
+    SSLKeyDerivation createKeyDerivation(
+            HandshakeContext context) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.SecretKey;
+
+interface SSLKeyDerivation {
+    SecretKey deriveKey(String algorithm,
+            AlgorithmParameterSpec params) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivationGenerator.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import javax.crypto.SecretKey;
+
+interface SSLKeyDerivationGenerator {
+    SSLKeyDerivation createKeyDerivation(HandshakeContext context,
+            SecretKey secretKey) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import sun.security.ssl.DHKeyExchange.DHEPossession;
+import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+import sun.security.ssl.X509Authentication.X509Possession;
+
+final class SSLKeyExchange implements SSLKeyAgreementGenerator,
+        SSLHandshakeBinding {
+    private final SSLAuthentication authentication;
+    private final SSLKeyAgreement keyAgreement;
+
+    SSLKeyExchange(X509Authentication authentication,
+            SSLKeyAgreement keyAgreement) {
+        this.authentication = authentication;
+        this.keyAgreement = keyAgreement;
+    }
+
+    SSLPossession[] createPossessions(HandshakeContext context) {
+        // authentication
+        SSLPossession authPossession = null;
+        if (authentication != null) {
+            authPossession = authentication.createPossession(context);
+            if (authPossession == null) {
+                return new SSLPossession[0];
+            } else if (context instanceof ServerHandshakeContext) {
+                // The authentication information may be used further for
+                // key agreement parameters negotiation.
+                ServerHandshakeContext shc = (ServerHandshakeContext)context;
+                shc.interimAuthn = authPossession;
+            }
+        }
+
+        // key agreement
+        SSLPossession kaPossession;
+        if (keyAgreement == T12KeyAgreement.RSA_EXPORT) {
+            // a special case
+            X509Possession x509Possession = (X509Possession)authPossession;
+            if (JsseJce.getRSAKeyLength(
+                    x509Possession.popCerts[0].getPublicKey()) > 512) {
+                kaPossession = keyAgreement.createPossession(context);
+
+                if (kaPossession == null) {
+                    return new SSLPossession[0];
+                } else {
+                    return authentication != null ?
+                            new SSLPossession[] {authPossession, kaPossession} :
+                            new SSLPossession[] {kaPossession};
+                }
+            } else {
+                return authentication != null ?
+                        new SSLPossession[] {authPossession} :
+                        new SSLPossession[0];
+            }
+        } else {
+            kaPossession = keyAgreement.createPossession(context);
+            if (kaPossession == null) {
+                // special cases
+                if (keyAgreement == T12KeyAgreement.RSA ||
+                        keyAgreement == T12KeyAgreement.ECDH) {
+                    return authentication != null ?
+                            new SSLPossession[] {authPossession} :
+                            new SSLPossession[0];
+                } else {
+                    return new SSLPossession[0];
+                }
+            } else {
+                return authentication != null ?
+                        new SSLPossession[] {authPossession, kaPossession} :
+                        new SSLPossession[] {kaPossession};
+            }
+        }
+    }
+
+    @Override
+    public SSLKeyDerivation createKeyDerivation(
+            HandshakeContext handshakeContext) throws IOException {
+        return keyAgreement.createKeyDerivation(handshakeContext);
+    }
+
+    @Override
+    public SSLHandshake[] getRelatedHandshakers(
+            HandshakeContext handshakeContext) {
+        SSLHandshake[] auHandshakes;
+        if (authentication != null) {
+            auHandshakes =
+                authentication.getRelatedHandshakers(handshakeContext);
+        } else {
+            auHandshakes = null;
+        }
+
+        SSLHandshake[] kaHandshakes =
+                keyAgreement.getRelatedHandshakers(handshakeContext);
+
+        if (auHandshakes == null || auHandshakes.length == 0) {
+            return kaHandshakes;
+        } else if (kaHandshakes == null || kaHandshakes.length == 0) {
+            return auHandshakes;
+        } else {
+            SSLHandshake[] producers = Arrays.copyOf(
+                     auHandshakes, auHandshakes.length + kaHandshakes.length);
+            System.arraycopy(kaHandshakes, 0,
+                    producers, auHandshakes.length, kaHandshakes.length);
+            return producers;
+        }
+    }
+
+    @Override
+    public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
+            HandshakeContext handshakeContext) {
+        Map.Entry<Byte, HandshakeProducer>[] auProducers;
+        if (authentication != null) {
+            auProducers =
+                authentication.getHandshakeProducers(handshakeContext);
+        } else {
+            auProducers = null;
+        }
+
+        Map.Entry<Byte, HandshakeProducer>[] kaProducers =
+                keyAgreement.getHandshakeProducers(handshakeContext);
+
+        if (auProducers == null || auProducers.length == 0) {
+            return kaProducers;
+        } else if (kaProducers == null || kaProducers.length == 0) {
+            return auProducers;
+        } else {
+            Map.Entry<Byte, HandshakeProducer>[] producers = Arrays.copyOf(
+                     auProducers, auProducers.length + kaProducers.length);
+            System.arraycopy(kaProducers, 0,
+                    producers, auProducers.length, kaProducers.length);
+            return producers;
+        }
+    }
+
+    @Override
+    public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
+            HandshakeContext handshakeContext) {
+        Map.Entry<Byte, SSLConsumer>[] auConsumers;
+        if (authentication != null) {
+            auConsumers =
+                authentication.getHandshakeConsumers(handshakeContext);
+        } else {
+            auConsumers = null;
+        }
+
+        Map.Entry<Byte, SSLConsumer>[] kaConsumers =
+                keyAgreement.getHandshakeConsumers(handshakeContext);
+
+        if (auConsumers == null || auConsumers.length == 0) {
+            return kaConsumers;
+        } else if (kaConsumers == null || kaConsumers.length == 0) {
+            return auConsumers;
+        } else {
+            Map.Entry<Byte, SSLConsumer>[] producers = Arrays.copyOf(
+                     auConsumers, auConsumers.length + kaConsumers.length);
+            System.arraycopy(kaConsumers, 0,
+                    producers, auConsumers.length, kaConsumers.length);
+            return producers;
+        }
+    }
+
+    // SSL 3.0 - (D)TLS 1.2
+    static SSLKeyExchange valueOf(
+            CipherSuite.KeyExchange keyExchange,
+            ProtocolVersion protocolVersion) {
+        if (keyExchange == null || protocolVersion == null) {
+            return null;
+        }
+
+        switch (keyExchange) {
+            case K_RSA:
+                return SSLKeyExRSA.KE;
+            case K_RSA_EXPORT:
+                return SSLKeyExRSAExport.KE;
+            case K_DHE_DSS:
+                return SSLKeyExDHEDSS.KE;
+            case K_DHE_DSS_EXPORT:
+                return SSLKeyExDHEDSSExport.KE;
+            case K_DHE_RSA:
+                if (protocolVersion.useTLS12PlusSpec()) {   // (D)TLS 1.2
+                    return SSLKeyExDHERSAOrPSS.KE;
+                } else {    // SSL 3.0, TLS 1.0/1.1
+                    return SSLKeyExDHERSA.KE;
+                }
+            case K_DHE_RSA_EXPORT:
+                return SSLKeyExDHERSAExport.KE;
+            case K_DH_ANON:
+                return SSLKeyExDHANON.KE;
+            case K_DH_ANON_EXPORT:
+                return SSLKeyExDHANONExport.KE;
+            case K_ECDH_ECDSA:
+                return SSLKeyExECDHECDSA.KE;
+            case K_ECDH_RSA:
+                return SSLKeyExECDHRSA.KE;
+            case K_ECDHE_ECDSA:
+                return SSLKeyExECDHEECDSA.KE;
+            case K_ECDHE_RSA:
+                if (protocolVersion.useTLS12PlusSpec()) {   // (D)TLS 1.2
+                    return SSLKeyExECDHERSAOrPSS.KE;
+                } else {    // SSL 3.0, TLS 1.0/1.1
+                    return SSLKeyExECDHERSA.KE;
+                }
+            case K_ECDH_ANON:
+                return SSLKeyExECDHANON.KE;
+        }
+
+        return null;
+    }
+
+    // TLS 1.3
+    static SSLKeyExchange valueOf(NamedGroup namedGroup) {
+        SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup);
+        if (ka != null) {
+            return new SSLKeyExchange(
+                null, T13KeyAgreement.valueOf(namedGroup));
+        }
+
+        return null;
+    }
+
+    private static class SSLKeyExRSA {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.RSA, T12KeyAgreement.RSA);
+    }
+
+    private static class SSLKeyExRSAExport {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.RSA, T12KeyAgreement.RSA_EXPORT);
+    }
+
+    private static class SSLKeyExDHEDSS {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.DSA, T12KeyAgreement.DHE);
+    }
+
+    private static class SSLKeyExDHEDSSExport {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.DSA, T12KeyAgreement.DHE_EXPORT);
+    }
+
+    private static class SSLKeyExDHERSA {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.RSA, T12KeyAgreement.DHE);
+    }
+
+    private static class SSLKeyExDHERSAOrPSS {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.RSA_OR_PSS, T12KeyAgreement.DHE);
+    }
+
+    private static class SSLKeyExDHERSAExport {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.RSA, T12KeyAgreement.DHE_EXPORT);
+    }
+
+    private static class SSLKeyExDHANON {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                null, T12KeyAgreement.DHE);
+    }
+
+    private static class SSLKeyExDHANONExport {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                null, T12KeyAgreement.DHE_EXPORT);
+    }
+
+    private static class SSLKeyExECDHECDSA {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.EC, T12KeyAgreement.ECDH);
+    }
+
+    private static class SSLKeyExECDHRSA {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.EC, T12KeyAgreement.ECDH);
+    }
+
+    private static class SSLKeyExECDHEECDSA {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.EC, T12KeyAgreement.ECDHE);
+    }
+
+    private static class SSLKeyExECDHERSA {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.RSA, T12KeyAgreement.ECDHE);
+    }
+
+    private static class SSLKeyExECDHERSAOrPSS {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                X509Authentication.RSA_OR_PSS, T12KeyAgreement.ECDHE);
+    }
+
+    private static class SSLKeyExECDHANON {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                null, T12KeyAgreement.ECDHE);
+    }
+
+    private enum T12KeyAgreement implements SSLKeyAgreement {
+        RSA             ("rsa",         null,
+                                        RSAKeyExchange.kaGenerator),
+        RSA_EXPORT      ("rsa_export",  RSAKeyExchange.poGenerator,
+                                        RSAKeyExchange.kaGenerator),
+        DHE             ("dhe",         DHKeyExchange.poGenerator,
+                                        DHKeyExchange.kaGenerator),
+        DHE_EXPORT      ("dhe_export",  DHKeyExchange.poExportableGenerator,
+                                        DHKeyExchange.kaGenerator),
+        ECDH            ("ecdh",        null,
+                                        ECDHKeyExchange.ecdhKAGenerator),
+        ECDHE           ("ecdhe",       ECDHKeyExchange.poGenerator,
+                                        ECDHKeyExchange.ecdheKAGenerator);
+
+        final String name;
+        final SSLPossessionGenerator possessionGenerator;
+        final SSLKeyAgreementGenerator keyAgreementGenerator;
+
+        T12KeyAgreement(String name,
+                SSLPossessionGenerator possessionGenerator,
+                SSLKeyAgreementGenerator keyAgreementGenerator) {
+            this.name = name;
+            this.possessionGenerator = possessionGenerator;
+            this.keyAgreementGenerator = keyAgreementGenerator;
+        }
+
+        @Override
+        public SSLPossession createPossession(HandshakeContext context) {
+            if (possessionGenerator != null) {
+                return possessionGenerator.createPossession(context);
+            }
+
+            return null;
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+            return keyAgreementGenerator.createKeyDerivation(context);
+        }
+
+        @Override
+        public SSLHandshake[] getRelatedHandshakers(
+                HandshakeContext handshakeContext) {
+            if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
+                if (this.possessionGenerator != null) {
+                    return new SSLHandshake[] {
+                            SSLHandshake.SERVER_KEY_EXCHANGE
+                        };
+                }
+            }
+
+            return new SSLHandshake[0];
+        }
+
+        @Override
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
+                HandshakeContext handshakeContext) {
+            if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
+                return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
+            }
+
+            if (handshakeContext.sslConfig.isClientMode) {
+                switch (this) {
+                    case RSA:
+                    case RSA_EXPORT:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                    RSAClientKeyExchange.rsaHandshakeProducer
+                            )
+                        });
+
+                    case DHE:
+                    case DHE_EXPORT:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<Byte, HandshakeProducer>(
+                                    SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                    DHClientKeyExchange.dhHandshakeProducer
+                            )
+                        });
+
+                    case ECDH:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                ECDHClientKeyExchange.ecdhHandshakeProducer
+                            )
+                        });
+
+                    case ECDHE:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                ECDHClientKeyExchange.ecdheHandshakeProducer
+                            )
+                        });
+                }
+            } else {
+                switch (this) {
+                    case RSA_EXPORT:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.SERVER_KEY_EXCHANGE.id,
+                                    RSAServerKeyExchange.rsaHandshakeProducer
+                            )
+                        });
+
+                    case DHE:
+                    case DHE_EXPORT:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.SERVER_KEY_EXCHANGE.id,
+                                    DHServerKeyExchange.dhHandshakeProducer
+                            )
+                        });
+
+                    case ECDHE:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.SERVER_KEY_EXCHANGE.id,
+                                    ECDHServerKeyExchange.ecdheHandshakeProducer
+                            )
+                        });
+                }
+            }
+
+            return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
+        }
+
+        @Override
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
+                HandshakeContext handshakeContext) {
+            if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
+                return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
+            }
+
+            if (handshakeContext.sslConfig.isClientMode) {
+                switch (this) {
+                    case RSA_EXPORT:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.SERVER_KEY_EXCHANGE.id,
+                                    RSAServerKeyExchange.rsaHandshakeConsumer
+                            )
+                        });
+
+                    case DHE:
+                    case DHE_EXPORT:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.SERVER_KEY_EXCHANGE.id,
+                                    DHServerKeyExchange.dhHandshakeConsumer
+                            )
+                        });
+
+                    case ECDHE:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.SERVER_KEY_EXCHANGE.id,
+                                    ECDHServerKeyExchange.ecdheHandshakeConsumer
+                            )
+                        });
+                }
+            } else {
+                switch (this) {
+                    case RSA:
+                    case RSA_EXPORT:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                    RSAClientKeyExchange.rsaHandshakeConsumer
+                            )
+                        });
+
+                    case DHE:
+                    case DHE_EXPORT:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                    SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                    DHClientKeyExchange.dhHandshakeConsumer
+                            )
+                        });
+
+                    case ECDH:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                ECDHClientKeyExchange.ecdhHandshakeConsumer
+                            )
+                        });
+
+                    case ECDHE:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                            new SimpleImmutableEntry<>(
+                                SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                ECDHClientKeyExchange.ecdheHandshakeConsumer
+                            )
+                        });
+                }
+            }
+
+            return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
+        }
+    }
+
+    private static final class T13KeyAgreement implements SSLKeyAgreement {
+        private final NamedGroup namedGroup;
+        static final Map<NamedGroup, T13KeyAgreement>
+                supportedKeyShares = new HashMap<>();
+
+        static {
+            for (NamedGroup namedGroup :
+                    SupportedGroups.supportedNamedGroups) {
+                supportedKeyShares.put(
+                        namedGroup, new T13KeyAgreement(namedGroup));
+            }
+        }
+
+        private T13KeyAgreement(NamedGroup namedGroup) {
+            this.namedGroup = namedGroup;
+        }
+
+        static T13KeyAgreement valueOf(NamedGroup namedGroup) {
+            return supportedKeyShares.get(namedGroup);
+        }
+
+        @Override
+        public SSLPossession createPossession(HandshakeContext hc) {
+            if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                return new ECDHEPossession(
+                        namedGroup, hc.sslContext.getSecureRandom());
+            } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+                return new DHEPossession(
+                        namedGroup, hc.sslContext.getSecureRandom());
+            }
+
+            return null;
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext hc) throws IOException {
+            if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
+            } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+                return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
+            }
+
+            return null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.security.cert.Certificate;
+import java.security.cert.Extension;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import sun.security.action.GetPropertyAction;
+import sun.security.util.HexDumpEncoder;
+import sun.security.x509.*;
+
+/**
+ * Implementation of SSL logger.
+ *
+ * If the system property "javax.net.debug" is not defined, the debug logging
+ * is turned off.  If the system property "javax.net.debug" is defined as
+ * empty, the debug logger is specified by System.getLogger("javax.net.ssl"),
+ * and applications can customize and configure the logger or use external
+ * logging mechanisms.  If the system property "javax.net.debug" is defined
+ * and non-empty, a private debug logger implemented in this class is used.
+ */
+public final class SSLLogger {
+    private static final System.Logger logger;
+    private static final String property;
+    public static final boolean isOn;
+
+    static {
+        String p = GetPropertyAction.privilegedGetProperty("javax.net.debug");
+        if (p != null) {
+            if (p.isEmpty()) {
+                property = "";
+                logger = System.getLogger("javax.net.ssl");
+            } else {
+                property = p.toLowerCase(Locale.ENGLISH);
+                if (property.equals("help")) {
+                    help();
+                }
+
+                logger = new SSLConsoleLogger("javax.net.ssl", p);
+            }
+            isOn = true;
+        } else {
+            property = null;
+            logger = null;
+            isOn = false;
+        }
+    }
+
+    private static void help() {
+        System.err.println();
+        System.err.println("help           print the help messages");
+        System.err.println("expand         expand debugging information");
+        System.err.println();
+        System.err.println("all            turn on all debugging");
+        System.err.println("ssl            turn on ssl debugging");
+        System.err.println();
+        System.err.println("The following can be used with ssl:");
+        System.err.println("\trecord       enable per-record tracing");
+        System.err.println("\thandshake    print each handshake message");
+        System.err.println("\tkeygen       print key generation data");
+        System.err.println("\tsession      print session activity");
+        System.err.println("\tdefaultctx   print default SSL initialization");
+        System.err.println("\tsslctx       print SSLContext tracing");
+        System.err.println("\tsessioncache print session cache tracing");
+        System.err.println("\tkeymanager   print key manager tracing");
+        System.err.println("\ttrustmanager print trust manager tracing");
+        System.err.println("\tpluggability print pluggability tracing");
+        System.err.println();
+        System.err.println("\thandshake debugging can be widened with:");
+        System.err.println("\tdata         hex dump of each handshake message");
+        System.err.println("\tverbose      verbose handshake message printing");
+        System.err.println();
+        System.err.println("\trecord debugging can be widened with:");
+        System.err.println("\tplaintext    hex dump of record plaintext");
+        System.err.println("\tpacket       print raw SSL/TLS packets");
+        System.err.println();
+        System.exit(0);
+    }
+
+    /**
+     * Return true if the "javax.net.debug" property contains the
+     * debug check points, or System.Logger is used.
+     */
+    public static boolean isOn(String checkPoints) {
+        if (property == null) {              // debugging is turned off
+            return false;
+        } else if (property.isEmpty()) {     // use System.Logger
+            return true;
+        }                                   // use provider logger
+
+        String[] options = checkPoints.split(",");
+        for (String option : options) {
+            option = option.trim();
+            if (!SSLLogger.hasOption(option)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean hasOption(String option) {
+        option = option.toLowerCase(Locale.ENGLISH);
+        if (property.contains("all")) {
+            return true;
+        } else {
+            int offset = property.indexOf("ssl");
+            if (offset != -1 && property.indexOf("sslctx", offset) != -1) {
+                // don't enable data and plaintext options by default
+                if (!(option.equals("data")
+                        || option.equals("packet")
+                        || option.equals("plaintext"))) {
+                    return true;
+                }
+            }
+        }
+
+        return property.contains(option);
+    }
+
+    public static void severe(String msg, Object... params) {
+        SSLLogger.log(Level.ERROR, msg, params);
+    }
+
+    public static void warning(String msg, Object... params) {
+        SSLLogger.log(Level.WARNING, msg, params);
+    }
+
+    public static void info(String msg, Object... params) {
+        SSLLogger.log(Level.INFO, msg, params);
+    }
+
+    public static void fine(String msg, Object... params) {
+        SSLLogger.log(Level.DEBUG, msg, params);
+    }
+
+    public static void finer(String msg, Object... params) {
+        SSLLogger.log(Level.TRACE, msg, params);
+    }
+
+    public static void finest(String msg, Object... params) {
+        SSLLogger.log(Level.ALL, msg, params);
+    }
+
+    private static void log(Level level, String msg, Object... params) {
+        if (logger.isLoggable(level)) {
+            if (params == null || params.length == 0) {
+                logger.log(level, msg);
+            } else {
+                try {
+                    String formatted =
+                            SSLSimpleFormatter.formatParameters(params);
+                    logger.log(level, msg, formatted);
+                } catch (Exception exp) {
+                    // ignore it, just for debugging.
+                }
+            }
+        }
+    }
+
+    static String toString(Object... params) {
+        try {
+            return SSLSimpleFormatter.formatParameters(params);
+        } catch (Exception exp) {
+            return "unexpected exception thrown: " + exp.getMessage();
+        }
+    }
+
+    private static class SSLConsoleLogger implements Logger {
+        private final String loggerName;
+        private final boolean useCompactFormat;
+
+        SSLConsoleLogger(String loggerName, String options) {
+            this.loggerName = loggerName;
+            options = options.toLowerCase(Locale.ENGLISH);
+            this.useCompactFormat = !options.contains("expand");
+        }
+
+        @Override
+        public String getName() {
+            return loggerName;
+        }
+
+        @Override
+        public boolean isLoggable(Level level) {
+            return (level != Level.OFF);
+        }
+
+        @Override
+        public void log(Level level,
+                ResourceBundle rb, String message, Throwable thrwbl) {
+            if (isLoggable(level)) {
+                try {
+                    String formatted =
+                        SSLSimpleFormatter.format(this, level, message, thrwbl);
+                    System.err.write(formatted.getBytes("UTF-8"));
+                } catch (Exception exp) {
+                    // ignore it, just for debugging.
+                }
+            }
+        }
+
+        @Override
+        public void log(Level level,
+                ResourceBundle rb, String message, Object... params) {
+            if (isLoggable(level)) {
+                try {
+                    String formatted =
+                        SSLSimpleFormatter.format(this, level, message, params);
+                    System.err.write(formatted.getBytes("UTF-8"));
+                } catch (Exception exp) {
+                    // ignore it, just for debugging.
+                }
+            }
+        }
+    }
+
+    private static class SSLSimpleFormatter {
+        private static final ThreadLocal<SimpleDateFormat> dateFormat =
+            new ThreadLocal<SimpleDateFormat>() {
+                @Override protected SimpleDateFormat initialValue() {
+                    return new SimpleDateFormat(
+                            "yyyy-MM-dd kk:mm:ss.SSS z", Locale.ENGLISH);
+                }
+            };
+
+        private static final MessageFormat basicCertFormat = new MessageFormat(
+                "\"version\"            : \"v{0}\",\n" +
+                "\"serial number\"      : \"{1}\",\n" +
+                "\"signature algorithm\": \"{2}\",\n" +
+                "\"issuer\"             : \"{3}\",\n" +
+                "\"not before\"         : \"{4}\",\n" +
+                "\"not  after\"         : \"{5}\",\n" +
+                "\"subject\"            : \"{6}\",\n" +
+                "\"subject public key\" : \"{7}\"\n",
+                Locale.ENGLISH);
+
+        private static final MessageFormat extendedCertFormart =
+            new MessageFormat(
+                "\"version\"            : \"v{0}\",\n" +
+                "\"serial number\"      : \"{1}\",\n" +
+                "\"signature algorithm\": \"{2}\",\n" +
+                "\"issuer\"             : \"{3}\",\n" +
+                "\"not before\"         : \"{4}\",\n" +
+                "\"not  after\"         : \"{5}\",\n" +
+                "\"subject\"            : \"{6}\",\n" +
+                "\"subject public key\" : \"{7}\",\n" +
+                "\"extensions\"         : [\n" +
+                "{8}\n" +
+                "]\n",
+                Locale.ENGLISH);
+
+        //
+        // private static MessageFormat certExtFormat = new MessageFormat(
+        //         "{0} [{1}] '{'\n" +
+        //         "  critical: {2}\n" +
+        //         "  value: {3}\n" +
+        //         "'}'",
+        //         Locale.ENGLISH);
+        //
+
+        private static final MessageFormat messageFormatNoParas =
+            new MessageFormat(
+                "'{'\n" +
+                "  \"logger\"      : \"{0}\",\n" +
+                "  \"level\"       : \"{1}\",\n" +
+                "  \"thread id\"   : \"{2}\",\n" +
+                "  \"thread name\" : \"{3}\",\n" +
+                "  \"time\"        : \"{4}\",\n" +
+                "  \"caller\"      : \"{5}\",\n" +
+                "  \"message\"     : \"{6}\"\n" +
+                "'}'\n",
+                Locale.ENGLISH);
+
+        private static final MessageFormat messageCompactFormatNoParas =
+            new MessageFormat(
+                "{0}|{1}|{2}|{3}|{4}|{5}|{6}\n",
+                Locale.ENGLISH);
+
+        private static final MessageFormat messageFormatWithParas =
+            new MessageFormat(
+                "'{'\n" +
+                "  \"logger\"      : \"{0}\",\n" +
+                "  \"level\"       : \"{1}\",\n" +
+                "  \"thread id\"   : \"{2}\",\n" +
+                "  \"thread name\" : \"{3}\",\n" +
+                "  \"time\"        : \"{4}\",\n" +
+                "  \"caller\"      : \"{5}\",\n" +
+                "  \"message\"     : \"{6}\",\n" +
+                "  \"specifics\"   : [\n" +
+                "{7}\n" +
+                "  ]\n" +
+                "'}'\n",
+                Locale.ENGLISH);
+
+        private static final MessageFormat messageCompactFormatWithParas =
+            new MessageFormat(
+                "{0}|{1}|{2}|{3}|{4}|{5}|{6} (\n" +
+                "{7}\n" +
+                ")\n",
+                Locale.ENGLISH);
+
+        private static final MessageFormat keyObjectFormat = new MessageFormat(
+                "\"{0}\" : '{'\n" +
+                "{1}" +
+                "'}'\n",
+                Locale.ENGLISH);
+
+        // INFO: [TH: 123450] 2011-08-20 23:12:32.3225 PDT
+        //     log message
+        //     log message
+        //     ...
+        private static String format(SSLConsoleLogger logger, Level level,
+                    String message, Object ... parameters) {
+
+            if (parameters == null || parameters.length == 0) {
+                Object[] messageFields = {
+                    logger.loggerName,
+                    level.getName(),
+                    Utilities.toHexString(Thread.currentThread().getId()),
+                    Thread.currentThread().getName(),
+                    dateFormat.get().format(new Date(System.currentTimeMillis())),
+                    formatCaller(),
+                    message
+                };
+
+                if (logger.useCompactFormat) {
+                    return messageCompactFormatNoParas.format(messageFields);
+                } else {
+                    return messageFormatNoParas.format(messageFields);
+                }
+            }
+
+            Object[] messageFields = {
+                    logger.loggerName,
+                    level.getName(),
+                    Utilities.toHexString(Thread.currentThread().getId()),
+                    Thread.currentThread().getName(),
+                    dateFormat.get().format(new Date(System.currentTimeMillis())),
+                    formatCaller(),
+                    message,
+                    (logger.useCompactFormat ?
+                        formatParameters(parameters) :
+                        Utilities.indent(formatParameters(parameters)))
+                };
+
+            if (logger.useCompactFormat) {
+                return messageCompactFormatWithParas.format(messageFields);
+            } else {
+                return messageFormatWithParas.format(messageFields);
+            }
+        }
+
+        private static String formatCaller() {
+            return StackWalker.getInstance().walk(s ->
+                s.dropWhile(f ->
+                    f.getClassName().startsWith("sun.security.ssl.SSLLogger") ||
+                    f.getClassName().startsWith("java.lang.System"))
+                .map(f -> f.getFileName() + ":" + f.getLineNumber())
+                .findFirst().orElse("unknown caller"));
+        }
+
+        private static String formatParameters(Object ... parameters) {
+            StringBuilder builder = new StringBuilder(512);
+            boolean isFirst = true;
+            for (Object parameter : parameters) {
+                if (isFirst) {
+                    isFirst = false;
+                } else {
+                    builder.append(",\n");
+                }
+
+                if (parameter instanceof Throwable) {
+                    builder.append(formatThrowable((Throwable)parameter));
+                } else if (parameter instanceof Certificate) {
+                    builder.append(formatCertificate((Certificate)parameter));
+                } else if (parameter instanceof ByteArrayInputStream) {
+                    builder.append(formatByteArrayInputStream(
+                        (ByteArrayInputStream)parameter));
+                } else if (parameter instanceof ByteBuffer) {
+                    builder.append(formatByteBuffer((ByteBuffer)parameter));
+                } else if (parameter instanceof byte[]) {
+                    builder.append(formatByteArrayInputStream(
+                        new ByteArrayInputStream((byte[])parameter)));
+                } else if (parameter instanceof Map.Entry) {
+                    @SuppressWarnings("unchecked")
+                    Map.Entry<String, ?> mapParameter =
+                        (Map.Entry<String, ?>)parameter;
+                    builder.append(formatMapEntry(mapParameter));
+                } else {
+                    builder.append(formatObject(parameter));
+                }
+            }
+
+            return builder.toString();
+        }
+
+        // "throwable": {
+        //   ...
+        // }
+        private static String formatThrowable(Throwable throwable) {
+            StringBuilder builder = new StringBuilder(512);
+            ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+            try (PrintStream out = new PrintStream(bytesOut)) {
+                throwable.printStackTrace(out);
+                builder.append(Utilities.indent(bytesOut.toString()));
+            }
+            Object[] fields = {
+                    "throwable",
+                    builder.toString()
+                };
+
+            return keyObjectFormat.format(fields);
+        }
+
+        // "certificate": {
+        //   ...
+        // }
+        private static String formatCertificate(Certificate certificate) {
+
+            if (!(certificate instanceof X509Certificate)) {
+                return Utilities.indent(certificate.toString());
+            }
+
+            StringBuilder builder = new StringBuilder(512);
+            try {
+                X509CertImpl x509 =
+                    X509CertImpl.toImpl((X509Certificate)certificate);
+                X509CertInfo certInfo =
+                        (X509CertInfo)x509.get(X509CertImpl.NAME + "." +
+                                                       X509CertImpl.INFO);
+                CertificateExtensions certExts = (CertificateExtensions)
+                        certInfo.get(X509CertInfo.EXTENSIONS);
+                if (certExts == null) {
+                    Object[] certFields = {
+                        x509.getVersion(),
+                        Utilities.toHexString(
+                                x509.getSerialNumber().toByteArray()),
+                        x509.getSigAlgName(),
+                        x509.getIssuerX500Principal().toString(),
+                        dateFormat.get().format(x509.getNotBefore()),
+                        dateFormat.get().format(x509.getNotAfter()),
+                        x509.getSubjectX500Principal().toString(),
+                        x509.getPublicKey().getAlgorithm()
+                        };
+                    builder.append(Utilities.indent(
+                            basicCertFormat.format(certFields)));
+                } else {
+                    StringBuilder extBuilder = new StringBuilder(512);
+                    boolean isFirst = true;
+                    for (Extension certExt : certExts.getAllExtensions()) {
+                        if (isFirst) {
+                            isFirst = false;
+                        } else {
+                            extBuilder.append(",\n");
+                        }
+                        extBuilder.append("{\n" +
+                            Utilities.indent(certExt.toString()) + "\n}");
+                    }
+                    Object[] certFields = {
+                        x509.getVersion(),
+                        Utilities.toHexString(
+                                x509.getSerialNumber().toByteArray()),
+                        x509.getSigAlgName(),
+                        x509.getIssuerX500Principal().toString(),
+                        dateFormat.get().format(x509.getNotBefore()),
+                        dateFormat.get().format(x509.getNotAfter()),
+                        x509.getSubjectX500Principal().toString(),
+                        x509.getPublicKey().getAlgorithm(),
+                        Utilities.indent(extBuilder.toString())
+                        };
+                    builder.append(Utilities.indent(
+                            extendedCertFormart.format(certFields)));
+                }
+            } catch (Exception ce) {
+                // ignore the exception
+            }
+
+            Object[] fields = {
+                    "certificate",
+                    builder.toString()
+                };
+
+            return Utilities.indent(keyObjectFormat.format(fields));
+        }
+
+        private static String formatByteArrayInputStream(
+                ByteArrayInputStream bytes) {
+            StringBuilder builder = new StringBuilder(512);
+
+            try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) {
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                hexEncoder.encodeBuffer(bytes, bytesOut);
+
+                builder.append(Utilities.indent(bytesOut.toString()));
+            } catch (IOException ioe) {
+                // ignore it, just for debugging.
+            }
+
+            return builder.toString();
+        }
+
+        private static String formatByteBuffer(ByteBuffer byteBuffer) {
+            StringBuilder builder = new StringBuilder(512);
+            try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) {
+                HexDumpEncoder hexEncoder = new HexDumpEncoder();
+                hexEncoder.encodeBuffer(byteBuffer.duplicate(), bytesOut);
+                builder.append(Utilities.indent(bytesOut.toString()));
+            } catch (IOException ioe) {
+                // ignore it, just for debugging.
+            }
+
+            return builder.toString();
+        }
+
+        private static String formatMapEntry(Map.Entry<String, ?> entry) {
+            String key = entry.getKey();
+            Object value = entry.getValue();
+
+            String formatted;
+            if (value instanceof String) {
+                // "key": "value"
+                formatted = "\"" + key + "\": \"" + (String)value + "\"";
+            } else if (value instanceof String[]) {
+                // "key": [ "string a",
+                //          "string b",
+                //          "string c"
+                //        ]
+                StringBuilder builder = new StringBuilder(512);
+                String[] strings = (String[])value;
+                builder.append("\"" + key + "\": [\n");
+                for (String string : strings) {
+                    builder.append("      \"" + string + "\"");
+                    if (string != strings[strings.length - 1]) {
+                        builder.append(",");
+                    }
+                    builder.append("\n");
+                }
+                builder.append("      ]");
+
+                formatted = builder.toString();
+            } else if (value instanceof byte[]) {
+                formatted = "\"" + key + "\": \"" +
+                    Utilities.toHexString((byte[])value) + "\"";
+            } else if (value instanceof Byte) {
+                formatted = "\"" + key + "\": \"" +
+                    Utilities.toHexString((byte)value) + "\"";
+            } else {
+                formatted = "\"" + key + "\": " +
+                    "\"" + value.toString() + "\"";
+            }
+
+            return Utilities.indent(formatted);
+        }
+
+        private static String formatObject(Object obj) {
+            return obj.toString();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import sun.security.internal.spec.TlsMasterSecretParameterSpec;
+import sun.security.ssl.CipherSuite.HashAlg;
+import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
+
+enum SSLMasterKeyDerivation implements SSLKeyDerivationGenerator {
+    SSL30       ("kdf_ssl30"),
+    TLS10       ("kdf_tls10"),
+    TLS12       ("kdf_tls12");
+
+    final String name;
+
+    private SSLMasterKeyDerivation(String name) {
+        this.name = name;
+    }
+
+    static SSLMasterKeyDerivation valueOf(ProtocolVersion protocolVersion) {
+        switch (protocolVersion) {
+            case SSL30:
+                return SSLMasterKeyDerivation.SSL30;
+            case TLS10:
+            case TLS11:
+            case DTLS10:
+                return SSLMasterKeyDerivation.TLS10;
+            case TLS12:
+            case DTLS12:
+                return SSLMasterKeyDerivation.TLS12;
+            default:
+                return null;
+        }
+    }
+
+    @Override
+    public SSLKeyDerivation createKeyDerivation(HandshakeContext context,
+            SecretKey secretKey) throws IOException {
+        return new LegacyMasterKeyDerivation(context, secretKey);
+    }
+
+    // Note, we may use different key derivation implementation in the future.
+    private static final
+            class LegacyMasterKeyDerivation implements SSLKeyDerivation {
+
+        final HandshakeContext context;
+        final SecretKey preMasterSecret;
+
+        LegacyMasterKeyDerivation(
+                HandshakeContext context, SecretKey preMasterSecret) {
+            this.context = context;
+            this.preMasterSecret = preMasterSecret;
+        }
+
+        @Override
+        @SuppressWarnings("deprecation")
+        public SecretKey deriveKey(String algorithm,
+                AlgorithmParameterSpec params) throws IOException {
+
+            CipherSuite cipherSuite = context.negotiatedCipherSuite;
+            ProtocolVersion protocolVersion = context.negotiatedProtocol;
+
+            // What algs/params do we need to use?
+            String masterAlg;
+            HashAlg hashAlg;
+
+            byte majorVersion = protocolVersion.major;
+            byte minorVersion = protocolVersion.minor;
+            if (protocolVersion.isDTLS) {
+                // Use TLS version number for DTLS key calculation
+                if (protocolVersion.id == ProtocolVersion.DTLS10.id) {
+                    majorVersion = ProtocolVersion.TLS11.major;
+                    minorVersion = ProtocolVersion.TLS11.minor;
+
+                    masterAlg = "SunTlsMasterSecret";
+                    hashAlg = H_NONE;
+                } else {    // DTLS 1.2
+                    majorVersion = ProtocolVersion.TLS12.major;
+                    minorVersion = ProtocolVersion.TLS12.minor;
+
+                    masterAlg = "SunTls12MasterSecret";
+                    hashAlg = cipherSuite.hashAlg;
+                }
+            } else {
+                if (protocolVersion.id >= ProtocolVersion.TLS12.id) {
+                    masterAlg = "SunTls12MasterSecret";
+                    hashAlg = cipherSuite.hashAlg;
+                } else {
+                    masterAlg = "SunTlsMasterSecret";
+                    hashAlg = H_NONE;
+                }
+            }
+
+            TlsMasterSecretParameterSpec spec;
+            if (context.handshakeSession.useExtendedMasterSecret) {
+                // reset to use the extended master secret algorithm
+                masterAlg = "SunTlsExtendedMasterSecret";
+
+                // For the session hash, use the handshake messages up to and
+                // including the ClientKeyExchange message.
+                context.handshakeHash.utilize();
+                byte[] sessionHash = context.handshakeHash.digest();
+                spec = new TlsMasterSecretParameterSpec(
+                        preMasterSecret,
+                        (majorVersion & 0xFF), (minorVersion & 0xFF),
+                        sessionHash,
+                        hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
+            } else {
+                spec = new TlsMasterSecretParameterSpec(
+                        preMasterSecret,
+                        (majorVersion & 0xFF), (minorVersion & 0xFF),
+                        context.clientHelloRandom.randomBytes,
+                        context.serverHelloRandom.randomBytes,
+                        hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
+            }
+
+            try {
+                KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
+                kg.init(spec);
+                return kg.generateKey();
+            } catch (InvalidAlgorithmParameterException |
+                    NoSuchAlgorithmException iae) {
+                // unlikely to happen, otherwise, must be a provider exception
+                //
+                // For RSA premaster secrets, do not signal a protocol error
+                // due to the Bleichenbacher attack. See comments further down.
+                if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
+                    SSLLogger.fine("RSA master secret generation error.", iae);
+                }
+                throw new ProviderException(iae);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLPossession.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+interface SSLPossession {
+    default byte[] encode() {
+        return new byte[0];
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLPossessionGenerator.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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;
+
+interface SSLPossessionGenerator {
+    SSLPossession createPossession(HandshakeContext handshakeContext);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLProducer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+
+interface SSLProducer {
+    // return the encoded producing if it has not been dumped to the context
+    byte[] produce(ConnectionContext context) throws IOException;
+}
--- a/src/java.base/share/classes/sun/security/ssl/SSLRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,7 +32,8 @@
  */
 interface SSLRecord extends Record {
 
-    static final int    headerSize = 5;         // SSLv3 record header
+    static final int    headerSize = 5;             // SSLv3 record header
+    static final int    handshakeHeaderSize = 4;    // SSLv3 handshake header
 
     /*
      * The size of the header plus the max IV length
@@ -67,19 +68,6 @@
                                     + maxMacSize;           // MAC or AEAD tag
 
     /*
-     * For CBC protection in SSL3/TLS1, we break some plaintext into two
-     * packets.  Max application data size for the second packet.
-     */
-    static final int    maxDataSizeMinusOneByteRecord =
-                                  maxDataSize       // max data size
-                                - (                 // max one byte record size
-                                      headerPlusMaxIVSize   // header + iv
-                                    + 1             // one byte data
-                                    + maxPadding    // padding
-                                    + maxMacSize    // MAC
-                                  );
-
-    /*
      * The maximum large record size.
      *
      * Some SSL/TLS implementations support large fragment upto 2^15 bytes,
@@ -92,18 +80,6 @@
                 maxRecordSize   // Max size with a conforming implementation
               + maxDataSize;    // extra 2^14 bytes for large data packets.
 
-
-    /*
-     * Maximum record size for alert and change cipher spec records.
-     * They only contain 2 and 1 bytes of data, respectively.
-     * Allocate a smaller array.
-     */
-    static final int    maxAlertRecordSize =
-                                      headerPlusMaxIVSize   // header + iv
-                                    + 2                     // alert
-                                    + maxPadding            // padding
-                                    + maxMacSize;           // MAC
-
     /*
      * We may need to send this SSL v2 "No Cipher" message back, if we
      * are faced with an SSLv2 "hello" that's not saying "I talk v3".
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.ssl.CipherSuite.HashAlg;
+
+final class SSLSecretDerivation implements SSLKeyDerivation {
+    private static final byte[] sha256EmptyDigest = new byte[] {
+        (byte)0xE3, (byte)0xB0, (byte)0xC4, (byte)0x42,
+        (byte)0x98, (byte)0xFC, (byte)0x1C, (byte)0x14,
+        (byte)0x9A, (byte)0xFB, (byte)0xF4, (byte)0xC8,
+        (byte)0x99, (byte)0x6F, (byte)0xB9, (byte)0x24,
+        (byte)0x27, (byte)0xAE, (byte)0x41, (byte)0xE4,
+        (byte)0x64, (byte)0x9B, (byte)0x93, (byte)0x4C,
+        (byte)0xA4, (byte)0x95, (byte)0x99, (byte)0x1B,
+        (byte)0x78, (byte)0x52, (byte)0xB8, (byte)0x55
+    };
+
+    private static final byte[] sha384EmptyDigest = new byte[] {
+        (byte)0x38, (byte)0xB0, (byte)0x60, (byte)0xA7,
+        (byte)0x51, (byte)0xAC, (byte)0x96, (byte)0x38,
+        (byte)0x4C, (byte)0xD9, (byte)0x32, (byte)0x7E,
+        (byte)0xB1, (byte)0xB1, (byte)0xE3, (byte)0x6A,
+        (byte)0x21, (byte)0xFD, (byte)0xB7, (byte)0x11,
+        (byte)0x14, (byte)0xBE, (byte)0x07, (byte)0x43,
+        (byte)0x4C, (byte)0x0C, (byte)0xC7, (byte)0xBF,
+        (byte)0x63, (byte)0xF6, (byte)0xE1, (byte)0xDA,
+        (byte)0x27, (byte)0x4E, (byte)0xDE, (byte)0xBF,
+        (byte)0xE7, (byte)0x6F, (byte)0x65, (byte)0xFB,
+        (byte)0xD5, (byte)0x1A, (byte)0xD2, (byte)0xF1,
+        (byte)0x48, (byte)0x98, (byte)0xB9, (byte)0x5B
+    };
+
+    private final HandshakeContext context;
+    private final String hkdfAlg;
+    private final HashAlg hashAlg;
+    private final SecretKey secret;
+    private final byte[] transcriptHash;  // handshake messages transcript hash
+
+    SSLSecretDerivation(
+            HandshakeContext context, SecretKey secret) {
+        this.context = context;
+        this.secret = secret;
+        this.hashAlg = context.negotiatedCipherSuite.hashAlg;
+        this.hkdfAlg =
+                "HKDF-Expand/Hmac" + hashAlg.name.replace("-", "");
+        context.handshakeHash.update();
+        this.transcriptHash = context.handshakeHash.digest();
+    }
+
+    SSLSecretDerivation forContext(HandshakeContext context) {
+        return new SSLSecretDerivation(context, secret);
+    }
+
+    @Override
+    public SecretKey deriveKey(String algorithm,
+            AlgorithmParameterSpec params) throws IOException {
+        SecretSchedule ks = SecretSchedule.valueOf(algorithm);
+        try {
+            byte[] expandContext;
+            if (ks == SecretSchedule.TlsSaltSecret) {
+                if (hashAlg == HashAlg.H_SHA256) {
+                    expandContext = sha256EmptyDigest;
+                } else if (hashAlg == HashAlg.H_SHA384) {
+                    expandContext = sha384EmptyDigest;
+                } else {
+                    // unlikely, but please update if more hash algorithm
+                    // get supported in the future.
+                    throw new SSLHandshakeException(
+                            "Unexpected unsupported hash algorithm: " +
+                            algorithm);
+                }
+            } else {
+                expandContext = transcriptHash;
+            }
+            byte[] hkdfInfo = createHkdfInfo(ks.label,
+                    expandContext, hashAlg.hashLength);
+
+            HKDF hkdf = new HKDF(hashAlg.name);
+            return hkdf.expand(secret, hkdfInfo, hashAlg.hashLength, algorithm);
+        } catch (GeneralSecurityException gse) {
+            throw (SSLHandshakeException) new SSLHandshakeException(
+                "Could not generate secret").initCause(gse);
+        }
+    }
+
+    public static byte[] createHkdfInfo(
+            byte[] label, byte[] context, int length) {
+        byte[] info = new byte[4 + label.length + context.length];
+        ByteBuffer m = ByteBuffer.wrap(info);
+        try {
+            Record.putInt16(m, length);
+            Record.putBytes8(m, label);
+            Record.putBytes8(m, context);
+        } catch (IOException ioe) {
+            // unlikely
+            throw new RuntimeException("Unexpected exception", ioe);
+        }
+
+        return info;
+    }
+
+    private enum SecretSchedule {
+        // Note that we use enum name as the key/secret name.
+        TlsSaltSecret                       ("derived"),
+        TlsExtBinderKey                     ("ext binder"),
+        TlsResBinderKey                     ("res binder"),
+        TlsClientEarlyTrafficSecret         ("c e traffic"),
+        TlsEarlyExporterMasterSecret        ("e exp master"),
+        TlsClientHandshakeTrafficSecret     ("c hs traffic"),
+        TlsServerHandshakeTrafficSecret     ("s hs traffic"),
+        TlsClientAppTrafficSecret           ("c ap traffic"),
+        TlsServerAppTrafficSecret           ("s ap traffic"),
+        TlsExporterMasterSecret             ("exp master"),
+        TlsResumptionMasterSecret           ("res master");
+
+        private final byte[] label;
+
+        private SecretSchedule(String label) {
+            this.label = ("tls13 " + label).getBytes();
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/SSLServerSocketFactoryImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLServerSocketFactoryImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.ServerSocket;
-
 import javax.net.ssl.SSLServerSocketFactory;
 
 /**
@@ -36,11 +35,9 @@
  *
  * @author David Brownell
  */
-final
-public class SSLServerSocketFactoryImpl extends SSLServerSocketFactory
-{
+public final class SSLServerSocketFactoryImpl extends SSLServerSocketFactory {
     private static final int DEFAULT_BACKLOG = 50;
-    private SSLContextImpl context;
+    private final SSLContextImpl context;
 
 
     /**
@@ -55,8 +52,7 @@
     /**
      * Called from SSLContextImpl's getSSLServerSocketFactory().
      */
-    SSLServerSocketFactoryImpl (SSLContextImpl context)
-    {
+    SSLServerSocketFactoryImpl(SSLContextImpl context) {
         this.context = context;
     }
 
@@ -73,26 +69,22 @@
     }
 
     @Override
-    public ServerSocket createServerSocket (int port)
-    throws IOException
-    {
-        return new SSLServerSocketImpl (port, DEFAULT_BACKLOG, context);
+    public ServerSocket createServerSocket(int port) throws IOException {
+        return new SSLServerSocketImpl(context, port, DEFAULT_BACKLOG);
     }
 
 
     @Override
-    public ServerSocket createServerSocket (int port, int backlog)
-    throws IOException
-    {
-        return new SSLServerSocketImpl (port, backlog, context);
+    public ServerSocket createServerSocket (
+            int port, int backlog) throws IOException {
+        return new SSLServerSocketImpl(context, port, backlog);
     }
 
     @Override
     public ServerSocket
-    createServerSocket (int port, int backlog, InetAddress ifAddress)
-    throws IOException
-    {
-        return new SSLServerSocketImpl (port, backlog, ifAddress, context);
+    createServerSocket (int port,
+            int backlog, InetAddress ifAddress) throws IOException {
+        return new SSLServerSocketImpl(context, port, backlog, ifAddress);
     }
 
     /**
@@ -104,7 +96,7 @@
      */
     @Override
     public String[] getDefaultCipherSuites() {
-        return context.getDefaultCipherSuiteList(true).toStringArray();
+        return CipherSuite.namesOf(context.getDefaultCipherSuites(true));
     }
 
     /**
@@ -119,7 +111,6 @@
      */
     @Override
     public String[] getSupportedCipherSuites() {
-        return context.getSupportedCipherSuiteList().toStringArray();
+        return CipherSuite.namesOf(context.getSupportedCipherSuites());
     }
-
 }
--- a/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,22 +23,13 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.Socket;
-
-import java.security.AlgorithmConstraints;
-
-import java.util.*;
-
-import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SNIMatcher;
-
 
 /**
  * This class provides a simple way for servers to support conventional
@@ -62,301 +53,157 @@
  *
  * @author David Brownell
  */
-final
-class SSLServerSocketImpl extends SSLServerSocket
-{
-    private SSLContextImpl      sslContext;
-
-    /* Do newly accepted connections require clients to authenticate? */
-    private ClientAuthType    clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
-
-    /* Do new connections created here use the "server" mode of SSL? */
-    private boolean             useServerMode = true;
+final class SSLServerSocketImpl extends SSLServerSocket {
+    private final SSLContextImpl        sslContext;
+    private final SSLConfiguration      sslConfig;
 
-    /* Can new connections created establish new sessions? */
-    private boolean             enableSessionCreation = true;
-
-    /* what cipher suites to use by default */
-    private CipherSuiteList     enabledCipherSuites = null;
-
-    /* which protocol to use by default */
-    private ProtocolList        enabledProtocols = null;
-
-    // the endpoint identification protocol to use by default
-    private String              identificationProtocol = null;
-
-    // The cryptographic algorithm constraints
-    private AlgorithmConstraints    algorithmConstraints = null;
+    SSLServerSocketImpl(SSLContextImpl sslContext) throws IOException {
 
-    // The server name indication
-    Collection<SNIMatcher>      sniMatchers =
-                                    Collections.<SNIMatcher>emptyList();
-
-    // Configured application protocol values
-    String[] applicationProtocols = new String[0];
-
-    /*
-     * Whether local cipher suites preference in server side should be
-     * honored during handshaking?
-     */
-    private boolean             preferLocalCipherSuites = false;
+        super();
+        this.sslContext = sslContext;
+        this.sslConfig = new SSLConfiguration(sslContext, false);
+        this.sslConfig.isClientMode = false;
+    }
 
-    /**
-     * Create an SSL server socket on a port, using a non-default
-     * authentication context and a specified connection backlog.
-     *
-     * @param port the port on which to listen
-     * @param backlog how many connections may be pending before
-     *          the system should start rejecting new requests
-     * @param context authentication context for this server
-     */
-    SSLServerSocketImpl(int port, int backlog, SSLContextImpl context)
-    throws IOException, SSLException
-    {
+    SSLServerSocketImpl(SSLContextImpl sslContext,
+            int port, int backlog) throws IOException {
+
         super(port, backlog);
-        initServer(context);
+        this.sslContext = sslContext;
+        this.sslConfig = new SSLConfiguration(sslContext, false);
+        this.sslConfig.isClientMode = false;
     }
 
+    SSLServerSocketImpl(SSLContextImpl sslContext,
+            int port, int backlog, InetAddress address) throws IOException {
 
-    /**
-     * Create an SSL server socket on a port, using a specified
-     * authentication context and a specified backlog of connections
-     * as well as a particular specified network interface.  This
-     * constructor is used on multihomed hosts, such as those used
-     * for firewalls or as routers, to control through which interface
-     * a network service is provided.
-     *
-     * @param port the port on which to listen
-     * @param backlog how many connections may be pending before
-     *          the system should start rejecting new requests
-     * @param address the address of the network interface through
-     *          which connections will be accepted
-     * @param context authentication context for this server
-     */
-    SSLServerSocketImpl(
-        int             port,
-        int             backlog,
-        InetAddress     address,
-        SSLContextImpl  context)
-        throws IOException
-    {
         super(port, backlog, address);
-        initServer(context);
-    }
-
-
-    /**
-     * Creates an unbound server socket.
-     */
-    SSLServerSocketImpl(SSLContextImpl context) throws IOException {
-        super();
-        initServer(context);
+        this.sslContext = sslContext;
+        this.sslConfig = new SSLConfiguration(sslContext, false);
+        this.sslConfig.isClientMode = false;
     }
 
-
-    /**
-     * Initializes the server socket.
-     */
-    private void initServer(SSLContextImpl context) throws SSLException {
-        if (context == null) {
-            throw new SSLException("No Authentication context given");
-        }
-        sslContext = context;
-        enabledCipherSuites = sslContext.getDefaultCipherSuiteList(true);
-        enabledProtocols = sslContext.getDefaultProtocolList(true);
+    @Override
+    public synchronized String[] getEnabledCipherSuites() {
+        return CipherSuite.namesOf(sslConfig.enabledCipherSuites);
     }
 
-    /**
-     * Returns the names of the cipher suites which could be enabled for use
-     * on an SSL connection.  Normally, only a subset of these will actually
-     * be enabled by default, since this list may include cipher suites which
-     * do not support the mutual authentication of servers and clients, or
-     * which do not protect data confidentiality.  Servers may also need
-     * certain kinds of certificates to use certain cipher suites.
-     *
-     * @return an array of cipher suite names
-     */
     @Override
-    public String[] getSupportedCipherSuites() {
-        return sslContext.getSupportedCipherSuiteList().toStringArray();
+    public synchronized void setEnabledCipherSuites(String[] suites) {
+        sslConfig.enabledCipherSuites =
+                CipherSuite.validValuesOf(suites);
     }
 
-    /**
-     * Returns the list of cipher suites which are currently enabled
-     * for use by newly accepted connections.  A null return indicates
-     * that the system defaults are in effect.
-     */
     @Override
-    public synchronized String[] getEnabledCipherSuites() {
-        return enabledCipherSuites.toStringArray();
-    }
-
-    /**
-     * Controls which particular SSL cipher suites are enabled for use
-     * by accepted connections.
-     *
-     * @param suites Names of all the cipher suites to enable; null
-     *  means to accept system defaults.
-     */
-    @Override
-    public synchronized void setEnabledCipherSuites(String[] suites) {
-        enabledCipherSuites = new CipherSuiteList(suites);
+    public String[] getSupportedCipherSuites() {
+        return CipherSuite.namesOf(sslContext.getSupportedCipherSuites());
     }
 
     @Override
     public String[] getSupportedProtocols() {
-        return sslContext.getSuportedProtocolList().toStringArray();
-    }
-
-    /**
-     * Controls which protocols are enabled for use.
-     * The protocols must have been listed by
-     * getSupportedProtocols() as being supported.
-     *
-     * @param protocols protocols to enable.
-     * @exception IllegalArgumentException when one of the protocols
-     *  named by the parameter is not supported.
-     */
-    @Override
-    public synchronized void setEnabledProtocols(String[] protocols) {
-        enabledProtocols = new ProtocolList(protocols);
+        return ProtocolVersion.toStringArray(
+                sslContext.getSupportedProtocolVersions());
     }
 
     @Override
     public synchronized String[] getEnabledProtocols() {
-        return enabledProtocols.toStringArray();
+        return ProtocolVersion.toStringArray(sslConfig.enabledProtocols);
     }
 
-    /**
-     * Controls whether the connections which are accepted must include
-     * client authentication.
-     */
     @Override
-    public void setNeedClientAuth(boolean flag) {
-        clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
-                ClientAuthType.CLIENT_AUTH_NONE);
+    public synchronized void setEnabledProtocols(String[] protocols) {
+        if (protocols == null) {
+            throw new IllegalArgumentException("Protocols cannot be null");
+        }
+
+        sslConfig.enabledProtocols = ProtocolVersion.namesOf(protocols);
     }
 
     @Override
-    public boolean getNeedClientAuth() {
-        return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUIRED);
-    }
-
-    /**
-     * Controls whether the connections which are accepted should request
-     * client authentication.
-     */
-    @Override
-    public void setWantClientAuth(boolean flag) {
-        clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
-                ClientAuthType.CLIENT_AUTH_NONE);
+    public synchronized void setNeedClientAuth(boolean need) {
+        sslConfig.clientAuthType =
+                (need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
+                        ClientAuthType.CLIENT_AUTH_NONE);
     }
 
     @Override
-    public boolean getWantClientAuth() {
-        return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUESTED);
+    public synchronized boolean getNeedClientAuth() {
+        return (sslConfig.clientAuthType ==
+                        ClientAuthType.CLIENT_AUTH_REQUIRED);
     }
 
-    /**
-     * Makes the returned sockets act in SSL "client" mode, not the usual
-     * server mode.  The canonical example of why this is needed is for
-     * FTP clients, which accept connections from servers and should be
-     * rejoining the already-negotiated SSL connection.
-     */
     @Override
-    public void setUseClientMode(boolean flag) {
-        /*
-         * If we need to change the socket mode and the enabled
-         * protocols haven't specifically been set by the user,
-         * change them to the corresponding default ones.
-         */
-        if (useServerMode != (!flag) &&
-                sslContext.isDefaultProtocolList(enabledProtocols)) {
-            enabledProtocols = sslContext.getDefaultProtocolList(!flag);
-        }
+    public synchronized void setWantClientAuth(boolean want) {
+        sslConfig.clientAuthType =
+                (want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
+                        ClientAuthType.CLIENT_AUTH_NONE);
+    }
 
-        useServerMode = !flag;
+    @Override
+    public synchronized boolean getWantClientAuth() {
+        return (sslConfig.clientAuthType ==
+                        ClientAuthType.CLIENT_AUTH_REQUESTED);
     }
 
     @Override
-    public boolean getUseClientMode() {
-        return !useServerMode;
+    public synchronized void setUseClientMode(boolean useClientMode) {
+        /*
+         * If we need to change the client mode and the enabled
+         * protocols and cipher suites haven't specifically been
+         * set by the user, change them to the corresponding
+         * default ones.
+         */
+        if (sslConfig.isClientMode != useClientMode) {
+            if (sslContext.isDefaultProtocolVesions(
+                    sslConfig.enabledProtocols)) {
+                sslConfig.enabledProtocols =
+                        sslContext.getDefaultProtocolVersions(!useClientMode);
+            }
+
+            if (sslContext.isDefaultCipherSuiteList(
+                    sslConfig.enabledCipherSuites)) {
+                sslConfig.enabledCipherSuites =
+                        sslContext.getDefaultCipherSuites(!useClientMode);
+            }
+
+            sslConfig.isClientMode = useClientMode;
+        }
     }
 
-
-    /**
-     * Controls whether new connections may cause creation of new SSL
-     * sessions.
-     */
     @Override
-    public void setEnableSessionCreation(boolean flag) {
-        enableSessionCreation = flag;
+    public synchronized boolean getUseClientMode() {
+        return sslConfig.isClientMode;
     }
 
-    /**
-     * Returns true if new connections may cause creation of new SSL
-     * sessions.
-     */
     @Override
-    public boolean getEnableSessionCreation() {
-        return enableSessionCreation;
+    public synchronized void setEnableSessionCreation(boolean flag) {
+        sslConfig.enableSessionCreation = flag;
     }
 
-    /**
-     * Returns the SSLParameters in effect for newly accepted connections.
-     */
+    @Override
+    public synchronized boolean getEnableSessionCreation() {
+        return sslConfig.enableSessionCreation;
+    }
+
     @Override
     public synchronized SSLParameters getSSLParameters() {
-        SSLParameters params = super.getSSLParameters();
-
-        // the super implementation does not handle the following parameters
-        params.setEndpointIdentificationAlgorithm(identificationProtocol);
-        params.setAlgorithmConstraints(algorithmConstraints);
-        params.setSNIMatchers(sniMatchers);
-        params.setUseCipherSuitesOrder(preferLocalCipherSuites);
-        params.setApplicationProtocols(applicationProtocols);
-
-        return params;
+        return sslConfig.getSSLParameters();
     }
 
-    /**
-     * Applies SSLParameters to newly accepted connections.
-     */
     @Override
     public synchronized void setSSLParameters(SSLParameters params) {
-        super.setSSLParameters(params);
-
-        // the super implementation does not handle the following parameters
-        identificationProtocol = params.getEndpointIdentificationAlgorithm();
-        algorithmConstraints = params.getAlgorithmConstraints();
-        preferLocalCipherSuites = params.getUseCipherSuitesOrder();
-        Collection<SNIMatcher> matchers = params.getSNIMatchers();
-        if (matchers != null) {
-            sniMatchers = params.getSNIMatchers();
-        }
-        applicationProtocols = params.getApplicationProtocols();
+        sslConfig.setSSLParameters(params);
     }
 
-    /**
-     * Accept a new SSL connection.  This server identifies itself with
-     * information provided in the authentication context which was
-     * presented during construction.
-     */
     @Override
     public Socket accept() throws IOException {
-        SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode,
-            enabledCipherSuites, clientAuthType, enableSessionCreation,
-            enabledProtocols, identificationProtocol, algorithmConstraints,
-            sniMatchers, preferLocalCipherSuites, applicationProtocols);
+        SSLSocketImpl s = new SSLSocketImpl(sslContext, sslConfig);
 
         implAccept(s);
         s.doneConnect();
         return s;
     }
 
-    /**
-     * Provides a brief description of this SSL socket.
-     */
     @Override
     public String toString() {
         return "[SSL: "+ super.toString() + "]";
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,23 +23,21 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
-import java.util.Vector;
 import java.util.Locale;
-
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSessionContext;
-
 import sun.security.util.Cache;
 
 
 final class SSLSessionContextImpl implements SSLSessionContext {
-    private Cache<SessionId, SSLSessionImpl> sessionCache;
+    private final Cache<SessionId, SSLSessionImpl> sessionCache;
                                         // session cache, session id as key
-    private Cache<String, SSLSessionImpl> sessionHostPortCache;
+    private final Cache<String, SSLSessionImpl> sessionHostPortCache;
                                         // session cache, "host:port" as key
     private int cacheLimit;             // the max cache size
     private int timeout;                // timeout in seconds
@@ -137,7 +135,6 @@
         return cacheLimit;
     }
 
-
     // package-private method, used ONLY by ServerHandshaker
     SSLSessionImpl get(byte[] id) {
         return (SSLSessionImpl)getSession(id);
@@ -161,7 +158,7 @@
         return null;
     }
 
-    private String getKey(String hostname, int port) {
+    private static String getKey(String hostname, int port) {
         return (hostname + ":" +
             String.valueOf(port)).toLowerCase(Locale.ENGLISH);
     }
@@ -192,29 +189,30 @@
         if (s != null) {
             sessionCache.remove(key);
             sessionHostPortCache.remove(
-                        getKey(s.getPeerHost(), s.getPeerPort()));
+                    getKey(s.getPeerHost(), s.getPeerPort()));
         }
     }
 
-    private int getDefaultCacheLimit() {
-        int cacheLimit = 0;
+    private static int getDefaultCacheLimit() {
+        int defaultCacheLimit = 0;
         try {
-        String s = java.security.AccessController.doPrivileged(
-                new java.security.PrivilegedAction<String>() {
-                @Override
-                public String run() {
-                    return System.getProperty(
-                        "javax.net.ssl.sessionCacheSize");
-                }
-            });
-            cacheLimit = (s != null) ? Integer.valueOf(s).intValue() : 0;
+            String s = java.security.AccessController.doPrivileged(
+                    new java.security.PrivilegedAction<String>() {
+                    @Override
+                    public String run() {
+                        return System.getProperty(
+                            "javax.net.ssl.sessionCacheSize");
+                    }
+                });
+                defaultCacheLimit = (s != null) ? Integer.parseInt(s) : 0;
         } catch (Exception e) {
+            // swallow the exception
         }
 
-        return (cacheLimit > 0) ? cacheLimit : 0;
+        return (defaultCacheLimit > 0) ? defaultCacheLimit : 0;
     }
 
-    boolean isTimedout(SSLSession sess) {
+    private boolean isTimedout(SSLSession sess) {
         if (timeout == 0) {
             return false;
         }
@@ -228,27 +226,26 @@
         return false;
     }
 
-    final class SessionCacheVisitor
+    private final class SessionCacheVisitor
             implements Cache.CacheVisitor<SessionId, SSLSessionImpl> {
-        Vector<byte[]> ids = null;
+        ArrayList<byte[]> ids = null;
 
         // public void visit(java.util.Map<K,V> map) {}
         @Override
         public void visit(java.util.Map<SessionId, SSLSessionImpl> map) {
-            ids = new Vector<>(map.size());
+            ids = new ArrayList<>(map.size());
 
             for (SessionId key : map.keySet()) {
                 SSLSessionImpl value = map.get(key);
                 if (!isTimedout(value)) {
-                    ids.addElement(key.getId());
+                    ids.add(key.getId());
                 }
             }
         }
 
-        public Enumeration<byte[]> getSessionIds() {
-            return  ids != null ? ids.elements() :
-                                  new Vector<byte[]>().elements();
+        Enumeration<byte[]> getSessionIds() {
+            return  ids != null ? Collections.enumeration(ids) :
+                                  Collections.emptyEnumeration();
         }
     }
-
 }
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,36 +22,31 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
-
 package sun.security.ssl;
 
-import java.net.*;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.Vector;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.ArrayList;
-
+import java.math.BigInteger;
+import java.net.InetAddress;
 import java.security.Principal;
 import java.security.PrivateKey;
-import java.security.SecureRandom;
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
-import java.security.cert.CertificateEncodingException;
-
+import java.util.ArrayList;
+import java.util.Queue;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import javax.crypto.SecretKey;
-
-import javax.net.ssl.SSLSessionContext;
-import javax.net.ssl.SSLSessionBindingListener;
-import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLPermission;
-import javax.net.ssl.ExtendedSSLSession;
-import javax.net.ssl.SNIServerName;
-
-import static sun.security.ssl.CipherSuite.KeyExchange.*;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
 
 /**
  * Implements the SSL session interface, and exposes the session context
@@ -78,9 +73,6 @@
      */
     static final SSLSessionImpl         nullSession = new SSLSessionImpl();
 
-    // compression methods
-    private static final byte           compression_null = 0;
-
     /*
      * The state of a single session, as described in section 7.1
      * of the SSLv3 spec.
@@ -88,35 +80,37 @@
     private final ProtocolVersion       protocolVersion;
     private final SessionId             sessionId;
     private X509Certificate[]   peerCerts;
-    private byte                compressionMethod;
     private CipherSuite         cipherSuite;
     private SecretKey           masterSecret;
-    private final boolean       useExtendedMasterSecret;
+    final boolean               useExtendedMasterSecret;
 
     /*
      * Information not part of the SSLv3 protocol spec, but used
      * to support session management policies.
      */
-    private final long          creationTime = System.currentTimeMillis();
+    private final long          creationTime;
     private long                lastUsedTime = 0;
     private final String        host;
     private final int           port;
     private SSLSessionContextImpl       context;
-    private int                 sessionCount;
     private boolean             invalidated;
     private X509Certificate[]   localCerts;
     private PrivateKey          localPrivateKey;
-    private String[]            localSupportedSignAlgs;
-    private String[]            peerSupportedSignAlgs;
-    private List<SNIServerName>    requestedServerNames;
+    private final String[]      localSupportedSignAlgs;
+    private String[]            peerSupportedSignAlgs;      // for certificate
+    private boolean             useDefaultPeerSignAlgs = false;
     private List<byte[]>        statusResponses;
+    private SecretKey           resumptionMasterSecret;
+    private SecretKey           preSharedKey;
+    private byte[]              pskIdentity;
+    private final long          ticketCreationTime = System.currentTimeMillis();
+    private int                 ticketAgeAdd;
 
-    private int                 negotiatedMaxFragLen;
+    private int                 negotiatedMaxFragLen = -1;
     private int                 maximumPacketSize;
 
-    // Principals for non-certificate based cipher suites
-    private Principal peerPrincipal;
-    private Principal localPrincipal;
+    private final Queue<SSLSessionImpl> childSessions =
+                                        new ConcurrentLinkedQueue<>();
 
     /*
      * Is the session currently re-established with a session-resumption
@@ -127,19 +121,16 @@
     private boolean isSessionResumption = false;
 
     /*
-     * We count session creations, eventually for statistical data but
-     * also since counters make shorter debugging IDs than the big ones
-     * we use in the protocol for uniqueness-over-time.
-     */
-    private static volatile int counter;
-
-    /*
      * Use of session caches is globally enabled/disabled.
      */
     private static boolean      defaultRejoinable = true;
 
-    /* Class and subclass dynamic debugging support */
-    private static final Debug debug = Debug.getInstance("ssl");
+    // server name indication
+    final SNIServerName         serverNameIndication;
+    private final List<SNIServerName>    requestedServerNames;
+
+    // Counter used to create unique nonces in NewSessionTicket
+    private BigInteger ticketNonceCounter = BigInteger.ONE;
 
     /*
      * Create a new non-rejoinable session, using the default (null)
@@ -148,8 +139,16 @@
      * first opened and before handshaking begins.
      */
     private SSLSessionImpl() {
-        this(ProtocolVersion.NONE, CipherSuite.C_NULL, null,
-            new SessionId(false, null), null, -1, false);
+        this.protocolVersion = ProtocolVersion.NONE;
+        this.cipherSuite = CipherSuite.C_NULL;
+        this.sessionId = new SessionId(false, null);
+        this.host = null;
+        this.port = -1;
+        this.localSupportedSignAlgs = new String[0];
+        this.serverNameIndication = null;
+        this.requestedServerNames = Collections.<SNIServerName>emptyList();
+        this.useExtendedMasterSecret = false;
+        this.creationTime = System.currentTimeMillis();
     }
 
     /*
@@ -157,48 +156,81 @@
      * be rejoinable if session caching is enabled; the constructor
      * is intended mostly for use by serves.
      */
-    SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,
-            Collection<SignatureAndHashAlgorithm> algorithms,
-            SecureRandom generator, String host, int port,
-            boolean useExtendedMasterSecret) {
-        this(protocolVersion, cipherSuite, algorithms,
-             new SessionId(defaultRejoinable, generator), host, port,
-             useExtendedMasterSecret);
+    SSLSessionImpl(HandshakeContext hc, CipherSuite cipherSuite) {
+        this(hc, cipherSuite,
+            new SessionId(defaultRejoinable, hc.sslContext.getSecureRandom()));
     }
 
     /*
      * Record a new session, using a given cipher spec and session ID.
      */
-    SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,
-            Collection<SignatureAndHashAlgorithm> algorithms,
-            SessionId id, String host, int port,
-            boolean useExtendedMasterSecret) {
-        this.protocolVersion = protocolVersion;
-        sessionId = id;
-        peerCerts = null;
-        compressionMethod = compression_null;
+    SSLSessionImpl(HandshakeContext hc, CipherSuite cipherSuite, SessionId id) {
+        this(hc, cipherSuite, id, System.currentTimeMillis());
+    }
+
+    /*
+     * Record a new session, using a given cipher spec, session ID,
+     * and creation time
+     */
+    SSLSessionImpl(HandshakeContext hc,
+            CipherSuite cipherSuite, SessionId id, long creationTime) {
+        this.protocolVersion = hc.negotiatedProtocol;
         this.cipherSuite = cipherSuite;
-        masterSecret = null;
-        this.host = host;
-        this.port = port;
-        sessionCount = ++counter;
-        localSupportedSignAlgs =
-            SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
-        negotiatedMaxFragLen = -1;
-        statusResponses = null;
-        this.useExtendedMasterSecret = useExtendedMasterSecret;
+        this.sessionId = id;
+        this.host = hc.conContext.transport.getPeerHost();
+        this.port = hc.conContext.transport.getPeerPort();
+        this.localSupportedSignAlgs =
+                SignatureScheme.getAlgorithmNames(hc.localSupportedSignAlgs);
+        this.serverNameIndication = hc.negotiatedServerName;
+        this.requestedServerNames = Collections.<SNIServerName>unmodifiableList(
+                hc.getRequestedServerNames());
+        if (hc.sslConfig.isClientMode) {
+            this.useExtendedMasterSecret =
+                (hc.handshakeExtensions.get(
+                        SSLExtension.CH_EXTENDED_MASTER_SECRET) != null) &&
+                (hc.handshakeExtensions.get(
+                        SSLExtension.SH_EXTENDED_MASTER_SECRET) != null);
+        } else {
+            this.useExtendedMasterSecret =
+                (hc.handshakeExtensions.get(
+                        SSLExtension.CH_EXTENDED_MASTER_SECRET) != null) &&
+                (!hc.negotiatedProtocol.useTLS13PlusSpec());
+        }
+        this.creationTime = creationTime;
 
-        if (debug != null && Debug.isOn("session")) {
-            System.out.println("%% Initialized:  " + this);
+        if (SSLLogger.isOn && SSLLogger.isOn("session")) {
+             SSLLogger.finest("Session initialized:  " + this);
         }
     }
 
     void setMasterSecret(SecretKey secret) {
-        if (masterSecret == null) {
-            masterSecret = secret;
-        } else {
-            throw new RuntimeException("setMasterSecret() error");
-        }
+        masterSecret = secret;
+    }
+
+    void setResumptionMasterSecret(SecretKey secret) {
+        resumptionMasterSecret = secret;
+    }
+
+    void setPreSharedKey(SecretKey key) {
+        preSharedKey = key;
+    }
+
+    void addChild(SSLSessionImpl session) {
+        childSessions.add(session);
+    }
+
+    void setTicketAgeAdd(int ticketAgeAdd) {
+        this.ticketAgeAdd = ticketAgeAdd;
+    }
+
+    void setPskIdentity(byte[] pskIdentity) {
+        this.pskIdentity = pskIdentity;
+    }
+
+    BigInteger incrTicketNonceCounter() {
+        BigInteger result = ticketNonceCounter;
+        ticketNonceCounter = ticketNonceCounter.add(BigInteger.valueOf(1));
+        return result;
     }
 
     /**
@@ -208,8 +240,39 @@
         return masterSecret;
     }
 
-    boolean getUseExtendedMasterSecret() {
-        return useExtendedMasterSecret;
+    Optional<SecretKey> getResumptionMasterSecret() {
+        return Optional.ofNullable(resumptionMasterSecret);
+    }
+
+    synchronized Optional<SecretKey> getPreSharedKey() {
+        return Optional.ofNullable(preSharedKey);
+    }
+
+    synchronized Optional<SecretKey> consumePreSharedKey() {
+        Optional<SecretKey> result = Optional.ofNullable(preSharedKey);
+        preSharedKey = null;
+        return result;
+    }
+
+    int getTicketAgeAdd() {
+        return ticketAgeAdd;
+    }
+
+    /*
+     * Get the PSK identity. Take care not to use it in multiple connections.
+     */
+    synchronized Optional<byte[]> getPskIdentity() {
+        return Optional.ofNullable(pskIdentity);
+    }
+
+    /* PSK identities created from new_session_ticket messages should only
+     * be used once. This method will return the identity and then clear it
+     * so it cannot be used again.
+     */
+    synchronized Optional<byte[]> consumePskIdentity() {
+        Optional<byte[]> result = Optional.ofNullable(pskIdentity);
+        pskIdentity = null;
+        return result;
     }
 
     void setPeerCertificates(X509Certificate[] peer) {
@@ -227,13 +290,35 @@
     }
 
     void setPeerSupportedSignatureAlgorithms(
-            Collection<SignatureAndHashAlgorithm> algorithms) {
+            Collection<SignatureScheme> signatureSchemes) {
         peerSupportedSignAlgs =
-            SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
+            SignatureScheme.getAlgorithmNames(signatureSchemes);
     }
 
-    void setRequestedServerNames(List<SNIServerName> requestedServerNames) {
-        this.requestedServerNames = new ArrayList<>(requestedServerNames);
+    // TLS 1.2 only
+    //
+    // Per RFC 5246, If the client supports only the default hash
+    // and signature algorithms, it MAY omit the
+    // signature_algorithms extension.  If the client does not
+    // support the default algorithms, or supports other hash
+    // and signature algorithms (and it is willing to use them
+    // for verifying messages sent by the server, i.e., server
+    // certificates and server key exchange), it MUST send the
+    // signature_algorithms extension, listing the algorithms it
+    // is willing to accept.
+    void setUseDefaultPeerSignAlgs() {
+        useDefaultPeerSignAlgs = true;
+        peerSupportedSignAlgs = new String[] {
+            "SHA1withRSA", "SHA1withDSA", "SHA1withECDSA"};
+    }
+
+    // Returns the connection session.
+    SSLSessionImpl finish() {
+        if (useDefaultPeerSignAlgs) {
+            this.peerSupportedSignAlgs = new String[0];
+        }
+
+        return this;
     }
 
     /**
@@ -250,22 +335,6 @@
     }
 
     /**
-     * Set the peer principal.
-     */
-    void setPeerPrincipal(Principal principal) {
-        if (peerPrincipal == null) {
-            peerPrincipal = principal;
-        }
-    }
-
-    /**
-     * Set the local principal.
-     */
-    void setLocalPrincipal(Principal principal) {
-        localPrincipal = principal;
-    }
-
-    /**
      * Returns true iff this session may be resumed ... sessions are
      * usually resumable.  Security policies may suggest otherwise,
      * for example sessions that haven't been used for a while (say,
@@ -286,7 +355,7 @@
      * Check if the authentication used when establishing this session
      * is still valid. Returns true if no authentication was used
      */
-    boolean isLocalAuthenticationValid() {
+    private boolean isLocalAuthenticationValid() {
         if (localPrivateKey != null) {
             try {
                 // if the private key is no longer valid, getAlgorithm()
@@ -298,6 +367,7 @@
                 return false;
             }
         }
+
         return true;
     }
 
@@ -354,8 +424,8 @@
     void setSuite(CipherSuite suite) {
        cipherSuite = suite;
 
-       if (debug != null && Debug.isOn("session")) {
-           System.out.println("%% Negotiating:  " + this);
+        if (SSLLogger.isOn && SSLLogger.isOn("session")) {
+             SSLLogger.finest("Negotiating session:  " + this);
        }
     }
 
@@ -396,13 +466,6 @@
     }
 
     /**
-     * Returns the compression technique used in this session
-     */
-    byte getCompression() {
-        return compressionMethod;
-    }
-
-    /**
      * Returns the hashcode for this session
      */
     @Override
@@ -410,7 +473,6 @@
         return sessionId.hashCode();
     }
 
-
     /**
      * Returns true if sessions have same ids, false otherwise.
      */
@@ -435,8 +497,8 @@
      * Return the cert chain presented by the peer in the
      * java.security.cert format.
      * Note: This method can be used only when using certificate-based
-     * cipher suites; using it with non-certificate-based cipher suites,
-     * such as Kerberos, will throw an SSLPeerUnverifiedException.
+     * cipher suites; using it with non-certificate-based cipher suites
+     * will throw an SSLPeerUnverifiedException.
      *
      * @return array of peer X.509 certs, with the peer's own cert
      *  first in the chain, and with the "root" CA last.
@@ -449,10 +511,6 @@
         // change record of peer identity even by accident, much
         // less do it intentionally.
         //
-        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
-            throw new SSLPeerUnverifiedException("no certificates expected"
-                        + " for " + cipherSuite.keyExchange + " cipher suites");
-        }
         if (peerCerts == null) {
             throw new SSLPeerUnverifiedException("peer not authenticated");
         }
@@ -485,8 +543,8 @@
      * Return the cert chain presented by the peer in the
      * javax.security.cert format.
      * Note: This method can be used only when using certificate-based
-     * cipher suites; using it with non-certificate-based cipher suites,
-     * such as Kerberos, will throw an SSLPeerUnverifiedException.
+     * cipher suites; using it with non-certificate-based cipher suites
+     * will throw an SSLPeerUnverifiedException.
      *
      * @return array of peer X.509 certs, with the peer's own cert
      *  first in the chain, and with the "root" CA last.
@@ -504,10 +562,6 @@
         // change record of peer identity even by accident, much
         // less do it intentionally.
         //
-        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
-            throw new SSLPeerUnverifiedException("no certificates expected"
-                        + " for " + cipherSuite.keyExchange + " cipher suites");
-        }
         if (peerCerts == null) {
             throw new SSLPeerUnverifiedException("peer not authenticated");
         }
@@ -531,8 +585,8 @@
     /**
      * Return the cert chain presented by the peer.
      * Note: This method can be used only when using certificate-based
-     * cipher suites; using it with non-certificate-based cipher suites,
-     * such as Kerberos, will throw an SSLPeerUnverifiedException.
+     * cipher suites; using it with non-certificate-based cipher suites
+     * will throw an SSLPeerUnverifiedException.
      *
      * @return array of peer X.509 certs, with the peer's own cert
      *  first in the chain, and with the "root" CA last.
@@ -544,10 +598,6 @@
          * change record of peer identity even by accident, much
          * less do it intentionally.
          */
-        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
-            throw new SSLPeerUnverifiedException("no certificates expected"
-                        + " for " + cipherSuite.keyExchange + " cipher suites");
-        }
         if (peerCerts != null) {
             return peerCerts.clone();
         } else {
@@ -584,8 +634,7 @@
      * defining the session.
      *
      * @return the peer's principal. Returns an X500Principal of the
-     * end-entity certificate for X509-based cipher suites, and
-     * Principal for Kerberos cipher suites, etc.
+     * end-entity certificate for X509-based cipher suites.
      *
      * @throws SSLPeerUnverifiedException if the peer's identity has not
      *          been verified
@@ -594,13 +643,6 @@
     public Principal getPeerPrincipal()
                 throws SSLPeerUnverifiedException
     {
-        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
-            if (peerPrincipal == null) {
-                throw new SSLPeerUnverifiedException("peer not authenticated");
-            } else {
-                return peerPrincipal;
-            }
-        }
         if (peerCerts == null) {
             throw new SSLPeerUnverifiedException("peer not authenticated");
         }
@@ -611,18 +653,20 @@
      * Returns the principal that was sent to the peer during handshaking.
      *
      * @return the principal sent to the peer. Returns an X500Principal
-     * of the end-entity certificate for X509-based cipher suites, and
-     * Principal for Kerberos cipher suites, etc. If no principal was
-     * sent, then null is returned.
+     * of the end-entity certificate for X509-based cipher suites.
+     * If no principal was sent, then null is returned.
      */
     @Override
     public Principal getLocalPrincipal() {
+        return ((localCerts == null && localCerts.length != 0) ? null :
+                localCerts[0].getSubjectX500Principal());
+    }
 
-        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
-                return (localPrincipal == null ? null : localPrincipal);
-        }
-        return (localCerts == null ? null :
-                localCerts[0].getSubjectX500Principal());
+    /*
+     * Return the time the ticket for this session was created.
+     */
+    public long getTicketCreationTime() {
+        return ticketCreationTime;
     }
 
     /**
@@ -695,14 +739,21 @@
         if (this == nullSession) {
             return;
         }
-        invalidated = true;
-        if (debug != null && Debug.isOn("session")) {
-            System.out.println("%% Invalidated:  " + this);
-        }
+
         if (context != null) {
             context.remove(sessionId);
             context = null;
         }
+        if (invalidated) {
+            return;
+        }
+        invalidated = true;
+        if (SSLLogger.isOn && SSLLogger.isOn("session")) {
+             SSLLogger.finest("Invalidated session:  " + this);
+        }
+        for (SSLSessionImpl child : childSessions) {
+            child.invalidate();
+        }
     }
 
     /*
@@ -710,7 +761,8 @@
      * key and the calling security context. This is important since
      * sessions can be shared across different protection domains.
      */
-    private Hashtable<SecureKey, Object> table = new Hashtable<>();
+    private final ConcurrentHashMap<SecureKey, Object> boundValues =
+            new ConcurrentHashMap<>();
 
     /**
      * Assigns a session value.  Session change events are given if
@@ -723,7 +775,7 @@
         }
 
         SecureKey secureKey = new SecureKey(key);
-        Object oldValue = table.put(secureKey, value);
+        Object oldValue = boundValues.put(secureKey, value);
 
         if (oldValue instanceof SSLSessionBindingListener) {
             SSLSessionBindingEvent e;
@@ -739,7 +791,6 @@
         }
     }
 
-
     /**
      * Returns the specified session value.
      */
@@ -750,7 +801,7 @@
         }
 
         SecureKey secureKey = new SecureKey(key);
-        return table.get(secureKey);
+        return boundValues.get(secureKey);
     }
 
 
@@ -765,7 +816,7 @@
         }
 
         SecureKey secureKey = new SecureKey(key);
-        Object value = table.remove(secureKey);
+        Object value = boundValues.remove(secureKey);
 
         if (value instanceof SSLSessionBindingListener) {
             SSLSessionBindingEvent e;
@@ -781,22 +832,17 @@
      */
     @Override
     public String[] getValueNames() {
-        Enumeration<SecureKey> e;
-        Vector<Object> v = new Vector<>();
-        SecureKey key;
+        ArrayList<Object> v = new ArrayList<>();
         Object securityCtx = SecureKey.getCurrentSecurityContext();
-
-        for (e = table.keys(); e.hasMoreElements(); ) {
-            key = e.nextElement();
-
+        for (Enumeration<SecureKey> e = boundValues.keys();
+                e.hasMoreElements(); ) {
+            SecureKey key = e.nextElement();
             if (securityCtx.equals(key.getSecurityContext())) {
-                v.addElement(key.getAppKey());
+                v.add(key.getAppKey());
             }
         }
-        String[] names = new String[v.size()];
-        v.copyInto(names);
 
-        return names;
+        return v.toArray(new String[0]);
     }
 
     /**
@@ -813,7 +859,8 @@
      * to "true".
      */
     private boolean acceptLargeFragments =
-        Debug.getBooleanProperty("jsse.SSLEngine.acceptLargeFragments", false);
+            Utilities.getBooleanProperty(
+                    "jsse.SSLEngine.acceptLargeFragments", false);
 
     /**
      * Expand the buffer size of both SSL/TLS network packet and
@@ -835,7 +882,7 @@
         if (negotiatedMaxFragLen > 0) {
             packetSize = cipherSuite.calculatePacketSize(
                     negotiatedMaxFragLen, protocolVersion,
-                    protocolVersion.isDTLSProtocol());
+                    protocolVersion.isDTLS);
         }
 
         if (maximumPacketSize > 0) {
@@ -847,7 +894,7 @@
            return packetSize;
         }
 
-        if (protocolVersion.isDTLSProtocol()) {
+        if (protocolVersion.isDTLS) {
             return DTLSRecord.maxRecordSize;
         } else {
             return acceptLargeFragments ?
@@ -867,7 +914,7 @@
         if (maximumPacketSize > 0) {
             fragmentSize = cipherSuite.calculateFragSize(
                     maximumPacketSize, protocolVersion,
-                    protocolVersion.isDTLSProtocol());
+                    protocolVersion.isDTLS);
         }
 
         if (negotiatedMaxFragLen > 0) {
@@ -879,7 +926,7 @@
             return fragmentSize;
         }
 
-        if (protocolVersion.isDTLSProtocol()) {
+        if (protocolVersion.isDTLS) {
             return Record.maxDataSize;
         } else {
             int maxPacketSize = acceptLargeFragments ?
@@ -953,33 +1000,24 @@
      */
     @Override
     public List<SNIServerName> getRequestedServerNames() {
-        if (requestedServerNames != null && !requestedServerNames.isEmpty()) {
-            return Collections.<SNIServerName>unmodifiableList(
-                                                requestedServerNames);
-        }
-
-        return Collections.<SNIServerName>emptyList();
+        return requestedServerNames;
     }
 
     /** Returns a string representation of this SSL session */
     @Override
     public String toString() {
-        return "[Session-" + sessionCount
-            + ", " + getCipherSuite()
-            + "]";
+        return "Session(" + creationTime + "|" + getCipherSuite() + ")";
     }
-
 }
 
-
 /**
  * This "struct" class serves as a Hash Key that combines an
  * application-specific key and a security context.
  */
 class SecureKey {
-    private static Object       nullObject = new Object();
-    private Object        appKey;
-    private Object      securityCtx;
+    private static final Object     nullObject = new Object();
+    private final Object            appKey;
+    private final Object            securityCtx;
 
     static Object getCurrentSecurityContext() {
         SecurityManager sm = System.getSecurityManager();
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,7 +43,7 @@
  */
 public final class SSLSocketFactoryImpl extends SSLSocketFactory {
 
-    private SSLContextImpl context;
+    private final SSLContextImpl context;
 
     /**
      * Constructor used to instantiate the default factory. This method is
@@ -180,7 +180,7 @@
      */
     @Override
     public String[] getDefaultCipherSuites() {
-        return context.getDefaultCipherSuiteList(false).toStringArray();
+        return CipherSuite.namesOf(context.getDefaultCipherSuites(false));
     }
 
     /**
@@ -193,6 +193,6 @@
      */
     @Override
     public String[] getSupportedCipherSuites() {
-        return context.getSupportedCipherSuiteList().toStringArray();
+        return CipherSuite.namesOf(context.getSupportedCipherSuites());
     }
 }
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,35 +23,41 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.net.*;
-import java.security.GeneralSecurityException;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.PrivilegedAction;
-import java.security.AlgorithmConstraints;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.ReentrantLock;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.List;
 import java.util.function.BiFunction;
-
-import javax.crypto.BadPaddingException;
-import javax.net.ssl.*;
-
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
 import jdk.internal.misc.JavaNetInetAddressAccess;
 import jdk.internal.misc.SharedSecrets;
 
 /**
- * Implementation of an SSL socket.  This is a normal connection type
- * socket, implementing SSL over some lower level socket, such as TCP.
- * Because it is layered over some lower level socket, it MUST override
- * all default socket methods.
- *
- * <P> This API offers a non-traditional option for establishing SSL
+ * Implementation of an SSL socket.
+ * <P>
+ * This is a normal connection type socket, implementing SSL over some lower
+ * level socket, such as TCP.  Because it is layered over some lower level
+ * socket, it MUST override all default socket methods.
+ * <P>
+ * This API offers a non-traditional option for establishing SSL
  * connections.  You may first establish the connection directly, then pass
  * that connection to the SSL socket constructor with a flag saying which
  * role should be taken in the handshake protocol.  (The two ends of the
@@ -64,328 +70,19 @@
  *
  * @author David Brownell
  */
-public final class SSLSocketImpl extends BaseSSLSocketImpl {
-
-    /*
-     * ERROR HANDLING GUIDELINES
-     * (which exceptions to throw and catch and which not to throw and catch)
-     *
-     * . if there is an IOException (SocketException) when accessing the
-     *   underlying Socket, pass it through
-     *
-     * . do not throw IOExceptions, throw SSLExceptions (or a subclass)
-     *
-     * . for internal errors (things that indicate a bug in JSSE or a
-     *   grossly misconfigured J2RE), throw either an SSLException or
-     *   a RuntimeException at your convenience.
-     *
-     * . handshaking code (Handshaker or HandshakeMessage) should generally
-     *   pass through exceptions, but can handle them if they know what to
-     *   do.
-     *
-     * . exception chaining should be used for all new code. If you happen
-     *   to touch old code that does not use chaining, you should change it.
-     *
-     * . there is a top level exception handler that sits at all entry
-     *   points from application code to SSLSocket read/write code. It
-     *   makes sure that all errors are handled (see handleException()).
-     *
-     * . JSSE internal code should generally not call close(), call
-     *   closeInternal().
-     */
+public final class SSLSocketImpl
+        extends BaseSSLSocketImpl implements SSLTransport {
 
-    /*
-     * There's a state machine associated with each connection, which
-     * among other roles serves to negotiate session changes.
-     *
-     * - START with constructor, until the TCP connection's around.
-     * - HANDSHAKE picks session parameters before allowing traffic.
-     *          There are many substates due to sequencing requirements
-     *          for handshake messages.
-     * - DATA may be transmitted.
-     * - RENEGOTIATE state allows concurrent data and handshaking
-     *          traffic ("same" substates as HANDSHAKE), and terminates
-     *          in selection of new session (and connection) parameters
-     * - ERROR state immediately precedes abortive disconnect.
-     * - SENT_CLOSE sent a close_notify to the peer. For layered,
-     *          non-autoclose socket, must now read close_notify
-     *          from peer before closing the connection. For nonlayered or
-     *          non-autoclose socket, close connection and go onto
-     *          cs_CLOSED state.
-     * - CLOSED after sending close_notify alert, & socket is closed.
-     *          SSL connection objects are not reused.
-     * - APP_CLOSED once the application calls close(). Then it behaves like
-     *          a closed socket, e.g.. getInputStream() throws an Exception.
-     *
-     * State affects what SSL record types may legally be sent:
-     *
-     * - Handshake ... only in HANDSHAKE and RENEGOTIATE states
-     * - App Data ... only in DATA and RENEGOTIATE states
-     * - Alert ... in HANDSHAKE, DATA, RENEGOTIATE
-     *
-     * Re what may be received:  same as what may be sent, except that
-     * HandshakeRequest handshaking messages can come from servers even
-     * in the application data state, to request entry to RENEGOTIATE.
-     *
-     * The state machine within HANDSHAKE and RENEGOTIATE states controls
-     * the pending session, not the connection state, until the change
-     * cipher spec and "Finished" handshake messages are processed and
-     * make the "new" session become the current one.
-     *
-     * NOTE: details of the SMs always need to be nailed down better.
-     * The text above illustrates the core ideas.
-     *
-     *                +---->-------+------>--------->-------+
-     *                |            |                        |
-     *     <-----<    ^            ^  <-----<               v
-     *START>----->HANDSHAKE>----->DATA>----->RENEGOTIATE  SENT_CLOSE
-     *                v            v               v        |   |
-     *                |            |               |        |   v
-     *                +------------+---------------+        v ERROR
-     *                |                                     |   |
-     *                v                                     |   |
-     *               ERROR>------>----->CLOSED<--------<----+-- +
-     *                                     |
-     *                                     v
-     *                                 APP_CLOSED
-     *
-     * ALSO, note that the purpose of handshaking (renegotiation is
-     * included) is to assign a different, and perhaps new, session to
-     * the connection.  The SSLv3 spec is a bit confusing on that new
-     * protocol feature.
-     */
-    private static final int    cs_START = 0;
-    private static final int    cs_HANDSHAKE = 1;
-    private static final int    cs_DATA = 2;
-    private static final int    cs_RENEGOTIATE = 3;
-    private static final int    cs_ERROR = 4;
-    private static final int    cs_SENT_CLOSE = 5;
-    private static final int    cs_CLOSED = 6;
-    private static final int    cs_APP_CLOSED = 7;
-
-    /*
-     * Drives the protocol state machine.
-     */
-    private volatile int        connectionState;
-
-    /*
-     * Flag indicating if the next record we receive MUST be a Finished
-     * message. Temporarily set during the handshake to ensure that
-     * a change cipher spec message is followed by a finished message.
-     */
-    private boolean             expectingFinished;
-
-    /*
-     * For improved diagnostics, we detail connection closure
-     * If the socket is closed (connectionState >= cs_ERROR),
-     * closeReason != null indicates if the socket was closed
-     * because of an error or because or normal shutdown.
-     */
-    private SSLException        closeReason;
-
-    /*
-     * Per-connection private state that doesn't change when the
-     * session is changed.
-     */
-    private ClientAuthType      doClientAuth =
-                                        ClientAuthType.CLIENT_AUTH_NONE;
-    private boolean             roleIsServer;
-    private boolean             enableSessionCreation = true;
-    private String              host;
-    private boolean             autoClose = true;
-    private AccessControlContext acc;
-
-    // The cipher suites enabled for use on this connection.
-    private CipherSuiteList     enabledCipherSuites;
-
-    // The endpoint identification protocol
-    private String              identificationProtocol = null;
-
-    // The cryptographic algorithm constraints
-    private AlgorithmConstraints    algorithmConstraints = null;
-
-    // The server name indication and matchers
-    List<SNIServerName>         serverNames =
-                                    Collections.<SNIServerName>emptyList();
-    Collection<SNIMatcher>      sniMatchers =
-                                    Collections.<SNIMatcher>emptyList();
-
-    // Is the serverNames set to empty with SSLParameters.setServerNames()?
-    private boolean             noSniExtension = false;
-
-    // Is the sniMatchers set to empty with SSLParameters.setSNIMatchers()?
-    private boolean             noSniMatcher = false;
-
-    // Configured application protocol values
-    String[] applicationProtocols = new String[0];
-
-    // Negotiated application protocol value.
-    //
-    // The value under negotiation will be obtained from handshaker.
-    String applicationProtocol = null;
+    final SSLContextImpl            sslContext;
+    final TransportContext          conContext;
 
-    // Callback function that selects the application protocol value during
-    // the SSL/TLS handshake.
-    BiFunction<SSLSocket, List<String>, String> applicationProtocolSelector;
-
-    /*
-     * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
-     * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES.
-     * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
-     *
-     * There are several locks here.
-     *
-     * The primary lock is the per-instance lock used by
-     * synchronized(this) and the synchronized methods.  It controls all
-     * access to things such as the connection state and variables which
-     * affect handshaking.  If we are inside a synchronized method, we
-     * can access the state directly, otherwise, we must use the
-     * synchronized equivalents.
-     *
-     * The handshakeLock is used to ensure that only one thread performs
-     * the *complete initial* handshake.  If someone is handshaking, any
-     * stray application or startHandshake() requests who find the
-     * connection state is cs_HANDSHAKE will stall on handshakeLock
-     * until handshaking is done.  Once the handshake is done, we either
-     * succeeded or failed, but we can never go back to the cs_HANDSHAKE
-     * or cs_START state again.
-     *
-     * Note that the read/write() calls here in SSLSocketImpl are not
-     * obviously synchronized.  In fact, it's very nonintuitive, and
-     * requires careful examination of code paths.  Grab some coffee,
-     * and be careful with any code changes.
-     *
-     * There can be only three threads active at a time in the I/O
-     * subsection of this class.
-     *    1.  startHandshake
-     *    2.  AppInputStream
-     *    3.  AppOutputStream
-     * One thread could call startHandshake().
-     * AppInputStream/AppOutputStream read() and write() calls are each
-     * synchronized on 'this' in their respective classes, so only one
-     * app. thread will be doing a SSLSocketImpl.read() or .write()'s at
-     * a time.
-     *
-     * If handshaking is required (state cs_HANDSHAKE), and
-     * getConnectionState() for some/all threads returns cs_HANDSHAKE,
-     * only one can grab the handshakeLock, and the rest will stall
-     * either on getConnectionState(), or on the handshakeLock if they
-     * happen to successfully race through the getConnectionState().
-     *
-     * If a writer is doing the initial handshaking, it must create a
-     * temporary reader to read the responses from the other side.  As a
-     * side-effect, the writer's reader will have priority over any
-     * other reader.  However, the writer's reader is not allowed to
-     * consume any application data.  When handshakeLock is finally
-     * released, we either have a cs_DATA connection, or a
-     * cs_CLOSED/cs_ERROR socket.
-     *
-     * The writeLock is held while writing on a socket connection and
-     * also to protect the MAC and cipher for their direction.  The
-     * writeLock is package private for Handshaker which holds it while
-     * writing the ChangeCipherSpec message.
-     *
-     * To avoid the problem of a thread trying to change operational
-     * modes on a socket while handshaking is going on, we synchronize
-     * on 'this'.  If handshaking has not started yet, we tell the
-     * handshaker to change its mode.  If handshaking has started,
-     * we simply store that request until the next pending session
-     * is created, at which time the new handshaker's state is set.
-     *
-     * The readLock is held during readRecord(), which is responsible
-     * for reading an SSLInputRecord, decrypting it, and processing it.
-     * The readLock ensures that these three steps are done atomically
-     * and that once started, no other thread can block on SSLInputRecord.read.
-     * This is necessary so that processing of close_notify alerts
-     * from the peer are handled properly.
-     */
-    private final Object        handshakeLock = new Object();
-    final ReentrantLock         writeLock = new ReentrantLock();
-    private final Object        readLock = new Object();
+    private final AppInputStream    appInput = new AppInputStream();
+    private final AppOutputStream   appOutput = new AppOutputStream();
 
-    InputRecord                 inputRecord;
-    OutputRecord                outputRecord;
-
-    /*
-     * security parameters for secure renegotiation.
-     */
-    private boolean             secureRenegotiation;
-    private byte[]              clientVerifyData;
-    private byte[]              serverVerifyData;
-
-    /*
-     * The authentication context holds all information used to establish
-     * who this end of the connection is (certificate chains, private keys,
-     * etc) and who is trusted (e.g. as CAs or websites).
-     */
-    private SSLContextImpl      sslContext;
-
-
-    /*
-     * This connection is one of (potentially) many associated with
-     * any given session.  The output of the handshake protocol is a
-     * new session ... although all the protocol description talks
-     * about changing the cipher spec (and it does change), in fact
-     * that's incidental since it's done by changing everything that
-     * is associated with a session at the same time.  (TLS/IETF may
-     * change that to add client authentication w/o new key exchg.)
-     */
-    private Handshaker                  handshaker;
-    private SSLSessionImpl              sess;
-    private volatile SSLSessionImpl     handshakeSession;
-
-
-    /*
-     * If anyone wants to get notified about handshake completions,
-     * they'll show up on this list.
-     */
-    private HashMap<HandshakeCompletedListener, AccessControlContext>
-                                                        handshakeListeners;
-
-    /*
-     * Reuse the same internal input/output streams.
-     */
-    private InputStream         sockInput;
-    private OutputStream        sockOutput;
-
-
-    /*
-     * These input and output streams block their data in SSL records,
-     * and usually arrange integrity and privacy protection for those
-     * records.  The guts of the SSL protocol are wrapped up in these
-     * streams, and in the handshaking that establishes the details of
-     * that integrity and privacy protection.
-     */
-    private AppInputStream      input;
-    private AppOutputStream     output;
-
-    /*
-     * The protocol versions enabled for use on this connection.
-     *
-     * Note: we support a pseudo protocol called SSLv2Hello which when
-     * set will result in an SSL v2 Hello being sent with SSL (version 3.0)
-     * or TLS (version 3.1, 3.2, etc.) version info.
-     */
-    private ProtocolList enabledProtocols;
-
-    /*
-     * The SSL version associated with this connection.
-     */
-    private ProtocolVersion     protocolVersion = ProtocolVersion.DEFAULT_TLS;
-
-    /* Class and subclass dynamic debugging support */
-    private static final Debug debug = Debug.getInstance("ssl");
-
-    /*
-     * Whether local cipher suites preference in server side should be
-     * honored during handshaking?
-     */
-    private boolean preferLocalCipherSuites = false;
-
-    /*
-     * The maximum expected network packet size for SSL/TLS/DTLS records.
-     */
-    private int maximumPacketSize = 0;
+    private String                  peerHost;
+    private boolean                 autoClose;
+    private boolean                 isConnected = false;
+    private boolean                 tlsIsClosed = false;
 
     /*
      * Is the local name service trustworthy?
@@ -393,177 +90,127 @@
      * If the local name service is not trustworthy, reverse host name
      * resolution should not be performed for endpoint identification.
      */
-    static final boolean trustNameService =
-            Debug.getBooleanProperty("jdk.tls.trustNameService", false);
+    private static final boolean trustNameService =
+            Utilities.getBooleanProperty("jdk.tls.trustNameService", false);
 
-    //
-    // CONSTRUCTORS AND INITIALIZATION CODE
-    //
+    /**
+     * Package-private constructor used to instantiate an unconnected
+     * socket.
+     *
+     * This instance is meant to set handshake state to use "client mode".
+     */
+    SSLSocketImpl(SSLContextImpl sslContext) {
+        super();
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash), true);
+    }
 
     /**
-     * Constructs an SSL connection to a named host at a specified port,
-     * using the authentication context provided.  This endpoint acts as
-     * the client, and may rejoin an existing SSL session if appropriate.
+     * Package-private constructor used to instantiate a server socket.
      *
-     * @param context authentication context to use
-     * @param host name of the host with which to connect
-     * @param port number of the server's port
+     * This instance is meant to set handshake state to use "server mode".
      */
-    SSLSocketImpl(SSLContextImpl context, String host, int port)
-            throws IOException, UnknownHostException {
+    SSLSocketImpl(SSLContextImpl sslContext, SSLConfiguration sslConfig) {
         super();
-        this.host = host;
-        this.serverNames =
-            Utilities.addToSNIServerNameList(this.serverNames, this.host);
-        init(context, false);
-        SocketAddress socketAddress =
-               host != null ? new InetSocketAddress(host, port) :
-               new InetSocketAddress(InetAddress.getByName(null), port);
-        connect(socketAddress, 0);
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this, sslConfig,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash));
     }
 
-
     /**
-     * Constructs an SSL connection to a server at a specified address.
-     * and TCP port, using the authentication context provided.  This
-     * endpoint acts as the client, and may rejoin an existing SSL session
-     * if appropriate.
+     * Constructs an SSL connection to a named host at a specified
+     * port, using the authentication context provided.
      *
-     * @param context authentication context to use
-     * @param address the server's host
-     * @param port its port
+     * This endpoint acts as the client, and may rejoin an existing SSL session
+     * if appropriate.
      */
-    SSLSocketImpl(SSLContextImpl context, InetAddress host, int port)
-            throws IOException {
+    SSLSocketImpl(SSLContextImpl sslContext, String peerHost,
+            int peerPort) throws IOException, UnknownHostException {
         super();
-        init(context, false);
-        SocketAddress socketAddress = new InetSocketAddress(host, port);
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash), true);
+        this.peerHost = peerHost;
+        SocketAddress socketAddress =
+               peerHost != null ? new InetSocketAddress(peerHost, peerPort) :
+               new InetSocketAddress(InetAddress.getByName(null), peerPort);
         connect(socketAddress, 0);
     }
 
     /**
-     * Constructs an SSL connection to a named host at a specified port,
-     * using the authentication context provided.  This endpoint acts as
-     * the client, and may rejoin an existing SSL session if appropriate.
+     * Constructs an SSL connection to a server at a specified
+     * address, and TCP port, using the authentication context
+     * provided.
      *
-     * @param context authentication context to use
-     * @param host name of the host with which to connect
-     * @param port number of the server's port
-     * @param localAddr the local address the socket is bound to
-     * @param localPort the local port the socket is bound to
+     * This endpoint acts as the client, and may rejoin an existing SSL
+     * session if appropriate.
      */
-    SSLSocketImpl(SSLContextImpl context, String host, int port,
-            InetAddress localAddr, int localPort)
-            throws IOException, UnknownHostException {
+    SSLSocketImpl(SSLContextImpl sslContext,
+            InetAddress address, int peerPort) throws IOException {
         super();
-        this.host = host;
-        this.serverNames =
-            Utilities.addToSNIServerNameList(this.serverNames, this.host);
-        init(context, false);
-        bind(new InetSocketAddress(localAddr, localPort));
-        SocketAddress socketAddress =
-               host != null ? new InetSocketAddress(host, port) :
-               new InetSocketAddress(InetAddress.getByName(null), port);
-        connect(socketAddress, 0);
-    }
-
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash), true);
 
-    /**
-     * Constructs an SSL connection to a server at a specified address.
-     * and TCP port, using the authentication context provided.  This
-     * endpoint acts as the client, and may rejoin an existing SSL session
-     * if appropriate.
-     *
-     * @param context authentication context to use
-     * @param address the server's host
-     * @param port its port
-     * @param localAddr the local address the socket is bound to
-     * @param localPort the local port the socket is bound to
-     */
-    SSLSocketImpl(SSLContextImpl context, InetAddress host, int port,
-            InetAddress localAddr, int localPort)
-            throws IOException {
-        super();
-        init(context, false);
-        bind(new InetSocketAddress(localAddr, localPort));
-        SocketAddress socketAddress = new InetSocketAddress(host, port);
+        SocketAddress socketAddress = new InetSocketAddress(address, peerPort);
         connect(socketAddress, 0);
     }
 
-    /*
-     * Package-private constructor used ONLY by SSLServerSocket.  The
-     * java.net package accepts the TCP connection after this call is
-     * made.  This just initializes handshake state to use "server mode",
-     * giving control over the use of SSL client authentication.
+    /**
+     * Constructs an SSL connection to a named host at a specified
+     * port, using the authentication context provided.
+     *
+     * This endpoint acts as the client, and may rejoin an existing SSL
+     * session if appropriate.
      */
-    SSLSocketImpl(SSLContextImpl context, boolean serverMode,
-            CipherSuiteList suites, ClientAuthType clientAuth,
-            boolean sessionCreation, ProtocolList protocols,
-            String identificationProtocol,
-            AlgorithmConstraints algorithmConstraints,
-            Collection<SNIMatcher> sniMatchers,
-            boolean preferLocalCipherSuites,
-            String[] applicationProtocols) throws IOException {
-
+    SSLSocketImpl(SSLContextImpl sslContext,
+            String peerHost, int peerPort, InetAddress localAddr,
+            int localPort) throws IOException, UnknownHostException {
         super();
-        doClientAuth = clientAuth;
-        enableSessionCreation = sessionCreation;
-        this.identificationProtocol = identificationProtocol;
-        this.algorithmConstraints = algorithmConstraints;
-        this.sniMatchers = sniMatchers;
-        this.preferLocalCipherSuites = preferLocalCipherSuites;
-        this.applicationProtocols = applicationProtocols;
-        init(context, serverMode);
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash), true);
+        this.peerHost = peerHost;
 
-        /*
-         * Override what was picked out for us.
-         */
-        enabledCipherSuites = suites;
-        enabledProtocols = protocols;
+        bind(new InetSocketAddress(localAddr, localPort));
+        SocketAddress socketAddress =
+               peerHost != null ? new InetSocketAddress(peerHost, peerPort) :
+               new InetSocketAddress(InetAddress.getByName(null), peerPort);
+        connect(socketAddress, 0);
     }
 
-
     /**
-     * Package-private constructor used to instantiate an unconnected
-     * socket. The java.net package will connect it, either when the
-     * connect() call is made by the application.  This instance is
-     * meant to set handshake state to use "client mode".
-     */
-    SSLSocketImpl(SSLContextImpl context) {
-        super();
-        init(context, false);
-    }
-
-
-    /**
-     * Layer SSL traffic over an existing connection, rather than creating
-     * a new connection.  The existing connection may be used only for SSL
-     * traffic (using this SSLSocket) until the SSLSocket.close() call
-     * returns. However, if a protocol error is detected, that existing
-     * connection is automatically closed.
+     * Constructs an SSL connection to a server at a specified
+     * address, and TCP port, using the authentication context
+     * provided.
      *
-     * <P> This particular constructor always uses the socket in the
-     * role of an SSL client. It may be useful in cases which start
-     * using SSL after some initial data transfers, for example in some
-     * SSL tunneling applications or as part of some kinds of application
-     * protocols which negotiate use of a SSL based security.
-     *
-     * @param sock the existing connection
-     * @param context the authentication context to use
+     * This endpoint acts as the client, and may rejoin an existing SSL
+     * session if appropriate.
      */
-    SSLSocketImpl(SSLContextImpl context, Socket sock, String host,
-            int port, boolean autoClose) throws IOException {
-        super(sock);
-        // We always layer over a connected socket
-        if (!sock.isConnected()) {
-            throw new SocketException("Underlying socket is not connected");
-        }
-        this.host = host;
-        this.serverNames =
-            Utilities.addToSNIServerNameList(this.serverNames, this.host);
-        init(context, false);
-        this.autoClose = autoClose;
-        doneConnect();
+    SSLSocketImpl(SSLContextImpl sslContext,
+            InetAddress peerAddr, int peerPort,
+            InetAddress localAddr, int localPort) throws IOException {
+        super();
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash), true);
+
+        bind(new InetSocketAddress(localAddr, localPort));
+        SocketAddress socketAddress = new InetSocketAddress(peerAddr, peerPort);
+        connect(socketAddress, 0);
     }
 
     /**
@@ -572,7 +219,7 @@
      * already been consumed/removed from the {@link Socket}'s
      * underlying {@link InputStream}.
      */
-    SSLSocketImpl(SSLContextImpl context, Socket sock,
+    SSLSocketImpl(SSLContextImpl sslContext, Socket sock,
             InputStream consumed, boolean autoClose) throws IOException {
         super(sock, consumed);
         // We always layer over a connected socket
@@ -580,70 +227,51 @@
             throw new SocketException("Underlying socket is not connected");
         }
 
-        // In server mode, it is not necessary to set host and serverNames.
-        // Otherwise, would require a reverse DNS lookup to get the hostname.
-
-        init(context, true);
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash), false);
         this.autoClose = autoClose;
         doneConnect();
     }
 
     /**
-     * Initializes the client socket.
+     * Layer SSL traffic over an existing connection, rather than
+     * creating a new connection.
+     *
+     * The existing connection may be used only for SSL traffic (using this
+     * SSLSocket) until the SSLSocket.close() call returns. However, if a
+     * protocol error is detected, that existing connection is automatically
+     * closed.
+     * <p>
+     * This particular constructor always uses the socket in the
+     * role of an SSL client. It may be useful in cases which start
+     * using SSL after some initial data transfers, for example in some
+     * SSL tunneling applications or as part of some kinds of application
+     * protocols which negotiate use of a SSL based security.
      */
-    private void init(SSLContextImpl context, boolean isServer) {
-        sslContext = context;
-        sess = SSLSessionImpl.nullSession;
-        handshakeSession = null;
-
-        /*
-         * role is as specified, state is START until after
-         * the low level connection's established.
-         */
-        roleIsServer = isServer;
-        connectionState = cs_START;
+    SSLSocketImpl(SSLContextImpl sslContext, Socket sock,
+            String peerHost, int port, boolean autoClose) throws IOException {
+        super(sock);
+        // We always layer over a connected socket
+        if (!sock.isConnected()) {
+            throw new SocketException("Underlying socket is not connected");
+        }
 
-        // initial security parameters for secure renegotiation
-        secureRenegotiation = false;
-        clientVerifyData = new byte[0];
-        serverVerifyData = new byte[0];
-
-        enabledCipherSuites =
-                sslContext.getDefaultCipherSuiteList(roleIsServer);
-        enabledProtocols =
-                sslContext.getDefaultProtocolList(roleIsServer);
-
-        inputRecord = new SSLSocketInputRecord();;
-        outputRecord = new SSLSocketOutputRecord();
-
-        maximumPacketSize = outputRecord.getMaxPacketSize();
-
-        // save the acc
-        acc = AccessController.getContext();
-
-        input = new AppInputStream(this);
-        output = new AppOutputStream(this);
+        this.sslContext = sslContext;
+        HandshakeHash handshakeHash = new HandshakeHash();
+        this.conContext = new TransportContext(sslContext, this,
+                new SSLSocketInputRecord(handshakeHash),
+                new SSLSocketOutputRecord(handshakeHash), true);
+        this.peerHost = peerHost;
+        this.autoClose = autoClose;
+        doneConnect();
     }
 
-    /**
-     * Connects this socket to the server with a specified timeout
-     * value.
-     *
-     * This method is either called on an unconnected SSLSocketImpl by the
-     * application, or it is called in the constructor of a regular
-     * SSLSocketImpl. If we are layering on top on another socket, then
-     * this method should not be called, because we assume that the
-     * underlying socket is already connected by the time it is passed to
-     * us.
-     *
-     * @param   endpoint the <code>SocketAddress</code>
-     * @param   timeout  the timeout value to be used, 0 is no timeout
-     * @throws  IOException if an error occurs during the connection
-     * @throws  SocketTimeoutException if timeout expires before connecting
-     */
     @Override
-    public void connect(SocketAddress endpoint, int timeout)
-            throws IOException {
+    public void connect(SocketAddress endpoint,
+            int timeout) throws IOException {
 
         if (isLayered()) {
             throw new SocketException("Already connected");
@@ -651,116 +279,504 @@
 
         if (!(endpoint instanceof InetSocketAddress)) {
             throw new SocketException(
-                                  "Cannot handle non-Inet socket addresses.");
+                    "Cannot handle non-Inet socket addresses.");
         }
 
         super.connect(endpoint, timeout);
+        doneConnect();
+    }
 
-        if (host == null || host.length() == 0) {
-            useImplicitHost(false);
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return CipherSuite.namesOf(sslContext.getSupportedCipherSuites());
+    }
+
+    @Override
+    public synchronized String[] getEnabledCipherSuites() {
+        return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites);
+    }
+
+    @Override
+    public synchronized void setEnabledCipherSuites(String[] suites) {
+        conContext.sslConfig.enabledCipherSuites =
+                CipherSuite.validValuesOf(suites);
+    }
+
+    @Override
+    public String[] getSupportedProtocols() {
+        return ProtocolVersion.toStringArray(
+                sslContext.getSupportedProtocolVersions());
+    }
+
+    @Override
+    public synchronized String[] getEnabledProtocols() {
+        return ProtocolVersion.toStringArray(
+                conContext.sslConfig.enabledProtocols);
+    }
+
+    @Override
+    public synchronized void setEnabledProtocols(String[] protocols) {
+        if (protocols == null) {
+            throw new IllegalArgumentException("Protocols cannot be null");
+        }
+
+        conContext.sslConfig.enabledProtocols =
+                ProtocolVersion.namesOf(protocols);
+    }
+
+    @Override
+    public synchronized SSLSession getSession() {
+        try {
+            // start handshaking, if failed, the connection will be closed.
+            ensureNegotiated();
+        } catch (IOException ioe) {
+            if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
+                SSLLogger.severe("handshake failed", ioe);
+            }
+
+            return SSLSessionImpl.nullSession;
+        }
+
+        return conContext.conSession;
+    }
+
+    @Override
+    public synchronized SSLSession getHandshakeSession() {
+        if (conContext.handshakeContext != null) {
+            return conContext.handshakeContext.handshakeSession;
+        }
+
+        return null;
+    }
+
+    @Override
+    public synchronized void addHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener is null");
+        }
+
+        conContext.sslConfig.addHandshakeCompletedListener(listener);
+    }
+
+    @Override
+    public synchronized void removeHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener is null");
         }
 
-        doneConnect();
+        conContext.sslConfig.removeHandshakeCompletedListener(listener);
+    }
+
+    @Override
+    public synchronized void startHandshake() throws IOException {
+        checkWrite();
+        try {
+            conContext.kickstart();
+
+            // All initial handshaking goes through this operation until we
+            // have a valid SSL connection.
+            //
+            // Handle handshake messages only, need no application data.
+            if (!conContext.isNegotiated) {
+                readRecord();
+            }
+        } catch (IOException ioe) {
+            conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                "Couldn't kickstart handshaking", ioe);
+        } catch (Exception oe) {    // including RuntimeException
+            handleException(oe);
+        }
+    }
+
+    @Override
+    public synchronized void setUseClientMode(boolean mode) {
+        conContext.setUseClientMode(mode);
+    }
+
+    @Override
+    public synchronized boolean getUseClientMode() {
+        return conContext.sslConfig.isClientMode;
+    }
+
+    @Override
+    public synchronized void setNeedClientAuth(boolean need) {
+        conContext.sslConfig.clientAuthType =
+                (need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
+                        ClientAuthType.CLIENT_AUTH_NONE);
+    }
+
+    @Override
+    public synchronized boolean getNeedClientAuth() {
+        return (conContext.sslConfig.clientAuthType ==
+                        ClientAuthType.CLIENT_AUTH_REQUIRED);
+    }
+
+    @Override
+    public synchronized void setWantClientAuth(boolean want) {
+        conContext.sslConfig.clientAuthType =
+                (want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
+                        ClientAuthType.CLIENT_AUTH_NONE);
+    }
+
+    @Override
+    public synchronized boolean getWantClientAuth() {
+        return (conContext.sslConfig.clientAuthType ==
+                        ClientAuthType.CLIENT_AUTH_REQUESTED);
+    }
+
+    @Override
+    public synchronized void setEnableSessionCreation(boolean flag) {
+        conContext.sslConfig.enableSessionCreation = flag;
+    }
+
+    @Override
+    public synchronized boolean getEnableSessionCreation() {
+        return conContext.sslConfig.enableSessionCreation;
+    }
+
+    @Override
+    public synchronized boolean isClosed() {
+        return tlsIsClosed && conContext.isClosed();
+    }
+
+    @Override
+    public synchronized void close() throws IOException {
+        try {
+            conContext.close();
+        } catch (IOException ioe) {
+            // ignore the exception
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.warning("connection context closure failed", ioe);
+            }
+        } finally {
+            tlsIsClosed = true;
+        }
+    }
+
+    @Override
+    public synchronized InputStream getInputStream() throws IOException {
+        if (isClosed() || conContext.isInboundDone()) {
+            throw new SocketException("Socket or inbound is closed");
+        }
+
+        if (!isConnected) {
+            throw new SocketException("Socket is not connected");
+        }
+
+        return appInput;
+    }
+
+    private synchronized void ensureNegotiated() throws IOException {
+        if (conContext.isNegotiated ||
+                conContext.isClosed() || conContext.isBroken) {
+            return;
+        }
+
+        startHandshake();
     }
 
     /**
-     * Initialize the handshaker and socket streams.
-     *
-     * Called by connect, the layered constructor, and SSLServerSocket.
+     * InputStream for application data as returned by
+     * SSLSocket.getInputStream().
      */
-    void doneConnect() throws IOException {
-        /*
-         * Save the input and output streams.  May be done only after
-         * java.net actually connects using the socket "self", else
-         * we get some pretty bizarre failure modes.
+    private class AppInputStream extends InputStream {
+        // One element array used to implement the single byte read() method
+        private final byte[] oneByte = new byte[1];
+
+        // the temporary buffer used to read network
+        private ByteBuffer buffer;
+
+        // Is application data available in the stream?
+        private boolean appDataIsAvailable;
+
+        AppInputStream() {
+            this.appDataIsAvailable = false;
+            this.buffer = ByteBuffer.allocate(4096);
+        }
+
+        /**
+         * Return the minimum number of bytes that can be read
+         * without blocking.
+         */
+        @Override
+        public int available() throws IOException {
+            // Currently not synchronized.
+            if ((!appDataIsAvailable) || checkEOF()) {
+                return 0;
+            }
+
+            return buffer.remaining();
+        }
+
+        /**
+         * Read a single byte, returning -1 on non-fault EOF status.
          */
-        sockInput = super.getInputStream();
-        sockOutput = super.getOutputStream();
+        @Override
+        public synchronized int read() throws IOException {
+            int n = read(oneByte, 0, 1);
+            if (n <= 0) {   // EOF
+                return -1;
+            }
+
+            return oneByte[0] & 0xFF;
+        }
 
-        inputRecord.setDeliverStream(sockOutput);
-        outputRecord.setDeliverStream(sockOutput);
+        /**
+         * Reads up to {@code len} bytes of data from the input stream
+         * into an array of bytes.
+         *
+         * An attempt is made to read as many as {@code len} bytes, but a
+         * smaller number may be read. The number of bytes actually read
+         * is returned as an integer.
+         *
+         * If the layer above needs more data, it asks for more, so we
+         * are responsible only for blocking to fill at most one buffer,
+         * and returning "-1" on non-fault EOF status.
+         */
+        @Override
+        public synchronized int read(byte[] b, int off, int len)
+                throws IOException {
+            if (b == null) {
+                throw new NullPointerException("the target buffer is null");
+            } else if (off < 0 || len < 0 || len > b.length - off) {
+                throw new IndexOutOfBoundsException(
+                        "buffer length: " + b.length + ", offset; " + off +
+                        ", bytes to read:" + len);
+            } else if (len == 0) {
+                return 0;
+            }
+
+            if (checkEOF()) {
+                return -1;
+            }
+
+            // start handshaking if the connection has not been negotiated.
+            if (!conContext.isNegotiated &&
+                    !conContext.isClosed() && !conContext.isBroken) {
+                ensureNegotiated();
+            }
+
+            // Read the available bytes at first.
+            int remains = available();
+            if (remains > 0) {
+                int howmany = Math.min(remains, len);
+                buffer.get(b, off, howmany);
+
+                return howmany;
+            }
 
-        /*
-         * Move to handshaking state, with pending session initialized
-         * to defaults and the appropriate kind of handshaker set up.
-         */
-        initHandshaker();
-    }
+            appDataIsAvailable = false;
+            int volume = 0;
+            try {
+                /*
+                 * Read data if needed ... notice that the connection
+                 * guarantees that handshake, alert, and change cipher spec
+                 * data streams are handled as they arrive, so we never
+                 * see them here.
+                 */
+                while (volume == 0) {
+                    // Clear the buffer for a new record reading.
+                    buffer.clear();
+
+                    // grow the buffer if needed
+                    int inLen = conContext.inputRecord.bytesInCompletePacket();
+                    if (inLen < 0) {    // EOF
+                        handleEOF(null);
+
+                        // if no exception thrown
+                        return -1;
+                    }
+
+                    // Is this packet bigger than SSL/TLS normally allows?
+                    if (inLen > SSLRecord.maxLargeRecordSize) {
+                        throw new SSLProtocolException(
+                                "Illegal packet size: " + inLen);
+                    }
+
+                    if (inLen > buffer.remaining()) {
+                        buffer = ByteBuffer.allocate(inLen);
+                    }
+
+                    volume = readRecord(buffer);
+                    buffer.flip();
+                    if (volume < 0) {   // EOF
+                        // treat like receiving a close_notify warning message.
+                        conContext.isInputCloseNotified = true;
+                        conContext.closeInbound();
+                        return -1;
+                    } else if (volume > 0) {
+                        appDataIsAvailable = true;
+                        break;
+                    }
+                }
 
-    private synchronized int getConnectionState() {
-        return connectionState;
-    }
+                // file the destination buffer
+                int howmany = Math.min(len, volume);
+                buffer.get(b, off, howmany);
+                return howmany;
+            } catch (Exception e) {   // including RuntimeException
+                // shutdown and rethrow (wrapped) exception as appropriate
+                handleException(e);
+
+                // dummy for compiler
+                return -1;
+            }
+        }
 
-    private synchronized void setConnectionState(int state) {
-        connectionState = state;
-    }
+        /**
+         * Skip n bytes.
+         *
+         * This implementation is somewhat less efficient than possible, but
+         * not badly so (redundant copy).  We reuse the read() code to keep
+         * things simpler. Note that SKIP_ARRAY is static and may garbled by
+         * concurrent use, but we are not interested in the data anyway.
+         */
+        @Override
+        public synchronized long skip(long n) throws IOException {
+            // dummy array used to implement skip()
+            byte[] skipArray = new byte[256];
 
-    AccessControlContext getAcc() {
-        return acc;
+            long skipped = 0;
+            while (n > 0) {
+                int len = (int)Math.min(n, skipArray.length);
+                int r = read(skipArray, 0, len);
+                if (r <= 0) {
+                    break;
+                }
+                n -= r;
+                skipped += r;
+            }
+
+            return skipped;
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.finest("Closing input stream");
+            }
+
+            conContext.closeInbound();
+        }
     }
 
-    //
-    // READING AND WRITING RECORDS
-    //
+    @Override
+    public synchronized OutputStream getOutputStream() throws IOException {
+        if (isClosed() || conContext.isOutboundDone()) {
+            throw new SocketException("Socket or outbound is closed");
+        }
 
-    /*
-     * Application data record output.
-     *
-     * Application data can't be sent until the first handshake establishes
-     * a session.
+        if (!isConnected) {
+            throw new SocketException("Socket is not connected");
+        }
+
+        return appOutput;
+    }
+
+
+    /**
+     * OutputStream for application data as returned by
+     * SSLSocket.getOutputStream().
      */
-    void writeRecord(byte[] source, int offset, int length) throws IOException {
-        /*
-         * The loop is in case of HANDSHAKE --> ERROR transitions, etc
-         */
-        // Don't bother to check the emptiness of source applicatoin data
-        // before the security connection established.
-        for (boolean readyForApp = false; !readyForApp;) {
-            /*
-             * Not all states support passing application data.  We
-             * synchronize access to the connection state, so that
-             * synchronous handshakes can complete cleanly.
-             */
-            switch (getConnectionState()) {
+    private class AppOutputStream extends OutputStream {
+        // One element array used to implement the write(byte) method
+        private final byte[] oneByte = new byte[1];
+
+        @Override
+        public void write(int i) throws IOException {
+            oneByte[0] = (byte)i;
+            write(oneByte, 0, 1);
+        }
+
+        @Override
+        public synchronized void write(byte[] b,
+                int off, int len) throws IOException {
+            if (b == null) {
+                throw new NullPointerException("the source buffer is null");
+            } else if (off < 0 || len < 0 || len > b.length - off) {
+                throw new IndexOutOfBoundsException(
+                        "buffer length: " + b.length + ", offset; " + off +
+                        ", bytes to read:" + len);
+            } else if (len == 0) {
+                return;
+            }
+
+            // start handshaking if the connection has not been negotiated.
+            if (!conContext.isNegotiated &&
+                    !conContext.isClosed() && !conContext.isBroken) {
+                ensureNegotiated();
+            }
+
+            // check if the Socket is invalid (error or closed)
+            checkWrite();
 
-                /*
-                 * We've deferred the initial handshaking till just now,
-                 * when presumably a thread's decided it's OK to block for
-                 * longish periods of time for I/O purposes (as well as
-                 * configured the cipher suites it wants to use).
-                 */
-                case cs_HANDSHAKE:
-                    performInitialHandshake();
-                    break;
+            // Delegate the writing to the underlying socket.
+            try {
+                writeRecord(b, off, len);
+                checkWrite();
+            } catch (IOException ioe) {
+                // shutdown and rethrow (wrapped) exception as appropriate
+                handleException(ioe);
+            }
+        }
 
-                case cs_DATA:
-                case cs_RENEGOTIATE:
-                    readyForApp = true;
-                    break;
+        @Override
+        public void close() throws IOException {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.finest("Closing output stream");
+            }
+
+            conContext.closeOutbound();
+        }
+    }
+
+    @Override
+    public synchronized SSLParameters getSSLParameters() {
+        return conContext.sslConfig.getSSLParameters();
+    }
+
+    @Override
+    public synchronized void setSSLParameters(SSLParameters params) {
+        conContext.sslConfig.setSSLParameters(params);
 
-                case cs_ERROR:
-                    fatal(Alerts.alert_close_notify,
-                            "error while writing to socket");
-                    break; // dummy
+        if (conContext.sslConfig.maximumPacketSize != 0) {
+            conContext.outputRecord.changePacketSize(
+                    conContext.sslConfig.maximumPacketSize);
+        }
+    }
+
+    @Override
+    public synchronized String getApplicationProtocol() {
+        return conContext.applicationProtocol;
+    }
+
+    @Override
+    public synchronized String getHandshakeApplicationProtocol() {
+        if (conContext.handshakeContext != null) {
+            return conContext.handshakeContext.applicationProtocol;
+        }
 
-                case cs_SENT_CLOSE:
-                case cs_CLOSED:
-                case cs_APP_CLOSED:
-                    // we should never get here (check in AppOutputStream)
-                    // this is just a fallback
-                    if (closeReason != null) {
-                        throw closeReason;
-                    } else {
-                        throw new SocketException("Socket closed");
-                    }
+        return null;
+    }
+
+    @Override
+    public synchronized void setHandshakeApplicationProtocolSelector(
+            BiFunction<SSLSocket, List<String>, String> selector) {
+        conContext.sslConfig.socketAPSelector = selector;
+    }
 
-                /*
-                 * Else something's goofy in this state machine's use.
-                 */
-                default:
-                    throw new SSLProtocolException(
-                            "State error, send app data");
-            }
+    @Override
+    public synchronized BiFunction<SSLSocket, List<String>, String>
+            getHandshakeApplicationProtocolSelector() {
+        return conContext.sslConfig.socketAPSelector;
+    }
+
+    private synchronized void writeRecord(byte[] source,
+            int offset, int length) throws IOException {
+        if (conContext.isOutboundDone()) {
+            throw new SocketException("Socket or outbound closed");
         }
 
         //
@@ -772,1374 +788,208 @@
         // records, so this also increases robustness.
         //
         if (length > 0) {
-            IOException ioe = null;
-            byte description = 0;    // 0: never used, make the compiler happy
-            writeLock.lock();
             try {
-                outputRecord.deliver(source, offset, length);
+                conContext.outputRecord.deliver(source, offset, length);
             } catch (SSLHandshakeException she) {
                 // may be record sequence number overflow
-                description = Alerts.alert_handshake_failure;
-                ioe = she;
+                conContext.fatal(Alert.HANDSHAKE_FAILURE, she);
             } catch (IOException e) {
-                description = Alerts.alert_unexpected_message;
-                ioe = e;
-            } finally {
-                writeLock.unlock();
-            }
-
-            // Be care of deadlock. Please don't place the call to fatal()
-            // into the writeLock locked block.
-            if (ioe != null) {
-                fatal(description, ioe);
+                conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
             }
         }
 
-        /*
-         * Check the sequence number state
-         *
-         * Note that in order to maintain the connection I/O
-         * properly, we check the sequence number after the last
-         * record writing process. As we request renegotiation
-         * or close the connection for wrapped sequence number
-         * when there is enough sequence number space left to
-         * handle a few more records, so the sequence number
-         * of the last record cannot be wrapped.
-         *
-         * Don't bother to kickstart the renegotiation when the
-         * local is asking for it.
-         */
-        if ((connectionState == cs_DATA) && outputRecord.seqNumIsHuge()) {
-            /*
-             * Ask for renegotiation when need to renew sequence number.
-             *
-             * Don't bother to kickstart the renegotiation when the local is
-             * asking for it.
-             */
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", request renegotiation " +
-                        "to avoid sequence number overflow");
-            }
-
-            startHandshake();
-        }
-    }
-
-    /*
-     * Alert record output.
-     */
-    void writeAlert(byte level, byte description) throws IOException {
-
-        // If the record is a close notify alert, we need to honor
-        // socket option SO_LINGER. Note that we will try to send
-        // the close notify even if the SO_LINGER set to zero.
-        if ((description == Alerts.alert_close_notify) && getSoLinger() >= 0) {
-
-            // keep and clear the current thread interruption status.
-            boolean interrupted = Thread.interrupted();
-            try {
-                if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) {
-                    try {
-                        outputRecord.encodeAlert(level, description);
-                    } finally {
-                        writeLock.unlock();
-                    }
-                } else {
-                    SSLException ssle = new SSLException(
-                            "SO_LINGER timeout," +
-                            " close_notify message cannot be sent.");
-
-
-                    // For layered, non-autoclose sockets, we are not
-                    // able to bring them into a usable state, so we
-                    // treat it as fatal error.
-                    if (isLayered() && !autoClose) {
-                        // Note that the alert description is
-                        // specified as -1, so no message will be send
-                        // to peer anymore.
-                        fatal((byte)(-1), ssle);
-                    } else if ((debug != null) && Debug.isOn("ssl")) {
-                        System.out.println(
-                            Thread.currentThread().getName() +
-                            ", received Exception: " + ssle);
-                    }
-
-                    // RFC2246 requires that the session becomes
-                    // unresumable if any connection is terminated
-                    // without proper close_notify messages with
-                    // level equal to warning.
-                    //
-                    // RFC4346 no longer requires that a session not be
-                    // resumed if failure to properly close a connection.
-                    //
-                    // We choose to make the session unresumable if
-                    // failed to send the close_notify message.
-                    //
-                    sess.invalidate();
-                }
-            } catch (InterruptedException ie) {
-                // keep interrupted status
-                interrupted = true;
-            }
-
-            // restore the interrupted status
-            if (interrupted) {
-                Thread.currentThread().interrupt();
-            }
-        } else {
-            writeLock.lock();
-            try {
-                outputRecord.encodeAlert(level, description);
-            } finally {
-                writeLock.unlock();
-            }
-        }
-
-        // Don't bother to check sequence number overlap here.  If sequence
-        // number is huge, there should be enough sequence number space to
-        // request renegotiation in next application data read and write.
-    }
-
-
-    int bytesInCompletePacket() throws IOException {
-        if (getConnectionState() == cs_HANDSHAKE) {
-            performInitialHandshake();
-        }
-
-        synchronized (readLock) {
-            int state = getConnectionState();
-            if ((state == cs_CLOSED) ||
-                    (state == cs_ERROR) || (state == cs_APP_CLOSED)) {
-                return -1;
-            }
-
-            try {
-                return inputRecord.bytesInCompletePacket(sockInput);
-            } catch (EOFException eofe) {
-                boolean handshaking = (connectionState <= cs_HANDSHAKE);
-                boolean rethrow = requireCloseNotify || handshaking;
-                if ((debug != null) && Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                        ", received EOFException: "
-                        + (rethrow ? "error" : "ignored"));
-                }
-
-                if (!rethrow) {
-                    // treat as if we had received a close_notify
-                    closeInternal(false);
-                } else {
-                    SSLException e;
-                    if (handshaking) {
-                        e = new SSLHandshakeException(
-                            "Remote host terminated the handshake");
-                    } else {
-                        e = new SSLProtocolException(
-                            "Remote host terminated the handshake");
-                    }
-                    e.initCause(eofe);
-                    throw e;
-                }
-            }
-
-            return -1;
+        // Is the sequence number is nearly overflow?
+        if (conContext.outputRecord.seqNumIsHuge()) {
+            tryKeyUpdate();
         }
     }
 
-    // the caller have synchronized readLock
-    void expectingFinishFlight() {
-        inputRecord.expectingFinishFlight();
-    }
-
-    /*
-     * Read an application data record.
-     *
-     * Alerts and handshake messages are internally handled directly.
-     */
-    int readRecord(ByteBuffer buffer) throws IOException {
-        if (getConnectionState() == cs_HANDSHAKE) {
-            performInitialHandshake();
+    private synchronized int readRecord() throws IOException {
+        while (!conContext.isInboundDone()) {
+            try {
+                Plaintext plainText = decode(null);
+                if ((plainText.contentType == ContentType.HANDSHAKE.id) &&
+                        conContext.isNegotiated) {
+                    return 0;
+                }
+            } catch (SSLException ssle) {
+                throw ssle;
+            } catch (IOException ioe) {
+                if (!(ioe instanceof SSLException)) {
+                    throw new SSLException("readRecord", ioe);
+                } else {
+                    throw ioe;
+                }
+            }
         }
 
-        return readRecord(buffer, true);
-    }
-
-    /*
-     * Read a record, no application data input required.
-     *
-     * Alerts and handshake messages are internally handled directly.
-     */
-    int readRecord(boolean needAppData) throws IOException {
-        return readRecord(null, needAppData);
-    }
-
-    /*
-     * Clear the pipeline of records from the peer, optionally returning
-     * application data.   Caller is responsible for knowing that it's
-     * possible to do this kind of clearing, if they don't want app
-     * data -- e.g. since it's the initial SSL handshake.
-     *
-     * Don't synchronize (this) during a blocking read() since it
-     * protects data which is accessed on the write side as well.
-     */
-    private int readRecord(ByteBuffer buffer, boolean needAppData)
-            throws IOException {
-        int state;
-
-        // readLock protects reading and processing of an SSLInputRecord.
-        // It keeps the reading from sockInput and processing of the record
-        // atomic so that no two threads can be blocked on the
-        // read from the same input stream at the same time.
-        // This is required for example when a reader thread is
-        // blocked on the read and another thread is trying to
-        // close the socket. For a non-autoclose, layered socket,
-        // the thread performing the close needs to read the close_notify.
-        //
-        // Use readLock instead of 'this' for locking because
-        // 'this' also protects data accessed during writing.
-        synchronized (readLock) {
-            /*
-             * Read and handle records ... return application data
-             * ONLY if it's needed.
-             */
-            Plaintext plainText = null;
-            while (((state = getConnectionState()) != cs_CLOSED) &&
-                    (state != cs_ERROR) && (state != cs_APP_CLOSED)) {
-
-                /*
-                 * clean the buffer and check if it is too small, e.g. because
-                 * the AppInputStream did not have the chance to see the
-                 * current packet length but rather something like that of the
-                 * handshake before. In that case we return 0 at this point to
-                 * give the caller the chance to adjust the buffer.
-                 */
-                if (buffer != null) {
-                    buffer.clear();
-
-                    if (buffer.remaining() <
-                            inputRecord.bytesInCompletePacket(sockInput)) {
-                        return 0;
-                    }
-                }
-
-                /*
-                 * Read a record ... maybe emitting an alert if we get a
-                 * comprehensible but unsupported "hello" message during
-                 * format checking (e.g. V2).
-                 */
-                try {
-                    plainText = inputRecord.decode(sockInput, buffer);
-                } catch (BadPaddingException bpe) {
-                    byte alertType = (state != cs_DATA) ?
-                            Alerts.alert_handshake_failure :
-                            Alerts.alert_bad_record_mac;
-                    fatal(alertType, bpe.getMessage(), bpe);
-                } catch (SSLProtocolException spe) {
-                    try {
-                        fatal(Alerts.alert_unexpected_message, spe);
-                    } catch (IOException x) {
-                        // discard this exception, throw the original exception
-                    }
-                    throw spe;
-                } catch (SSLHandshakeException she) {
-                    // may be record sequence number overflow
-                    fatal(Alerts.alert_handshake_failure, she);
-                } catch (EOFException eof) {
-                    boolean handshaking = (connectionState <= cs_HANDSHAKE);
-                    boolean rethrow = requireCloseNotify || handshaking;
-                    if ((debug != null) && Debug.isOn("ssl")) {
-                        System.out.println(Thread.currentThread().getName() +
-                            ", received EOFException: "
-                            + (rethrow ? "error" : "ignored"));
-                    }
-                    if (rethrow) {
-                        SSLException e;
-                        if (handshaking) {
-                            e = new SSLHandshakeException(
-                                    "Remote host terminated the handshake");
-                        } else {
-                            e = new SSLProtocolException(
-                                    "Remote host terminated the connection");
-                        }
-                        e.initCause(eof);
-                        throw e;
-                    } else {
-                        // treat as if we had received a close_notify
-                        closeInternal(false);
-                        continue;
-                    }
-                }
-
-                // PlainText should never be null. Process input record.
-                int volume = processInputRecord(plainText, needAppData);
-
-                if (plainText.contentType == Record.ct_application_data) {
-                    return volume;
-                }
-
-                if (plainText.contentType == Record.ct_handshake) {
-                    if (!needAppData && connectionState == cs_DATA) {
-                        return volume;
-                    }   // otherwise, need to read more for app data.
-                }
-
-                // continue to read more net data
-            }   // while
-
-            //
-            // couldn't read, due to some kind of error
-            //
-            return -1;
-        }  // readLock synchronization
+        return -1;
     }
 
-    /*
-     * Process the plainText input record.
-     */
-    private synchronized int processInputRecord(
-            Plaintext plainText, boolean needAppData) throws IOException {
-
-        /*
-         * Process the record.
-         */
-        int volume = 0;    // no application data
-        switch (plainText.contentType) {
-            case Record.ct_handshake:
-                /*
-                 * Handshake messages always go to a pending session
-                 * handshaker ... if there isn't one, create one.  This
-                 * must work asynchronously, for renegotiation.
-                 *
-                 * NOTE that handshaking will either resume a session
-                 * which was in the cache (and which might have other
-                 * connections in it already), or else will start a new
-                 * session (new keys exchanged) with just this connection
-                 * in it.
-                 */
-                initHandshaker();
-                if (!handshaker.activated()) {
-                    // prior to handshaking, activate the handshake
-                    if (connectionState == cs_RENEGOTIATE) {
-                        // don't use SSLv2Hello when renegotiating
-                        handshaker.activate(protocolVersion);
-                    } else {
-                        handshaker.activate(null);
-                    }
-                }
+    private synchronized int readRecord(ByteBuffer buffer) throws IOException {
+        while (!conContext.isInboundDone()) {
+            /*
+             * clean the buffer and check if it is too small, e.g. because
+             * the AppInputStream did not have the chance to see the
+             * current packet length but rather something like that of the
+             * handshake before. In that case we return 0 at this point to
+             * give the caller the chance to adjust the buffer.
+             */
+            buffer.clear();
+            int inLen = conContext.inputRecord.bytesInCompletePacket();
+            if (inLen < 0) {    // EOF
+                handleEOF(null);
 
-                /*
-                 * process the handshake record ... may contain just
-                 * a partial handshake message or multiple messages.
-                 *
-                 * The handshaker state machine will ensure that it's
-                 * a finished message.
-                 */
-                handshaker.processRecord(plainText.fragment, expectingFinished);
-                expectingFinished = false;
-
-                if (handshaker.invalidated) {
-                    handshaker = null;
-                    inputRecord.setHandshakeHash(null);
-                    outputRecord.setHandshakeHash(null);
-
-                    // if state is cs_RENEGOTIATE, revert it to cs_DATA
-                    if (connectionState == cs_RENEGOTIATE) {
-                        connectionState = cs_DATA;
-                    }
-                } else if (handshaker.isDone()) {
-                    // reset the parameters for secure renegotiation.
-                    secureRenegotiation =
-                                    handshaker.isSecureRenegotiation();
-                    clientVerifyData = handshaker.getClientVerifyData();
-                    serverVerifyData = handshaker.getServerVerifyData();
-                    // set connection ALPN value
-                    applicationProtocol =
-                        handshaker.getHandshakeApplicationProtocol();
-
-                    sess = handshaker.getSession();
-                    handshakeSession = null;
-                    handshaker = null;
-                    inputRecord.setHandshakeHash(null);
-                    outputRecord.setHandshakeHash(null);
-                    connectionState = cs_DATA;
-
-                    //
-                    // Tell folk about handshake completion, but do
-                    // it in a separate thread.
-                    //
-                    if (handshakeListeners != null) {
-                        HandshakeCompletedEvent event =
-                            new HandshakeCompletedEvent(this, sess);
+                // if no exception thrown
+                return -1;
+            }
 
-                        Thread thread = new Thread(
-                            null,
-                            new NotifyHandshake(
-                                handshakeListeners.entrySet(), event),
-                            "HandshakeCompletedNotify-Thread",
-                            0,
-                            false);
-                        thread.start();
-                    }
-                }
-
-                break;
-
-            case Record.ct_application_data:
-                if (connectionState != cs_DATA
-                        && connectionState != cs_RENEGOTIATE
-                        && connectionState != cs_SENT_CLOSE) {
-                    throw new SSLProtocolException(
-                        "Data received in non-data state: " +
-                        connectionState);
-                }
-                if (expectingFinished) {
-                    throw new SSLProtocolException
-                            ("Expecting finished message, received data");
-                }
-                if (!needAppData) {
-                    throw new SSLException("Discarding app data");
-                }
-
-                volume = plainText.fragment.remaining();
-                break;
-
-            case Record.ct_alert:
-                recvAlert(plainText.fragment);
-                break;
+            if (buffer.remaining() < inLen) {
+                return 0;
+            }
 
-            case Record.ct_change_cipher_spec:
-                if ((connectionState != cs_HANDSHAKE
-                        && connectionState != cs_RENEGOTIATE)) {
-                    // For the CCS message arriving in the wrong state
-                    fatal(Alerts.alert_unexpected_message,
-                            "illegal change cipher spec msg, conn state = "
-                            + connectionState);
-                } else if (plainText.fragment.remaining() != 1
-                        || plainText.fragment.get() != 1) {
-                    // For structural/content issues with the CCS
-                    fatal(Alerts.alert_unexpected_message,
-                            "Malformed change cipher spec msg");
+            try {
+                Plaintext plainText = decode(buffer);
+                if (plainText.contentType == ContentType.APPLICATION_DATA.id) {
+                    return buffer.position();
                 }
-
-                //
-                // The first message after a change_cipher_spec
-                // record MUST be a "Finished" handshake record,
-                // else it's a protocol violation.  We force this
-                // to be checked by a minor tweak to the state
-                // machine.
-                //
-                handshaker.receiveChangeCipherSpec();
-
-                CipherBox readCipher;
-                Authenticator readAuthenticator;
-                try {
-                    readCipher = handshaker.newReadCipher();
-                    readAuthenticator = handshaker.newReadAuthenticator();
-                } catch (GeneralSecurityException e) {
-                    // can't happen
-                    throw new SSLException("Algorithm missing:  ", e);
+            } catch (SSLException ssle) {
+                throw ssle;
+            } catch (IOException ioe) {
+                if (!(ioe instanceof SSLException)) {
+                    throw new SSLException("readRecord", ioe);
+                } else {
+                    throw ioe;
                 }
-                inputRecord.changeReadCiphers(readAuthenticator, readCipher);
-
-                // next message MUST be a finished message
-                expectingFinished = true;
-
-                break;
-
-            default:
-                //
-                // TLS requires that unrecognized records be ignored.
-                //
-                if (debug != null && Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                        ", Received record type: " + plainText.contentType);
-                }
-                break;
+            }
         }
 
-        /*
-         * Check the sequence number state
-         *
-         * Note that in order to maintain the connection I/O
-         * properly, we check the sequence number after the last
-         * record reading process. As we request renegotiation
-         * or close the connection for wrapped sequence number
-         * when there is enough sequence number space left to
-         * handle a few more records, so the sequence number
-         * of the last record cannot be wrapped.
-         *
-         * Don't bother to kickstart the renegotiation when the
-         * local is asking for it.
-         */
-        if ((connectionState == cs_DATA) && inputRecord.seqNumIsHuge()) {
-            /*
-             * Ask for renegotiation when need to renew sequence number.
-             *
-             * Don't bother to kickstart the renegotiation when the local is
-             * asking for it.
-             */
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", request renegotiation " +
-                        "to avoid sequence number overflow");
+        //
+        // couldn't read, due to some kind of error
+        //
+        return -1;
+    }
+
+    private Plaintext decode(ByteBuffer destination) throws IOException {
+        Plaintext plainText;
+        try {
+            if (destination == null) {
+                plainText = SSLTransport.decode(conContext,
+                        null, 0, 0, null, 0, 0);
+            } else {
+                plainText = SSLTransport.decode(conContext,
+                        null, 0, 0, new ByteBuffer[]{destination}, 0, 1);
             }
-
-            startHandshake();
+        } catch (EOFException eofe) {
+            // EOFException is special as it is related to close_notify.
+            plainText = handleEOF(eofe);
         }
 
-        return volume;
-    }
-
+        // Is the sequence number is nearly overflow?
+        if (plainText != Plaintext.PLAINTEXT_NULL &&
+                conContext.inputRecord.seqNumIsHuge()) {
+            tryKeyUpdate();
+        }
 
-    //
-    // HANDSHAKE RELATED CODE
-    //
-
-    /**
-     * Return the AppInputStream. For use by Handshaker only.
-     */
-    AppInputStream getAppInputStream() {
-        return input;
+        return plainText;
     }
 
     /**
-     * Return the AppOutputStream. For use by Handshaker only.
+     * Try renegotiation or key update for sequence number wrap.
+     *
+     * Note that in order to maintain the handshake status properly, we check
+     * the sequence number after the last record reading/writing process.  As
+     * we request renegotiation or close the connection for wrapped sequence
+     * number when there is enough sequence number space left to handle a few
+     * more records, so the sequence number of the last record cannot be
+     * wrapped.
      */
-    AppOutputStream getAppOutputStream() {
-        return output;
+    private void tryKeyUpdate() throws IOException {
+        // Don't bother to kickstart the renegotiation or key update when the
+        // local is asking for it.
+        if ((conContext.handshakeContext == null) &&
+                !conContext.isClosed() && !conContext.isBroken) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.finest("key update to wrap sequence number");
+            }
+            conContext.keyUpdate();
+        }
     }
 
-    /**
-     * Initialize the handshaker object. This means:
-     *
-     *  . if a handshake is already in progress (state is cs_HANDSHAKE
-     *    or cs_RENEGOTIATE), do nothing and return
-     *
-     *  . if the socket is already closed, throw an Exception (internal error)
-     *
-     *  . otherwise (cs_START or cs_DATA), create the appropriate handshaker
-     *    object, and advance the connection state (to cs_HANDSHAKE or
-     *    cs_RENEGOTIATE, respectively).
-     *
-     * This method is called right after a new socket is created, when
-     * starting renegotiation, or when changing client/ server mode of the
-     * socket.
-     */
-    private void initHandshaker() {
-        switch (connectionState) {
-
-        //
-        // Starting a new handshake.
-        //
-        case cs_START:
-        case cs_DATA:
-            break;
-
-        //
-        // We're already in the middle of a handshake.
-        //
-        case cs_HANDSHAKE:
-        case cs_RENEGOTIATE:
-            return;
-
-        //
-        // Anyone allowed to call this routine is required to
-        // do so ONLY if the connection state is reasonable...
-        //
-        default:
-            throw new IllegalStateException("Internal error");
+    private void closeSocket(boolean selfInitiated) throws IOException {
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+            SSLLogger.fine("close the ssl connection " +
+                (selfInitiated ? "(initiative)" : "(passive)"));
         }
 
-        // state is either cs_START or cs_DATA
-        if (connectionState == cs_START) {
-            connectionState = cs_HANDSHAKE;
-        } else { // cs_DATA
-            connectionState = cs_RENEGOTIATE;
+        if (autoClose || !isLayered()) {
+            super.close();
+        } else if (selfInitiated) {
+            // wait for close_notify alert to clear input stream.
+            waitForClose();
+        }
+    }
+
+   /**
+    * Wait for close_notify alert for a graceful closure.
+    *
+    * [RFC 5246] If the application protocol using TLS provides that any
+    * data may be carried over the underlying transport after the TLS
+    * connection is closed, the TLS implementation must receive the responding
+    * close_notify alert before indicating to the application layer that
+    * the TLS connection has ended.  If the application protocol will not
+    * transfer any additional data, but will only close the underlying
+    * transport connection, then the implementation MAY choose to close the
+    * transport without waiting for the responding close_notify.
+    */
+    private void waitForClose() throws IOException {
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+            SSLLogger.fine("wait for close_notify or alert");
         }
 
-        if (roleIsServer) {
-            handshaker = new ServerHandshaker(this, sslContext,
-                    enabledProtocols, doClientAuth,
-                    protocolVersion, connectionState == cs_HANDSHAKE,
-                    secureRenegotiation, clientVerifyData, serverVerifyData);
-            handshaker.setSNIMatchers(sniMatchers);
-            handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
-        } else {
-            handshaker = new ClientHandshaker(this, sslContext,
-                    enabledProtocols,
-                    protocolVersion, connectionState == cs_HANDSHAKE,
-                    secureRenegotiation, clientVerifyData, serverVerifyData);
-            handshaker.setSNIServerNames(serverNames);
-        }
-        handshaker.setMaximumPacketSize(maximumPacketSize);
-        handshaker.setEnabledCipherSuites(enabledCipherSuites);
-        handshaker.setEnableSessionCreation(enableSessionCreation);
-        handshaker.setApplicationProtocols(applicationProtocols);
-        handshaker.setApplicationProtocolSelectorSSLSocket(
-            applicationProtocolSelector);
-    }
-
-    /**
-     * Synchronously perform the initial handshake.
-     *
-     * If the handshake is already in progress, this method blocks until it
-     * is completed. If the initial handshake has already been completed,
-     * it returns immediately.
-     */
-    private void performInitialHandshake() throws IOException {
-        // use handshakeLock and the state check to make sure only
-        // one thread performs the handshake
-        synchronized (handshakeLock) {
-            if (getConnectionState() == cs_HANDSHAKE) {
-                kickstartHandshake();
-
-                /*
-                 * All initial handshaking goes through this operation
-                 * until we have a valid SSL connection.
-                 *
-                 * Handle handshake messages only, need no application data.
-                 */
-                readRecord(false);
+        while (!conContext.isInboundDone()) {
+            try {
+                Plaintext plainText = decode(null);
+                // discard and continue
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest(
+                        "discard plaintext while waiting for close", plainText);
+                }
+            } catch (Exception e) {   // including RuntimeException
+                handleException(e);
             }
         }
     }
 
     /**
-     * Starts an SSL handshake on this connection.
-     */
-    @Override
-    public void startHandshake() throws IOException {
-        // start an ssl handshake that could be resumed from timeout exception
-        startHandshake(true);
-    }
-
-    /**
-     * Starts an ssl handshake on this connection.
-     *
-     * @param resumable indicates the handshake process is resumable from a
-     *          certain exception. If <code>resumable</code>, the socket will
-     *          be reserved for exceptions like timeout; otherwise, the socket
-     *          will be closed, no further communications could be done.
-     */
-    private void startHandshake(boolean resumable) throws IOException {
-        checkWrite();
-        try {
-            if (getConnectionState() == cs_HANDSHAKE) {
-                // do initial handshake
-                performInitialHandshake();
-            } else {
-                // start renegotiation
-                kickstartHandshake();
-            }
-        } catch (Exception e) {
-            // shutdown and rethrow (wrapped) exception as appropriate
-            handleException(e, resumable);
-        }
-    }
-
-    /**
-     * Kickstart the handshake if it is not already in progress.
-     * This means:
-     *
-     *  . if handshaking is already underway, do nothing and return
-     *
-     *  . if the socket is not connected or already closed, throw an
-     *    Exception.
+     * Initialize the handshaker and socket streams.
      *
-     *  . otherwise, call initHandshake() to initialize the handshaker
-     *    object and progress the state. Then, send the initial
-     *    handshaking message if appropriate (always on clients and
-     *    on servers when renegotiating).
-     */
-    private synchronized void kickstartHandshake() throws IOException {
-
-        switch (connectionState) {
-
-        case cs_HANDSHAKE:
-            // handshaker already setup, proceed
-            break;
-
-        case cs_DATA:
-            if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) {
-                throw new SSLHandshakeException(
-                        "Insecure renegotiation is not allowed");
-            }
-
-            if (!secureRenegotiation) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println(
-                        "Warning: Using insecure renegotiation");
-                }
-            }
-
-            // initialize the handshaker, move to cs_RENEGOTIATE
-            initHandshaker();
-            break;
-
-        case cs_RENEGOTIATE:
-            // handshaking already in progress, return
-            return;
-
-        /*
-         * The only way to get a socket in the state is when
-         * you have an unconnected socket.
-         */
-        case cs_START:
-            throw new SocketException(
-                "handshaking attempted on unconnected socket");
-
-        default:
-            throw new SocketException("connection is closed");
-        }
-
-        //
-        // Kickstart handshake state machine if we need to ...
-        //
-        // Note that handshaker.kickstart() writes the message
-        // to its HandshakeOutStream, which calls back into
-        // SSLSocketImpl.writeRecord() to send it.
-        //
-        if (!handshaker.activated()) {
-             // prior to handshaking, activate the handshake
-            if (connectionState == cs_RENEGOTIATE) {
-                // don't use SSLv2Hello when renegotiating
-                handshaker.activate(protocolVersion);
-            } else {
-                handshaker.activate(null);
-            }
-
-            if (handshaker instanceof ClientHandshaker) {
-                // send client hello
-                handshaker.kickstart();
-            } else {
-                if (connectionState == cs_HANDSHAKE) {
-                    // initial handshake, no kickstart message to send
-                } else {
-                    // we want to renegotiate, send hello request
-                    handshaker.kickstart();
-                }
-            }
-        }
-    }
-
-    //
-    // CLOSURE RELATED CALLS
-    //
-
-    /**
-     * Return whether the socket has been explicitly closed by the application.
+     * Called by connect, the layered constructor, and SSLServerSocket.
      */
-    @Override
-    public boolean isClosed() {
-        return connectionState == cs_APP_CLOSED;
-    }
-
-    /**
-     * Return whether we have reached end-of-file.
-     *
-     * If the socket is not connected, has been shutdown because of an error
-     * or has been closed, throw an Exception.
-     */
-    boolean checkEOF() throws IOException {
-        switch (getConnectionState()) {
-        case cs_START:
-            throw new SocketException("Socket is not connected");
-
-        case cs_HANDSHAKE:
-        case cs_DATA:
-        case cs_RENEGOTIATE:
-        case cs_SENT_CLOSE:
-            return false;
-
-        case cs_APP_CLOSED:
-            throw new SocketException("Socket is closed");
-
-        case cs_ERROR:
-        case cs_CLOSED:
-        default:
-            // either closed because of error, or normal EOF
-            if (closeReason == null) {
-                return true;
-            }
-            IOException e = new SSLException
-                        ("Connection has been shutdown: " + closeReason);
-            e.initCause(closeReason);
-            throw e;
-
-        }
-    }
-
-    /**
-     * Check if we can write data to this socket. If not, throw an IOException.
-     */
-    void checkWrite() throws IOException {
-        if (checkEOF() || (getConnectionState() == cs_SENT_CLOSE)) {
-            // we are at EOF, write must throw Exception
-            throw new SocketException("Connection closed by remote host");
-        }
-    }
-
-    private void closeSocket() throws IOException {
-
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                                                ", called closeSocket()");
-        }
-
-        super.close();
-    }
-
-    private void closeSocket(boolean selfInitiated) throws IOException {
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                ", called closeSocket(" + selfInitiated + ")");
-        }
-        if (!isLayered() || autoClose) {
-            super.close();
-        } else if (selfInitiated) {
-            // layered && non-autoclose
-            // read close_notify alert to clear input stream
-            waitForClose(false);
-        }
-    }
-
-    /*
-     * Closing the connection is tricky ... we can't officially close the
-     * connection until we know the other end is ready to go away too,
-     * and if ever the connection gets aborted we must forget session
-     * state (it becomes invalid).
-     */
-
-    /**
-     * Closes the SSL connection.  SSL includes an application level
-     * shutdown handshake; you should close SSL sockets explicitly
-     * rather than leaving it for finalization, so that your remote
-     * peer does not experience a protocol error.
-     */
-    @Override
-    public void close() throws IOException {
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                                                    ", called close()");
-        }
-        closeInternal(true);  // caller is initiating close
-
-        // Clearup the resources.
-        try {
-            synchronized (readLock) {
-                inputRecord.close();
-            }
-
-            writeLock.lock();
-            try {
-                outputRecord.close();
-            } finally {
-                writeLock.unlock();
-            }
-        } catch (IOException ioe) {
-           // ignore
-        }
-
-        setConnectionState(cs_APP_CLOSED);
-    }
-
-    /**
-     * Don't synchronize the whole method because waitForClose()
-     * (which calls readRecord()) might be called.
-     *
-     * @param selfInitiated Indicates which party initiated the close.
-     * If selfInitiated, this side is initiating a close; for layered and
-     * non-autoclose socket, wait for close_notify response.
-     * If !selfInitiated, peer sent close_notify; we reciprocate but
-     * no need to wait for response.
-     */
-    private void closeInternal(boolean selfInitiated) throws IOException {
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                        ", called closeInternal(" + selfInitiated + ")");
+    synchronized void doneConnect() throws IOException {
+        // In server mode, it is not necessary to set host and serverNames.
+        // Otherwise, would require a reverse DNS lookup to get the hostname.
+        if ((peerHost == null) || (peerHost.length() == 0)) {
+            boolean useNameService =
+                    trustNameService && conContext.sslConfig.isClientMode;
+            useImplicitHost(useNameService);
+        } else {
+            conContext.sslConfig.serverNames =
+                    Utilities.addToSNIServerNameList(
+                            conContext.sslConfig.serverNames, peerHost);
         }
 
-        int state = getConnectionState();
-        boolean closeSocketCalled = false;
-        Throwable cachedThrowable = null;
-        try {
-            switch (state) {
-            case cs_START:
-                // unconnected socket or handshaking has not been initialized
-                closeSocket(selfInitiated);
-                break;
-
-            /*
-             * If we're closing down due to error, we already sent (or else
-             * received) the fatal alert ... no niceties, blow the connection
-             * away as quickly as possible (even if we didn't allocate the
-             * socket ourselves; it's unusable, regardless).
-             */
-            case cs_ERROR:
-                closeSocket();
-                break;
-
-            /*
-             * Sometimes close() gets called more than once.
-             */
-            case cs_CLOSED:
-            case cs_APP_CLOSED:
-                 break;
+        InputStream sockInput = super.getInputStream();
+        conContext.inputRecord.setReceiverStream(sockInput);
 
-            /*
-             * Otherwise we indicate clean termination.
-             */
-            // case cs_HANDSHAKE:
-            // case cs_DATA:
-            // case cs_RENEGOTIATE:
-            // case cs_SENT_CLOSE:
-            default:
-                synchronized (this) {
-                    if (((state = getConnectionState()) == cs_CLOSED) ||
-                       (state == cs_ERROR) || (state == cs_APP_CLOSED)) {
-                        return;  // connection was closed while we waited
-                    }
-                    if (state != cs_SENT_CLOSE) {
-                        try {
-                            warning(Alerts.alert_close_notify);
-                            connectionState = cs_SENT_CLOSE;
-                        } catch (Throwable th) {
-                            // we need to ensure socket is closed out
-                            // if we encounter any errors.
-                            connectionState = cs_ERROR;
-                            // cache this for later use
-                            cachedThrowable = th;
-                            closeSocketCalled = true;
-                            closeSocket(selfInitiated);
-                        }
-                    }
-                }
-                // If state was cs_SENT_CLOSE before, we don't do the actual
-                // closing since it is already in progress.
-                if (state == cs_SENT_CLOSE) {
-                    if (debug != null && Debug.isOn("ssl")) {
-                        System.out.println(Thread.currentThread().getName() +
-                            ", close invoked again; state = " +
-                            getConnectionState());
-                    }
-                    if (selfInitiated == false) {
-                        // We were called because a close_notify message was
-                        // received. This may be due to another thread calling
-                        // read() or due to our call to waitForClose() below.
-                        // In either case, just return.
-                        return;
-                    }
-                    // Another thread explicitly called close(). We need to
-                    // wait for the closing to complete before returning.
-                    synchronized (this) {
-                        while (connectionState < cs_CLOSED) {
-                            try {
-                                this.wait();
-                            } catch (InterruptedException e) {
-                                // ignore
-                            }
-                        }
-                    }
-                    if ((debug != null) && Debug.isOn("ssl")) {
-                        System.out.println(Thread.currentThread().getName() +
-                            ", after primary close; state = " +
-                            getConnectionState());
-                    }
-                    return;
-                }
-
-                if (!closeSocketCalled)  {
-                    closeSocketCalled = true;
-                    closeSocket(selfInitiated);
-                }
+        OutputStream sockOutput = super.getOutputStream();
+        conContext.inputRecord.setDeliverStream(sockOutput);
+        conContext.outputRecord.setDeliverStream(sockOutput);
 
-                break;
-            }
-        } finally {
-            synchronized (this) {
-                // Upon exit from this method, the state is always >= cs_CLOSED
-                connectionState = (connectionState == cs_APP_CLOSED)
-                                ? cs_APP_CLOSED : cs_CLOSED;
-                // notify any threads waiting for the closing to finish
-                this.notifyAll();
-            }
-
-            if (cachedThrowable != null) {
-               /*
-                * Rethrow the error to the calling method
-                * The Throwable caught can only be an Error or RuntimeException
-                */
-                if (cachedThrowable instanceof Error) {
-                    throw (Error)cachedThrowable;
-                } else if (cachedThrowable instanceof RuntimeException) {
-                    throw (RuntimeException)cachedThrowable;
-                }   // Otherwise, unlikely
-            }
-        }
-    }
-
-    /**
-     * Reads a close_notify or a fatal alert from the input stream.
-     * Keep reading records until we get a close_notify or until
-     * the connection is otherwise closed.  The close_notify or alert
-     * might be read by another reader,
-     * which will then process the close and set the connection state.
-     */
-    void waitForClose(boolean rethrow) throws IOException {
-        if (debug != null && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                ", waiting for close_notify or alert: state "
-                + getConnectionState());
-        }
-
-        try {
-            int state;
-
-            while (((state = getConnectionState()) != cs_CLOSED) &&
-                   (state != cs_ERROR) && (state != cs_APP_CLOSED)) {
-
-                // Ask for app data and then throw it away
-                try {
-                    readRecord(true);
-                } catch (SocketTimeoutException e) {
-                    if ((debug != null) && Debug.isOn("ssl")) {
-                        System.out.println(
-                            Thread.currentThread().getName() +
-                            ", received Exception: " + e);
-                    }
-                    fatal((byte)(-1), "Did not receive close_notify from peer", e);
-                }
-            }
-        } catch (IOException e) {
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", Exception while waiting for close " +e);
-            }
-            if (rethrow) {
-                throw e; // pass exception up
-            }
-        }
-    }
-
-    //
-    // EXCEPTION AND ALERT HANDLING
-    //
-
-    /**
-     * Handle an exception. This method is called by top level exception
-     * handlers (in read(), write()) to make sure we always shutdown the
-     * connection correctly and do not pass runtime exception to the
-     * application.
-     */
-    void handleException(Exception e) throws IOException {
-        handleException(e, true);
+        this.isConnected = true;
     }
 
-    /**
-     * Handle an exception. This method is called by top level exception
-     * handlers (in read(), write(), startHandshake()) to make sure we
-     * always shutdown the connection correctly and do not pass runtime
-     * exception to the application.
-     *
-     * This method never returns normally, it always throws an IOException.
-     *
-     * We first check if the socket has already been shutdown because of an
-     * error. If so, we just rethrow the exception. If the socket has not
-     * been shutdown, we sent a fatal alert and remember the exception.
-     *
-     * @param e the Exception
-     * @param resumable indicates the caller process is resumable from the
-     *          exception. If <code>resumable</code>, the socket will be
-     *          reserved for exceptions like timeout; otherwise, the socket
-     *          will be closed, no further communications could be done.
-     */
-    private synchronized void handleException(Exception e, boolean resumable)
-        throws IOException {
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                        ", handling exception: " + e.toString());
-        }
-
-        // don't close the Socket in case of timeouts or interrupts if
-        // the process is resumable.
-        if (e instanceof InterruptedIOException && resumable) {
-            throw (IOException)e;
-        }
-
-        // if we've already shutdown because of an error,
-        // there is nothing to do except rethrow the exception
-        if (closeReason != null) {
-            if (e instanceof IOException) { // includes SSLException
-                throw (IOException)e;
-            } else {
-                // this is odd, not an IOException.
-                // normally, this should not happen
-                // if closeReason has been already been set
-                throw Alerts.getSSLException(Alerts.alert_internal_error, e,
-                                      "Unexpected exception");
-            }
-        }
-
-        // need to perform error shutdown
-        boolean isSSLException = (e instanceof SSLException);
-        if ((!isSSLException) && (e instanceof IOException)) {
-            // IOException from the socket
-            // this means the TCP connection is already dead
-            // we call fatal just to set the error status
-            try {
-                fatal(Alerts.alert_unexpected_message, e);
-            } catch (IOException ee) {
-                // ignore (IOException wrapped in SSLException)
-            }
-            // rethrow original IOException
-            throw (IOException)e;
-        }
-
-        // must be SSLException or RuntimeException
-        byte alertType;
-        if (isSSLException) {
-            if (e instanceof SSLHandshakeException) {
-                alertType = Alerts.alert_handshake_failure;
-            } else {
-                alertType = Alerts.alert_unexpected_message;
-            }
-        } else {
-            alertType = Alerts.alert_internal_error;
-        }
-        fatal(alertType, e);
-    }
-
-    /*
-     * Send a warning alert.
-     */
-    void warning(byte description) {
-        sendAlert(Alerts.alert_warning, description);
-    }
-
-    synchronized void fatal(byte description, String diagnostic)
-            throws IOException {
-        fatal(description, diagnostic, null);
-    }
-
-    synchronized void fatal(byte description, Throwable cause)
-            throws IOException {
-        fatal(description, null, cause);
-    }
-
-    /*
-     * Send a fatal alert, and throw an exception so that callers will
-     * need to stand on their heads to accidentally continue processing.
-     */
-    synchronized void fatal(byte description, String diagnostic,
-            Throwable cause) throws IOException {
-
-        // Be care of deadlock. Please don't synchronize readLock.
-        try {
-            inputRecord.close();
-        } catch (IOException ioe) {
-            // ignore
-        }
-
-        sess.invalidate();
-        if (handshakeSession != null) {
-            handshakeSession.invalidate();
-        }
-
-        int oldState = connectionState;
-        if (connectionState < cs_ERROR) {
-            connectionState = cs_ERROR;
-        }
-
-        /*
-         * Has there been an error received yet?  If not, remember it.
-         * By RFC 2246, we don't bother waiting for a response.
-         * Fatal errors require immediate shutdown.
-         */
-        if (closeReason == null) {
-            /*
-             * Try to clear the kernel buffer to avoid TCP connection resets.
-             */
-            if (oldState == cs_HANDSHAKE) {
-                sockInput.skip(sockInput.available());
-            }
-
-            // If the description equals -1, the alert won't be sent to peer.
-            if (description != -1) {
-                sendAlert(Alerts.alert_fatal, description);
-            }
-            if (cause instanceof SSLException) { // only true if != null
-                closeReason = (SSLException)cause;
-            } else {
-                closeReason =
-                    Alerts.getSSLException(description, cause, diagnostic);
-            }
-        }
-
-        /*
-         * Clean up our side.
-         */
-        closeSocket();
-
-        // Be care of deadlock. Please don't synchronize writeLock.
-        try {
-            outputRecord.close();
-        } catch (IOException ioe) {
-            // ignore
-        }
-
-        throw closeReason;
-    }
-
-
-    /*
-     * Process an incoming alert ... caller must already have synchronized
-     * access to "this".
-     */
-    private void recvAlert(ByteBuffer fragment) throws IOException {
-        byte level = fragment.get();
-        byte description = fragment.get();
-
-        if (description == -1) { // check for short message
-            fatal(Alerts.alert_illegal_parameter, "Short alert message");
-        }
-
-        if (debug != null && (Debug.isOn("record") ||
-                Debug.isOn("handshake"))) {
-            synchronized (System.out) {
-                System.out.print(Thread.currentThread().getName());
-                System.out.print(", RECV " + protocolVersion + " ALERT:  ");
-                if (level == Alerts.alert_fatal) {
-                    System.out.print("fatal, ");
-                } else if (level == Alerts.alert_warning) {
-                    System.out.print("warning, ");
-                } else {
-                    System.out.print("<level " + (0x0ff & level) + ">, ");
-                }
-                System.out.println(Alerts.alertDescription(description));
-            }
-        }
-
-        if (level == Alerts.alert_warning) {
-            if (description == Alerts.alert_close_notify) {
-                if (connectionState == cs_HANDSHAKE) {
-                    fatal(Alerts.alert_unexpected_message,
-                                "Received close_notify during handshake");
-                } else {
-                    closeInternal(false);  // reply to close
-                }
-            } else {
-
-                //
-                // The other legal warnings relate to certificates,
-                // e.g. no_certificate, bad_certificate, etc; these
-                // are important to the handshaking code, which can
-                // also handle illegal protocol alerts if needed.
-                //
-                if (handshaker != null) {
-                    handshaker.handshakeAlert(description);
-                }
-            }
-        } else { // fatal or unknown level
-            String reason = "Received fatal alert: "
-                + Alerts.alertDescription(description);
-            if (closeReason == null) {
-                closeReason = Alerts.getSSLException(description, reason);
-            }
-            fatal(Alerts.alert_unexpected_message, reason);
-        }
-    }
-
-
-    /*
-     * Emit alerts.  Caller must have synchronized with "this".
-     */
-    private void sendAlert(byte level, byte description) {
-        // the connectionState cannot be cs_START
-        if (connectionState >= cs_SENT_CLOSE) {
-            return;
-        }
-
-        // For initial handshaking, don't send alert message to peer if
-        // handshaker has not started.
-        //
-        // Shall we send an fatal alter to terminate the connection gracefully?
-        if (connectionState <= cs_HANDSHAKE &&
-                (handshaker == null || !handshaker.started() ||
-                        !handshaker.activated())) {
-            return;
-        }
-
-        boolean useDebug = debug != null && Debug.isOn("ssl");
-        if (useDebug) {
-            synchronized (System.out) {
-                System.out.print(Thread.currentThread().getName());
-                System.out.print(", SEND " + protocolVersion + " ALERT:  ");
-                if (level == Alerts.alert_fatal) {
-                    System.out.print("fatal, ");
-                } else if (level == Alerts.alert_warning) {
-                    System.out.print("warning, ");
-                } else {
-                    System.out.print("<level = " + (0x0ff & level) + ">, ");
-                }
-                System.out.println("description = "
-                        + Alerts.alertDescription(description));
-            }
-        }
-
-        try {
-            writeAlert(level, description);
-        } catch (IOException e) {
-            if (useDebug) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", Exception sending alert: " + e);
-            }
-        }
-    }
-
-    //
-    // VARIOUS OTHER METHODS
-    //
-
-    // used by Handshaker
-    void changeWriteCiphers() throws IOException {
-        Authenticator writeAuthenticator;
-        CipherBox writeCipher;
-        try {
-            writeCipher = handshaker.newWriteCipher();
-            writeAuthenticator = handshaker.newWriteAuthenticator();
-        } catch (GeneralSecurityException e) {
-            // "can't happen"
-            throw new SSLException("Algorithm missing:  ", e);
-        }
-        outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher);
-    }
-
-    /*
-     * Updates the SSL version associated with this connection.
-     * Called from Handshaker once it has determined the negotiated version.
-     */
-    synchronized void setVersion(ProtocolVersion protocolVersion) {
-        this.protocolVersion = protocolVersion;
-        outputRecord.setVersion(protocolVersion);
-    }
-
-    //
-    // ONLY used by ClientHandshaker for the server hostname during handshaking
-    //
-    synchronized String getHost() {
-        // Note that the host may be null or empty for localhost.
-        if (host == null || host.length() == 0) {
-            useImplicitHost(true);
-        }
-
-        return host;
-    }
-
-    /*
-     * Try to set and use the implicit specified hostname
-     */
-    private synchronized void useImplicitHost(boolean noSniUpdate) {
-
+    private void useImplicitHost(boolean useNameService) {
         // Note: If the local name service is not trustworthy, reverse
         // host name resolution should not be performed for endpoint
         // identification.  Use the application original specified
@@ -2157,27 +1007,24 @@
         if ((originalHostname != null) &&
                 (originalHostname.length() != 0)) {
 
-            host = originalHostname;
-            if (!noSniUpdate && serverNames.isEmpty() && !noSniExtension) {
-                serverNames =
-                        Utilities.addToSNIServerNameList(serverNames, host);
-
-                if (!roleIsServer &&
-                        (handshaker != null) && !handshaker.activated()) {
-                    handshaker.setSNIServerNames(serverNames);
-                }
+            this.peerHost = originalHostname;
+            if (conContext.sslConfig.serverNames.isEmpty() &&
+                    !conContext.sslConfig.noSniExtension) {
+                conContext.sslConfig.serverNames =
+                        Utilities.addToSNIServerNameList(
+                                conContext.sslConfig.serverNames, peerHost);
             }
 
             return;
         }
 
         // No explicitly specified hostname, no server name indication.
-        if (!trustNameService) {
+        if (!useNameService) {
             // The local name service is not trustworthy, use IP address.
-            host = inetAddress.getHostAddress();
+            this.peerHost = inetAddress.getHostAddress();
         } else {
             // Use the underlying reverse host name resolution service.
-            host = getInetAddress().getHostName();
+            this.peerHost = getInetAddress().getHostName();
         }
     }
 
@@ -2187,557 +1034,144 @@
     // SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter
     // may override SNIHostName in the customized server name indication.
     public synchronized void setHost(String host) {
-        this.host = host;
-        this.serverNames =
-            Utilities.addToSNIServerNameList(this.serverNames, this.host);
-
-        if (!roleIsServer && (handshaker != null) && !handshaker.activated()) {
-            handshaker.setSNIServerNames(serverNames);
-        }
-    }
-
-    /**
-     * Gets an input stream to read from the peer on the other side.
-     * Data read from this stream was always integrity protected in
-     * transit, and will usually have been confidentiality protected.
-     */
-    @Override
-    public synchronized InputStream getInputStream() throws IOException {
-        if (isClosed()) {
-            throw new SocketException("Socket is closed");
-        }
-
-        /*
-         * Can't call isConnected() here, because the Handshakers
-         * do some initialization before we actually connect.
-         */
-        if (connectionState == cs_START) {
-            throw new SocketException("Socket is not connected");
-        }
-
-        return input;
-    }
-
-    /**
-     * Gets an output stream to write to the peer on the other side.
-     * Data written on this stream is always integrity protected, and
-     * will usually be confidentiality protected.
-     */
-    @Override
-    public synchronized OutputStream getOutputStream() throws IOException {
-        if (isClosed()) {
-            throw new SocketException("Socket is closed");
-        }
-
-        /*
-         * Can't call isConnected() here, because the Handshakers
-         * do some initialization before we actually connect.
-         */
-        if (connectionState == cs_START) {
-            throw new SocketException("Socket is not connected");
-        }
-
-        return output;
+        this.peerHost = host;
+        this.conContext.sslConfig.serverNames =
+                Utilities.addToSNIServerNameList(
+                        conContext.sslConfig.serverNames, host);
     }
 
     /**
-     * Returns the SSL Session in use by this connection.  These can
-     * be long lived, and frequently correspond to an entire login session
-     * for some user.
+     * Return whether we have reached end-of-file.
+     *
+     * If the socket is not connected, has been shutdown because of an error
+     * or has been closed, throw an Exception.
      */
-    @Override
-    public SSLSession getSession() {
-        /*
-         * Force a synchronous handshake, if appropriate.
-         */
-        if (getConnectionState() == cs_HANDSHAKE) {
-            try {
-                // start handshaking, if failed, the connection will be closed.
-                startHandshake(false);
-            } catch (IOException e) {
-                // handshake failed. log and return a nullSession
-                if (debug != null && Debug.isOn("handshake")) {
-                      System.out.println(Thread.currentThread().getName() +
-                          ", IOException in getSession():  " + e);
-                }
+    synchronized boolean checkEOF() throws IOException {
+        if (conContext.isClosed()) {
+            return true;
+        } else if (conContext.isInputCloseNotified || conContext.isBroken) {
+            if (conContext.closeReason == null) {
+                return true;
+            } else {
+                throw new SSLException(
+                    "Connection has been shutdown: " + conContext.closeReason,
+                    conContext.closeReason);
             }
         }
-        synchronized (this) {
-            return sess;
-        }
-    }
 
-    @Override
-    public synchronized SSLSession getHandshakeSession() {
-        return handshakeSession;
-    }
-
-    synchronized void setHandshakeSession(SSLSessionImpl session) {
-        // update the fragment size, which may be negotiated during handshaking
-        inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
-        outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize());
-
-        handshakeSession = session;
+        return false;
     }
 
     /**
-     * Controls whether new connections may cause creation of new SSL
-     * sessions.
-     *
-     * As long as handshaking has not started, we can change
-     * whether we enable session creations.  Otherwise,
-     * we will need to wait for the next handshake.
+     * Check if we can write data to this socket.
      */
-    @Override
-    public synchronized void setEnableSessionCreation(boolean flag) {
-        enableSessionCreation = flag;
-
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setEnableSessionCreation(enableSessionCreation);
+    synchronized void checkWrite() throws IOException {
+        if (checkEOF() || conContext.isOutboundClosed()) {
+            // we are at EOF, write must throw Exception
+            throw new SocketException("Connection closed");
+        }
+        if (!isConnected) {
+            throw new SocketException("Socket is not connected");
         }
     }
 
     /**
-     * Returns true if new connections may cause creation of new SSL
-     * sessions.
-     */
-    @Override
-    public synchronized boolean getEnableSessionCreation() {
-        return enableSessionCreation;
-    }
-
-
-    /**
-     * Sets the flag controlling whether a server mode socket
-     * *REQUIRES* SSL client authentication.
+     * Handle an exception.
+     *
+     * This method is called by top level exception handlers (in read(),
+     * write()) to make sure we always shutdown the connection correctly
+     * and do not pass runtime exception to the application.
      *
-     * As long as handshaking has not started, we can change
-     * whether client authentication is needed.  Otherwise,
-     * we will need to wait for the next handshake.
+     * This method never returns normally, it always throws an IOException.
      */
-    @Override
-    public synchronized void setNeedClientAuth(boolean flag) {
-        doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
-                ClientAuthType.CLIENT_AUTH_NONE);
+    private void handleException(Exception cause) throws IOException {
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+            SSLLogger.warning("handling exception", cause);
+        }
+
+        // Don't close the Socket in case of timeouts or interrupts.
+        if (cause instanceof InterruptedIOException) {
+            throw (IOException)cause;
+        }
 
-        if ((handshaker != null) &&
-                (handshaker instanceof ServerHandshaker) &&
-                !handshaker.activated()) {
-            ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
+        // need to perform error shutdown
+        boolean isSSLException = (cause instanceof SSLException);
+        Alert alert;
+        if (isSSLException) {
+            if (cause instanceof SSLHandshakeException) {
+                alert = Alert.HANDSHAKE_FAILURE;
+            } else {
+                alert = Alert.UNEXPECTED_MESSAGE;
+            }
+        } else {
+            if (cause instanceof IOException) {
+                alert = Alert.UNEXPECTED_MESSAGE;
+            } else {
+                // RuntimeException
+                alert = Alert.INTERNAL_ERROR;
+            }
         }
+        conContext.fatal(alert, cause);
     }
 
-    @Override
-    public synchronized boolean getNeedClientAuth() {
-        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED);
-    }
+    private Plaintext handleEOF(EOFException eofe) throws IOException {
+        if (requireCloseNotify || conContext.handshakeContext != null) {
+            SSLException ssle;
+            if (conContext.handshakeContext != null) {
+                ssle = new SSLHandshakeException(
+                        "Remote host terminated the handshake");
+            } else {
+                ssle = new SSLProtocolException(
+                        "Remote host terminated the connection");
+            }
 
-    /**
-     * Sets the flag controlling whether a server mode socket
-     * *REQUESTS* SSL client authentication.
-     *
-     * As long as handshaking has not started, we can change
-     * whether client authentication is requested.  Otherwise,
-     * we will need to wait for the next handshake.
-     */
-    @Override
-    public synchronized void setWantClientAuth(boolean flag) {
-        doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
-                ClientAuthType.CLIENT_AUTH_NONE);
+            if (eofe != null) {
+                ssle.initCause(eofe);
+            }
+            throw ssle;
+        } else {
+            // treat as if we had received a close_notify
+            conContext.isInputCloseNotified = true;
+            conContext.transport.shutdown();
 
-        if ((handshaker != null) &&
-                (handshaker instanceof ServerHandshaker) &&
-                !handshaker.activated()) {
-            ((ServerHandshaker) handshaker).setClientAuth(doClientAuth);
+            return Plaintext.PLAINTEXT_NULL;
         }
     }
 
-    @Override
-    public synchronized boolean getWantClientAuth() {
-        return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED);
-    }
-
 
-    /**
-     * Sets the flag controlling whether the socket is in SSL
-     * client or server mode.  Must be called before any SSL
-     * traffic has started.
-     */
     @Override
-    @SuppressWarnings("fallthrough")
-    public synchronized void setUseClientMode(boolean flag) {
-        switch (connectionState) {
-
-        case cs_START:
-            /*
-             * If we need to change the socket mode and the enabled
-             * protocols and cipher suites haven't specifically been
-             * set by the user, change them to the corresponding
-             * default ones.
-             */
-            if (roleIsServer != (!flag)) {
-                if (sslContext.isDefaultProtocolList(enabledProtocols)) {
-                    enabledProtocols =
-                            sslContext.getDefaultProtocolList(!flag);
-                }
-
-                if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) {
-                    enabledCipherSuites =
-                            sslContext.getDefaultCipherSuiteList(!flag);
-                }
-            }
-
-            roleIsServer = !flag;
-            break;
-
-        case cs_HANDSHAKE:
-            /*
-             * If we have a handshaker, but haven't started
-             * SSL traffic, we can throw away our current
-             * handshaker, and start from scratch.  Don't
-             * need to call doneConnect() again, we already
-             * have the streams.
-             */
-            assert(handshaker != null);
-            if (!handshaker.activated()) {
-                /*
-                 * If we need to change the socket mode and the enabled
-                 * protocols and cipher suites haven't specifically been
-                 * set by the user, change them to the corresponding
-                 * default ones.
-                 */
-                if (roleIsServer != (!flag)) {
-                    if (sslContext.isDefaultProtocolList(enabledProtocols)) {
-                        enabledProtocols =
-                                sslContext.getDefaultProtocolList(!flag);
-                    }
-
-                    if (sslContext.isDefaultCipherSuiteList(
-                                                    enabledCipherSuites)) {
-                        enabledCipherSuites =
-                            sslContext.getDefaultCipherSuiteList(!flag);
-                    }
-                }
-
-                roleIsServer = !flag;
-                connectionState = cs_START;
-                initHandshaker();
-                break;
-            }
-
-            // If handshake has started, that's an error.  Fall through...
-
-        default:
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", setUseClientMode() invoked in state = " +
-                    connectionState);
-            }
-            throw new IllegalArgumentException(
-                "Cannot change mode after SSL traffic has started");
-        }
-    }
-
-    @Override
-    public synchronized boolean getUseClientMode() {
-        return !roleIsServer;
-    }
-
-
-    /**
-     * Returns the names of the cipher suites which could be enabled for use
-     * on an SSL connection.  Normally, only a subset of these will actually
-     * be enabled by default, since this list may include cipher suites which
-     * do not support the mutual authentication of servers and clients, or
-     * which do not protect data confidentiality.  Servers may also need
-     * certain kinds of certificates to use certain cipher suites.
-     *
-     * @return an array of cipher suite names
-     */
-    @Override
-    public String[] getSupportedCipherSuites() {
-        return sslContext.getSupportedCipherSuiteList().toStringArray();
-    }
-
-    /**
-     * Controls which particular cipher suites are enabled for use on
-     * this connection.  The cipher suites must have been listed by
-     * getCipherSuites() as being supported.  Even if a suite has been
-     * enabled, it might never be used if no peer supports it or the
-     * requisite certificates (and private keys) are not available.
-     *
-     * @param suites Names of all the cipher suites to enable.
-     */
-    @Override
-    public synchronized void setEnabledCipherSuites(String[] suites) {
-        enabledCipherSuites = new CipherSuiteList(suites);
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setEnabledCipherSuites(enabledCipherSuites);
-        }
-    }
-
-    /**
-     * Returns the names of the SSL cipher suites which are currently enabled
-     * for use on this connection.  When an SSL socket is first created,
-     * all enabled cipher suites <em>(a)</em> protect data confidentiality,
-     * by traffic encryption, and <em>(b)</em> can mutually authenticate
-     * both clients and servers.  Thus, in some environments, this value
-     * might be empty.
-     *
-     * @return an array of cipher suite names
-     */
-    @Override
-    public synchronized String[] getEnabledCipherSuites() {
-        return enabledCipherSuites.toStringArray();
-    }
-
-
-    /**
-     * Returns the protocols that are supported by this implementation.
-     * A subset of the supported protocols may be enabled for this connection
-     * @return an array of protocol names.
-     */
-    @Override
-    public String[] getSupportedProtocols() {
-        return sslContext.getSuportedProtocolList().toStringArray();
-    }
-
-    /**
-     * Controls which protocols are enabled for use on
-     * this connection.  The protocols must have been listed by
-     * getSupportedProtocols() as being supported.
-     *
-     * @param protocols protocols to enable.
-     * @exception IllegalArgumentException when one of the protocols
-     *  named by the parameter is not supported.
-     */
-    @Override
-    public synchronized void setEnabledProtocols(String[] protocols) {
-        enabledProtocols = new ProtocolList(protocols);
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setEnabledProtocols(enabledProtocols);
-        }
+    public String getPeerHost() {
+        return peerHost;
     }
 
     @Override
-    public synchronized String[] getEnabledProtocols() {
-        return enabledProtocols.toStringArray();
-    }
-
-    /**
-     * Assigns the socket timeout.
-     * @see java.net.Socket#setSoTimeout
-     */
-    @Override
-    public void setSoTimeout(int timeout) throws SocketException {
-        if ((debug != null) && Debug.isOn("ssl")) {
-            System.out.println(Thread.currentThread().getName() +
-                ", setSoTimeout(" + timeout + ") called");
-        }
-
-        super.setSoTimeout(timeout);
+    public int getPeerPort() {
+        return getPort();
     }
 
-    /**
-     * Registers an event listener to receive notifications that an
-     * SSL handshake has completed on this connection.
-     */
     @Override
-    public synchronized void addHandshakeCompletedListener(
-            HandshakeCompletedListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener is null");
-        }
-        if (handshakeListeners == null) {
-            handshakeListeners = new
-                HashMap<HandshakeCompletedListener, AccessControlContext>(4);
-        }
-        handshakeListeners.put(listener, AccessController.getContext());
-    }
-
-
-    /**
-     * Removes a previously registered handshake completion listener.
-     */
-    @Override
-    public synchronized void removeHandshakeCompletedListener(
-            HandshakeCompletedListener listener) {
-        if (handshakeListeners == null) {
-            throw new IllegalArgumentException("no listeners");
-        }
-        if (handshakeListeners.remove(listener) == null) {
-            throw new IllegalArgumentException("listener not registered");
-        }
-        if (handshakeListeners.isEmpty()) {
-            handshakeListeners = null;
-        }
+    public boolean useDelegatedTask() {
+        return false;
     }
 
-    /**
-     * Returns the SSLParameters in effect for this SSLSocket.
-     */
     @Override
-    public synchronized SSLParameters getSSLParameters() {
-        SSLParameters params = super.getSSLParameters();
-
-        // the super implementation does not handle the following parameters
-        params.setEndpointIdentificationAlgorithm(identificationProtocol);
-        params.setAlgorithmConstraints(algorithmConstraints);
-
-        if (sniMatchers.isEmpty() && !noSniMatcher) {
-            // 'null' indicates none has been set
-            params.setSNIMatchers(null);
-        } else {
-            params.setSNIMatchers(sniMatchers);
-        }
-
-        if (serverNames.isEmpty() && !noSniExtension) {
-            // 'null' indicates none has been set
-            params.setServerNames(null);
-        } else {
-            params.setServerNames(serverNames);
-        }
-
-        params.setUseCipherSuitesOrder(preferLocalCipherSuites);
-        params.setMaximumPacketSize(maximumPacketSize);
-        params.setApplicationProtocols(applicationProtocols);
-
-        // DTLS handshake retransmissions parameter does not apply here.
-
-        return params;
-    }
+    public void shutdown() throws IOException {
+        if (!isClosed()) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("close the underlying socket");
+            }
 
-    /**
-     * Applies SSLParameters to this socket.
-     */
-    @Override
-    public synchronized void setSSLParameters(SSLParameters params) {
-        super.setSSLParameters(params);
-
-        // the super implementation does not handle the following parameters
-        identificationProtocol = params.getEndpointIdentificationAlgorithm();
-        algorithmConstraints = params.getAlgorithmConstraints();
-        preferLocalCipherSuites = params.getUseCipherSuitesOrder();
-        maximumPacketSize = params.getMaximumPacketSize();
-
-        // DTLS handshake retransmissions parameter does not apply here.
-
-        if (maximumPacketSize != 0) {
-            outputRecord.changePacketSize(maximumPacketSize);
-        } else {
-            // use the implicit maximum packet size.
-            maximumPacketSize = outputRecord.getMaxPacketSize();
-        }
-
-        List<SNIServerName> sniNames = params.getServerNames();
-        if (sniNames != null) {
-            noSniExtension = sniNames.isEmpty();
-            serverNames = sniNames;
-        }
-
-        Collection<SNIMatcher> matchers = params.getSNIMatchers();
-        if (matchers != null) {
-            noSniMatcher = matchers.isEmpty();
-            sniMatchers = matchers;
-        }
-
-        applicationProtocols = params.getApplicationProtocols();
-
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setIdentificationProtocol(identificationProtocol);
-            handshaker.setAlgorithmConstraints(algorithmConstraints);
-            handshaker.setMaximumPacketSize(maximumPacketSize);
-            handshaker.setApplicationProtocols(applicationProtocols);
-            if (roleIsServer) {
-                handshaker.setSNIMatchers(sniMatchers);
-                handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites);
-            } else {
-                handshaker.setSNIServerNames(serverNames);
+            try {
+                if (conContext.isInputCloseNotified) {
+                    // Close the connection, no wait for more peer response.
+                    closeSocket(false);
+                } else {
+                    // Close the connection, may wait for peer close_notify.
+                    closeSocket(true);
+                }
+            } finally {
+                tlsIsClosed = true;
             }
         }
     }
-
-    @Override
-    public synchronized String getApplicationProtocol() {
-        return applicationProtocol;
-    }
-
-    @Override
-    public synchronized String getHandshakeApplicationProtocol() {
-        if ((handshaker != null) && handshaker.started()) {
-            return handshaker.getHandshakeApplicationProtocol();
-        }
-        return null;
-    }
-
-    @Override
-    public synchronized void setHandshakeApplicationProtocolSelector(
-        BiFunction<SSLSocket, List<String>, String> selector) {
-        applicationProtocolSelector = selector;
-        if ((handshaker != null) && !handshaker.activated()) {
-            handshaker.setApplicationProtocolSelectorSSLSocket(selector);
-        }
-    }
-
-    @Override
-    public synchronized BiFunction<SSLSocket, List<String>, String>
-        getHandshakeApplicationProtocolSelector() {
-        return this.applicationProtocolSelector;
-    }
-
-    //
-    // We allocate a separate thread to deliver handshake completion
-    // events.  This ensures that the notifications don't block the
-    // protocol state machine.
-    //
-    private static class NotifyHandshake implements Runnable {
-
-        private Set<Map.Entry<HandshakeCompletedListener,AccessControlContext>>
-                targets;        // who gets notified
-        private HandshakeCompletedEvent event;          // the notification
-
-        NotifyHandshake(
-            Set<Map.Entry<HandshakeCompletedListener,AccessControlContext>>
-            entrySet, HandshakeCompletedEvent e) {
-
-            targets = new HashSet<>(entrySet);          // clone the entry set
-            event = e;
-        }
-
-        @Override
-        public void run() {
-            // Don't need to synchronize, as it only runs in one thread.
-            for (Map.Entry<HandshakeCompletedListener,AccessControlContext>
-                entry : targets) {
-
-                final HandshakeCompletedListener l = entry.getKey();
-                AccessControlContext acc = entry.getValue();
-                AccessController.doPrivileged(new PrivilegedAction<Void>() {
-                    @Override
-                    public Void run() {
-                        l.handshakeCompleted(event);
-                        return null;
-                    }
-                }, acc);
-            }
-        }
-    }
-
-    /**
-     * Returns a printable representation of this end of the connection.
-     */
-    @Override
-    public String toString() {
-        StringBuilder retval = new StringBuilder(80);
-
-        retval.append(Integer.toHexString(hashCode()));
-        retval.append("[");
-        retval.append(sess.getCipherSuite());
-        retval.append(": ");
-
-        retval.append(super.toString());
-        retval.append("]");
-
-        return retval.toString();
-    }
 }
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,15 +25,21 @@
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
 import javax.crypto.BadPaddingException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLProtocolException;
 
-import javax.net.ssl.*;
-
-import sun.security.util.HexDumpEncoder;
-
+import sun.security.ssl.SSLCipher.SSLReadCipher;
+import sun.security.ssl.KeyUpdate.KeyUpdateMessage;
+import sun.security.ssl.KeyUpdate.KeyUpdateRequest;
 
 /**
  * {@code InputRecord} implementation for {@code SSLSocket}.
@@ -41,30 +47,34 @@
  * @author David Brownell
  */
 final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
-    private OutputStream deliverStream = null;
-    private byte[] temporary = new byte[1024];
-
-    // used by handshake hash computation for handshake fragment
-    private byte prevType = -1;
-    private int hsMsgOff = 0;
-    private int hsMsgLen = 0;
+    private InputStream is = null;
+    private OutputStream os = null;
+    private final byte[] temporary = new byte[1024];
 
     private boolean formatVerified = false;     // SSLv2 ruled out?
 
+    // Cache for incomplete handshake messages.
+    private ByteBuffer handshakeBuffer = null;
+
     private boolean hasHeader = false;          // Had read the record header
 
-    SSLSocketInputRecord() {
-        this.readAuthenticator = MAC.TLS_NULL;
+    SSLSocketInputRecord(HandshakeHash handshakeHash) {
+        super(handshakeHash, SSLReadCipher.nullTlsReadCipher());
     }
 
     @Override
-    int bytesInCompletePacket(InputStream is) throws IOException {
-
+    int bytesInCompletePacket() throws IOException {
         if (!hasHeader) {
             // read exactly one record
-            int really = read(is, temporary, 0, headerSize);
-            if (really < 0) {
-                throw new EOFException("SSL peer shut down incorrectly");
+            try {
+                int really = read(is, temporary, 0, headerSize);
+                if (really < 0) {
+                    // EOF: peer shut down incorrectly
+                    return -1;
+                }
+            } catch (EOFException eofe) {
+                // The caller will handle EOF.
+                return -1;
             }
             hasHeader = true;
         }
@@ -75,19 +85,21 @@
         /*
          * If we have already verified previous packets, we can
          * ignore the verifications steps, and jump right to the
-         * determination.  Otherwise, try one last hueristic to
+         * determination.  Otherwise, try one last heuristic to
          * see if it's SSL/TLS.
          */
         if (formatVerified ||
-                (byteZero == ct_handshake) || (byteZero == ct_alert)) {
+                (byteZero == ContentType.HANDSHAKE.id) ||
+                (byteZero == ContentType.ALERT.id)) {
             /*
              * Last sanity check that it's not a wild record
              */
-            ProtocolVersion recordVersion =
-                    ProtocolVersion.valueOf(temporary[1], temporary[2]);
-
-            // check the record version
-            checkRecordVersion(recordVersion, false);
+            if (!ProtocolVersion.isNegotiable(
+                    temporary[1], temporary[2], false, false)) {
+                throw new SSLException("Unrecognized record version " +
+                        ProtocolVersion.nameOf(temporary[1], temporary[2]) +
+                        " , plaintext connection?");
+            }
 
             /*
              * Reasonably sure this is a V3, disable further checks.
@@ -112,11 +124,12 @@
             boolean isShort = ((byteZero & 0x80) != 0);
 
             if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) {
-                ProtocolVersion recordVersion =
-                        ProtocolVersion.valueOf(temporary[3], temporary[4]);
-
-                // check the record version
-                checkRecordVersion(recordVersion, true);
+                if (!ProtocolVersion.isNegotiable(
+                        temporary[3], temporary[4], false, false)) {
+                    throw new SSLException("Unrecognized record version " +
+                            ProtocolVersion.nameOf(temporary[3], temporary[4]) +
+                            " , plaintext connection?");
+                }
 
                 /*
                  * Client or Server Hello
@@ -140,10 +153,10 @@
         return len;
     }
 
-    // destination.position() is zero.
+    // Note that the input arguments are not used actually.
     @Override
-    Plaintext decode(InputStream is, ByteBuffer destination)
-            throws IOException, BadPaddingException {
+    Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
+            int srcsLength) throws IOException, BadPaddingException {
 
         if (isClosed) {
             return null;
@@ -167,36 +180,44 @@
              * alert message. If it's not, it is either invalid or an
              * SSLv2 message.
              */
-            if ((temporary[0] != ct_handshake) &&
-                (temporary[0] != ct_alert)) {
-
-                plaintext = handleUnknownRecord(is, temporary, destination);
+            if ((temporary[0] != ContentType.HANDSHAKE.id) &&
+                (temporary[0] != ContentType.ALERT.id)) {
+                hasHeader = false;
+                return handleUnknownRecord(temporary);
             }
         }
 
-        if (plaintext == null) {
-            plaintext = decodeInputRecord(is, temporary, destination);
-        }
+        // The record header should has consumed.
+        hasHeader = false;
+        return decodeInputRecord(temporary);
+    }
 
-        // The record header should has comsumed.
-        hasHeader = false;
-
-        return plaintext;
+    @Override
+    void setReceiverStream(InputStream inputStream) {
+        this.is = inputStream;
     }
 
     @Override
     void setDeliverStream(OutputStream outputStream) {
-        this.deliverStream = outputStream;
+        this.os = outputStream;
     }
 
     // Note that destination may be null
-    private Plaintext decodeInputRecord(InputStream is, byte[] header,
-            ByteBuffer destination) throws IOException, BadPaddingException {
+    private Plaintext[] decodeInputRecord(
+            byte[] header) throws IOException, BadPaddingException {
+        byte contentType = header[0];                   // pos: 0
+        byte majorVersion = header[1];                  // pos: 1
+        byte minorVersion = header[2];                  // pos: 2
+        int contentLen = ((header[3] & 0xFF) << 8) +
+                           (header[4] & 0xFF);          // pos: 3, 4
 
-        byte contentType = header[0];
-        byte majorVersion = header[1];
-        byte minorVersion = header[2];
-        int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF);
+        if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+            SSLLogger.fine(
+                    "READ: " +
+                    ProtocolVersion.nameOf(majorVersion, minorVersion) +
+                    " " + ContentType.nameOf(contentType) + ", length = " +
+                    contentLen);
+        }
 
         //
         // Check for upper bound.
@@ -210,10 +231,7 @@
         //
         // Read a complete record.
         //
-        if (destination == null) {
-            destination = ByteBuffer.allocate(headerSize + contentLen);
-        }  // Otherwise, the destination buffer should have enough room.
-
+        ByteBuffer destination = ByteBuffer.allocate(headerSize + contentLen);
         int dstPos = destination.position();
         destination.put(temporary, 0, headerSize);
         while (contentLen > 0) {
@@ -229,100 +247,129 @@
         destination.flip();
         destination.position(dstPos + headerSize);
 
-        if (debug != null && Debug.isOn("record")) {
-             System.out.println(Thread.currentThread().getName() +
-                    ", READ: " +
-                    ProtocolVersion.valueOf(majorVersion, minorVersion) +
-                    " " + Record.contentName(contentType) + ", length = " +
+        if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+            SSLLogger.fine(
+                    "READ: " +
+                    ProtocolVersion.nameOf(majorVersion, minorVersion) +
+                    " " + ContentType.nameOf(contentType) + ", length = " +
                     destination.remaining());
         }
 
         //
         // Decrypt the fragment
         //
-        ByteBuffer plaintext =
-            decrypt(readAuthenticator, readCipher, contentType, destination);
+        ByteBuffer fragment;
+        try {
+            Plaintext plaintext =
+                    readCipher.decrypt(contentType, destination, null);
+            fragment = plaintext.fragment;
+            contentType = plaintext.contentType;
+        } catch (BadPaddingException bpe) {
+            throw bpe;
+        } catch (GeneralSecurityException gse) {
+            throw (SSLProtocolException)(new SSLProtocolException(
+                    "Unexpected exception")).initCause(gse);
+        }
 
-        if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
+        if (contentType != ContentType.HANDSHAKE.id &&
+                handshakeBuffer != null && handshakeBuffer.hasRemaining()) {
             throw new SSLProtocolException(
-                    "Expected to get a handshake fragment");
+                    "Expecting a handshake fragment, but received " +
+                    ContentType.nameOf(contentType));
         }
 
         //
-        // handshake hashing
+        // parse handshake messages
         //
-        if (contentType == ct_handshake) {
-            int pltPos = plaintext.position();
-            int pltLim = plaintext.limit();
-            int frgPos = pltPos;
-            for (int remains = plaintext.remaining(); remains > 0;) {
-                int howmuch;
-                byte handshakeType;
-                if (hsMsgOff < hsMsgLen) {
-                    // a fragment of the handshake message
-                    howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
-                    handshakeType = prevType;
+        if (contentType == ContentType.HANDSHAKE.id) {
+            ByteBuffer handshakeFrag = fragment;
+            if ((handshakeBuffer != null) &&
+                    (handshakeBuffer.remaining() != 0)) {
+                ByteBuffer bb = ByteBuffer.wrap(new byte[
+                        handshakeBuffer.remaining() + fragment.remaining()]);
+                bb.put(handshakeBuffer);
+                bb.put(fragment);
+                handshakeFrag = bb.rewind();
+                handshakeBuffer = null;
+            }
 
-                    hsMsgOff += howmuch;
-                    if (hsMsgOff == hsMsgLen) {
-                        // Now is a complete handshake message.
-                        hsMsgOff = 0;
-                        hsMsgLen = 0;
-                    }
-                } else {    // hsMsgOff == hsMsgLen, a new handshake message
-                    handshakeType = plaintext.get();
-                    int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
-                                       ((plaintext.get() & 0xFF) << 8) |
-                                        (plaintext.get() & 0xFF);
-                    plaintext.position(frgPos);
-                    if (remains < (handshakeLen + 1)) { // 1: handshake type
-                        // This handshake message is fragmented.
-                        prevType = handshakeType;
-                        hsMsgOff = remains - 4;         // 4: handshake header
-                        hsMsgLen = handshakeLen;
-                    }
-
-                    howmuch = Math.min(handshakeLen + 4, remains);
+            ArrayList<Plaintext> plaintexts = new ArrayList<>(5);
+            while (handshakeFrag.hasRemaining()) {
+                int remaining = handshakeFrag.remaining();
+                if (remaining < handshakeHeaderSize) {
+                    handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
+                    handshakeBuffer.put(handshakeFrag);
+                    handshakeBuffer.rewind();
+                    break;
                 }
 
-                plaintext.limit(frgPos + howmuch);
+                handshakeFrag.mark();
+                // skip the first byte: handshake type
+                byte handshakeType = handshakeFrag.get();
+                int handshakeBodyLen = Record.getInt24(handshakeFrag);
+                handshakeFrag.reset();
+                int handshakeMessageLen =
+                        handshakeHeaderSize + handshakeBodyLen;
+                if (remaining < handshakeMessageLen) {
+                    handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
+                    handshakeBuffer.put(handshakeFrag);
+                    handshakeBuffer.rewind();
+                    break;
+                } if (remaining == handshakeMessageLen) {
+                    if (handshakeHash.isHashable(handshakeType)) {
+                        handshakeHash.receive(handshakeFrag);
+                    }
 
-                if (handshakeType == HandshakeMessage.ht_hello_request) {
-                    // omitted from handshake hash computation
-                } else if ((handshakeType != HandshakeMessage.ht_finished) &&
-                    (handshakeType != HandshakeMessage.ht_certificate_verify)) {
-
-                    if (handshakeHash == null) {
-                        // used for cache only
-                        handshakeHash = new HandshakeHash(false);
-                    }
-                    handshakeHash.update(plaintext);
+                    plaintexts.add(
+                        new Plaintext(contentType,
+                            majorVersion, minorVersion, -1, -1L, handshakeFrag)
+                    );
+                    break;
                 } else {
-                    // Reserve until this handshake message has been processed.
-                    if (handshakeHash == null) {
-                        // used for cache only
-                        handshakeHash = new HandshakeHash(false);
+                    int fragPos = handshakeFrag.position();
+                    int fragLim = handshakeFrag.limit();
+                    int nextPos = fragPos + handshakeMessageLen;
+                    handshakeFrag.limit(nextPos);
+
+                    if (handshakeHash.isHashable(handshakeType)) {
+                        handshakeHash.receive(handshakeFrag);
                     }
-                    handshakeHash.reserve(plaintext);
-                }
 
-                plaintext.position(frgPos + howmuch);
-                plaintext.limit(pltLim);
+                    plaintexts.add(
+                        new Plaintext(contentType, majorVersion, minorVersion,
+                            -1, -1L, handshakeFrag.slice())
+                    );
 
-                frgPos += howmuch;
-                remains -= howmuch;
+                    handshakeFrag.position(nextPos);
+                    handshakeFrag.limit(fragLim);
+                }
             }
-            plaintext.position(pltPos);
+
+            return plaintexts.toArray(new Plaintext[0]);
         }
 
-        return new Plaintext(contentType,
-                majorVersion, minorVersion, -1, -1L, plaintext);
-                // recordEpoch, recordSeq, plaintext);
+        // KeyLimit check during application data.
+        // atKeyLimit() inactive when limits not checked, tc set when limits
+        // are active.
+
+        if (readCipher.atKeyLimit()) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine("KeyUpdate: triggered, read side.");
+            }
+
+            PostHandshakeContext p = new PostHandshakeContext(tc);
+            KeyUpdate.handshakeProducer.produce(p,
+                    new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED));
+        }
+
+        return new Plaintext[] {
+                new Plaintext(contentType,
+                    majorVersion, minorVersion, -1, -1L, fragment)
+            };
     }
 
-    private Plaintext handleUnknownRecord(InputStream is, byte[] header,
-            ByteBuffer destination) throws IOException, BadPaddingException {
-
+    private Plaintext[] handleUnknownRecord(
+            byte[] header) throws IOException, BadPaddingException {
         byte firstByte = header[0];
         byte thirdByte = header[2];
 
@@ -347,19 +394,16 @@
                  * error message, one that's treated as fatal by
                  * clients (Otherwise we'll hang.)
                  */
-                deliverStream.write(SSLRecord.v2NoCipher);      // SSLv2Hello
+                os.write(SSLRecord.v2NoCipher);      // SSLv2Hello
 
-                if (debug != null) {
-                    if (Debug.isOn("record")) {
-                         System.out.println(Thread.currentThread().getName() +
+                if (SSLLogger.isOn) {
+                    if (SSLLogger.isOn("record")) {
+                         SSLLogger.fine(
                                 "Requested to negotiate unsupported SSLv2!");
                     }
 
-                    if (Debug.isOn("packet")) {
-                        Debug.printHex(
-                                "[Raw write]: length = " +
-                                SSLRecord.v2NoCipher.length,
-                                SSLRecord.v2NoCipher);
+                    if (SSLLogger.isOn("packet")) {
+                        SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
                     }
                 }
 
@@ -368,9 +412,7 @@
 
             int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF);
 
-            if (destination == null) {
-                destination = ByteBuffer.allocate(headerSize + msgLen);
-            }
+            ByteBuffer destination = ByteBuffer.allocate(headerSize + msgLen);
             destination.put(temporary, 0, headerSize);
             msgLen -= 3;            // had read 3 bytes of content as header
             while (msgLen > 0) {
@@ -391,23 +433,20 @@
              * V3 ClientHello message, and pass it up.
              */
             destination.position(2);     // exclude the header
-
-            if (handshakeHash == null) {
-                // used for cache only
-                handshakeHash = new HandshakeHash(false);
-            }
-            handshakeHash.update(destination);
+            handshakeHash.receive(destination);
             destination.position(0);
 
             ByteBuffer converted = convertToClientHello(destination);
 
-            if (debug != null && Debug.isOn("packet")) {
-                 Debug.printHex(
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                SSLLogger.fine(
                         "[Converted] ClientHello", converted);
             }
 
-            return new Plaintext(ct_handshake,
-                majorVersion, minorVersion, -1, -1L, converted);
+            return new Plaintext[] {
+                    new Plaintext(ContentType.HANDSHAKE.id,
+                    majorVersion, minorVersion, -1, -1L, converted)
+                };
         } else {
             if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
                 throw new SSLException("SSL V2.0 servers are not supported.");
@@ -424,13 +463,15 @@
         while (n < len) {
             int readLen = is.read(buffer, offset + n, len - n);
             if (readLen < 0) {
+                if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                    SSLLogger.fine("Raw read: EOF");
+                }
                 return -1;
             }
 
-            if (debug != null && Debug.isOn("packet")) {
-                 Debug.printHex(
-                        "[Raw read]: length = " + readLen,
-                        buffer, offset + n, readLen);
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                ByteBuffer bb = ByteBuffer.wrap(buffer, offset + n, readLen);
+                SSLLogger.fine("Raw read", bb);
             }
 
             n += readLen;
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,14 +25,14 @@
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.util.Arrays;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLHandshakeException;
 
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import sun.security.util.HexDumpEncoder;
-
+import sun.security.ssl.KeyUpdate.KeyUpdateMessage;
+import sun.security.ssl.KeyUpdate.KeyUpdateRequest;
 
 /**
  * {@code OutputRecord} implementation for {@code SSLSocket}.
@@ -40,11 +40,16 @@
 final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord {
     private OutputStream deliverStream = null;
 
-    SSLSocketOutputRecord() {
-        this.writeAuthenticator = MAC.TLS_NULL;
+    SSLSocketOutputRecord(HandshakeHash handshakeHash) {
+        this(handshakeHash, null);
+    }
 
+    SSLSocketOutputRecord(HandshakeHash handshakeHash,
+            TransportContext tc) {
+        super(handshakeHash, SSLCipher.SSLWriteCipher.nullTlsWriteCipher());
+        this.tc = tc;
         this.packetSize = SSLRecord.maxRecordSize;
-        this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
+        this.protocolVersion = ProtocolVersion.NONE;
     }
 
     @Override
@@ -55,25 +60,22 @@
 
         write(level);
         write(description);
-
-        if (debug != null && Debug.isOn("record")) {
-            System.out.println(Thread.currentThread().getName() +
-                    ", WRITE: " + protocolVersion +
-                    " " + Record.contentName(Record.ct_alert) +
+        if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+            SSLLogger.fine("WRITE: " + protocolVersion +
+                    " " + ContentType.ALERT.name +
                     ", length = " + (count - headerSize));
         }
 
         // Encrypt the fragment and wrap up a record.
-        encrypt(writeAuthenticator, writeCipher,
-                Record.ct_alert, headerSize);
+        encrypt(writeCipher, ContentType.ALERT.id, headerSize);
 
         // deliver this message
         deliverStream.write(buf, 0, count);    // may throw IOException
         deliverStream.flush();                 // may throw IOException
 
-        if (debug != null && Debug.isOn("packet")) {
-             Debug.printHex(
-                    "[Raw write]: length = " + count, buf, 0, count);
+        if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+            SSLLogger.fine("Raw write",
+                    (new ByteArrayInputStream(buf, 0, count)));
         }
 
         // reset the internal buffer
@@ -83,12 +85,11 @@
     @Override
     void encodeHandshake(byte[] source,
             int offset, int length) throws IOException {
-
         if (firstMessage) {
             firstMessage = false;
 
             if ((helloVersion == ProtocolVersion.SSL20Hello) &&
-                (source[offset] == HandshakeMessage.ht_client_hello) &&
+                (source[offset] == SSLHandshake.CLIENT_HELLO.id) &&
                                             //  5: recode header size
                 (source[offset + 4 + 2 + 32] == 0)) {
                                             // V3 session ID is empty
@@ -101,12 +102,12 @@
 
                 byte[] record = v2ClientHello.array();  // array offset is zero
                 int limit = v2ClientHello.limit();
-                handshakeHash.update(record, 2, (limit - 2));
+                handshakeHash.deliver(record, 2, (limit - 2));
 
-                if (debug != null && Debug.isOn("record")) {
-                     System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: SSLv2 ClientHello message" +
-                        ", length = " + limit);
+                if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                    SSLLogger.fine(
+                            "WRITE: SSLv2 ClientHello message" +
+                            ", length = " + limit);
                 }
 
                 // deliver this message
@@ -117,9 +118,9 @@
                 deliverStream.write(record, 0, limit);
                 deliverStream.flush();
 
-                if (debug != null && Debug.isOn("packet")) {
-                     Debug.printHex(
-                            "[Raw write]: length = " + count, record, 0, limit);
+                if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                    SSLLogger.fine("Raw write",
+                            (new ByteArrayInputStream(record, 0, limit)));
                 }
 
                 return;
@@ -127,8 +128,8 @@
         }
 
         byte handshakeType = source[0];
-        if (handshakeType != HandshakeMessage.ht_hello_request) {
-            handshakeHash.update(source, offset, length);
+        if (handshakeHash.isHashable(handshakeType)) {
+            handshakeHash.deliver(source, offset, length);
         }
 
         int fragLimit = getFragLimit();
@@ -153,24 +154,23 @@
                 return;
             }
 
-            if (debug != null && Debug.isOn("record")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: " + protocolVersion +
-                        " " + Record.contentName(Record.ct_handshake) +
+            if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                SSLLogger.fine(
+                        "WRITE: " + protocolVersion +
+                        " " + ContentType.HANDSHAKE.name +
                         ", length = " + (count - headerSize));
             }
 
             // Encrypt the fragment and wrap up a record.
-            encrypt(writeAuthenticator, writeCipher,
-                    Record.ct_handshake, headerSize);
+            encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize);
 
             // deliver this message
             deliverStream.write(buf, 0, count);    // may throw IOException
             deliverStream.flush();                 // may throw IOException
 
-            if (debug != null && Debug.isOn("packet")) {
-                 Debug.printHex(
-                        "[Raw write]: length = " + count, buf, 0, count);
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                SSLLogger.fine("Raw write",
+                        (new ByteArrayInputStream(buf, 0, count)));
             }
 
             // reset the offset
@@ -190,24 +190,16 @@
 
         write((byte)1);         // byte 1: change_cipher_spec(
 
-        if (debug != null && Debug.isOn("record")) {
-            System.out.println(Thread.currentThread().getName() +
-                    ", WRITE: " + protocolVersion +
-                    " " + Record.contentName(Record.ct_change_cipher_spec) +
-                    ", length = " + (count - headerSize));
-        }
-
         // Encrypt the fragment and wrap up a record.
-        encrypt(writeAuthenticator, writeCipher,
-                Record.ct_change_cipher_spec, headerSize);
+        encrypt(writeCipher, ContentType.CHANGE_CIPHER_SPEC.id, headerSize);
 
         // deliver this message
         deliverStream.write(buf, 0, count);        // may throw IOException
         // deliverStream.flush();                  // flush in Finished
 
-        if (debug != null && Debug.isOn("packet")) {
-             Debug.printHex(
-                    "[Raw write]: length = " + count, buf, 0, count);
+        if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+            SSLLogger.fine("Raw write",
+                    (new ByteArrayInputStream(buf, 0, count)));
         }
 
         // reset the internal buffer
@@ -221,24 +213,23 @@
             return;
         }
 
-        if (debug != null && Debug.isOn("record")) {
-            System.out.println(Thread.currentThread().getName() +
-                    ", WRITE: " + protocolVersion +
-                    " " + Record.contentName(Record.ct_handshake) +
+        if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+            SSLLogger.fine(
+                    "WRITE: " + protocolVersion +
+                    " " + ContentType.HANDSHAKE.name +
                     ", length = " + (count - headerSize));
         }
 
         // Encrypt the fragment and wrap up a record.
-        encrypt(writeAuthenticator, writeCipher,
-                    Record.ct_handshake, headerSize);
+        encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize);
 
         // deliver this message
         deliverStream.write(buf, 0, count);    // may throw IOException
         deliverStream.flush();                 // may throw IOException
 
-        if (debug != null && Debug.isOn("packet")) {
-             Debug.printHex(
-                    "[Raw write]: length = " + count, buf, 0, count);
+        if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+            SSLLogger.fine("Raw write",
+                    (new ByteArrayInputStream(buf, 0, count)));
         }
 
         // reset the internal buffer
@@ -247,11 +238,10 @@
 
     @Override
     void deliver(byte[] source, int offset, int length) throws IOException {
-
-        if (writeAuthenticator.seqNumOverflow()) {
-            if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(Thread.currentThread().getName() +
-                    ", sequence number extremely close to overflow " +
+        if (writeCipher.authenticator.seqNumOverflow()) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.fine(
+                    "sequence number extremely close to overflow " +
                     "(2^64-1 packets). Closing connection.");
             }
 
@@ -260,16 +250,11 @@
 
         boolean isFirstRecordOfThePayload = true;
         for (int limit = (offset + length); offset < limit;) {
-            int macLen = 0;
-            if (writeAuthenticator instanceof MAC) {
-                macLen = ((MAC)writeAuthenticator).MAClen();
-            }
-
             int fragLen;
             if (packetSize > 0) {
                 fragLen = Math.min(maxRecordSize, packetSize);
-                fragLen = writeCipher.calculateFragmentSize(
-                        fragLen, macLen, headerSize);
+                fragLen =
+                        writeCipher.calculateFragmentSize(fragLen, headerSize);
 
                 fragLen = Math.min(fragLen, Record.maxDataSize);
             } else {
@@ -292,24 +277,23 @@
             count = position;
             write(source, offset, fragLen);
 
-            if (debug != null && Debug.isOn("record")) {
-                System.out.println(Thread.currentThread().getName() +
-                        ", WRITE: " + protocolVersion +
-                        " " + Record.contentName(Record.ct_application_data) +
-                        ", length = " + (count - headerSize));
+            if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+                SSLLogger.fine(
+                        "WRITE: " + protocolVersion +
+                        " " + ContentType.APPLICATION_DATA.name +
+                        ", length = " + (count - position));
             }
 
             // Encrypt the fragment and wrap up a record.
-            encrypt(writeAuthenticator, writeCipher,
-                    Record.ct_application_data, headerSize);
+            encrypt(writeCipher, ContentType.APPLICATION_DATA.id, headerSize);
 
             // deliver this message
             deliverStream.write(buf, 0, count);    // may throw IOException
             deliverStream.flush();                 // may throw IOException
 
-            if (debug != null && Debug.isOn("packet")) {
-                 Debug.printHex(
-                        "[Raw write]: length = " + count, buf, 0, count);
+            if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+                SSLLogger.fine("Raw write",
+                        (new ByteArrayInputStream(buf, 0, count)));
             }
 
             // reset the internal buffer
@@ -320,6 +304,18 @@
             }
 
             offset += fragLen;
+
+            // atKeyLimit() inactive when limits not checked, tc set when limits
+            // are active.
+            if (writeCipher.atKeyLimit()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine("KeyUpdate: triggered, write side.");
+                }
+
+                PostHandshakeContext p = new PostHandshakeContext(tc);
+                KeyUpdate.handshakeProducer.produce(p,
+                        new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED));
+            }
         }
     }
 
@@ -358,16 +354,11 @@
     }
 
     private int getFragLimit() {
-        int macLen = 0;
-        if (writeAuthenticator instanceof MAC) {
-            macLen = ((MAC)writeAuthenticator).MAClen();
-        }
-
         int fragLimit;
         if (packetSize > 0) {
             fragLimit = Math.min(maxRecordSize, packetSize);
-            fragLimit = writeCipher.calculateFragmentSize(
-                    fragLimit, macLen, headerSize);
+            fragLimit =
+                    writeCipher.calculateFragmentSize(fragLimit, headerSize);
 
             fragLimit = Math.min(fragLimit, Record.maxDataSize);
         } else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLStringizer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.nio.ByteBuffer;
+
+/**
+ * Interface to decode a {@code ByteBuffer} into legible {@code String}.
+ */
+interface SSLStringizer {
+    /**
+     * Returns a legible string representation of a {@code ByteBuffer}.
+     *
+     * Note that the implementation MUST not change the internal status of
+     * the {@code buffer}.
+     */
+    String toString(ByteBuffer buffer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.internal.spec.TlsKeyMaterialParameterSpec;
+import sun.security.internal.spec.TlsKeyMaterialSpec;
+import sun.security.ssl.CipherSuite.HashAlg;
+import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
+
+enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
+    SSL30       ("kdf_ssl30", new S30TrafficKeyDerivationGenerator()),
+    TLS10       ("kdf_tls10", new T10TrafficKeyDerivationGenerator()),
+    TLS12       ("kdf_tls12", new T12TrafficKeyDerivationGenerator()),
+    TLS13       ("kdf_tls13", new T13TrafficKeyDerivationGenerator());
+
+    final String name;
+    final SSLKeyDerivationGenerator keyDerivationGenerator;
+
+    SSLTrafficKeyDerivation(String name,
+            SSLKeyDerivationGenerator keyDerivationGenerator) {
+        this.name = name;
+        this.keyDerivationGenerator = keyDerivationGenerator;
+    }
+
+    static SSLTrafficKeyDerivation valueOf(ProtocolVersion protocolVersion) {
+        switch (protocolVersion) {
+            case SSL30:
+                return SSLTrafficKeyDerivation.SSL30;
+            case TLS10:
+            case TLS11:
+            case DTLS10:
+                return SSLTrafficKeyDerivation.TLS10;
+            case TLS12:
+            case DTLS12:
+                return SSLTrafficKeyDerivation.TLS12;
+            case TLS13:
+                return SSLTrafficKeyDerivation.TLS13;
+        }
+
+        return null;
+    }
+
+    @Override
+    public SSLKeyDerivation createKeyDerivation(HandshakeContext context,
+            SecretKey secretKey) throws IOException {
+        return keyDerivationGenerator.createKeyDerivation(context, secretKey);
+    }
+
+    private static final class S30TrafficKeyDerivationGenerator
+            implements SSLKeyDerivationGenerator {
+        private S30TrafficKeyDerivationGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+            HandshakeContext context, SecretKey secretKey) throws IOException {
+            return new LegacyTrafficKeyDerivation(context, secretKey);
+        }
+    }
+
+    private static final class T10TrafficKeyDerivationGenerator
+            implements SSLKeyDerivationGenerator {
+        private T10TrafficKeyDerivationGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+            HandshakeContext context, SecretKey secretKey) throws IOException {
+            return new LegacyTrafficKeyDerivation(context, secretKey);
+        }
+    }
+
+    private static final class T12TrafficKeyDerivationGenerator
+            implements SSLKeyDerivationGenerator {
+        private T12TrafficKeyDerivationGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+            HandshakeContext context, SecretKey secretKey) throws IOException {
+            return new LegacyTrafficKeyDerivation(context, secretKey);
+        }
+    }
+
+    private static final class T13TrafficKeyDerivationGenerator
+            implements SSLKeyDerivationGenerator {
+        private T13TrafficKeyDerivationGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context,
+                SecretKey secretKey) throws IOException {
+            return new T13TrafficKeyDerivation(context, secretKey);
+        }
+    }
+
+    static final class T13TrafficKeyDerivation implements SSLKeyDerivation {
+        private final CipherSuite cs;
+        private final SecretKey secret;
+
+        T13TrafficKeyDerivation(
+                HandshakeContext context, SecretKey secret) {
+            this.secret = secret;
+            this.cs = context.negotiatedCipherSuite;
+        }
+
+        @Override
+        public SecretKey deriveKey(String algorithm,
+                AlgorithmParameterSpec params) throws IOException {
+            KeySchedule ks = KeySchedule.valueOf(algorithm);
+            try {
+                HKDF hkdf = new HKDF(cs.hashAlg.name);
+                byte[] hkdfInfo =
+                        createHkdfInfo(ks.label, ks.getKeyLength(cs));
+                return hkdf.expand(secret, hkdfInfo,
+                        ks.getKeyLength(cs),
+                        ks.getAlgorithm(cs, algorithm));
+            } catch (GeneralSecurityException gse) {
+                throw (SSLHandshakeException)(new SSLHandshakeException(
+                    "Could not generate secret").initCause(gse));
+            }
+        }
+
+        private static byte[] createHkdfInfo(
+                byte[] label, int length) throws IOException {
+            byte[] info = new byte[4 + label.length];
+            ByteBuffer m = ByteBuffer.wrap(info);
+            try {
+                Record.putInt16(m, length);
+                Record.putBytes8(m, label);
+                Record.putInt8(m, 0x00);    // zero-length context
+            } catch (IOException ioe) {
+                // unlikely
+                throw new RuntimeException("Unexpected exception", ioe);
+            }
+
+            return info;
+        }
+    }
+
+    private enum KeySchedule {
+        // Note that we use enum name as the key/ name.
+        TlsKey              ("key", false),
+        TlsIv               ("iv",  true),
+        TlsUpdateNplus1     ("traffic upd", false);
+
+        private final byte[] label;
+        private final boolean isIv;
+
+        private KeySchedule(String label, boolean isIv) {
+            this.label = ("tls13 " + label).getBytes();
+            this.isIv = isIv;
+        }
+
+        int getKeyLength(CipherSuite cs) {
+            if (this == KeySchedule.TlsUpdateNplus1)
+                return cs.hashAlg.hashLength;
+            return isIv ? cs.bulkCipher.ivSize : cs.bulkCipher.keySize;
+        }
+
+        String getAlgorithm(CipherSuite cs, String algorithm) {
+            return isIv ? algorithm : cs.bulkCipher.algorithm;
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    static final class LegacyTrafficKeyDerivation implements SSLKeyDerivation {
+        private final HandshakeContext context;
+        private final SecretKey masterSecret;
+        private final TlsKeyMaterialSpec keyMaterialSpec;
+
+        LegacyTrafficKeyDerivation(
+                HandshakeContext context, SecretKey masterSecret) {
+            this.context = context;
+            this.masterSecret = masterSecret;
+
+            CipherSuite cipherSuite = context.negotiatedCipherSuite;
+            ProtocolVersion protocolVersion = context.negotiatedProtocol;
+
+            /*
+             * For both the read and write sides of the protocol, we use the
+             * master to generate MAC secrets and cipher keying material.  Block
+             * ciphers need initialization vectors, which we also generate.
+             *
+             * First we figure out how much keying material is needed.
+             */
+            int hashSize = cipherSuite.macAlg.size;
+            boolean is_exportable = cipherSuite.exportable;
+            SSLCipher cipher = cipherSuite.bulkCipher;
+            int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0;
+
+            // Which algs/params do we need to use?
+            String keyMaterialAlg;
+            HashAlg hashAlg;
+
+            byte majorVersion = protocolVersion.major;
+            byte minorVersion = protocolVersion.minor;
+            if (protocolVersion.isDTLS) {
+                // Use TLS version number for DTLS key calculation
+                if (protocolVersion.id == ProtocolVersion.DTLS10.id) {
+                    majorVersion = ProtocolVersion.TLS11.major;
+                    minorVersion = ProtocolVersion.TLS11.minor;
+
+                    keyMaterialAlg = "SunTlsKeyMaterial";
+                    hashAlg = H_NONE;
+                } else {    // DTLS 1.2+
+                    majorVersion = ProtocolVersion.TLS12.major;
+                    minorVersion = ProtocolVersion.TLS12.minor;
+
+                    keyMaterialAlg = "SunTls12KeyMaterial";
+                    hashAlg = cipherSuite.hashAlg;
+                }
+            } else {
+                if (protocolVersion.id >= ProtocolVersion.TLS12.id) {
+                    keyMaterialAlg = "SunTls12KeyMaterial";
+                    hashAlg = cipherSuite.hashAlg;
+                } else {
+                    keyMaterialAlg = "SunTlsKeyMaterial";
+                    hashAlg = H_NONE;
+                }
+            }
+
+            // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to
+            // protect against the CBC attacks.  AEAD/GCM cipher suites in
+            // TLS v1.2 or later use a fixed IV as the implicit part of the
+            // partially implicit nonce technique described in RFC 5116.
+            int ivSize = cipher.ivSize;
+            if (cipher.cipherType == CipherType.AEAD_CIPHER) {
+                ivSize = cipher.fixedIvSize;
+            } else if (
+                    cipher.cipherType == CipherType.BLOCK_CIPHER &&
+                    protocolVersion.useTLS11PlusSpec()) {
+                ivSize = 0;
+            }
+
+            TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec(
+                    masterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF),
+                    context.clientHelloRandom.randomBytes,
+                    context.serverHelloRandom.randomBytes,
+                    cipher.algorithm, cipher.keySize, expandedKeySize,
+                    ivSize, hashSize,
+                    hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
+
+            try {
+                KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
+                kg.init(spec);
+
+                this.keyMaterialSpec = (TlsKeyMaterialSpec)kg.generateKey();
+            } catch (GeneralSecurityException e) {
+                throw new ProviderException(e);
+            }
+        }
+
+        SecretKey getTrafficKey(String algorithm) {
+            switch (algorithm) {
+                case "clientMacKey":
+                    return keyMaterialSpec.getClientMacKey();
+                case "serverMacKey":
+                    return keyMaterialSpec.getServerMacKey();
+                case "clientWriteKey":
+                    return keyMaterialSpec.getClientCipherKey();
+                case "serverWriteKey":
+                    return keyMaterialSpec.getServerCipherKey();
+                case "clientWriteIv":
+                    IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv();
+                    return  (cliIvSpec == null) ? null :
+                            new SecretKeySpec(cliIvSpec.getIV(), "TlsIv");
+                case "serverWriteIv":
+                    IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv();
+                    return  (srvIvSpec == null) ? null :
+                            new SecretKeySpec(srvIvSpec.getIV(), "TlsIv");
+            }
+
+            return null;
+        }
+
+        @Override
+        public SecretKey deriveKey(String algorithm,
+                AlgorithmParameterSpec params) throws IOException {
+            return getTrafficKey(algorithm);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import javax.crypto.BadPaddingException;
+import javax.net.ssl.SSLHandshakeException;
+
+/**
+ * Interface for SSL/(D)TLS transportation.
+ */
+interface SSLTransport {
+
+    /**
+     * Returns the host name of the peer.
+     *
+     * @return  the host name of the peer, or null if nothing is
+     *          available.
+     */
+    String getPeerHost();
+
+    /**
+     * Returns the port number of the peer.
+     *
+     * @return  the port number of the peer, or -1 if nothing is
+     *          available.
+     */
+    int getPeerPort();
+
+    /**
+     * Shutdown the transport.
+     */
+    default void shutdown() throws IOException {
+        // blank
+    }
+
+    /**
+     * Return true if delegated tasks used for handshaking operations.
+     *
+     * @return true if delegated tasks used for handshaking operations.
+     */
+    boolean useDelegatedTask();
+
+    /**
+     * Decodes an array of SSL/(D)TLS network source data into the
+     * destination application data buffers.
+     *
+     * For SSL/TLS connections, if no source data, the network data may be
+     * received from the underlying underlying SSL/TLS input stream.
+     *
+     * @param context      the transportation context
+     * @param srcs         an array of {@code ByteBuffers} containing the
+     *                      inbound network data
+     * @param srcsOffset   The offset within the {@code srcs} buffer array
+     *                      of the first buffer from which bytes are to be
+     *                      retrieved; it must be non-negative and no larger
+     *                      than {@code srcs.length}.
+     * @param srcsLength   The maximum number of {@code srcs} buffers to be
+     *                      accessed; it must be non-negative and no larger than
+     *                      {@code srcs.length}&nbsp;-&nbsp;{@code srcsOffset}.
+     * @param dsts         an array of {@code ByteBuffers} to hold inbound
+     *                      application data
+     * @param dstsOffset   The offset within the {@code dsts} buffer array
+     *                      of the first buffer from which bytes are to be
+     *                      placed; it must be non-negative and no larger
+     *                      than {@code dsts.length}.
+     * @param dstsLength   The maximum number of {@code dsts} buffers to be
+     *                      accessed; it must be non-negative and no larger than
+     *                      {@code dsts.length}&nbsp;-&nbsp;{@code dstsOffset}.
+     *
+     * @return             a {@code Plaintext} describing the result of
+     *                      the operation
+     * @throws IOException if a problem was encountered while receiving or
+     *                      decoding networking data
+     */
+    static Plaintext decode(TransportContext context,
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
+
+        Plaintext[] plaintexts = null;
+        try {
+            plaintexts =
+                    context.inputRecord.decode(srcs, srcsOffset, srcsLength);
+        } catch (UnsupportedOperationException unsoe) {         // SSLv2Hello
+            // Hack code to deliver SSLv2 error message for SSL/TLS connections.
+            if (!context.sslContext.isDTLS()) {
+                context.outputRecord.encodeV2NoCipher();
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest("may be talking to SSLv2");
+                }
+            }
+
+            context.fatal(Alert.UNEXPECTED_MESSAGE, unsoe);
+        } catch (BadPaddingException bpe) {
+            /*
+             * The basic SSLv3 record protection involves (optional)
+             * encryption for privacy, and an integrity check ensuring
+             * data origin authentication.  We do them both here, and
+             * throw a fatal alert if the integrity check fails.
+             */
+            Alert alert = (context.handshakeContext != null) ?
+                    Alert.HANDSHAKE_FAILURE :
+                    Alert.BAD_RECORD_MAC;
+            context.fatal(alert, bpe);
+        } catch (SSLHandshakeException she) {
+            // may be record sequence number overflow
+            context.fatal(Alert.HANDSHAKE_FAILURE, she);
+        } catch (EOFException eofe) {
+            // rethrow EOFException, the call will handle it if neede.
+            throw eofe;
+        } catch (IOException ioe) {
+            context.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+        }
+
+        if (plaintexts == null || plaintexts.length == 0) {
+            // Connection closed or record should be discarded.
+            return Plaintext.PLAINTEXT_NULL;
+        }
+
+        Plaintext finalPlaintext = Plaintext.PLAINTEXT_NULL;
+        for (Plaintext plainText : plaintexts) {
+            // plainText should never be null for TLS protocols
+            if (plainText == Plaintext.PLAINTEXT_NULL) {
+                // Only happens for DTLS protocols.
+                //
+                // Received a retransmitted flight, and need to retransmit the
+                // previous delivered handshake flight messages.
+                if (context.handshakeContext != null &&
+                    context.handshakeContext.sslConfig.enableRetransmissions &&
+                    context.sslContext.isDTLS()) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) {
+                        SSLLogger.finest("retransmited handshake flight");
+                    }
+
+                    context.outputRecord.launchRetransmission();
+                }   // Otherwise, discard the retransmitted flight.
+            } else if (plainText != null &&
+                    plainText.contentType != ContentType.APPLICATION_DATA.id) {
+                context.dispatch(plainText);
+            }
+
+            if (plainText == null) {
+                plainText = Plaintext.PLAINTEXT_NULL;
+            } else {
+                // File the destination buffers.
+                if (dsts != null && dstsLength > 0 &&
+                    plainText.contentType == ContentType.APPLICATION_DATA.id) {
+
+                    ByteBuffer fragment = plainText.fragment;
+                    int remains = fragment.remaining();
+
+                    // Should have enough room in the destination buffers.
+                    int limit = dstsOffset + dstsLength;
+                    for (int i = dstsOffset;
+                            ((i < limit) && (remains > 0)); i++) {
+
+                        int amount = Math.min(dsts[i].remaining(), remains);
+                        fragment.limit(fragment.position() + amount);
+                        dsts[i].put(fragment);
+                        remains -= amount;
+
+                        if (!dsts[i].hasRemaining()) {
+                            dstsOffset++;
+                        }
+                    }
+
+                    if (remains > 0) {
+                        context.fatal(Alert.INTERNAL_ERROR,
+                            "no sufficient room in the destination buffers");
+                    }
+                }
+            }
+
+            finalPlaintext = plainText;
+        }
+
+        return finalPlaintext;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.security.AlgorithmConstraints;
+import java.security.AccessController;
+import sun.security.util.LegacyAlgorithmConstraints;
+import sun.security.action.GetLongAction;
+
+class ServerHandshakeContext extends HandshakeContext {
+    // To prevent the TLS renegotiation issues, by setting system property
+    // "jdk.tls.rejectClientInitiatedRenegotiation" to true, applications in
+    // server side can disable all client initiated SSL renegotiation
+    // regardless of the support of TLS protocols.
+    //
+    // By default, allow client initiated renegotiation.
+    static final boolean rejectClientInitiatedRenego =
+            Utilities.getBooleanProperty(
+                "jdk.tls.rejectClientInitiatedRenegotiation", false);
+
+    // legacy algorithm constraints
+    static final AlgorithmConstraints legacyAlgorithmConstraints =
+            new LegacyAlgorithmConstraints(
+                    LegacyAlgorithmConstraints.PROPERTY_TLS_LEGACY_ALGS,
+                    new SSLAlgorithmDecomposer());
+
+    // temporary authentication information
+    SSLPossession interimAuthn;
+
+    StatusResponseManager.StaplingParameters stapleParams;
+    CertificateMessage.CertificateEntry currentCertEntry;
+    private static final long DEFAULT_STATUS_RESP_DELAY = 5000L;
+    final long statusRespTimeout;
+
+
+    ServerHandshakeContext(SSLContextImpl sslContext,
+            TransportContext conContext) throws IOException {
+        super(sslContext, conContext);
+        long respTimeOut = AccessController.doPrivileged(
+                    new GetLongAction("jdk.tls.stapling.responseTimeout",
+                        DEFAULT_STATUS_RESP_DELAY));
+        statusRespTimeout = respTimeOut >= 0 ? respTimeOut :
+                DEFAULT_STATUS_RESP_DELAY;
+        handshakeConsumers.put(
+                SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
+    }
+
+    @Override
+    void kickstart() throws IOException {
+        if (!conContext.isNegotiated || kickstartMessageDelivered) {
+            return;
+        }
+
+        SSLHandshake.kickstart(this);
+        kickstartMessageDelivered = true;
+    }
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Mon Jun 25 21:22:16 2018 +0300
+++ /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;
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,1458 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.security.AlgorithmConstraints;
+import java.security.GeneralSecurityException;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import sun.security.ssl.CipherSuite.KeyExchange;
+import sun.security.ssl.ClientHello.ClientHelloMessage;
+import sun.security.ssl.SSLCipher.SSLReadCipher;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedVersionsExtension.SHSupportedVersionsSpec;
+
+/**
+ * Pack of the ServerHello/HelloRetryRequest handshake message.
+ */
+final class ServerHello {
+    static final SSLConsumer handshakeConsumer =
+        new ServerHelloConsumer();
+    static final HandshakeProducer t12HandshakeProducer =
+        new T12ServerHelloProducer();
+    static final HandshakeProducer t13HandshakeProducer =
+        new T13ServerHelloProducer();
+    static final HandshakeProducer hrrHandshakeProducer =
+        new T13HelloRetryRequestProducer();
+
+    static final HandshakeProducer hrrReproducer =
+        new T13HelloRetryRequestReproducer();
+
+    private static final HandshakeConsumer t12HandshakeConsumer =
+        new T12ServerHelloConsumer();
+    private static final HandshakeConsumer t13HandshakeConsumer =
+        new T13ServerHelloConsumer();
+
+    private static final HandshakeConsumer d12HandshakeConsumer =
+        new T12ServerHelloConsumer();
+    private static final HandshakeConsumer d13HandshakeConsumer =
+        new T13ServerHelloConsumer();
+
+    private static final HandshakeConsumer t13HrrHandshakeConsumer =
+        new T13HelloRetryRequestConsumer();
+    private static final HandshakeConsumer d13HrrHandshakeConsumer =
+        new T13HelloRetryRequestConsumer();
+
+    /**
+     * The ServerHello handshake message.
+     */
+    static final class ServerHelloMessage extends HandshakeMessage {
+        final ProtocolVersion           serverVersion;      // TLS 1.3 legacy
+        final RandomCookie              serverRandom;
+        final SessionId                 sessionId;          // TLS 1.3 legacy
+        final CipherSuite               cipherSuite;
+        final byte                      compressionMethod;  // TLS 1.3 legacy
+        final SSLExtensions             extensions;
+
+        // The HelloRetryRequest producer needs to use the ClientHello message
+        // for cookie generation.  Please don't use this field for other
+        // purpose unless it is really necessary.
+        final ClientHelloMessage        clientHello;
+
+        // Reserved for HelloRetryRequest consumer.  Please don't use this
+        // field for other purpose unless it is really necessary.
+        final ByteBuffer                handshakeRecord;
+
+        ServerHelloMessage(HandshakeContext context,
+                ProtocolVersion serverVersion, SessionId sessionId,
+                CipherSuite cipherSuite, RandomCookie serverRandom,
+                ClientHelloMessage clientHello) {
+            super(context);
+
+            this.serverVersion = serverVersion;
+            this.serverRandom = serverRandom;
+            this.sessionId = sessionId;
+            this.cipherSuite = cipherSuite;
+            this.compressionMethod = 0x00;      // Don't support compression.
+            this.extensions = new SSLExtensions(this);
+
+            // Reserve the ClientHello message for cookie generation.
+            this.clientHello = clientHello;
+
+            // The handshakeRecord field is used for HelloRetryRequest consumer
+            // only.  It's fine to set it to null for generating side of the
+            // ServerHello/HelloRetryRequest message.
+            this.handshakeRecord = null;
+        }
+
+        ServerHelloMessage(HandshakeContext context,
+                ByteBuffer m) throws IOException {
+            super(context);
+
+            // Reserve for HelloRetryRequest consumer if needed.
+            this.handshakeRecord = m.duplicate();
+
+            byte major = m.get();
+            byte minor = m.get();
+            this.serverVersion = ProtocolVersion.valueOf(major, minor);
+            if (this.serverVersion == null) {
+                // The client should only request for known protocol versions.
+                context.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "Unsupported protocol version: " +
+                    ProtocolVersion.nameOf(major, minor));
+            }
+
+            this.serverRandom = new RandomCookie(m);
+            this.sessionId = new SessionId(Record.getBytes8(m));
+            sessionId.checkLength(serverVersion.id);
+
+
+            int cipherSuiteId = Record.getInt16(m);
+            this.cipherSuite = CipherSuite.valueOf(cipherSuiteId);
+            if (cipherSuite == null || !context.isNegotiable(cipherSuite)) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Server selected improper ciphersuite " +
+                    CipherSuite.nameOf(cipherSuiteId));
+            }
+
+            this.compressionMethod = m.get();
+            if (compressionMethod != 0) {
+                context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "compression type not supported, " + compressionMethod);
+            }
+
+            SSLExtension[] supportedExtensions;
+            if (serverRandom.isHelloRetryRequest()) {
+                supportedExtensions = context.sslConfig.getEnabledExtensions(
+                            SSLHandshake.HELLO_RETRY_REQUEST);
+            } else {
+                supportedExtensions = context.sslConfig.getEnabledExtensions(
+                            SSLHandshake.SERVER_HELLO);
+            }
+
+            if (m.hasRemaining()) {
+                this.extensions =
+                    new SSLExtensions(this, m, supportedExtensions);
+            } else {
+                this.extensions = new SSLExtensions(this);
+            }
+
+            // The clientHello field is used for HelloRetryRequest producer
+            // only.  It's fine to set it to null for receiving side of
+            // ServerHello/HelloRetryRequest message.
+            this.clientHello = null;        // not used, let it be null;
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return serverRandom.isHelloRetryRequest() ?
+                SSLHandshake.HELLO_RETRY_REQUEST : SSLHandshake.SERVER_HELLO;
+        }
+
+        @Override
+        public int messageLength() {
+            // almost fixed header size, except session ID and extensions:
+            //      major + minor = 2
+            //      random = 32
+            //      session ID len field = 1
+            //      cipher suite = 2
+            //      compression = 1
+            //      extensions: if present, 2 + length of extensions
+            // In TLS 1.3, use of certain extensions is mandatory.
+            return 38 + sessionId.length() + extensions.length();
+        }
+
+        @Override
+        public void send(HandshakeOutStream hos) throws IOException {
+            hos.putInt8(serverVersion.major);
+            hos.putInt8(serverVersion.minor);
+            hos.write(serverRandom.randomBytes);
+            hos.putBytes8(sessionId.getId());
+            hos.putInt8((cipherSuite.id >> 8) & 0xFF);
+            hos.putInt8(cipherSuite.id & 0xff);
+            hos.putInt8(compressionMethod);
+
+            extensions.send(hos);           // In TLS 1.3, use of certain
+                                            // extensions is mandatory.
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"{0}\": '{'\n" +
+                "  \"server version\"      : \"{1}\",\n" +
+                "  \"random\"              : \"{2}\",\n" +
+                "  \"session id\"          : \"{3}\",\n" +
+                "  \"cipher suite\"        : \"{4}\",\n" +
+                "  \"compression methods\" : \"{5}\",\n" +
+                "  \"extensions\"          : [\n" +
+                "{6}\n" +
+                "  ]\n" +
+                "'}'",
+                Locale.ENGLISH);
+            Object[] messageFields = {
+                serverRandom.isHelloRetryRequest() ?
+                    "HelloRetryRequest" : "ServerHello",
+                serverVersion.name,
+                Utilities.toHexString(serverRandom.randomBytes),
+                sessionId.toString(),
+                cipherSuite.name + "(" +
+                        Utilities.byte16HexString(cipherSuite.id) + ")",
+                Utilities.toHexString(compressionMethod),
+                Utilities.indent(extensions.toString(), "    ")
+            };
+
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The "ServerHello" handshake message producer.
+     */
+    private static final class T12ServerHelloProducer
+            implements HandshakeProducer {
+
+        // Prevent instantiation of this class.
+        private T12ServerHelloProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            // 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 (!shc.isResumption || shc.resumingSession == null) {
+                if (!shc.sslConfig.enableSessionCreation) {
+                    throw new SSLException(
+                        "Not resumption, and no new session is allowed");
+                }
+
+                if (shc.localSupportedSignAlgs == null) {
+                    shc.localSupportedSignAlgs =
+                        SignatureScheme.getSupportedAlgorithms(
+                                shc.algorithmConstraints, shc.activeProtocols);
+                }
+
+                SSLSessionImpl session =
+                        new SSLSessionImpl(shc, CipherSuite.C_NULL);
+                session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize);
+                shc.handshakeSession = session;
+
+                // consider the handshake extension impact
+                SSLExtension[] enabledExtensions =
+                        shc.sslConfig.getEnabledExtensions(
+                            SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol);
+                clientHello.extensions.consumeOnTrade(shc, enabledExtensions);
+
+                // negotiate the cipher suite.
+                KeyExchangeProperties credentials =
+                        chooseCipherSuite(shc, clientHello);
+                if (credentials == null) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "no cipher suites in common");
+
+                    return null;    // make the compiler happy
+                }
+                shc.negotiatedCipherSuite = credentials.cipherSuite;
+                shc.handshakeKeyExchange = credentials.keyExchange;
+                shc.handshakeSession.setSuite(credentials.cipherSuite);
+                shc.handshakePossessions.addAll(
+                        Arrays.asList(credentials.possessions));
+                shc.handshakeHash.determine(
+                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);
+
+                // Check the incoming OCSP stapling extensions and attempt
+                // to get responses.  If the resulting stapleParams is non
+                // null, it implies that stapling is enabled on the server side.
+                shc.stapleParams = StatusResponseManager.processStapling(shc);
+                shc.staplingActive = (shc.stapleParams != null);
+
+                // update the responders
+                SSLKeyExchange ke = credentials.keyExchange;
+                if (ke != null) {
+                    for (Map.Entry<Byte, HandshakeProducer> me :
+                            ke.getHandshakeProducers(shc)) {
+                        shc.handshakeProducers.put(
+                                me.getKey(), me.getValue());
+                    }
+                }
+
+                if ((ke != null) &&
+                        (shc.sslConfig.clientAuthType !=
+                                ClientAuthType.CLIENT_AUTH_NONE) &&
+                        !shc.negotiatedCipherSuite.isAnonymous()) {
+                    for (SSLHandshake hs :
+                            ke.getRelatedHandshakers(shc)) {
+                        if (hs == SSLHandshake.CERTIFICATE) {
+                            shc.handshakeProducers.put(
+                                    SSLHandshake.CERTIFICATE_REQUEST.id,
+                                    SSLHandshake.CERTIFICATE_REQUEST);
+                            break;
+                        }
+                    }
+                }
+                shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO_DONE.id,
+                        SSLHandshake.SERVER_HELLO_DONE);
+            } else {
+                shc.handshakeSession = shc.resumingSession;
+                shc.negotiatedProtocol =
+                        shc.resumingSession.getProtocolVersion();
+                shc.negotiatedCipherSuite = shc.resumingSession.getSuite();
+                shc.handshakeHash.determine(
+                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);
+            }
+
+            // Generate the ServerHello handshake message.
+            ServerHelloMessage shm = new ServerHelloMessage(shc,
+                    shc.negotiatedProtocol,
+                    shc.handshakeSession.getSessionId(),
+                    shc.negotiatedCipherSuite,
+                    new RandomCookie(shc),
+                    clientHello);
+            shc.serverHelloRandom = shm.serverRandom;
+
+            // Produce extensions for ServerHello handshake message.
+            SSLExtension[] serverHelloExtensions =
+                shc.sslConfig.getEnabledExtensions(
+                        SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol);
+            shm.extensions.produce(shc, serverHelloExtensions);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced ServerHello handshake message", shm);
+            }
+
+            // Output the handshake message.
+            shm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            if (shc.isResumption && shc.resumingSession != null) {
+                SSLTrafficKeyDerivation kdg =
+                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+                if (kdg == null) {
+                    // unlikely
+                    shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                            shc.negotiatedProtocol);
+                } else {
+                    shc.handshakeKeyDerivation = kdg.createKeyDerivation(
+                            shc, shc.resumingSession.getMasterSecret());
+                }
+
+                // update the responders
+                shc.handshakeProducers.put(SSLHandshake.FINISHED.id,
+                        SSLHandshake.FINISHED);
+            }
+
+            // The handshake message has been delivered.
+            return null;
+        }
+
+        private static KeyExchangeProperties chooseCipherSuite(
+                ServerHandshakeContext shc,
+                ClientHelloMessage clientHello) throws IOException {
+            List<CipherSuite> preferred;
+            List<CipherSuite> proposed;
+            if (shc.sslConfig.preferLocalCipherSuites) {
+                preferred = shc.activeCipherSuites;
+                proposed = clientHello.cipherSuites;
+            } else {
+                preferred = clientHello.cipherSuites;
+                proposed = shc.activeCipherSuites;
+            }
+
+            List<CipherSuite> legacySuites = new LinkedList<>();
+            for (CipherSuite cs : preferred) {
+                if (!HandshakeContext.isNegotiable(
+                        proposed, shc.negotiatedProtocol, cs)) {
+                    continue;
+                }
+
+                if (shc.sslConfig.clientAuthType ==
+                        ClientAuthType.CLIENT_AUTH_REQUIRED) {
+                    if ((cs.keyExchange == KeyExchange.K_DH_ANON) ||
+                        (cs.keyExchange == KeyExchange.K_ECDH_ANON)) {
+                        continue;
+                    }
+                }
+
+                SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                        cs.keyExchange, shc.negotiatedProtocol);
+                if (ke == null) {
+                    continue;
+                }
+                if (!ServerHandshakeContext.legacyAlgorithmConstraints.permits(
+                        null, cs.name, null)) {
+                    legacySuites.add(cs);
+                    continue;
+                }
+
+                SSLPossession[] hcds = ke.createPossessions(shc);
+                if ((hcds == null) || (hcds.length == 0)) {
+                    continue;
+                }
+
+                // The cipher suite has been negotiated.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("use cipher suite " + cs.name);
+                }
+
+                return new KeyExchangeProperties(cs, ke, hcds);
+            }
+
+            for (CipherSuite cs : legacySuites) {
+                SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                        cs.keyExchange,  shc.negotiatedProtocol);
+                if (ke != null) {
+                    SSLPossession[] hcds = ke.createPossessions(shc);
+                    if ((hcds != null) && (hcds.length != 0)) {
+                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                            SSLLogger.warning(
+                                "use legacy cipher suite " + cs.name);
+                        }
+                        return new KeyExchangeProperties(cs, ke, hcds);
+                    }
+                }
+            }
+
+            shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "no cipher suites in common");
+
+            return null;    // make the compiler happy.
+        }
+
+        private static final class KeyExchangeProperties {
+            final CipherSuite cipherSuite;
+            final SSLKeyExchange keyExchange;
+            final SSLPossession[] possessions;
+
+            private KeyExchangeProperties(CipherSuite cipherSuite,
+                    SSLKeyExchange keyExchange, SSLPossession[] possessions) {
+                this.cipherSuite = cipherSuite;
+                this.keyExchange = keyExchange;
+                this.possessions = possessions;
+            }
+        }
+    }
+
+    /**
+     * The "ServerHello" handshake message producer.
+     */
+    private static final
+            class T13ServerHelloProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T13ServerHelloProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+            ClientHelloMessage clientHello = (ClientHelloMessage)message;
+
+            // 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 (!shc.isResumption || shc.resumingSession == null) {
+                if (!shc.sslConfig.enableSessionCreation) {
+                    throw new SSLException(
+                        "Not resumption, and no new session is allowed");
+                }
+
+                if (shc.localSupportedSignAlgs == null) {
+                    shc.localSupportedSignAlgs =
+                        SignatureScheme.getSupportedAlgorithms(
+                                shc.algorithmConstraints, shc.activeProtocols);
+                }
+
+                SSLSessionImpl session =
+                        new SSLSessionImpl(shc, CipherSuite.C_NULL);
+                session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize);
+                shc.handshakeSession = session;
+
+                // consider the handshake extension impact
+                SSLExtension[] enabledExtensions =
+                        shc.sslConfig.getEnabledExtensions(
+                            SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol);
+                clientHello.extensions.consumeOnTrade(shc, enabledExtensions);
+
+                // negotiate the cipher suite.
+                CipherSuite cipherSuite = chooseCipherSuite(shc, clientHello);
+                if (cipherSuite == null) {
+                    shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                            "no cipher suites in common");
+                    return null;    // make the compiler happy
+                }
+                shc.negotiatedCipherSuite = cipherSuite;
+                shc.handshakeSession.setSuite(cipherSuite);
+                shc.handshakeHash.determine(
+                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);
+            } else {
+                shc.handshakeSession = shc.resumingSession;
+
+                // consider the handshake extension impact
+                SSLExtension[] enabledExtensions =
+                shc.sslConfig.getEnabledExtensions(
+                SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol);
+                clientHello.extensions.consumeOnTrade(shc, enabledExtensions);
+
+                shc.negotiatedProtocol =
+                        shc.resumingSession.getProtocolVersion();
+                shc.negotiatedCipherSuite = shc.resumingSession.getSuite();
+                shc.handshakeHash.determine(
+                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);
+
+                setUpPskKD(shc,
+                        shc.resumingSession.consumePreSharedKey().get());
+
+                // The session can't be resumed again---remove it from cache
+                SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
+                    shc.sslContext.engineGetServerSessionContext();
+                sessionCache.remove(shc.resumingSession.getSessionId());
+            }
+
+            // update the responders
+            shc.handshakeProducers.put(SSLHandshake.ENCRYPTED_EXTENSIONS.id,
+                    SSLHandshake.ENCRYPTED_EXTENSIONS);
+            shc.handshakeProducers.put(SSLHandshake.FINISHED.id,
+                    SSLHandshake.FINISHED);
+
+            // Generate the ServerHello handshake message.
+            ServerHelloMessage shm = new ServerHelloMessage(shc,
+                    ProtocolVersion.TLS12,      // use legacy version
+                    clientHello.sessionId,      // echo back
+                    shc.negotiatedCipherSuite,
+                    new RandomCookie(shc),
+                    clientHello);
+            shc.serverHelloRandom = shm.serverRandom;
+
+            // Produce extensions for ServerHello handshake message.
+            SSLExtension[] serverHelloExtensions =
+                    shc.sslConfig.getEnabledExtensions(
+                        SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol);
+            shm.extensions.produce(shc, serverHelloExtensions);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Produced ServerHello handshake message", shm);
+            }
+
+            // Output the handshake message.
+            shm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // Change client/server handshake traffic secrets.
+            // Refresh handshake hash
+            shc.handshakeHash.update();
+
+            // Change client/server handshake traffic secrets.
+            SSLKeyExchange ke = shc.handshakeKeyExchange;
+            if (ke == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not negotiated key shares");
+                return null;    // make the compiler happy
+            }
+
+            SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc);
+            SecretKey handshakeSecret = handshakeKD.deriveKey(
+                    "TlsHandshakeSecret", null);
+
+            SSLTrafficKeyDerivation kdg =
+                SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+            if (kdg == null) {
+                // unlikely
+                shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                        shc.negotiatedProtocol);
+                return null;    // make the compiler happy
+            }
+
+            SSLKeyDerivation kd =
+                    new SSLSecretDerivation(shc, handshakeSecret);
+
+            // update the handshake traffic read keys.
+            SecretKey readSecret = kd.deriveKey(
+                    "TlsClientHandshakeTrafficSecret", null);
+            SSLKeyDerivation readKD =
+                    kdg.createKeyDerivation(shc, readSecret);
+            SecretKey readKey = readKD.deriveKey(
+                    "TlsKey", null);
+            SecretKey readIvSecret = readKD.deriveKey(
+                    "TlsIv", null);
+            IvParameterSpec readIv =
+                    new IvParameterSpec(readIvSecret.getEncoded());
+            SSLReadCipher readCipher;
+            try {
+                readCipher =
+                    shc.negotiatedCipherSuite.bulkCipher.createReadCipher(
+                        Authenticator.valueOf(shc.negotiatedProtocol),
+                        shc.negotiatedProtocol, readKey, readIv,
+                        shc.sslContext.getSecureRandom());
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Missing cipher algorithm", gse);
+                return null;    // make the compiler happy
+            }
+
+            shc.baseReadSecret = readSecret;
+            shc.conContext.inputRecord.changeReadCiphers(readCipher);
+
+            // update the handshake traffic write secret.
+            SecretKey writeSecret = kd.deriveKey(
+                    "TlsServerHandshakeTrafficSecret", null);
+            SSLKeyDerivation writeKD =
+                    kdg.createKeyDerivation(shc, writeSecret);
+            SecretKey writeKey = writeKD.deriveKey(
+                    "TlsKey", null);
+            SecretKey writeIvSecret = writeKD.deriveKey(
+                    "TlsIv", null);
+            IvParameterSpec writeIv =
+                    new IvParameterSpec(writeIvSecret.getEncoded());
+            SSLWriteCipher writeCipher;
+            try {
+                writeCipher =
+                    shc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
+                        Authenticator.valueOf(shc.negotiatedProtocol),
+                        shc.negotiatedProtocol, writeKey, writeIv,
+                        shc.sslContext.getSecureRandom());
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Missing cipher algorithm", gse);
+                return null;    //  make the compiler happy
+            }
+
+            shc.baseWriteSecret = writeSecret;
+            shc.conContext.outputRecord.changeWriteCiphers(
+                    writeCipher, (clientHello.sessionId.length() != 0));
+
+            // Update the context for master key derivation.
+            shc.handshakeKeyDerivation = kd;
+
+            // The handshake message has been delivered.
+            return null;
+        }
+
+        private static CipherSuite chooseCipherSuite(
+                ServerHandshakeContext shc,
+                ClientHelloMessage clientHello) throws IOException {
+            List<CipherSuite> preferred;
+            List<CipherSuite> proposed;
+            if (shc.sslConfig.preferLocalCipherSuites) {
+                preferred = shc.activeCipherSuites;
+                proposed = clientHello.cipherSuites;
+            } else {
+                preferred = clientHello.cipherSuites;
+                proposed = shc.activeCipherSuites;
+            }
+
+            CipherSuite legacySuite = null;
+            AlgorithmConstraints legacyConstraints =
+                    ServerHandshakeContext.legacyAlgorithmConstraints;
+            for (CipherSuite cs : preferred) {
+                if (!HandshakeContext.isNegotiable(
+                        proposed, shc.negotiatedProtocol, cs)) {
+                    continue;
+                }
+
+                if ((legacySuite == null) &&
+                        !legacyConstraints.permits(null, cs.name, null)) {
+                    legacySuite = cs;
+                    continue;
+                }
+
+                // The cipher suite has been negotiated.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("use cipher suite " + cs.name);
+                }
+                return cs;
+            }
+
+            if (legacySuite != null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "use legacy cipher suite " + legacySuite.name);
+                }
+                return legacySuite;
+            }
+
+            // no cipher suites in common
+            return null;
+        }
+    }
+
+    /**
+     * The "HelloRetryRequest" handshake message producer.
+     */
+    private static final
+            class T13HelloRetryRequestProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T13HelloRetryRequestProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ServerHandshakeContext shc = (ServerHandshakeContext) context;
+            ClientHelloMessage clientHello = (ClientHelloMessage) message;
+
+            // negotiate the cipher suite.
+            CipherSuite cipherSuite =
+                    T13ServerHelloProducer.chooseCipherSuite(shc, clientHello);
+            if (cipherSuite == null) {
+                shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "no cipher suites in common for hello retry request");
+                return null;    // make the compiler happy
+            }
+
+            ServerHelloMessage hhrm = new ServerHelloMessage(shc,
+                    ProtocolVersion.TLS12,      // use legacy version
+                    clientHello.sessionId,      //  echo back
+                    cipherSuite,
+                    RandomCookie.hrrRandom,
+                    clientHello
+            );
+
+            shc.negotiatedCipherSuite = cipherSuite;
+            shc.handshakeHash.determine(
+                    shc.negotiatedProtocol, shc.negotiatedCipherSuite);
+
+            // Produce extensions for HelloRetryRequest handshake message.
+            SSLExtension[] serverHelloExtensions =
+                shc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.HELLO_RETRY_REQUEST, shc.negotiatedProtocol);
+            hhrm.extensions.produce(shc, serverHelloExtensions);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced HelloRetryRequest handshake message", hhrm);
+            }
+
+            // Output the handshake message.
+            hhrm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            // Stateless, shall we clean up the handshake context as well?
+            shc.handshakeHash.finish();     // forgot about the handshake hash
+            shc.handshakeExtensions.clear();
+
+            // What's the expected response?
+            shc.handshakeConsumers.put(
+                    SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "HelloRetryRequest" handshake message reproducer.
+     */
+    private static final
+            class T13HelloRetryRequestReproducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private T13HelloRetryRequestReproducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            ServerHandshakeContext shc = (ServerHandshakeContext) context;
+            ClientHelloMessage clientHello = (ClientHelloMessage) message;
+
+            // negotiate the cipher suite.
+            CipherSuite cipherSuite = shc.negotiatedCipherSuite;
+            ServerHelloMessage hhrm = new ServerHelloMessage(shc,
+                    ProtocolVersion.TLS12,      // use legacy version
+                    clientHello.sessionId,      //  echo back
+                    cipherSuite,
+                    RandomCookie.hrrRandom,
+                    clientHello
+            );
+
+            // Produce extensions for HelloRetryRequest handshake message.
+            SSLExtension[] serverHelloExtensions =
+                shc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.MESSAGE_HASH, shc.negotiatedProtocol);
+            hhrm.extensions.produce(shc, serverHelloExtensions);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Reproduced HelloRetryRequest handshake message", hhrm);
+            }
+
+            HandshakeOutStream hos = new HandshakeOutStream(null);
+            hhrm.write(hos);
+
+            return hos.toByteArray();
+        }
+    }
+
+    /**
+     * The "ServerHello" handshake message consumer.
+     */
+    private static final
+            class ServerHelloConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ServerHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.remove(SSLHandshake.SERVER_HELLO.id);
+            if (!chc.handshakeConsumers.isEmpty()) {
+                // DTLS 1.0/1.2
+                chc.handshakeConsumers.remove(
+                        SSLHandshake.HELLO_VERIFY_REQUEST.id);
+            }
+            if (!chc.handshakeConsumers.isEmpty()) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "No more message expected before ServerHello is processed");
+            }
+
+            ServerHelloMessage shm = new ServerHelloMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Consuming ServerHello handshake message", shm);
+            }
+
+            if (shm.serverRandom.isHelloRetryRequest()) {
+                onHelloRetryRequest(chc, shm);
+            } else {
+                onServerHello(chc, shm);
+            }
+        }
+
+        private void onHelloRetryRequest(ClientHandshakeContext chc,
+                ServerHelloMessage helloRetryRequest) throws IOException {
+            // Negotiate protocol version.
+            //
+            // Check and launch SupportedVersions.
+            SSLExtension[] extTypes = new SSLExtension[] {
+                    SSLExtension.HRR_SUPPORTED_VERSIONS
+                };
+            helloRetryRequest.extensions.consumeOnLoad(chc, extTypes);
+
+            ProtocolVersion serverVersion;
+            SHSupportedVersionsSpec svs =
+                    (SHSupportedVersionsSpec)chc.handshakeExtensions.get(
+                            SSLExtension.HRR_SUPPORTED_VERSIONS);
+            if (svs != null) {
+                serverVersion =            // could be null
+                        ProtocolVersion.valueOf(svs.selectedVersion);
+            } else {
+                serverVersion = helloRetryRequest.serverVersion;
+            }
+
+            if (!chc.activeProtocols.contains(serverVersion)) {
+                chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "The server selected protocol version " + serverVersion +
+                    " is not accepted by client preferences " +
+                    chc.activeProtocols);
+            }
+
+            if (!serverVersion.useTLS13PlusSpec()) {
+                chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "Unexpected HelloRetryRequest for " + serverVersion.name);
+            }
+
+            chc.negotiatedProtocol = serverVersion;
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Negotiated protocol version: " + serverVersion.name);
+            }
+
+            // TLS 1.3 key share extension may have produced client
+            // possessions for TLS 1.3 key exchanges.
+            //
+            // Clean up before producing new client key share possessions.
+            chc.handshakePossessions.clear();
+
+            if (serverVersion.isDTLS) {
+                d13HrrHandshakeConsumer.consume(chc, helloRetryRequest);
+            } else {
+                t13HrrHandshakeConsumer.consume(chc, helloRetryRequest);
+            }
+        }
+
+        private void onServerHello(ClientHandshakeContext chc,
+                ServerHelloMessage serverHello) throws IOException {
+            // Negotiate protocol version.
+            //
+            // Check and launch SupportedVersions.
+            SSLExtension[] extTypes = new SSLExtension[] {
+                    SSLExtension.SH_SUPPORTED_VERSIONS
+                };
+            serverHello.extensions.consumeOnLoad(chc, extTypes);
+
+            ProtocolVersion serverVersion;
+            SHSupportedVersionsSpec svs =
+                    (SHSupportedVersionsSpec)chc.handshakeExtensions.get(
+                            SSLExtension.SH_SUPPORTED_VERSIONS);
+            if (svs != null) {
+                serverVersion =            // could be null
+                        ProtocolVersion.valueOf(svs.selectedVersion);
+            } else {
+                serverVersion = serverHello.serverVersion;
+            }
+
+            if (!chc.activeProtocols.contains(serverVersion)) {
+                chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "The server selected protocol version " + serverVersion +
+                    " is not accepted by client preferences " +
+                    chc.activeProtocols);
+            }
+
+            chc.negotiatedProtocol = serverVersion;
+            if (!chc.conContext.isNegotiated) {
+                chc.conContext.protocolVersion = chc.negotiatedProtocol;
+                chc.conContext.outputRecord.setVersion(chc.negotiatedProtocol);
+            }
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Negotiated protocol version: " + serverVersion.name);
+            }
+
+            if (serverHello.serverRandom.isVersionDowngrade(chc)) {
+                chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "A potential protocol version downgrade attack");
+            }
+
+            // Consume the handshake message for the specific protocol version.
+            if (serverVersion.isDTLS) {
+                if (serverVersion.useTLS13PlusSpec()) {
+                    d13HandshakeConsumer.consume(chc, serverHello);
+                } else {
+                    // TLS 1.3 key share extension may have produced client
+                    // possessions for TLS 1.3 key exchanges.
+                    chc.handshakePossessions.clear();
+
+                    d12HandshakeConsumer.consume(chc, serverHello);
+                }
+            } else {
+                if (serverVersion.useTLS13PlusSpec()) {
+                    t13HandshakeConsumer.consume(chc, serverHello);
+                } else {
+                    // TLS 1.3 key share extension may have produced client
+                    // possessions for TLS 1.3 key exchanges.
+                    chc.handshakePossessions.clear();
+
+                    t12HandshakeConsumer.consume(chc, serverHello);
+                }
+            }
+        }
+    }
+
+    private static final
+            class T12ServerHelloConsumer implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private T12ServerHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            ServerHelloMessage serverHello = (ServerHelloMessage)message;
+            if (!chc.isNegotiable(serverHello.serverVersion)) {
+                chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "Server chose " + serverHello.serverVersion +
+                    ", but that protocol version is not enabled or " +
+                    "not supported by the client.");
+            }
+
+            // chc.negotiatedProtocol = serverHello.serverVersion;
+            chc.negotiatedCipherSuite = serverHello.cipherSuite;
+            chc.handshakeHash.determine(
+                    chc.negotiatedProtocol, chc.negotiatedCipherSuite);
+            chc.serverHelloRandom = serverHello.serverRandom;
+            if (chc.negotiatedCipherSuite.keyExchange == null) {
+                chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "TLS 1.2 or prior version does not support the " +
+                    "server cipher suite: " + chc.negotiatedCipherSuite.name);
+            }
+
+            //
+            // validate
+            //
+
+            // Check and launch the "renegotiation_info" extension.
+            SSLExtension[] extTypes = new SSLExtension[] {
+                    SSLExtension.SH_RENEGOTIATION_INFO
+                };
+            serverHello.extensions.consumeOnLoad(chc, extTypes);
+
+            // Is it session resuming?
+            if (chc.resumingSession != null) {
+                // we tried to resume, let's see what the server decided
+                if (serverHello.sessionId.equals(
+                        chc.resumingSession.getSessionId())) {
+                    // server resumed the session, let's make sure everything
+                    // checks out
+
+                    // Verify that the session ciphers are unchanged.
+                    CipherSuite sessionSuite = chc.resumingSession.getSuite();
+                    if (chc.negotiatedCipherSuite != sessionSuite) {
+                        chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                            "Server returned wrong cipher suite for session");
+                    }
+
+                    // verify protocol version match
+                    ProtocolVersion sessionVersion =
+                            chc.resumingSession.getProtocolVersion();
+                    if (chc.negotiatedProtocol != sessionVersion) {
+                        chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                            "Server resumed with wrong protocol version");
+                    }
+
+                    // looks fine;  resume it.
+                    chc.isResumption = true;
+                    chc.resumingSession.setAsSessionResumption(true);
+                    chc.handshakeSession = chc.resumingSession;
+                } else {
+                    // we wanted to resume, but the server refused
+                    //
+                    // Invalidate the session for initial handshake in case
+                    // of reusing next time.
+                    if (chc.resumingSession != null) {
+                        chc.resumingSession.invalidate();
+                        chc.resumingSession = null;
+                    }
+                    chc.isResumption = false;
+                    if (!chc.sslConfig.enableSessionCreation) {
+                        chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                            "New session creation is disabled");
+                    }
+                }
+            }
+
+            // Check and launch ClientHello extensions.
+            extTypes = chc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.SERVER_HELLO);
+            serverHello.extensions.consumeOnLoad(chc, extTypes);
+
+            if (!chc.isResumption) {
+                if (chc.resumingSession != null) {
+                    // in case the resumption happens next time.
+                    chc.resumingSession.invalidate();
+                    chc.resumingSession = null;
+                }
+
+                if (!chc.sslConfig.enableSessionCreation) {
+                    chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                        "New session creation is disabled");
+                }
+                chc.handshakeSession = new SSLSessionImpl(chc,
+                        chc.negotiatedCipherSuite,
+                        serverHello.sessionId);
+                chc.handshakeSession.setMaximumPacketSize(
+                        chc.sslConfig.maximumPacketSize);
+            }
+
+            //
+            // update
+            //
+            serverHello.extensions.consumeOnTrade(chc, extTypes);
+
+            // update the consumers and producers
+            if (chc.isResumption) {
+                SSLTrafficKeyDerivation kdg =
+                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+                if (kdg == null) {
+                    // unlikely
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                            chc.negotiatedProtocol);
+                } else {
+                    chc.handshakeKeyDerivation = kdg.createKeyDerivation(
+                            chc, chc.resumingSession.getMasterSecret());
+                }
+
+                chc.conContext.consumers.putIfAbsent(
+                        ContentType.CHANGE_CIPHER_SPEC.id,
+                        ChangeCipherSpec.t10Consumer);
+                chc.handshakeConsumers.put(
+                        SSLHandshake.FINISHED.id,
+                        SSLHandshake.FINISHED);
+            } else {
+                SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                        chc.negotiatedCipherSuite.keyExchange,
+                        chc.negotiatedProtocol);
+                chc.handshakeKeyExchange = ke;
+                if (ke != null) {
+                    for (SSLHandshake handshake :
+                            ke.getRelatedHandshakers(chc)) {
+                        chc.handshakeConsumers.put(handshake.id, handshake);
+                    }
+                }
+
+                chc.handshakeConsumers.put(SSLHandshake.SERVER_HELLO_DONE.id,
+                        SSLHandshake.SERVER_HELLO_DONE);
+            }
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+
+    private static void setUpPskKD(HandshakeContext hc,
+            SecretKey psk) throws SSLHandshakeException {
+
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+            SSLLogger.fine("Using PSK to derive early secret");
+        }
+
+        try {
+            CipherSuite.HashAlg hashAlg = hc.negotiatedCipherSuite.hashAlg;
+            HKDF hkdf = new HKDF(hashAlg.name);
+            byte[] zeros = new byte[hashAlg.hashLength];
+            SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
+            hc.handshakeKeyDerivation =
+                    new SSLSecretDerivation(hc, earlySecret);
+        } catch  (GeneralSecurityException gse) {
+            throw (SSLHandshakeException) new SSLHandshakeException(
+                "Could not generate secret").initCause(gse);
+        }
+    }
+
+    private static final
+            class T13ServerHelloConsumer implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private T13ServerHelloConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            ServerHelloMessage serverHello = (ServerHelloMessage)message;
+            if (serverHello.serverVersion != ProtocolVersion.TLS12) {
+                chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "The ServerHello.legacy_version field is not TLS 1.2");
+            }
+
+            chc.negotiatedCipherSuite = serverHello.cipherSuite;
+            chc.handshakeHash.determine(
+                    chc.negotiatedProtocol, chc.negotiatedCipherSuite);
+            chc.serverHelloRandom = serverHello.serverRandom;
+
+            //
+            // validate
+            //
+
+            // Check and launch ServerHello extensions.
+            SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.SERVER_HELLO);
+            serverHello.extensions.consumeOnLoad(chc, extTypes);
+            if (!chc.isResumption) {
+                if (chc.resumingSession != null) {
+                    // in case the resumption happens next time.
+                    chc.resumingSession.invalidate();
+                    chc.resumingSession = null;
+                }
+
+                if (!chc.sslConfig.enableSessionCreation) {
+                    chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                        "New session creation is disabled");
+                }
+                chc.handshakeSession = new SSLSessionImpl(chc,
+                        chc.negotiatedCipherSuite,
+                        serverHello.sessionId);
+                chc.handshakeSession.setMaximumPacketSize(
+                        chc.sslConfig.maximumPacketSize);
+            } else {
+                // The PSK is consumed to allow it to be deleted
+                Optional<SecretKey> psk =
+                        chc.resumingSession.consumePreSharedKey();
+                if(!psk.isPresent()) {
+                    chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                    "No PSK available. Unable to resume.");
+                }
+
+                chc.handshakeSession = chc.resumingSession;
+
+                setUpPskKD(chc, psk.get());
+            }
+
+            //
+            // update
+            //
+            serverHello.extensions.consumeOnTrade(chc, extTypes);
+
+            // Change client/server handshake traffic secrets.
+            // Refresh handshake hash
+            chc.handshakeHash.update();
+
+            SSLKeyExchange ke = chc.handshakeKeyExchange;
+            if (ke == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not negotiated key shares");
+                return;     // make the compiler happy
+            }
+
+            SSLKeyDerivation handshakeKD = ke.createKeyDerivation(chc);
+            SecretKey handshakeSecret = handshakeKD.deriveKey(
+                    "TlsHandshakeSecret", null);
+            SSLTrafficKeyDerivation kdg =
+                SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+            if (kdg == null) {
+                // unlikely
+                chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key derivation: " +
+                        chc.negotiatedProtocol);
+                return;     // make the compiler happy
+            }
+
+            SSLKeyDerivation secretKD =
+                    new SSLSecretDerivation(chc, handshakeSecret);
+
+            // update the handshake traffic read keys.
+            SecretKey readSecret = secretKD.deriveKey(
+                    "TlsServerHandshakeTrafficSecret", null);
+
+            SSLKeyDerivation readKD =
+                    kdg.createKeyDerivation(chc, readSecret);
+            SecretKey readKey = readKD.deriveKey(
+                    "TlsKey", null);
+            SecretKey readIvSecret = readKD.deriveKey(
+                    "TlsIv", null);
+            IvParameterSpec readIv =
+                    new IvParameterSpec(readIvSecret.getEncoded());
+            SSLReadCipher readCipher;
+            try {
+                readCipher =
+                    chc.negotiatedCipherSuite.bulkCipher.createReadCipher(
+                        Authenticator.valueOf(chc.negotiatedProtocol),
+                        chc.negotiatedProtocol, readKey, readIv,
+                        chc.sslContext.getSecureRandom());
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Missing cipher algorithm", gse);
+                return;     // make the compiler happy
+            }
+
+            chc.baseReadSecret = readSecret;
+            chc.conContext.inputRecord.changeReadCiphers(readCipher);
+
+            // update the handshake traffic write keys.
+            SecretKey writeSecret = secretKD.deriveKey(
+                    "TlsClientHandshakeTrafficSecret", null);
+            SSLKeyDerivation writeKD =
+                    kdg.createKeyDerivation(chc, writeSecret);
+            SecretKey writeKey = writeKD.deriveKey(
+                    "TlsKey", null);
+            SecretKey writeIvSecret = writeKD.deriveKey(
+                    "TlsIv", null);
+            IvParameterSpec writeIv =
+                    new IvParameterSpec(writeIvSecret.getEncoded());
+            SSLWriteCipher writeCipher;
+            try {
+                writeCipher =
+                    chc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
+                        Authenticator.valueOf(chc.negotiatedProtocol),
+                        chc.negotiatedProtocol, writeKey, writeIv,
+                        chc.sslContext.getSecureRandom());
+            } catch (GeneralSecurityException gse) {
+                // unlikely
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "Missing cipher algorithm", gse);
+                return;     //  make the compiler happy
+            }
+
+            chc.baseWriteSecret = writeSecret;
+            chc.conContext.outputRecord.changeWriteCiphers(
+                    writeCipher, (serverHello.sessionId.length() != 0));
+
+            // Should use resumption_master_secret for TLS 1.3.
+            // chc.handshakeSession.setMasterSecret(masterSecret);
+
+            // Update the context for master key derivation.
+            chc.handshakeKeyDerivation = secretKD;
+
+            // update the consumers and producers
+            //
+            // The server sends a dummy change_cipher_spec record immediately
+            // after its first handshake message.  This may either be after a
+            // ServerHello or a HelloRetryRequest.
+            chc.conContext.consumers.putIfAbsent(
+                    ContentType.CHANGE_CIPHER_SPEC.id,
+                    ChangeCipherSpec.t13Consumer);
+
+            chc.handshakeConsumers.put(
+                    SSLHandshake.ENCRYPTED_EXTENSIONS.id,
+                    SSLHandshake.ENCRYPTED_EXTENSIONS);
+
+            // Support cert authentication only, when not PSK.
+            chc.handshakeConsumers.put(
+                    SSLHandshake.CERTIFICATE_REQUEST.id,
+                    SSLHandshake.CERTIFICATE_REQUEST);
+            chc.handshakeConsumers.put(
+                    SSLHandshake.CERTIFICATE.id,
+                    SSLHandshake.CERTIFICATE);
+            chc.handshakeConsumers.put(
+                    SSLHandshake.CERTIFICATE_VERIFY.id,
+                    SSLHandshake.CERTIFICATE_VERIFY);
+
+            chc.handshakeConsumers.put(
+                    SSLHandshake.FINISHED.id,
+                    SSLHandshake.FINISHED);
+
+            //
+            // produce
+            //
+            // Need no new handshake message producers here.
+        }
+    }
+
+    private static final
+            class T13HelloRetryRequestConsumer implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private T13HelloRetryRequestConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+            ServerHelloMessage helloRetryRequest = (ServerHelloMessage)message;
+            if (helloRetryRequest.serverVersion != ProtocolVersion.TLS12) {
+                chc.conContext.fatal(Alert.PROTOCOL_VERSION,
+                    "The HelloRetryRequest.legacy_version is not TLS 1.2");
+            }
+
+            chc.negotiatedCipherSuite = helloRetryRequest.cipherSuite;
+
+            //
+            // validate
+            //
+
+            // Check and launch ClientHello extensions.
+            SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
+                    SSLHandshake.HELLO_RETRY_REQUEST);
+            helloRetryRequest.extensions.consumeOnLoad(chc, extTypes);
+
+            //
+            // update
+            //
+            helloRetryRequest.extensions.consumeOnTrade(chc, extTypes);
+
+            // Change client/server handshake traffic secrets.
+            // Refresh handshake hash
+            chc.handshakeHash.finish();     // reset the handshake hash
+
+            // calculate the transcript hash of the 1st ClientHello message
+            HandshakeOutStream hos = new HandshakeOutStream(null);
+            try {
+                chc.initialClientHelloMsg.write(hos);
+            } catch (IOException ioe) {
+                // unlikely
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "Failed to construct message hash", ioe);
+            }
+            chc.handshakeHash.deliver(hos.toByteArray());
+            chc.handshakeHash.determine(
+                    chc.negotiatedProtocol, chc.negotiatedCipherSuite);
+            byte[] clientHelloHash = chc.handshakeHash.digest();
+
+            // calculate the message_hash
+            //
+            // Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
+            //   Hash(message_hash ||    /* Handshake type */
+            //     00 00 Hash.length ||  /* Handshake message length (bytes) */
+            //     Hash(ClientHello1) || /* Hash of ClientHello1 */
+            //     HelloRetryRequest || ... || Mn)
+            int hashLen = chc.negotiatedCipherSuite.hashAlg.hashLength;
+            byte[] hashedClientHello = new byte[4 + hashLen];
+            hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id;
+            hashedClientHello[1] = (byte)0x00;
+            hashedClientHello[2] = (byte)0x00;
+            hashedClientHello[3] = (byte)(hashLen & 0xFF);
+            System.arraycopy(clientHelloHash, 0,
+                    hashedClientHello, 4, hashLen);
+
+            chc.handshakeHash.finish();     // reset the handshake hash
+            chc.handshakeHash.deliver(hashedClientHello);
+
+            int hrrBodyLen = helloRetryRequest.handshakeRecord.remaining();
+            byte[] hrrMessage = new byte[4 + hrrBodyLen];
+            hrrMessage[0] = SSLHandshake.HELLO_RETRY_REQUEST.id;
+            hrrMessage[1] = (byte)((hrrBodyLen >> 16) & 0xFF);
+            hrrMessage[2] = (byte)((hrrBodyLen >> 8) & 0xFF);
+            hrrMessage[3] = (byte)(hrrBodyLen & 0xFF);
+
+            ByteBuffer hrrBody = helloRetryRequest.handshakeRecord.duplicate();
+            hrrBody.get(hrrMessage, 4, hrrBodyLen);
+
+            chc.handshakeHash.receive(hrrMessage);
+
+            // Update the initial ClientHello handshake message.
+            chc.initialClientHelloMsg.extensions.reproduce(chc,
+                    new SSLExtension[] {
+                        SSLExtension.CH_COOKIE,
+                        SSLExtension.CH_KEY_SHARE,
+                        SSLExtension.CH_PRE_SHARED_KEY
+                    });
+
+            //
+            // produce response handshake message
+            //
+            SSLHandshake.CLIENT_HELLO.produce(context, helloRetryRequest);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the ServerHelloDone handshake message.
+ */
+final class ServerHelloDone {
+    static final SSLConsumer handshakeConsumer =
+        new ServerHelloDoneConsumer();
+    static final HandshakeProducer handshakeProducer =
+        new ServerHelloDoneProducer();
+
+    /**
+     * The ServerHelloDone handshake message.
+     */
+    static final class ServerHelloDoneMessage extends HandshakeMessage {
+        ServerHelloDoneMessage(HandshakeContext handshakeContext) {
+            super(handshakeContext);
+        }
+
+        ServerHelloDoneMessage(HandshakeContext handshakeContext,
+                ByteBuffer m) throws IOException {
+            super(handshakeContext);
+            if (m.hasRemaining()) {
+                handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "Error parsing ServerHelloDone message: not empty");
+            }
+        }
+
+        @Override
+        public SSLHandshake handshakeType() {
+            return SSLHandshake.SERVER_HELLO_DONE;
+        }
+
+        @Override
+        public int messageLength() {
+            return 0;
+        }
+
+        @Override
+        public void send(HandshakeOutStream s) throws IOException {
+            // empty, nothing to send
+        }
+
+        @Override
+        public String toString() {
+            return "<empty>";
+        }
+    }
+
+    /**
+     * The "ServerHelloDone" handshake message producer.
+     */
+    private static final
+            class ServerHelloDoneProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private ServerHelloDoneProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            ServerHelloDoneMessage shdm = new ServerHelloDoneMessage(shc);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Produced ServerHelloDone handshake message", shdm);
+            }
+
+            // Output the handshake message.
+            shdm.write(shc.handshakeOutput);
+            shc.handshakeOutput.flush();
+
+            //
+            // update
+            //
+            shc.handshakeConsumers.put(SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                    SSLHandshake.CLIENT_KEY_EXCHANGE);
+            shc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id,
+                    ChangeCipherSpec.t10Consumer);
+            shc.handshakeConsumers.put(SSLHandshake.FINISHED.id,
+                    SSLHandshake.FINISHED);
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The "ServerHelloDone" handshake message consumer.
+     */
+    private static final
+            class ServerHelloDoneConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ServerHelloDoneConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.clear();
+
+            ServerHelloDoneMessage shdm =
+                    new ServerHelloDoneMessage(chc, message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                        "Consuming ServerHelloDone handshake message", shdm);
+            }
+
+            //
+            // validate
+            //
+            // blank
+
+            //
+            // update
+            //
+            chc.handshakeProducers.put(SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                    SSLHandshake.CLIENT_KEY_EXCHANGE);
+            chc.handshakeProducers.put(SSLHandshake.FINISHED.id,
+                    SSLHandshake.FINISHED);
+            //
+            // produce
+            //
+            SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] {
+                // full handshake messages
+                SSLHandshake.CERTIFICATE,
+                SSLHandshake.CLIENT_KEY_EXCHANGE,
+                SSLHandshake.CERTIFICATE_VERIFY,
+                SSLHandshake.FINISHED
+            };
+
+            for (SSLHandshake hs : probableHandshakeMessages) {
+                HandshakeProducer handshakeProducer =
+                        chc.handshakeProducers.remove(hs.id);
+                if (handshakeProducer != null) {
+                    handshakeProducer.produce(context, null);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the ServerKeyExchange handshake message.
+ */
+final class ServerKeyExchange {
+    static final SSLConsumer handshakeConsumer =
+        new ServerKeyExchangeConsumer();
+    static final HandshakeProducer handshakeProducer =
+        new ServerKeyExchangeProducer();
+
+    /**
+     * The "ServerKeyExchange" handshake message producer.
+     */
+    private static final
+            class ServerKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private ServerKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    shc.negotiatedCipherSuite.keyExchange,
+                    shc.negotiatedProtocol);
+            if (ke != null) {
+                for (Map.Entry<Byte, HandshakeProducer> hc :
+                        ke.getHandshakeProducers(shc)) {
+                    if (hc.getKey() == SSLHandshake.SERVER_KEY_EXCHANGE.id) {
+                        return hc.getValue().produce(context, message);
+                    }
+                }
+            }
+
+            // not producer defined.
+            shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                    "No ServerKeyExchange handshake message can be produced.");
+            return null;    // make the compiler happe
+        }
+    }
+
+    /**
+     * The "ServerKeyExchange" handshake message consumer.
+     */
+    private static final
+            class ServerKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private ServerKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // clean up this consumer
+            chc.handshakeConsumers.remove(SSLHandshake.SERVER_KEY_EXCHANGE.id);
+
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    chc.negotiatedCipherSuite.keyExchange,
+                    chc.negotiatedProtocol);
+            if (ke != null) {
+                for (Map.Entry<Byte, SSLConsumer> hc :
+                        ke.getHandshakeConsumers(chc)) {
+                    if (hc.getKey() == SSLHandshake.SERVER_KEY_EXCHANGE.id) {
+                        hc.getValue().consume(context, message);
+                        return;
+                    }
+                }
+            }
+
+            // no consumer defined.
+            chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected ServerKeyExchange handshake message.");
+        }
+    }
+}
+
--- a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,258 +26,561 @@
 package sun.security.ssl;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.LinkedHashMap;
 import java.util.Map;
-
+import java.util.Objects;
 import javax.net.ssl.SNIHostName;
 import javax.net.ssl.SNIMatcher;
 import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLProtocolException;
 import javax.net.ssl.StandardConstants;
+import static sun.security.ssl.SSLExtension.CH_SERVER_NAME;
+import static sun.security.ssl.SSLExtension.EE_SERVER_NAME;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import static sun.security.ssl.SSLExtension.SH_SERVER_NAME;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
 
-/*
- * [RFC 4366/6066] To facilitate secure connections to servers that host
- * multiple 'virtual' servers at a single underlying network address, clients
- * MAY include an extension of type "server_name" in the (extended) client
- * hello.  The "extension_data" field of this extension SHALL contain
- * "ServerNameList" where:
- *
- *     struct {
- *         NameType name_type;
- *         select (name_type) {
- *             case host_name: HostName;
- *         } name;
- *     } ServerName;
- *
- *     enum {
- *         host_name(0), (255)
- *     } NameType;
- *
- *     opaque HostName<1..2^16-1>;
- *
- *     struct {
- *         ServerName server_name_list<1..2^16-1>
- *     } ServerNameList;
+/**
+ * Pack of the "server_name" extensions [RFC 4366/6066].
  */
-final class ServerNameExtension extends HelloExtension {
+final class ServerNameExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHServerNameProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHServerNameConsumer();
+    static final SSLStringizer chStringizer =
+            new CHServerNamesStringizer();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHServerNameProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHServerNameConsumer();
+    static final SSLStringizer shStringizer =
+            new SHServerNamesStringizer();
 
-    // For backward compatibility, all future data structures associated with
-    // new NameTypes MUST begin with a 16-bit length field.
-    static final int NAME_HEADER_LENGTH = 3;    // NameType: 1 byte
-                                                // Name length: 2 bytes
-    private Map<Integer, SNIServerName> sniMap;
-    private int listLength;     // ServerNameList length
-
-    // constructor for ServerHello
-    ServerNameExtension() throws IOException {
-        super(ExtensionType.EXT_SERVER_NAME);
+    static final HandshakeProducer eeNetworkProducer =
+            new EEServerNameProducer();
+    static final ExtensionConsumer eeOnLoadConsumer =
+            new EEServerNameConsumer();
 
-        listLength = 0;
-        sniMap = Collections.<Integer, SNIServerName>emptyMap();
-    }
-
-    // constructor for ClientHello
-    ServerNameExtension(List<SNIServerName> serverNames)
-            throws IOException {
-        super(ExtensionType.EXT_SERVER_NAME);
+    /**
+     * The "server_name" extension.
+     *
+     * See RFC 4366/6066 for the specification of the extension.
+     */
+    static final class CHServerNamesSpec implements SSLExtensionSpec {
+        // For backward compatibility, all future data structures associated
+        // with new NameTypes MUST begin with a 16-bit length field.
+        static final int NAME_HEADER_LENGTH = 3;    //  1: NameType
+                                                    // +2: Name length
+        final List<SNIServerName> serverNames;
 
-        listLength = 0;
-        sniMap = new LinkedHashMap<>();
-        for (SNIServerName serverName : serverNames) {
-            // check for duplicated server name type
-            if (sniMap.put(serverName.getType(), serverName) != null) {
-                // unlikely to happen, but in case ...
-                throw new RuntimeException(
-                    "Duplicated server name of type " + serverName.getType());
+        private CHServerNamesSpec(List<SNIServerName> serverNames) {
+            this.serverNames =
+                    Collections.<SNIServerName>unmodifiableList(serverNames);
+        }
+
+        private CHServerNamesSpec(ByteBuffer buffer) throws IOException {
+            if (buffer.remaining() < 2) {
+                throw new SSLProtocolException(
+                    "Invalid server_name extension: insufficient data");
             }
 
-            listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH;
-        }
-
-        // This constructor is used for ClientHello only.  Empty list is
-        // not allowed in client mode.
-        if (listLength == 0) {
-            throw new RuntimeException("The ServerNameList cannot be empty");
-        }
-    }
-
-    // constructor for ServerHello for parsing SNI extension
-    ServerNameExtension(HandshakeInStream s, int len)
-            throws IOException {
-        super(ExtensionType.EXT_SERVER_NAME);
-
-        int remains = len;
-        if (len >= 2) {    // "server_name" extension in ClientHello
-            listLength = s.getInt16();     // ServerNameList length
-            if (listLength == 0 || listLength + 2 != len) {
+            int sniLen = Record.getInt16(buffer);
+            if ((sniLen == 0) || sniLen != buffer.remaining()) {
                 throw new SSLProtocolException(
-                        "Invalid " + type + " extension");
+                    "Invalid server_name extension: incomplete data");
             }
 
-            remains -= 2;
-            sniMap = new LinkedHashMap<>();
-            while (remains > 0) {
-                int code = s.getInt8();       // NameType
+            Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>();
+            while (buffer.hasRemaining()) {
+                int nameType = Record.getInt8(buffer);
+                SNIServerName serverName;
 
                 // HostName (length read in getBytes16);
-                byte[] encoded = s.getBytes16();
-                SNIServerName serverName;
-                switch (code) {
-                    case StandardConstants.SNI_HOST_NAME:
-                        if (encoded.length == 0) {
-                            throw new SSLProtocolException(
-                                "Empty HostName in server name indication");
-                        }
-                        try {
-                            serverName = new SNIHostName(encoded);
-                        } catch (IllegalArgumentException iae) {
-                            SSLProtocolException spe = new SSLProtocolException(
-                                "Illegal server name, type=host_name(" +
-                                code + "), name=" +
-                                (new String(encoded, StandardCharsets.UTF_8)) +
-                                ", value=" + Debug.toString(encoded));
-                            spe.initCause(iae);
-                            throw spe;
-                        }
-                        break;
-                    default:
-                        try {
-                            serverName = new UnknownServerName(code, encoded);
-                        } catch (IllegalArgumentException iae) {
-                            SSLProtocolException spe = new SSLProtocolException(
-                                "Illegal server name, type=(" + code +
-                                "), value=" + Debug.toString(encoded));
-                            spe.initCause(iae);
-                            throw spe;
-                        }
+                //
+                // [RFC 6066] The data structure associated with the host_name
+                // NameType is a variable-length vector that begins with a
+                // 16-bit length.  For backward compatibility, all future data
+                // structures associated with new NameTypes MUST begin with a
+                // 16-bit length field.  TLS MAY treat provided server names as
+                // opaque data and pass the names and types to the application.
+                byte[] encoded = Record.getBytes16(buffer);
+                if (nameType == StandardConstants.SNI_HOST_NAME) {
+                    if (encoded.length == 0) {
+                        throw new SSLProtocolException(
+                            "Empty HostName in server_name extension");
+                    }
+
+                    try {
+                        serverName = new SNIHostName(encoded);
+                    } catch (IllegalArgumentException iae) {
+                        SSLProtocolException spe = new SSLProtocolException(
+                            "Illegal server name, type=host_name(" +
+                            nameType + "), name=" +
+                            (new String(encoded, StandardCharsets.UTF_8)) +
+                            ", value={" +
+                            Utilities.toHexString(encoded) + "}");
+                        throw (SSLProtocolException)spe.initCause(iae);
+                    }
+                } else {
+                    try {
+                        serverName = new UnknownServerName(nameType, encoded);
+                    } catch (IllegalArgumentException iae) {
+                        SSLProtocolException spe = new SSLProtocolException(
+                            "Illegal server name, type=(" + nameType +
+                            "), value={" +
+                            Utilities.toHexString(encoded) + "}");
+                        throw (SSLProtocolException)spe.initCause(iae);
+                    }
                 }
+
                 // check for duplicated server name type
                 if (sniMap.put(serverName.getType(), serverName) != null) {
                     throw new SSLProtocolException(
                             "Duplicated server name of type " +
                             serverName.getType());
                 }
-
-                remains -= encoded.length + NAME_HEADER_LENGTH;
             }
-        } else if (len == 0) {     // "server_name" extension in ServerHello
-            listLength = 0;
-            sniMap = Collections.<Integer, SNIServerName>emptyMap();
-        }
 
-        if (remains != 0) {
-            throw new SSLProtocolException("Invalid server_name extension");
-        }
-    }
-
-    List<SNIServerName> getServerNames() {
-        if (sniMap != null && !sniMap.isEmpty()) {
-            return Collections.<SNIServerName>unmodifiableList(
-                                        new ArrayList<>(sniMap.values()));
+            this.serverNames = new ArrayList<>(sniMap.values());
         }
 
-        return Collections.<SNIServerName>emptyList();
-    }
+        @Override
+        public String toString() {
+            if (serverNames == null || serverNames.isEmpty()) {
+                return "<no server name indicator specified>";
+            } else {
+                StringBuilder builder = new StringBuilder(512);
+                for (SNIServerName sn : serverNames) {
+                    builder.append(sn.toString());
+                    builder.append("\n");
+                }
 
-    /*
-     * Is the extension recognized by the corresponding matcher?
-     *
-     * This method is used to check whether the server name indication can
-     * be recognized by the server name matchers.
-     *
-     * Per RFC 6066, if the server understood the ClientHello extension but
-     * does not recognize the server name, the server SHOULD take one of two
-     * actions: either abort the handshake by sending a fatal-level
-     * unrecognized_name(112) alert or continue the handshake.
-     *
-     * If there is an instance of SNIMatcher defined for a particular name
-     * type, it must be used to perform match operations on the server name.
-     */
-    boolean isMatched(Collection<SNIMatcher> matchers) {
-        if (sniMap != null && !sniMap.isEmpty()) {
-            for (SNIMatcher matcher : matchers) {
-                SNIServerName sniName = sniMap.get(matcher.getType());
-                if (sniName != null && (!matcher.matches(sniName))) {
-                    return false;
-                }
+                return builder.toString();
             }
         }
 
-        return true;
+        private static class UnknownServerName extends SNIServerName {
+            UnknownServerName(int code, byte[] encoded) {
+                super(code, encoded);
+            }
+        }
     }
 
-    /*
-     * Is the extension is identical to a server name list?
-     *
-     * This method is used to check the server name indication during session
-     * resumption.
-     *
-     * Per RFC 6066, when the server is deciding whether or not to accept a
-     * request to resume a session, the contents of a server_name extension
-     * MAY be used in the lookup of the session in the session cache.  The
-     * client SHOULD include the same server_name extension in the session
-     * resumption request as it did in the full handshake that established
-     * the session.  A server that implements this extension MUST NOT accept
-     * the request to resume the session if the server_name extension contains
-     * a different name.  Instead, it proceeds with a full handshake to
-     * establish a new session.  When resuming a session, the server MUST NOT
-     * include a server_name extension in the server hello.
-     */
-    boolean isIdentical(List<SNIServerName> other) {
-        if (other.size() == sniMap.size()) {
-            for(SNIServerName sniInOther : other) {
-                SNIServerName sniName = sniMap.get(sniInOther.getType());
-                if (sniName == null || !sniInOther.equals(sniName)) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    @Override
-    int length() {
-        return listLength == 0 ? 4 : 6 + listLength;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        if (listLength == 0) {
-            s.putInt16(listLength);     // in ServerHello, empty extension_data
-        } else {
-            s.putInt16(listLength + 2); // length of extension_data
-            s.putInt16(listLength);     // length of ServerNameList
-
-            for (SNIServerName sniName : sniMap.values()) {
-                s.putInt8(sniName.getType());         // server name type
-                s.putBytes16(sniName.getEncoded());   // server name value
+    private static final class CHServerNamesStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new CHServerNamesSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
             }
         }
     }
 
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        for (SNIServerName sniName : sniMap.values()) {
-            sb.append("[" + sniName + "]");
+    /**
+     * Network data producer of a "server_name" extension in the
+     * ClientHello handshake message.
+     */
+    private static final
+            class CHServerNameProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHServerNameProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(CH_SERVER_NAME)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                        "Ignore unavailable server_name extension");
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            List<SNIServerName> serverNames;
+            if (chc.isResumption && (chc.resumingSession != null)) {
+                serverNames =
+                        chc.resumingSession.getRequestedServerNames();
+            } else {
+                serverNames = chc.sslConfig.serverNames;
+            }   // Shall we use host too?
+
+            // Empty server name list is not allowed in client mode.
+            if ((serverNames != null) && !serverNames.isEmpty()) {
+                int sniLen = 0;
+                for (SNIServerName sniName : serverNames) {
+                    // For backward compatibility, all future data structures
+                    // associated with new NameTypes MUST begin with a 16-bit
+                    // length field.  The header length of server name is 3
+                    // bytes, including 1 byte NameType, and 2 bytes length
+                    // of the name.
+                    sniLen += CHServerNamesSpec.NAME_HEADER_LENGTH;
+                    sniLen += sniName.getEncoded().length;
+                }
+
+                byte[] extData = new byte[sniLen + 2];
+                ByteBuffer m = ByteBuffer.wrap(extData);
+                Record.putInt16(m, sniLen);
+                for (SNIServerName sniName : serverNames) {
+                    Record.putInt8(m, sniName.getType());
+                    Record.putBytes16(m, sniName.getEncoded());
+                }
+
+                // Update the context.
+                chc.requestedServerNames = serverNames;
+                chc.handshakeExtensions.put(CH_SERVER_NAME,
+                        new CHServerNamesSpec(serverNames));
+
+                return extData;
+            }
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.warning("Unable to indicate server name");
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Network data consumer of a "server_name" extension in the
+     * ClientHello handshake message.
+     */
+    private static final
+            class CHServerNameConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHServerNameConsumer() {
+            // blank
         }
 
-        return "Extension " + type + ", server_name: " + sb;
-    }
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(CH_SERVER_NAME)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " + CH_SERVER_NAME.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            CHServerNamesSpec spec;
+            try {
+                spec = new CHServerNamesSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(CH_SERVER_NAME, spec);
 
-    private static class UnknownServerName extends SNIServerName {
-        UnknownServerName(int code, byte[] encoded) {
-            super(code, encoded);
+            // Does the server match the server name request?
+            SNIServerName sni = null;
+            if (!shc.sslConfig.sniMatchers.isEmpty()) {
+                sni = chooseSni(shc.sslConfig.sniMatchers, spec.serverNames);
+                if (sni != null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                                "server name indication (" +
+                                sni + ") is accepted");
+                    }
+                } else {
+                    // We do not reject client without SNI extension currently.
+                    shc.conContext.fatal(Alert.UNRECOGNIZED_NAME,
+                            "Unrecognized server name indication");
+                }
+            } else {
+                // Note: Servers MAY require clients to send a valid
+                // "server_name" extension and respond to a ClientHello
+                // lacking a "server_name" extension by terminating the
+                // connection with a "missing_extension" alert.
+                //
+                // We do not reject client without SNI extension currently.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "no server name matchers, " +
+                            "ignore server name indication");
+                }
+            }
+
+            // Impact on session resumption.
+            //
+            // Does the resuming session have the same principal?
+            if (shc.isResumption && shc.resumingSession != null) {
+                // A server that implements this extension MUST NOT accept
+                // the request to resume the session if the server_name
+                // extension contains a different name.
+                //
+                // May only need to check that the session SNI is one of
+                // the requested server names.
+                if (!Objects.equals(
+                        sni, shc.resumingSession.serverNameIndication)) {
+                    shc.isResumption = false;
+                    shc.resumingSession = null;
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine(
+                                "abort session resumption, " +
+                                "different server name indication used");
+                    }
+                }
+            }
+
+            shc.requestedServerNames = spec.serverNames;
+            shc.negotiatedServerName = sni;
+        }
+
+        private static SNIServerName chooseSni(Collection<SNIMatcher> matchers,
+                List<SNIServerName> sniNames) {
+            if (sniNames != null && !sniNames.isEmpty()) {
+                for (SNIMatcher matcher : matchers) {
+                    int matcherType = matcher.getType();
+                    for (SNIServerName sniName : sniNames) {
+                        if (sniName.getType() == matcherType) {
+                            if (matcher.matches(sniName)) {
+                                return sniName;
+                            }
+
+                            // no duplicated entry in the server names list.
+                            break;
+                        }
+                    }
+                }
+            }
+
+            return null;
         }
     }
 
+    /**
+     * The "server_name" extension in the ServerHello handshake message.
+     *
+     * The "extension_data" field of this extension shall be empty.
+     */
+    static final class SHServerNamesSpec implements SSLExtensionSpec {
+        static final SHServerNamesSpec DEFAULT = new SHServerNamesSpec();
+
+        private SHServerNamesSpec() {
+            // blank
+        }
+
+        private SHServerNamesSpec(ByteBuffer buffer) throws IOException {
+            if (buffer.remaining() != 0) {
+                throw new SSLProtocolException(
+                    "Invalid ServerHello server_name extension: not empty");
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "<empty extension_data field>";
+        }
+    }
+
+    private static final class SHServerNamesStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new SHServerNamesSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "server_name" extension in the
+     * ServerHello handshake message.
+     */
+    private static final
+            class SHServerNameProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHServerNameProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to "server_name" extension request only
+            CHServerNamesSpec spec = (CHServerNamesSpec)
+                    shc.handshakeExtensions.get(CH_SERVER_NAME);
+            if (spec == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable extension: " + SH_SERVER_NAME.name);
+                }
+                return null;        // ignore the extension
+            }
+
+            // When resuming a session, the server MUST NOT include a
+            // server_name extension in the server hello.
+            if (shc.isResumption || shc.negotiatedServerName == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "No expected server name indication response");
+                }
+                return null;        // ignore the extension
+            }
+
+            // Produce the extension and update the context.
+            shc.handshakeExtensions.put(
+                    SH_SERVER_NAME, SHServerNamesSpec.DEFAULT);
+
+            return (new byte[0]);   // the empty extension_data
+        }
+    }
+
+    /**
+     * Network data consumer of a "server_name" extension in the
+     * ServerHello handshake message.
+     */
+    private static final
+            class SHServerNameConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHServerNameConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to "server_name" extension request only
+            CHServerNamesSpec spec = (CHServerNamesSpec)
+                    chc.handshakeExtensions.get(CH_SERVER_NAME);
+            if (spec == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected ServerHello server_name extension");
+            }
+
+            // Parse the extension.
+            if (buffer.remaining() != 0) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Invalid ServerHello server_name extension");
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SH_SERVER_NAME, SHServerNamesSpec.DEFAULT);
+            // The negotiated server name is unknown in client side. Just
+            // use the first request name as the value is not actually used
+            // in the current implementation.
+            chc.negotiatedServerName = spec.serverNames.get(0);
+        }
+    }
+
+    /**
+     * Network data producer of a "server_name" extension in the
+     * EncryptedExtensions handshake message.
+     */
+    private static final
+            class EEServerNameProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private EEServerNameProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to "server_name" extension request only
+            CHServerNamesSpec spec = (CHServerNamesSpec)
+                    shc.handshakeExtensions.get(CH_SERVER_NAME);
+            if (spec == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "Ignore unavailable extension: " + EE_SERVER_NAME.name);
+                }
+                return null;        // ignore the extension
+            }
+
+            // When resuming a session, the server MUST NOT include a
+            // server_name extension in the server hello.
+            if (shc.isResumption || shc.negotiatedServerName == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest(
+                        "No expected server name indication response");
+                }
+                return null;        // ignore the extension
+            }
+
+            // Produce the extension and update the context.
+            shc.handshakeExtensions.put(
+                    EE_SERVER_NAME, SHServerNamesSpec.DEFAULT);
+
+            return (new byte[0]);   // the empty extension_data
+        }
+    }
+
+    /**
+     * Network data consumer of a "server_name" extension in the
+     * EncryptedExtensions handshake message.
+     */
+    private static final
+            class EEServerNameConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private EEServerNameConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // In response to "server_name" extension request only
+            CHServerNamesSpec spec = (CHServerNamesSpec)
+                    chc.handshakeExtensions.get(CH_SERVER_NAME);
+            if (spec == null) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Unexpected EncryptedExtensions server_name extension");
+            }
+
+            // Parse the extension.
+            if (buffer.remaining() != 0) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+                    "Invalid EncryptedExtensions server_name extension");
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    EE_SERVER_NAME, SHServerNamesSpec.DEFAULT);
+            // The negotiated server name is unknown in client side. Just
+            // use the first request name as the value is not actually used
+            // in the current implementation.
+            chc.negotiatedServerName = spec.serverNames.get(0);
+        }
+    }
 }
--- a/src/java.base/share/classes/sun/security/ssl/SessionId.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SessionId.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,105 +23,83 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.security.SecureRandom;
+import java.util.Arrays;
 import javax.net.ssl.SSLProtocolException;
 
 /**
- * Encapsulates an SSL session ID.  SSL Session IDs are not reused by
- * servers during the lifetime of any sessions it created.  Sessions may
- * be used by many connections, either concurrently (for example, two
- * connections to a web server at the same time) or sequentially (over as
- * long a time period as is allowed by a given server).
+ * Encapsulates an SSL session ID.
  *
  * @author Satish Dharmaraj
  * @author David Brownell
  */
-final
-class SessionId
-{
-    static int MAX_LENGTH = 32;
-    private byte[] sessionId;          // max 32 bytes
+final class SessionId {
+    private static final int MAX_LENGTH = 32;
+    private final byte[] sessionId;          // max 32 bytes
 
-    /** Constructs a new session ID ... perhaps for a rejoinable session */
-    SessionId (boolean isRejoinable, SecureRandom generator)
-    {
-        if (isRejoinable)
-            // this will be unique, it's a timestamp plus much randomness
-            sessionId = new RandomCookie (generator).random_bytes;
-        else
-            sessionId = new byte [0];
+    // Constructs a new session ID ... perhaps for a rejoinable session
+    SessionId(boolean isRejoinable, SecureRandom generator) {
+        if (isRejoinable && (generator != null)) {
+            sessionId = new RandomCookie(generator).randomBytes;
+        } else {
+            sessionId = new byte[0];
+        }
+    }
+
+    // Constructs a session ID from a byte array (max size 32 bytes)
+    SessionId(byte[] sessionId) {
+        this.sessionId = sessionId.clone();
     }
 
-    /** Constructs a session ID from a byte array (max size 32 bytes) */
-    SessionId (byte[] sessionId)
-        { this.sessionId = sessionId; }
-
-    /** Returns the length of the ID, in bytes */
-    int length ()
-        { return sessionId.length; }
-
-    /** Returns the bytes in the ID.  May be an empty array.  */
-    byte[] getId ()
-    {
-        return sessionId.clone ();
+    // Returns the length of the ID, in bytes
+    int length() {
+        return sessionId.length;
     }
 
-    /** Returns the ID as a string */
-    @Override
-    public String toString ()
-    {
-        int             len = sessionId.length;
-        StringBuilder    sb = new StringBuilder (10 + 2 * len);
+    // Returns the bytes in the ID.  May be an empty array.
+    byte[] getId() {
+        return sessionId.clone();
+    }
 
-        sb.append("{");
-        for (int i = 0; i < len; i++) {
-            sb.append(0x0ff & sessionId[i]);
-            if (i != (len - 1))
-                sb.append (", ");
+    // Returns the ID as a string
+    @Override
+    public String toString() {
+        if (sessionId.length == 0) {
+            return "";
         }
-        sb.append("}");
-        return sb.toString ();
+
+        return Utilities.toHexString(sessionId);
     }
 
 
-    /** Returns a value which is the same for session IDs which are equal */
+    // Returns a value which is the same for session IDs which are equal
     @Override
-    public int hashCode ()
-    {
-        int     retval = 0;
-
-        for (int i = 0; i < sessionId.length; i++)
-            retval += sessionId [i];
-        return retval;
+    public int hashCode() {
+        return Arrays.hashCode(sessionId);
     }
 
-    /** Returns true if the parameter is the same session ID */
+    // Returns true if the parameter is the same session ID
     @Override
-    public boolean equals (Object obj)
-    {
-        if (!(obj instanceof SessionId))
-            return false;
+    public boolean equals (Object obj) {
+        if (obj == this) {
+            return true;
+        }
 
-        SessionId s = (SessionId) obj;
-        byte[] b = s.getId ();
+        if (obj instanceof SessionId) {
+            SessionId that = (SessionId)obj;
+            return Arrays.equals(this.sessionId, that.sessionId);
+        }
 
-        if (b.length != sessionId.length)
-            return false;
-        for (int i = 0; i < sessionId.length; i++) {
-            if (b [i] != sessionId [i])
-                return false;
-        }
-        return true;
+        return false;
     }
 
     /**
      * Checks the length of the session ID to make sure it sits within
      * the range called out in the specification
      */
-    void checkLength(ProtocolVersion pv) throws SSLProtocolException {
+    void checkLength(int protocolVersion) throws SSLProtocolException {
         // As of today all versions of TLS have a 32-byte maximum length.
         // In the future we can do more here to support protocol versions
         // that may have longer max lengths.
@@ -130,5 +108,4 @@
                     sessionId.length + " bytes)");
         }
     }
-
 }
--- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,110 +26,530 @@
 package sun.security.ssl;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "signature_algorithms" extensions [RFC 5246].
+ */
+final class SignatureAlgorithmsExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHSignatureSchemesProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHSignatureSchemesConsumer();
+    static final HandshakeAbsence chOnLoadAbsence =
+            new CHSignatureSchemesOnLoadAbsence();
+    static final HandshakeConsumer chOnTradeConsumer =
+            new CHSignatureSchemesUpdate();
+    static final HandshakeAbsence chOnTradeAbsence =
+            new CHSignatureSchemesOnTradeAbsence();
 
-import javax.net.ssl.SSLProtocolException;
+    static final HandshakeProducer crNetworkProducer =
+            new CRSignatureSchemesProducer();
+    static final ExtensionConsumer crOnLoadConsumer =
+            new CRSignatureSchemesConsumer();
+    static final HandshakeAbsence crOnLoadAbsence =
+            new CRSignatureSchemesAbsence();
+    static final HandshakeConsumer crOnTradeConsumer =
+            new CRSignatureSchemesUpdate();
+
+    static final SSLStringizer ssStringizer =
+            new SignatureSchemesStringizer();
+
+    /**
+     * The "signature_algorithms" extension.
+     */
+    static final class SignatureSchemesSpec implements SSLExtensionSpec {
+        final int[] signatureSchemes;
+
+        SignatureSchemesSpec(List<SignatureScheme> schemes) {
+            if (schemes != null) {
+                signatureSchemes = new int[schemes.size()];
+                int i = 0;
+                for (SignatureScheme scheme : schemes) {
+                    signatureSchemes[i++] = scheme.id;
+                }
+            } else {
+                this.signatureSchemes = new int[0];
+            }
+        }
 
-/*
- * [RFC5246] The client uses the "signature_algorithms" extension to
- * indicate to the server which signature/hash algorithm pairs may be
- * used in digital signatures.  The "extension_data" field of this
- * extension contains a "supported_signature_algorithms" value.
- *
- *     enum {
- *         none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
- *         sha512(6), (255)
- *     } HashAlgorithm;
- *
- *     enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
- *       SignatureAlgorithm;
- *
- *     struct {
- *           HashAlgorithm hash;
- *           SignatureAlgorithm signature;
- *     } SignatureAndHashAlgorithm;
- *
- *     SignatureAndHashAlgorithm
- *       supported_signature_algorithms<2..2^16-2>;
- */
-final class SignatureAlgorithmsExtension extends HelloExtension {
+        SignatureSchemesSpec(ByteBuffer buffer) throws IOException {
+            if (buffer.remaining() < 2) {      // 2: the length of the list
+                throw new SSLProtocolException(
+                    "Invalid signature_algorithms: insufficient data");
+            }
+
+            byte[] algs = Record.getBytes16(buffer);
+            if (buffer.hasRemaining()) {
+                throw new SSLProtocolException(
+                    "Invalid signature_algorithms: unknown extra data");
+            }
+
+            if (algs == null || algs.length == 0 || (algs.length & 0x01) != 0) {
+                throw new SSLProtocolException(
+                    "Invalid signature_algorithms: incomplete data");
+            }
+
+            int[] schemes = new int[algs.length / 2];
+            for (int i = 0, j = 0; i < algs.length;) {
+                byte hash = algs[i++];
+                byte sign = algs[i++];
+                schemes[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF);
+            }
+
+            this.signatureSchemes = schemes;
+        }
 
-    private Collection<SignatureAndHashAlgorithm> algorithms;
-    private int algorithmsLen;  // length of supported_signature_algorithms
-
-    SignatureAlgorithmsExtension(
-            Collection<SignatureAndHashAlgorithm> signAlgs) {
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"signature schemes\": '['{0}']'", Locale.ENGLISH);
 
-        super(ExtensionType.EXT_SIGNATURE_ALGORITHMS);
+            if (signatureSchemes == null || signatureSchemes.length == 0) {
+                Object[] messageFields = {
+                        "<no supported signature schemes specified>"
+                    };
+                return messageFormat.format(messageFields);
+            } else {
+                StringBuilder builder = new StringBuilder(512);
+                boolean isFirst = true;
+                for (int pv : signatureSchemes) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        builder.append(", ");
+                    }
 
-        algorithms = new ArrayList<SignatureAndHashAlgorithm>(signAlgs);
-        algorithmsLen =
-            SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size();
+                    builder.append(SignatureScheme.nameOf(pv));
+                }
+
+                Object[] messageFields = {
+                        builder.toString()
+                    };
+
+                return messageFormat.format(messageFields);
+            }
+        }
     }
 
-    SignatureAlgorithmsExtension(HandshakeInStream s, int len)
-                throws IOException {
-        super(ExtensionType.EXT_SIGNATURE_ALGORITHMS);
+    private static final
+            class SignatureSchemesStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new SignatureSchemesSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
 
-        algorithmsLen = s.getInt16();
-        if (algorithmsLen == 0 || algorithmsLen + 2 != len) {
-            throw new SSLProtocolException("Invalid " + type + " extension");
+    /**
+     * Network data producer of a "signature_algorithms" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHSignatureSchemesProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHSignatureSchemesProducer() {
+            // blank
         }
 
-        algorithms = new ArrayList<SignatureAndHashAlgorithm>();
-        int remains = algorithmsLen;
-        int sequence = 0;
-        while (remains > 1) {   // needs at least two bytes
-            int hash = s.getInt8();         // hash algorithm
-            int signature = s.getInt8();    // signature algorithm
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable signature_algorithms extension");
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            if (chc.localSupportedSignAlgs == null) {
+                chc.localSupportedSignAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            chc.algorithmConstraints, chc.activeProtocols);
+            }
 
-            SignatureAndHashAlgorithm algorithm =
-                SignatureAndHashAlgorithm.valueOf(hash, signature, ++sequence);
-            algorithms.add(algorithm);
-            remains -= 2;  // one byte for hash, one byte for signature
+            int vectorLen = SignatureScheme.sizeInRecord() *
+                    chc.localSupportedSignAlgs.size();
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (SignatureScheme ss : chc.localSupportedSignAlgs) {
+                Record.putInt16(m, ss.id);
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS,
+                    new SignatureSchemesSpec(chc.localSupportedSignAlgs));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "signature_algorithms" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHSignatureSchemesConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHSignatureSchemesConsumer() {
+            // blank
         }
 
-        if (remains != 0) {
-            throw new SSLProtocolException("Invalid server_name extension");
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable signature_algorithms extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            SignatureSchemesSpec spec;
+            try {
+                spec = new SignatureSchemesSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS, spec);
+
+            // No impact on session resumption.
         }
     }
 
-    Collection<SignatureAndHashAlgorithm> getSignAlgorithms() {
-        return algorithms;
-    }
+    /**
+     * After session creation consuming of a "signature_algorithms"
+     * extension in the ClientHello handshake message.
+     */
+    private static final class CHSignatureSchemesUpdate
+            implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CHSignatureSchemesUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            SignatureSchemesSpec spec =
+                    (SignatureSchemesSpec)shc.handshakeExtensions.get(
+                            SSLExtension.CH_SIGNATURE_ALGORITHMS);
+            if (spec == null) {
+                // Ignore, no "signature_algorithms" extension requested.
+                return;
+            }
 
-    @Override
-    int length() {
-        return 6 + algorithmsLen;
+            // update the context
+            List<SignatureScheme> sss =
+                    SignatureScheme.getSupportedAlgorithms(
+                            shc.algorithmConstraints, shc.negotiatedProtocol,
+                            spec.signatureSchemes);
+            shc.peerRequestedSignatureSchemes = sss;
+
+            // If no "signature_algorithms_cert" extension is present, then
+            // the "signature_algorithms" extension also applies to
+            // signatures appearing in certificates.
+            SignatureSchemesSpec certSpec =
+                    (SignatureSchemesSpec)shc.handshakeExtensions.get(
+                            SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
+            if (certSpec == null) {
+                shc.peerRequestedCertSignSchemes = sss;
+                shc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
+            }
+
+            if (!shc.isResumption &&
+                    shc.negotiatedProtocol.useTLS13PlusSpec()) {
+                if (shc.sslConfig.clientAuthType !=
+                        ClientAuthType.CLIENT_AUTH_NONE) {
+                    shc.handshakeProducers.putIfAbsent(
+                            SSLHandshake.CERTIFICATE_REQUEST.id,
+                            SSLHandshake.CERTIFICATE_REQUEST);
+                }
+                shc.handshakeProducers.put(
+                        SSLHandshake.CERTIFICATE.id,
+                        SSLHandshake.CERTIFICATE);
+                shc.handshakeProducers.putIfAbsent(
+                        SSLHandshake.CERTIFICATE_VERIFY.id,
+                        SSLHandshake.CERTIFICATE_VERIFY);
+            }
+        }
     }
 
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(algorithmsLen + 2);
-        s.putInt16(algorithmsLen);
+    /**
+     * The absence processing if a "signature_algorithms" extension is
+     * not present in the ClientHello handshake message.
+     */
+    private static final
+            class CHSignatureSchemesOnLoadAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // This is a mandatory extension for certificate authentication
+            // in TLS 1.3.
+            //
+            // We may support the server authentication other than X.509
+            // certificate later.
+            if (shc.negotiatedProtocol.useTLS13PlusSpec()) {
+                shc.conContext.fatal(Alert.MISSING_EXTENSION,
+                    "No mandatory signature_algorithms extension in the " +
+                    "received CertificateRequest handshake message");
+            }
+        }
+    }
 
-        for (SignatureAndHashAlgorithm algorithm : algorithms) {
-            s.putInt8(algorithm.getHashValue());      // HashAlgorithm
-            s.putInt8(algorithm.getSignatureValue()); // SignatureAlgorithm
+    /**
+     * The absence processing if a "signature_algorithms" extension is
+     * not present in the ClientHello handshake message.
+     */
+    private static final
+            class CHSignatureSchemesOnTradeAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            if (shc.negotiatedProtocol.useTLS12PlusSpec()) {
+                // Use default hash and signature algorithm:
+                //      {sha1,rsa}
+                //      {sha1,dsa}
+                //      {sha1,ecdsa}
+                // Per RFC 5246, If the client supports only the default hash
+                // and signature algorithms, it MAY omit the
+                // signature_algorithms extension.  If the client does not
+                // support the default algorithms, or supports other hash
+                // and signature algorithms (and it is willing to use them
+                // for verifying messages sent by the server, i.e., server
+                // certificates and server key exchange), it MUST send the
+                // signature_algorithms extension, listing the algorithms it
+                // is willing to accept.
+                List<SignatureScheme> shemes = Arrays.asList(
+                        SignatureScheme.RSA_PKCS1_SHA1,
+                        SignatureScheme.DSA_SHA1,
+                        SignatureScheme.ECDSA_SHA1
+                );
+
+                shc.peerRequestedSignatureSchemes = shemes;
+                if (shc.peerRequestedCertSignSchemes == null ||
+                    shc.peerRequestedCertSignSchemes.isEmpty()) {
+                        shc.peerRequestedCertSignSchemes = shemes;
+                }
+
+                // Use the default peer signature algorithms.
+                shc.handshakeSession.setUseDefaultPeerSignAlgs();
+            }
         }
     }
 
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        boolean opened = false;
-        for (SignatureAndHashAlgorithm signAlg : algorithms) {
-            if (opened) {
-                sb.append(", " + signAlg.getAlgorithmName());
-            } else {
-                sb.append(signAlg.getAlgorithmName());
-                opened = true;
+    /**
+     * Network data producer of a "signature_algorithms" extension in
+     * the CertificateRequest handshake message.
+     */
+    private static final
+            class CRSignatureSchemesProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CRSignatureSchemesProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            //
+            // Note that this is a mandatory extension for CertificateRequest
+            // handshake message in TLS 1.3.
+            if (!shc.sslConfig.isAvailable(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
+                shc.conContext.fatal(Alert.MISSING_EXTENSION,
+                        "No available signature_algorithms extension " +
+                        "for client certificate authentication");
+                return null;    // make the compiler happy
+            }
+
+            // Produce the extension.
+            if (shc.localSupportedSignAlgs == null) {
+                shc.localSupportedSignAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            shc.algorithmConstraints, shc.activeProtocols);
+            }
+
+            int vectorLen = SignatureScheme.sizeInRecord() *
+                    shc.localSupportedSignAlgs.size();
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (SignatureScheme ss : shc.localSupportedSignAlgs) {
+                Record.putInt16(m, ss.id);
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS,
+                    new SignatureSchemesSpec(shc.localSupportedSignAlgs));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "signature_algorithms" extension in
+     * the CertificateRequest handshake message.
+     */
+    private static final
+            class CRSignatureSchemesConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CRSignatureSchemesConsumer() {
+            // blank
+        }
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            //
+            // Note that this is a mandatory extension for CertificateRequest
+            // handshake message in TLS 1.3.
+            if (!chc.sslConfig.isAvailable(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No available signature_algorithms extension " +
+                        "for client certificate authentication");
+                return;     // make the compiler happy
+            }
+
+            // Parse the extension.
+            SignatureSchemesSpec spec;
+            try {
+                spec = new SignatureSchemesSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            List<SignatureScheme> knownSignatureSchemes = new LinkedList<>();
+            for (int id : spec.signatureSchemes) {
+                SignatureScheme ss = SignatureScheme.valueOf(id);
+                if (ss != null) {
+                    knownSignatureSchemes.add(ss);
+                }
+            }
+
+            // Update the context.
+            // chc.peerRequestedSignatureSchemes = knownSignatureSchemes;
+            chc.handshakeExtensions.put(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS, spec);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * After session creation consuming of a "signature_algorithms"
+     * extension in the CertificateRequest handshake message.
+     */
+    private static final class CRSignatureSchemesUpdate
+            implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CRSignatureSchemesUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            SignatureSchemesSpec spec =
+                    (SignatureSchemesSpec)chc.handshakeExtensions.get(
+                            SSLExtension.CR_SIGNATURE_ALGORITHMS);
+            if (spec == null) {
+                // Ignore, no "signature_algorithms" extension requested.
+                return;
+            }
+
+            // update the context
+            List<SignatureScheme> sss =
+                    SignatureScheme.getSupportedAlgorithms(
+                            chc.algorithmConstraints, chc.negotiatedProtocol,
+                            spec.signatureSchemes);
+            chc.peerRequestedSignatureSchemes = sss;
+
+            // If no "signature_algorithms_cert" extension is present, then
+            // the "signature_algorithms" extension also applies to
+            // signatures appearing in certificates.
+            SignatureSchemesSpec certSpec =
+                    (SignatureSchemesSpec)chc.handshakeExtensions.get(
+                            SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
+            if (certSpec == null) {
+                chc.peerRequestedCertSignSchemes = sss;
+                chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
             }
         }
+    }
 
-        return "Extension " + type + ", signature_algorithms: " + sb;
+    /**
+     * The absence processing if a "signature_algorithms" extension is
+     * not present in the CertificateRequest handshake message.
+     */
+    private static final
+            class CRSignatureSchemesAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // This is a mandatory extension for CertificateRequest handshake
+            // message in TLS 1.3.
+            chc.conContext.fatal(Alert.MISSING_EXTENSION,
+                    "No mandatory signature_algorithms extension in the " +
+                    "received CertificateRequest handshake message");
+        }
     }
 }
-
--- a/src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,441 +0,0 @@
-/*
- * Copyright (c) 2010, 2016, 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.security.AlgorithmConstraints;
-import java.security.CryptoPrimitive;
-import java.security.PrivateKey;
-import java.security.Security;
-
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.EnumSet;
-import java.util.TreeMap;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.ArrayList;
-
-import sun.security.util.KeyUtil;
-
-/**
- * Signature and hash algorithm.
- *
- * [RFC5246] The client uses the "signature_algorithms" extension to
- * indicate to the server which signature/hash algorithm pairs may be
- * used in digital signatures.  The "extension_data" field of this
- * extension contains a "supported_signature_algorithms" value.
- *
- *     enum {
- *         none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
- *         sha512(6), (255)
- *     } HashAlgorithm;
- *
- *     enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
- *       SignatureAlgorithm;
- *
- *     struct {
- *           HashAlgorithm hash;
- *           SignatureAlgorithm signature;
- *     } SignatureAndHashAlgorithm;
- */
-final class SignatureAndHashAlgorithm {
-
-    // minimum priority for default enabled algorithms
-    static final int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0;
-
-    // performance optimization
-    private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
-        Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
-
-    // supported pairs of signature and hash algorithm
-    private static final Map<Integer, SignatureAndHashAlgorithm> supportedMap;
-    private static final Map<Integer, SignatureAndHashAlgorithm> priorityMap;
-
-    // the hash algorithm
-    private HashAlgorithm hash;
-
-    // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA
-    private int id;
-
-    // the standard algorithm name, for example "SHA512withECDSA"
-    private String algorithm;
-
-    // Priority for the preference order. The lower the better.
-    //
-    // If the algorithm is unsupported, its priority should be bigger
-    // than SUPPORTED_ALG_PRIORITY_MAX_NUM.
-    private int priority;
-
-    // constructor for supported algorithm
-    private SignatureAndHashAlgorithm(HashAlgorithm hash,
-            SignatureAlgorithm signature, String algorithm, int priority) {
-        this.hash = hash;
-        this.algorithm = algorithm;
-        this.id = ((hash.value & 0xFF) << 8) | (signature.value & 0xFF);
-        this.priority = priority;
-    }
-
-    // constructor for unsupported algorithm
-    private SignatureAndHashAlgorithm(String algorithm, int id, int sequence) {
-        this.hash = HashAlgorithm.valueOf((id >> 8) & 0xFF);
-        this.algorithm = algorithm;
-        this.id = id;
-
-        // add one more to the sequence number, in case that the number is zero
-        this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1;
-    }
-
-    // Note that we do not use the sequence argument for supported algorithms,
-    // so please don't sort by comparing the objects read from handshake
-    // messages.
-    static SignatureAndHashAlgorithm valueOf(int hash,
-            int signature, int sequence) {
-        hash &= 0xFF;
-        signature &= 0xFF;
-
-        int id = (hash << 8) | signature;
-        SignatureAndHashAlgorithm signAlg = supportedMap.get(id);
-        if (signAlg == null) {
-            // unsupported algorithm
-            signAlg = new SignatureAndHashAlgorithm(
-                "Unknown (hash:0x" + Integer.toString(hash, 16) +
-                ", signature:0x" + Integer.toString(signature, 16) + ")",
-                id, sequence);
-        }
-
-        return signAlg;
-    }
-
-    int getHashValue() {
-        return (id >> 8) & 0xFF;
-    }
-
-    int getSignatureValue() {
-        return id & 0xFF;
-    }
-
-    String getAlgorithmName() {
-        return algorithm;
-    }
-
-    // return the size of a SignatureAndHashAlgorithm structure in TLS record
-    static int sizeInRecord() {
-        return 2;
-    }
-
-    // Get local supported algorithm collection complying to
-    // algorithm constraints
-    static Collection<SignatureAndHashAlgorithm>
-            getSupportedAlgorithms(AlgorithmConstraints constraints) {
-
-        Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
-        for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {
-            if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
-                    constraints.permits(SIGNATURE_PRIMITIVE_SET,
-                            sigAlg.algorithm, null)) {
-                supported.add(sigAlg);
-            }
-        }
-
-        return supported;
-    }
-
-    // Get supported algorithm collection from an untrusted collection
-    static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms(
-            AlgorithmConstraints constraints,
-            Collection<SignatureAndHashAlgorithm> algorithms ) {
-        Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
-        for (SignatureAndHashAlgorithm sigAlg : algorithms) {
-            if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
-                    constraints.permits(SIGNATURE_PRIMITIVE_SET,
-                                sigAlg.algorithm, null)) {
-                supported.add(sigAlg);
-            }
-        }
-
-        return supported;
-    }
-
-    static String[] getAlgorithmNames(
-            Collection<SignatureAndHashAlgorithm> algorithms) {
-        ArrayList<String> algorithmNames = new ArrayList<>();
-        if (algorithms != null) {
-            for (SignatureAndHashAlgorithm sigAlg : algorithms) {
-                algorithmNames.add(sigAlg.algorithm);
-            }
-        }
-
-        String[] array = new String[algorithmNames.size()];
-        return algorithmNames.toArray(array);
-    }
-
-    static Set<String> getHashAlgorithmNames(
-            Collection<SignatureAndHashAlgorithm> algorithms) {
-        Set<String> algorithmNames = new HashSet<>();
-        if (algorithms != null) {
-            for (SignatureAndHashAlgorithm sigAlg : algorithms) {
-                if (sigAlg.hash.value > 0) {
-                    algorithmNames.add(sigAlg.hash.standardName);
-                }
-            }
-        }
-
-        return algorithmNames;
-    }
-
-    static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) {
-        return algorithm.hash.standardName;
-    }
-
-    private static void supports(HashAlgorithm hash,
-            SignatureAlgorithm signature, String algorithm, int priority) {
-
-        SignatureAndHashAlgorithm pair =
-            new SignatureAndHashAlgorithm(hash, signature, algorithm, priority);
-        if (supportedMap.put(pair.id, pair) != null) {
-            throw new RuntimeException(
-                "Duplicate SignatureAndHashAlgorithm definition, id: " +
-                pair.id);
-        }
-        if (priorityMap.put(pair.priority, pair) != null) {
-            throw new RuntimeException(
-                "Duplicate SignatureAndHashAlgorithm definition, priority: " +
-                pair.priority);
-        }
-    }
-
-    static SignatureAndHashAlgorithm getPreferableAlgorithm(
-        Collection<SignatureAndHashAlgorithm> algorithms, String expected) {
-
-        return SignatureAndHashAlgorithm.getPreferableAlgorithm(
-                algorithms, expected, null);
-    }
-
-    static SignatureAndHashAlgorithm getPreferableAlgorithm(
-            Collection<SignatureAndHashAlgorithm> algorithms,
-            String expected, PrivateKey signingKey) {
-
-        int maxDigestLength = getMaxDigestLength(signingKey);
-        for (SignatureAndHashAlgorithm algorithm : algorithms) {
-            int signValue = algorithm.id & 0xFF;
-            if ((expected == null) ||
-                    (expected.equalsIgnoreCase("rsa") &&
-                            signValue == SignatureAlgorithm.RSA.value) ||
-                    (expected.equalsIgnoreCase("dsa") &&
-                            signValue == SignatureAlgorithm.DSA.value) ||
-                    (expected.equalsIgnoreCase("ecdsa") &&
-                            signValue == SignatureAlgorithm.ECDSA.value) ||
-                    (expected.equalsIgnoreCase("ec") &&
-                            signValue == SignatureAlgorithm.ECDSA.value)) {
-
-                if (algorithm.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
-                        algorithm.hash.length <= maxDigestLength) {
-
-                    return algorithm;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /*
-     * Need to check key length to match the length of hash value
-     */
-    private static int getMaxDigestLength(PrivateKey signingKey) {
-        int maxDigestLength = Integer.MAX_VALUE;
-
-        // only need to check RSA algorithm at present.
-        if (signingKey != null &&
-                "rsa".equalsIgnoreCase(signingKey.getAlgorithm())) {
-            /*
-             * RSA keys of 512 bits have been shown to be practically
-             * breakable, it does not make much sense to use the strong
-             * hash algorithm for keys whose key size less than 512 bits.
-             * So it is not necessary to caculate the required max digest
-             * length exactly.
-             *
-             * If key size is greater than or equals to 768, there is no max
-             * digest length limitation in currect implementation.
-             *
-             * If key size is greater than or equals to 512, but less than
-             * 768, the digest length should be less than or equal to 32 bytes.
-             *
-             * If key size is less than 512, the  digest length should be
-             * less than or equal to 20 bytes.
-             */
-            int keySize = KeyUtil.getKeySize(signingKey);
-            if (keySize >= 768) {
-                maxDigestLength = HashAlgorithm.SHA512.length;
-            } else if ((keySize >= 512) && (keySize < 768)) {
-                maxDigestLength = HashAlgorithm.SHA256.length;
-            } else if ((keySize > 0) && (keySize < 512)) {
-                maxDigestLength = HashAlgorithm.SHA1.length;
-            }   // Otherwise, cannot determine the key size, prefer the most
-                // preferable hash algorithm.
-        }
-
-        return maxDigestLength;
-    }
-
-    static enum HashAlgorithm {
-        UNDEFINED("undefined",        "", -1, -1),
-        NONE(          "none",    "NONE",  0, -1),
-        MD5(            "md5",     "MD5",  1, 16),
-        SHA1(          "sha1",   "SHA-1",  2, 20),
-        SHA224(      "sha224", "SHA-224",  3, 28),
-        SHA256(      "sha256", "SHA-256",  4, 32),
-        SHA384(      "sha384", "SHA-384",  5, 48),
-        SHA512(      "sha512", "SHA-512",  6, 64);
-
-        final String name;  // not the standard signature algorithm name
-                            // except the UNDEFINED, other names are defined
-                            // by TLS 1.2 protocol
-        final String standardName; // the standard MessageDigest algorithm name
-        final int value;
-        final int length;   // digest length in bytes, -1 means not applicable
-
-        private HashAlgorithm(String name, String standardName,
-                int value, int length) {
-            this.name = name;
-            this.standardName = standardName;
-            this.value = value;
-            this.length = length;
-        }
-
-        static HashAlgorithm valueOf(int value) {
-            HashAlgorithm algorithm = UNDEFINED;
-            switch (value) {
-                case 0:
-                    algorithm = NONE;
-                    break;
-                case 1:
-                    algorithm = MD5;
-                    break;
-                case 2:
-                    algorithm = SHA1;
-                    break;
-                case 3:
-                    algorithm = SHA224;
-                    break;
-                case 4:
-                    algorithm = SHA256;
-                    break;
-                case 5:
-                    algorithm = SHA384;
-                    break;
-                case 6:
-                    algorithm = SHA512;
-                    break;
-            }
-
-            return algorithm;
-        }
-    }
-
-    static enum SignatureAlgorithm {
-        UNDEFINED("undefined", -1),
-        ANONYMOUS("anonymous",  0),
-        RSA(            "rsa",  1),
-        DSA(            "dsa",  2),
-        ECDSA(        "ecdsa",  3);
-
-        final String name;  // not the standard signature algorithm name
-                            // except the UNDEFINED, other names are defined
-                            // by TLS 1.2 protocol
-        final int value;
-
-        private SignatureAlgorithm(String name, int value) {
-            this.name = name;
-            this.value = value;
-        }
-
-        static SignatureAlgorithm valueOf(int value) {
-            SignatureAlgorithm algorithm = UNDEFINED;
-            switch (value) {
-                case 0:
-                    algorithm = ANONYMOUS;
-                    break;
-                case 1:
-                    algorithm = RSA;
-                    break;
-                case 2:
-                    algorithm = DSA;
-                    break;
-                case 3:
-                    algorithm = ECDSA;
-                    break;
-            }
-
-            return algorithm;
-        }
-    }
-
-    static {
-        supportedMap = Collections.synchronizedSortedMap(
-            new TreeMap<Integer, SignatureAndHashAlgorithm>());
-        priorityMap = Collections.synchronizedSortedMap(
-            new TreeMap<Integer, SignatureAndHashAlgorithm>());
-
-        synchronized (supportedMap) {
-            int p = SUPPORTED_ALG_PRIORITY_MAX_NUM;
-            supports(HashAlgorithm.MD5,         SignatureAlgorithm.RSA,
-                    "MD5withRSA",           --p);
-            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.DSA,
-                    "SHA1withDSA",          --p);
-            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.RSA,
-                    "SHA1withRSA",          --p);
-            supports(HashAlgorithm.SHA1,        SignatureAlgorithm.ECDSA,
-                    "SHA1withECDSA",        --p);
-
-            if (Security.getProvider("SunMSCAPI") == null) {
-                supports(HashAlgorithm.SHA224,      SignatureAlgorithm.DSA,
-                        "SHA224withDSA",        --p);
-                supports(HashAlgorithm.SHA224,      SignatureAlgorithm.RSA,
-                        "SHA224withRSA",        --p);
-                supports(HashAlgorithm.SHA224,      SignatureAlgorithm.ECDSA,
-                        "SHA224withECDSA",      --p);
-            }
-
-            supports(HashAlgorithm.SHA256,      SignatureAlgorithm.DSA,
-                    "SHA256withDSA",        --p);
-            supports(HashAlgorithm.SHA256,      SignatureAlgorithm.RSA,
-                    "SHA256withRSA",        --p);
-            supports(HashAlgorithm.SHA256,      SignatureAlgorithm.ECDSA,
-                    "SHA256withECDSA",      --p);
-            supports(HashAlgorithm.SHA384,      SignatureAlgorithm.RSA,
-                    "SHA384withRSA",        --p);
-            supports(HashAlgorithm.SHA384,      SignatureAlgorithm.ECDSA,
-                    "SHA384withECDSA",      --p);
-            supports(HashAlgorithm.SHA512,      SignatureAlgorithm.RSA,
-                    "SHA512withRSA",        --p);
-            supports(HashAlgorithm.SHA512,      SignatureAlgorithm.ECDSA,
-                    "SHA512withECDSA",      --p);
-        }
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.security.*;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+import sun.security.util.KeyUtil;
+
+enum SignatureScheme {
+    // EdDSA algorithms
+    ED25519                 (0x0807, "ed25519", "ed25519",
+                                    "ed25519",
+                                    ProtocolVersion.PROTOCOLS_OF_13),
+    ED448                   (0x0808, "ed448", "ed448",
+                                    "ed448",
+                                    ProtocolVersion.PROTOCOLS_OF_13),
+
+    // ECDSA algorithms
+    ECDSA_SECP256R1_SHA256  (0x0403, "ecdsa_secp256r1_sha256",
+                                    "SHA256withECDSA",
+                                    "EC",
+                                    NamedGroup.SECP256_R1,
+                                    ProtocolVersion.PROTOCOLS_TO_13),
+    ECDSA_SECP384R1_SHA384  (0x0503, "ecdsa_secp384r1_sha384",
+                                    "SHA384withECDSA",
+                                    "EC",
+                                    NamedGroup.SECP384_R1,
+                                    ProtocolVersion.PROTOCOLS_TO_13),
+    ECDSA_SECP512R1_SHA512  (0x0603, "ecdsa_secp512r1_sha512",
+                                    "SHA512withECDSA",
+                                    "EC",
+                                    NamedGroup.SECP521_R1,
+                                    ProtocolVersion.PROTOCOLS_TO_13),
+
+    // RSASSA-PSS algorithms with public key OID rsaEncryption
+    //
+    // The minimalKeySize is calculated as (See RFC 8017 for details):
+    //     hash length + salt length + 16
+    RSA_PSS_RSAE_SHA256     (0x0804, "rsa_pss_rsae_sha256",
+                                    "RSASSA-PSS", "RSA",
+                                    SigAlgParamSpec.RSA_PSS_SHA256, 528,
+                                    ProtocolVersion.PROTOCOLS_12_13),
+    RSA_PSS_RSAE_SHA384     (0x0805, "rsa_pss_rsae_sha384",
+                                    "RSASSA-PSS", "RSA",
+                                    SigAlgParamSpec.RSA_PSS_SHA384, 784,
+                                    ProtocolVersion.PROTOCOLS_12_13),
+    RSA_PSS_RSAE_SHA512     (0x0806, "rsa_pss_rsae_sha512",
+                                    "RSASSA-PSS", "RSA",
+                                    SigAlgParamSpec.RSA_PSS_SHA512, 1040,
+                                    ProtocolVersion.PROTOCOLS_12_13),
+
+    // RSASSA-PSS algorithms with public key OID RSASSA-PSS
+    //
+    // The minimalKeySize is calculated as (See RFC 8017 for details):
+    //     hash length + salt length + 16
+    RSA_PSS_PSS_SHA256      (0x0809, "rsa_pss_pss_sha256",
+                                    "RSASSA-PSS", "RSASSA-PSS",
+                                    SigAlgParamSpec.RSA_PSS_SHA256, 528,
+                                    ProtocolVersion.PROTOCOLS_12_13),
+    RSA_PSS_PSS_SHA384      (0x080A, "rsa_pss_pss_sha384",
+                                    "RSASSA-PSS", "RSASSA-PSS",
+                                    SigAlgParamSpec.RSA_PSS_SHA384, 784,
+                                    ProtocolVersion.PROTOCOLS_12_13),
+    RSA_PSS_PSS_SHA512      (0x080B, "rsa_pss_pss_sha512",
+                                    "RSASSA-PSS", "RSASSA-PSS",
+                                    SigAlgParamSpec.RSA_PSS_SHA512, 1040,
+                                    ProtocolVersion.PROTOCOLS_12_13),
+
+    // RSASSA-PKCS1-v1_5 algorithms
+    RSA_PKCS1_SHA256        (0x0401, "rsa_pkcs1_sha256", "SHA256withRSA",
+                                    "RSA", null, null, 511,
+                                    ProtocolVersion.PROTOCOLS_TO_13,
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    RSA_PKCS1_SHA384        (0x0501, "rsa_pkcs1_sha384", "SHA384withRSA",
+                                    "RSA", null, null, 768,
+                                    ProtocolVersion.PROTOCOLS_TO_13,
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    RSA_PKCS1_SHA512        (0x0601, "rsa_pkcs1_sha512", "SHA512withRSA",
+                                    "RSA", null, null, 768,
+                                    ProtocolVersion.PROTOCOLS_TO_13,
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+
+    // Legacy algorithms
+    DSA_SHA256              (0x0402, "dsa_sha256", "SHA256withDSA",
+                                    "DSA",
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    ECDSA_SHA224            (0x0303, "ecdsa_sha224", "SHA224withECDSA",
+                                    "EC",
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    RSA_SHA224              (0x0301, "rsa_sha224", "SHA224withRSA",
+                                    "RSA", 511,
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    DSA_SHA224              (0x0302, "dsa_sha224", "SHA224withDSA",
+                                    "DSA",
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    ECDSA_SHA1              (0x0203, "ecdsa_sha1", "SHA1withECDSA",
+                                    "EC",
+                                    ProtocolVersion.PROTOCOLS_TO_13),
+    RSA_PKCS1_SHA1          (0x0201, "rsa_pkcs1_sha1", "SHA1withRSA",
+                                    "RSA", null, null, 511,
+                                    ProtocolVersion.PROTOCOLS_TO_13,
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    DSA_SHA1                (0x0202, "dsa_sha1", "SHA1withDSA",
+                                    "DSA",
+                                    ProtocolVersion.PROTOCOLS_TO_12),
+    RSA_MD5                 (0x0101, "rsa_md5", "MD5withRSA",
+                                    "RSA", 511,
+                                    ProtocolVersion.PROTOCOLS_TO_12);
+
+    final int id;                       // hash + signature
+    final String name;                  // literal name
+    private final String algorithm;     // signature algorithm
+    final String keyAlgorithm;          // signature key algorithm
+    private final AlgorithmParameterSpec signAlgParameter;
+    private final NamedGroup namedGroup;    // associated named group
+
+    // The minimal required key size in bits.
+    //
+    // Only need to check RSA algorithm at present. RSA keys of 512 bits
+    // have been shown to be practically breakable, it does not make much
+    // sense to use the strong hash algorithm for keys whose key size less
+    // than 512 bits.  So it is not necessary to calculate the minimal
+    // required key size exactly for a hash algorithm.
+    //
+    // Note that some provider may use 511 bits for 512-bit strength RSA keys.
+    final int minimalKeySize;
+    final List<ProtocolVersion> supportedProtocols;
+
+    // Some signature schemes are supported in different versions for handshake
+    // messages and certificates. This field holds the supported protocols
+    // for handshake messages.
+    final List<ProtocolVersion> handshakeSupportedProtocols;
+    final boolean isAvailable;
+
+    private static final String[] hashAlgorithms = new String[] {
+            "none",         "md5",      "sha1",     "sha224",
+            "sha256",       "sha384",   "sha512"
+        };
+
+    private static final String[] signatureAlgorithms = new String[] {
+            "anonymous",    "rsa",      "dsa",      "ecdsa",
+        };
+
+    static enum SigAlgParamSpec {   // support RSASSA-PSS only now
+        RSA_PSS_SHA256 ("SHA-256", 32),
+        RSA_PSS_SHA384 ("SHA-384", 48),
+        RSA_PSS_SHA512 ("SHA-512", 64);
+
+        final private AlgorithmParameterSpec parameterSpec;
+        final boolean isAvailable;
+
+        SigAlgParamSpec(String hash, int saltLength) {
+            // See RFC 8017
+            PSSParameterSpec pssParamSpec =
+                    new PSSParameterSpec(hash, "MGF1",
+                            new MGF1ParameterSpec(hash), saltLength, 1);
+
+            boolean mediator = true;
+            try {
+                Signature signer = JsseJce.getSignature("RSASSA-PSS");
+                signer.setParameter(pssParamSpec);
+            } catch (InvalidAlgorithmParameterException |
+                    NoSuchAlgorithmException exp) {
+                mediator = false;
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                        "RSASSA-PSS signature with " + hash +
+                        " is not supported by the underlying providers", exp);
+                }
+            }
+
+            this.isAvailable = mediator;
+            this.parameterSpec = mediator ? pssParamSpec : null;
+        }
+
+        AlgorithmParameterSpec getParameterSpec() {
+            return parameterSpec;
+        }
+    }
+
+    // performance optimization
+    private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
+        Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
+
+
+    private SignatureScheme(int id, String name,
+            String algorithm, String keyAlgorithm,
+            ProtocolVersion[] supportedProtocols) {
+        this(id, name, algorithm, keyAlgorithm, -1, supportedProtocols);
+    }
+
+    private SignatureScheme(int id, String name,
+            String algorithm, String keyAlgorithm,
+            int minimalKeySize,
+            ProtocolVersion[] supportedProtocols) {
+        this(id, name, algorithm, keyAlgorithm,
+                null, minimalKeySize, supportedProtocols);
+    }
+
+    private SignatureScheme(int id, String name,
+            String algorithm, String keyAlgorithm,
+            SigAlgParamSpec signAlgParamSpec, int minimalKeySize,
+            ProtocolVersion[] supportedProtocols) {
+        this(id, name, algorithm, keyAlgorithm,
+                signAlgParamSpec, null, minimalKeySize,
+                supportedProtocols, supportedProtocols);
+    }
+
+    private SignatureScheme(int id, String name,
+            String algorithm, String keyAlgorithm,
+            NamedGroup namedGroup,
+            ProtocolVersion[] supportedProtocols) {
+        this(id, name, algorithm, keyAlgorithm,
+                null, namedGroup, -1,
+                supportedProtocols, supportedProtocols);
+    }
+
+    private SignatureScheme(int id, String name,
+            String algorithm, String keyAlgorithm,
+            SigAlgParamSpec signAlgParamSpec,
+            NamedGroup namedGroup, int minimalKeySize,
+            ProtocolVersion[] supportedProtocols,
+            ProtocolVersion[] handshakeSupportedProtocols) {
+        this.id = id;
+        this.name = name;
+        this.algorithm = algorithm;
+        this.keyAlgorithm = keyAlgorithm;
+        this.signAlgParameter =
+            signAlgParamSpec != null ? signAlgParamSpec.parameterSpec : null;
+        this.namedGroup = namedGroup;
+        this.minimalKeySize = minimalKeySize;
+        this.supportedProtocols = Arrays.asList(supportedProtocols);
+        this.handshakeSupportedProtocols =
+                Arrays.asList(handshakeSupportedProtocols);
+
+        boolean mediator = true;
+        if (signAlgParamSpec != null) {
+            mediator = signAlgParamSpec.isAvailable;
+        } else {
+            try {
+                JsseJce.getSignature(algorithm);
+            } catch (Exception e) {
+                mediator = false;
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                        "Signature algorithm, " + algorithm +
+                        ", is not supported by the underlying providers");
+                }
+            }
+        }
+
+        if (mediator && ((id >> 8) & 0xFF) == 0x03) {   // SHA224
+            // There are some problems to use SHA224 on Windows.
+            if (Security.getProvider("SunMSCAPI") != null) {
+                mediator = false;
+            }
+        }
+
+        this.isAvailable = mediator;
+    }
+
+    static SignatureScheme valueOf(int id) {
+        for (SignatureScheme ss: SignatureScheme.values()) {
+            if (ss.id == id) {
+                return ss;
+            }
+        }
+
+        return null;
+    }
+
+    static String nameOf(int id) {
+        for (SignatureScheme ss: SignatureScheme.values()) {
+            if (ss.id == id) {
+                return ss.name;
+            }
+        }
+
+        // Use TLS 1.2 style name for unknown signature scheme.
+        int hashId = ((id >> 8) & 0xFF);
+        int signId = (id & 0xFF);
+        String hashName = (hashId >= hashAlgorithms.length) ?
+            "UNDEFINED-HASH(" + hashId + ")" : hashAlgorithms[hashId];
+        String signName = (signId >= signatureAlgorithms.length) ?
+            "UNDEFINED-SIGNATURE(" + signId + ")" :
+            signatureAlgorithms[signId];
+
+        return signName + "_" + hashName;
+    }
+
+    // Return the size of a SignatureScheme structure in TLS record
+    static int sizeInRecord() {
+        return 2;
+    }
+
+    // Get local supported algorithm collection complying to algorithm
+    // constraints.
+    static List<SignatureScheme> getSupportedAlgorithms(
+            AlgorithmConstraints constraints,
+            List<ProtocolVersion> activeProtocols) {
+        List<SignatureScheme> supported = new LinkedList<>();
+        for (SignatureScheme ss: SignatureScheme.values()) {
+            if (!ss.isAvailable) {
+                continue;
+            }
+
+            boolean isMatch = false;
+            for (ProtocolVersion pv : activeProtocols) {
+                if (ss.supportedProtocols.contains(pv)) {
+                    isMatch = true;
+                    break;
+                }
+            }
+
+            if (isMatch) {
+                if (constraints.permits(
+                        SIGNATURE_PRIMITIVE_SET, ss.algorithm, null)) {
+                    supported.add(ss);
+                } else if (SSLLogger.isOn &&
+                        SSLLogger.isOn("ssl,handshake,verbose")) {
+                    SSLLogger.finest(
+                        "Ignore disabled signature sheme: " + ss.name);
+                }
+            } else if (SSLLogger.isOn &&
+                    SSLLogger.isOn("ssl,handshake,verbose")) {
+                SSLLogger.finest(
+                    "Ignore inactive signature sheme: " + ss.name);
+            }
+        }
+
+        return supported;
+    }
+
+    static List<SignatureScheme> getSupportedAlgorithms(
+            AlgorithmConstraints constraints,
+            ProtocolVersion protocolVersion, int[] algorithmIds) {
+        List<SignatureScheme> supported = new LinkedList<>();
+        for (int ssid : algorithmIds) {
+            SignatureScheme ss = SignatureScheme.valueOf(ssid);
+            if (ss == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Unsupported signature scheme: " +
+                            SignatureScheme.nameOf(ssid));
+                }
+            } else if (ss.isAvailable &&
+                    ss.supportedProtocols.contains(protocolVersion) &&
+                    constraints.permits(SIGNATURE_PRIMITIVE_SET,
+                           ss.algorithm, null)) {
+                supported.add(ss);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Unsupported signature scheme: " + ss.name);
+                }
+            }
+        }
+
+        return supported;
+    }
+
+    static SignatureScheme getPreferableAlgorithm(
+            List<SignatureScheme> schemes,
+            SignatureScheme certScheme,
+            ProtocolVersion version) {
+
+        for (SignatureScheme ss : schemes) {
+            if (ss.isAvailable &&
+            ss.handshakeSupportedProtocols.contains(version) &&
+            certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
+
+                return ss;
+            }
+        }
+
+        return null;
+    }
+
+    static SignatureScheme getPreferableAlgorithm(
+            List<SignatureScheme> schemes,
+            PrivateKey signingKey,
+            ProtocolVersion version) {
+
+        String keyAlgorithm = signingKey.getAlgorithm();
+        int keySize;
+        // Only need to check RSA algorithm at present.
+        if (keyAlgorithm.equalsIgnoreCase("RSA") ||
+                keyAlgorithm.equalsIgnoreCase("RSASSA-PSS")) {
+            keySize = KeyUtil.getKeySize(signingKey);
+        } else {
+            keySize = Integer.MAX_VALUE;
+        }
+        for (SignatureScheme ss : schemes) {
+            if (ss.isAvailable && (keySize >= ss.minimalKeySize) &&
+                ss.handshakeSupportedProtocols.contains(version) &&
+                keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
+                if (ss.namedGroup != null &&
+                    ss.namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                    ECParameterSpec params =
+                                ((ECPrivateKey)signingKey).getParams();
+                    if (ss.namedGroup == NamedGroup.valueOf(params)) {
+                        return ss;
+                    }
+                } else {
+                    return ss;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    static String[] getAlgorithmNames(Collection<SignatureScheme> schemes) {
+        if (schemes != null) {
+            ArrayList<String> names = new ArrayList<>(schemes.size());
+            for (SignatureScheme scheme : schemes) {
+                names.add(scheme.algorithm);
+            }
+
+            return names.toArray(new String[0]);
+        }
+
+        return new String[0];
+    }
+
+    Signature getSignature(Key key) throws NoSuchAlgorithmException,
+            InvalidAlgorithmParameterException, InvalidKeyException {
+        if (!isAvailable) {
+            return null;
+        }
+
+        Signature signer = JsseJce.getSignature(algorithm);
+        if (key instanceof PublicKey) {
+            signer.initVerify((PublicKey)(key));
+        } else {
+            signer.initSign((PrivateKey)key);
+        }
+
+        // Important note:  Please don't set the parameters before signature
+        // or verification initialization, so that the crypto provider can
+        // be selected properly.
+        if (signAlgParameter != null) {
+            signer.setParameter(signAlgParameter);
+        }
+
+        return signer;
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/StatusRequest.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-
-/*
- * RFC 6066 defines the TLS extension,"status_request" (type 0x5),
- * which allows the client to request that the server perform OCSP
- * on the client's behalf.
- *
- * This class is an interface for multiple types of StatusRequests
- * (e.g. OCSPStatusRequest).
- */
-interface StatusRequest {
-
-    /**
-     * Obtain the length of the {@code StatusRequest} object in encoded form
-     *
-     * @return the length of the {@code StatusRequest} object in encoded form
-     */
-    int length();
-
-    /**
-     * Place the encoded {@code StatusRequest} bytes into the
-     *      {@code HandshakeOutputStream}
-     *
-     * @param s the target {@code HandshakeOutputStream}
-     *
-     * @throws IOException if any encoding error occurs
-     */
-    void send(HandshakeOutStream s) throws IOException;
-}
--- a/src/java.base/share/classes/sun/security/ssl/StatusRequestType.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2015, 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.util.ArrayList;
-import java.util.List;
-
-final class StatusRequestType {
-
-    final int id;
-    final String name;
-    static List<StatusRequestType> knownTypes = new ArrayList<>(4);
-
-    private StatusRequestType(int id, String name) {
-        this.id = id;
-        this.name = name;
-    }
-
-    static StatusRequestType get(int id) {
-        for (StatusRequestType ext : knownTypes) {
-            if (ext.id == id) {
-                return ext;
-            }
-        }
-        return new StatusRequestType(id, "type_" + id);
-    }
-
-    private static StatusRequestType e(int id, String name) {
-        StatusRequestType ext = new StatusRequestType(id, name);
-        knownTypes.add(ext);
-        return ext;
-    }
-
-    @Override
-    public String toString() {
-        return (name == null || name.isEmpty()) ?
-                String.format("Unknown (0x%04X", id) : name;
-    }
-
-    // Status request types defined in RFC 6066 and 6961
-    static final StatusRequestType OCSP = e(0x01, "ocsp");
-    static final StatusRequestType OCSP_MULTI = e(0x02, "ocsp_multi");
-}
--- a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,18 +22,32 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package sun.security.ssl;
 
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.AccessController;
+import java.security.cert.Extension;
 import java.security.cert.X509Certificate;
-import java.security.cert.Extension;
-import java.util.*;
-import java.util.concurrent.*;
-
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import sun.security.action.GetBooleanAction;
+import sun.security.action.GetIntegerAction;
+import sun.security.action.GetPropertyAction;
 import sun.security.provider.certpath.CertId;
 import sun.security.provider.certpath.OCSP;
 import sun.security.provider.certpath.OCSPResponse;
@@ -41,15 +55,13 @@
 import sun.security.util.Cache;
 import sun.security.x509.PKIXExtensions;
 import sun.security.x509.SerialNumber;
-import sun.security.action.GetBooleanAction;
-import sun.security.action.GetIntegerAction;
-import sun.security.action.GetPropertyAction;
+import sun.security.ssl.X509Authentication.X509Possession;
+import static sun.security.ssl.CertStatusExtension.*;
 
 final class StatusResponseManager {
     private static final int DEFAULT_CORE_THREADS = 8;
     private static final int DEFAULT_CACHE_SIZE = 256;
-    private static final int DEFAULT_CACHE_LIFETIME = 3600;         // seconds
-    private static final Debug debug = Debug.getInstance("ssl");
+    private static final int DEFAULT_CACHE_LIFETIME = 3600;     // seconds
 
     private final ScheduledThreadPoolExecutor threadMgr;
     private final Cache<CertId, ResponseCacheEntry> responseCache;
@@ -99,10 +111,12 @@
             }
         }, new ThreadPoolExecutor.DiscardPolicy());
         threadMgr.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
-        threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
+        threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy(
+                false);
         threadMgr.setKeepAliveTime(5000, TimeUnit.MILLISECONDS);
         threadMgr.allowCoreThreadTimeOut(true);
-        responseCache = Cache.newSoftMemoryCache(cacheCapacity, cacheLifetime);
+        responseCache = Cache.newSoftMemoryCache(
+                cacheCapacity, cacheLifetime);
     }
 
     /**
@@ -147,8 +161,8 @@
      * Get the ignore extensions setting.
      *
      * @return {@code true} if the {@code StatusResponseManager} will not
-     * pass OCSP Extensions in the TLS {@code status_request[_v2]} extensions,
-     * {@code false} if extensions will be passed (the default).
+     * pass OCSP Extensions in the TLS {@code status_request[_v2]}
+     * extensions, {@code false} if extensions will be passed (the default).
      */
     boolean getIgnoreExtensions() {
         return ignoreExtensions;
@@ -158,7 +172,9 @@
      * Clear the status response cache
      */
     void clear() {
-        debugLog("Clearing response cache");
+        if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+            SSLLogger.fine("Clearing response cache");
+        }
         responseCache.clear();
     }
 
@@ -172,16 +188,18 @@
     }
 
     /**
-     * Obtain the URI use by the {@code StatusResponseManager} during lookups.
+     * Obtain the URI use by the {@code StatusResponseManager} during
+     * lookups.
+     *
      * This method takes into account not only the AIA extension from a
      * certificate to be checked, but also any default URI and possible
      * override settings for the response manager.
      *
      * @param cert the subject to get the responder URI from
      *
-     * @return a {@code URI} containing the address to the OCSP responder, or
-     *      {@code null} if no AIA extension exists in the certificate and no
-     *      default responder has been configured.
+     * @return a {@code URI} containing the address to the OCSP responder,
+     *      or {@code null} if no AIA extension exists in the certificate
+     *      and no default responder has been configured.
      *
      * @throws NullPointerException if {@code cert} is {@code null}.
      */
@@ -190,10 +208,16 @@
 
         if (cert.getExtensionValue(
                 PKIXExtensions.OCSPNoCheck_Id.toString()) != null) {
-            debugLog("OCSP NoCheck extension found.  OCSP will be skipped");
+            if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                SSLLogger.fine(
+                    "OCSP NoCheck extension found.  OCSP will be skipped");
+            }
             return null;
         } else if (defaultResponder != null && respOverride) {
-            debugLog("Responder override: URI is " + defaultResponder);
+            if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+              SSLLogger.fine(
+                    "Responder override: URI is " + defaultResponder);
+            }
             return defaultResponder;
         } else {
             URI certURI = OCSP.getResponderURI(cert);
@@ -205,25 +229,29 @@
      * Shutdown the thread pool
      */
     void shutdown() {
-        debugLog("Shutting down " + threadMgr.getActiveCount() +
-                " active threads");
+        if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+            SSLLogger.fine("Shutting down " + threadMgr.getActiveCount() +
+                    " active threads");
+        }
         threadMgr.shutdown();
     }
 
     /**
      * Get a list of responses for a chain of certificates.
-     * This will find OCSP responses from the cache, or failing that, directly
-     * contact the OCSP responder.  It is assumed that the certificates in
-     * the provided chain are in their proper order (from end-entity to
-     * trust anchor).
+     *
+     * This will find OCSP responses from the cache, or failing that,
+     * directly contact the OCSP responder.  It is assumed that the
+     * certificates in the provided chain are in their proper order
+     * (from end-entity to trust anchor).
      *
      * @param type the type of request being made of the
      *      {@code StatusResponseManager}
-     * @param request the {@code StatusRequest} from the status_request or
-     *      status_request_v2 ClientHello extension.  A value of {@code null}
-     *      is interpreted as providing no responder IDs or extensions.
-     * @param chain an array of 2 or more certificates.  Each certificate must
-     *      be issued by the next certificate in the chain.
+     * @param request the {@code CertStatusRequest} from the
+     *      status_request or status_request_v2 ClientHello extension.
+     *      A value of {@code null} is interpreted as providing no
+     *      responder IDs or extensions.
+     * @param chain an array of 2 or more certificates.  Each certificate
+     *      must be issued by the next certificate in the chain.
      * @param delay the number of time units to delay before returning
      *      responses.
      * @param unit the unit of time applied to the {@code delay} parameter
@@ -231,17 +259,20 @@
      * @return an unmodifiable {@code Map} containing the certificate and
      *      its usually
      *
-     * @throws SSLHandshakeException if an unsupported {@code StatusRequest}
-     *      is provided.
+     * @throws SSLHandshakeException if an unsupported
+     *      {@code CertStatusRequest} is provided.
      */
-    Map<X509Certificate, byte[]> get(StatusRequestType type,
-            StatusRequest request, X509Certificate[] chain, long delay,
+    Map<X509Certificate, byte[]> get(CertStatusRequestType type,
+            CertStatusRequest request, X509Certificate[] chain, long delay,
             TimeUnit unit) {
         Map<X509Certificate, byte[]> responseMap = new HashMap<>();
         List<OCSPFetchCall> requestList = new ArrayList<>();
 
-        debugLog("Beginning check: Type = " + type + ", Chain length = " +
+        if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+            SSLLogger.fine(
+                "Beginning check: Type = " + type + ", Chain length = " +
                 chain.length);
+        }
 
         // It is assumed that the caller has ordered the certs in the chain
         // in the proper order (each certificate is issued by the next entry
@@ -250,7 +281,7 @@
             return Collections.emptyMap();
         }
 
-        if (type == StatusRequestType.OCSP) {
+        if (type == CertStatusRequestType.OCSP) {
             try {
                 // For type OCSP, we only check the end-entity certificate
                 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request;
@@ -264,22 +295,26 @@
                     requestList.add(new OCSPFetchCall(sInfo, ocspReq));
                 }
             } catch (IOException exc) {
-                debugLog("Exception during CertId creation: " + exc);
+                if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                    SSLLogger.fine(
+                        "Exception during CertId creation: ", exc);
+                }
             }
-        } else if (type == StatusRequestType.OCSP_MULTI) {
+        } else if (type == CertStatusRequestType.OCSP_MULTI) {
             // For type OCSP_MULTI, we check every cert in the chain that
-            // has a direct issuer at the next index.  We won't have an issuer
-            // certificate for the last certificate in the chain and will
-            // not be able to create a CertId because of that.
+            // has a direct issuer at the next index.  We won't have an
+            // issuer certificate for the last certificate in the chain
+            // and will not be able to create a CertId because of that.
             OCSPStatusRequest ocspReq = (OCSPStatusRequest)request;
             int ctr;
             for (ctr = 0; ctr < chain.length - 1; ctr++) {
                 try {
-                    // The cert at "ctr" is the subject cert, "ctr + 1" is the
-                    // issuer certificate.
+                    // The cert at "ctr" is the subject cert, "ctr + 1"
+                    // is the issuer certificate.
                     CertId cid = new CertId(chain[ctr + 1],
-                            new SerialNumber(chain[ctr].getSerialNumber()));
-                    ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq);
+                        new SerialNumber(chain[ctr].getSerialNumber()));
+                    ResponseCacheEntry cacheEntry =
+                        getFromCache(cid, ocspReq);
                     if (cacheEntry != null) {
                         responseMap.put(chain[ctr], cacheEntry.ocspBytes);
                     } else {
@@ -287,17 +322,22 @@
                         requestList.add(new OCSPFetchCall(sInfo, ocspReq));
                     }
                 } catch (IOException exc) {
-                    debugLog("Exception during CertId creation: " + exc);
+                    if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                        SSLLogger.fine(
+                            "Exception during CertId creation: ", exc);
+                    }
                 }
             }
         } else {
-            debugLog("Unsupported status request type: " + type);
+            if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                SSLLogger.fine("Unsupported status request type: " + type);
+            }
         }
 
         // If we were able to create one or more Fetches, go and run all
         // of them in separate threads.  For all the threads that completed
-        // in the allotted time, put those status responses into the returned
-        // Map.
+        // in the allotted time, put those status responses into the
+        // returned Map.
         if (!requestList.isEmpty()) {
             try {
                 // Set a bunch of threads to go do the fetching
@@ -307,23 +347,31 @@
                 // Go through the Futures and from any non-cancelled task,
                 // get the bytes and attach them to the responseMap.
                 for (Future<StatusInfo> task : resultList) {
-                    if (task.isDone()) {
-                        if (!task.isCancelled()) {
-                            StatusInfo info = task.get();
-                            if (info != null && info.responseData != null) {
-                                responseMap.put(info.cert,
-                                        info.responseData.ocspBytes);
-                            } else {
-                                debugLog("Completed task had no response data");
-                            }
-                        } else {
-                            debugLog("Found cancelled task");
+                    if (!task.isDone()) {
+                        continue;
+                    }
+
+                    if (!task.isCancelled()) {
+                        StatusInfo info = task.get();
+                        if (info != null && info.responseData != null) {
+                            responseMap.put(info.cert,
+                                    info.responseData.ocspBytes);
+                        } else if (SSLLogger.isOn &&
+                                SSLLogger.isOn("respmgr")) {
+                            SSLLogger.fine(
+                                "Completed task had no response data");
+                        }
+                    } else {
+                        if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                            SSLLogger.fine("Found cancelled task");
                         }
                     }
                 }
             } catch (InterruptedException | ExecutionException exc) {
                 // Not sure what else to do here
-                debugLog("Exception when getting data: " + exc);
+                if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                    SSLLogger.fine("Exception when getting data: ", exc);
+                }
             }
         }
 
@@ -345,9 +393,13 @@
             OCSPStatusRequest ocspRequest) {
         // Determine if the nonce extension is present in the request.  If
         // so, then do not attempt to retrieve the response from the cache.
-        for (Extension ext : ocspRequest.getExtensions()) {
-            if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) {
-                debugLog("Nonce extension found, skipping cache check");
+        for (Extension ext : ocspRequest.extensions) {
+            if (ext.getId().equals(
+                    PKIXExtensions.OCSPNonce_Id.toString())) {
+                if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                    SSLLogger.fine(
+                            "Nonce extension found, skipping cache check");
+                }
                 return null;
             }
         }
@@ -359,12 +411,18 @@
         // and do not return it as a cache hit.
         if (respEntry != null && respEntry.nextUpdate != null &&
                 respEntry.nextUpdate.before(new Date())) {
-            debugLog("nextUpdate threshold exceeded, purging from cache");
+            if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                SSLLogger.fine(
+                    "nextUpdate threshold exceeded, purging from cache");
+            }
             respEntry = null;
         }
 
-        debugLog("Check cache for SN" + cid.getSerialNumber() + ": " +
-                (respEntry != null ? "HIT" : "MISS"));
+        if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+            SSLLogger.fine(
+                    "Check cache for SN" + cid.getSerialNumber() + ": " +
+                    (respEntry != null ? "HIT" : "MISS"));
+        }
         return respEntry;
     }
 
@@ -398,20 +456,6 @@
     }
 
     /**
-     * Log messages through the SSL Debug facility.
-     *
-     * @param message the message to be displayed
-     */
-    static void debugLog(String message) {
-        if (debug != null && Debug.isOn("respmgr")) {
-            StringBuilder sb = new StringBuilder();
-            sb.append("[").append(Thread.currentThread().getName());
-            sb.append("] ").append(message);
-            System.out.println(sb.toString());
-        }
-    }
-
-    /**
      * Inner class used to group request and response data.
      */
     class StatusInfo {
@@ -426,7 +470,7 @@
          * @param subjectCert the certificate to be checked for revocation
          * @param issuerCert the issuer of the {@code subjectCert}
          *
-         * @throws IOException if CertId creation from the certificates fails
+         * @throws IOException if CertId creation from the certificate fails
          */
         StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert)
                 throws IOException {
@@ -471,11 +515,14 @@
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder("StatusInfo:");
-            sb.append("\n\tCert: ").append(this.cert.getSubjectX500Principal());
+            sb.append("\n\tCert: ").append(
+                    this.cert.getSubjectX500Principal());
             sb.append("\n\tSerial: ").append(this.cert.getSerialNumber());
             sb.append("\n\tResponder: ").append(this.responder);
-            sb.append("\n\tResponse data: ").append(this.responseData != null ?
-                    (this.responseData.ocspBytes.length + " bytes") : "<NULL>");
+            sb.append("\n\tResponse data: ").append(
+                    this.responseData != null ?
+                        (this.responseData.ocspBytes.length + " bytes") :
+                        "<NULL>");
             return sb.toString();
         }
     }
@@ -483,7 +530,7 @@
     /**
      * Static nested class used as the data kept in the response cache.
      */
-    static class ResponseCacheEntry {
+    class ResponseCacheEntry {
         final OCSPResponse.ResponseStatus status;
         final byte[] ocspBytes;
         final Date nextUpdate;
@@ -495,8 +542,8 @@
          *
          * @param responseBytes the DER encoding for the OCSP response
          *
-         * @throws IOException if an {@code OCSPResponse} cannot be created from
-         *      the encoded bytes.
+         * @throws IOException if an {@code OCSPResponse} cannot be
+         *         created from the encoded bytes.
          */
         ResponseCacheEntry(byte[] responseBytes, CertId cid)
                 throws IOException {
@@ -515,8 +562,9 @@
                     // Date is cloned.
                     nextUpdate = singleResp.getNextUpdate();
                 } else {
-                    throw new IOException("Unable to find SingleResponse for " +
-                            "SN " + cid.getSerialNumber());
+                    throw new IOException(
+                            "Unable to find SingleResponse for SN " +
+                            cid.getSerialNumber());
                 }
             } else {
                 nextUpdate = null;
@@ -537,7 +585,8 @@
 
         /**
          * A constructor that builds the OCSPFetchCall from the provided
-         * StatusInfo and information from the status_request[_v2] extension.
+         * StatusInfo and information from the status_request[_v2]
+         * extension.
          *
          * @param info the {@code StatusInfo} containing the subject
          * certificate, CertId, and other supplemental info.
@@ -549,37 +598,54 @@
                     "Null StatusInfo not allowed");
             ocspRequest = Objects.requireNonNull(request,
                     "Null OCSPStatusRequest not allowed");
-            extensions = ocspRequest.getExtensions();
-            responderIds = ocspRequest.getResponderIds();
+            extensions = ocspRequest.extensions;
+            responderIds = ocspRequest.responderIds;
         }
 
         /**
          * Get an OCSP response, either from the cache or from a responder.
          *
-         * @return The StatusInfo object passed into the {@code OCSPFetchCall}
-         * constructor, with the {@code responseData} field filled in with the
-         * response or {@code null} if no response can be obtained.
+         * @return The StatusInfo object passed into the
+         *         {@code OCSPFetchCall} constructor, with the
+         *         {@code responseData} field filled in with the response
+         *         or {@code null} if no response can be obtained.
          */
         @Override
         public StatusInfo call() {
-            debugLog("Starting fetch for SN " + statInfo.cid.getSerialNumber());
+            if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                SSLLogger.fine(
+                    "Starting fetch for SN " +
+                    statInfo.cid.getSerialNumber());
+            }
             try {
                 ResponseCacheEntry cacheEntry;
                 List<Extension> extsToSend;
 
                 if (statInfo.responder == null) {
-                    // If we have no URI then there's nothing to do but return
-                    debugLog("Null URI detected, OCSP fetch aborted.");
+                    // If we have no URI then there's nothing to do
+                    // but return.
+                    if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                        SSLLogger.fine(
+                            "Null URI detected, OCSP fetch aborted");
+                    }
                     return statInfo;
                 } else {
-                    debugLog("Attempting fetch from " + statInfo.responder);
+                    if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                        SSLLogger.fine(
+                            "Attempting fetch from " + statInfo.responder);
+                    }
                 }
 
                 // If the StatusResponseManager has been configured to not
-                // forward extensions, then set extensions to an empty list.
-                // We will forward the extensions unless one of two conditions
-                // occur: (1) The jdk.tls.stapling.ignoreExtensions property is
-                // true or (2) There is a non-empty ResponderId list.
+                // forward extensions, then set extensions to an empty
+                // list.
+                //
+                // We will forward the extensions unless one of two
+                // conditions occur:
+                // (1) The jdk.tls.stapling.ignoreExtensions property is
+                //     true, or
+                // (2) There is a non-empty ResponderId list.
+                //
                 // ResponderId selection is a feature that will be
                 // supported in the future.
                 extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ?
@@ -595,8 +661,10 @@
                             statInfo.cid);
 
                     // Get the response status and act on it appropriately
-                    debugLog("OCSP Status: " + cacheEntry.status +
+                    if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                        SSLLogger.fine("OCSP Status: " + cacheEntry.status +
                             " (" + respBytes.length + " bytes)");
+                    }
                     if (cacheEntry.status ==
                             OCSPResponse.ResponseStatus.SUCCESSFUL) {
                         // Set the response in the returned StatusInfo
@@ -606,10 +674,15 @@
                         addToCache(statInfo.cid, cacheEntry);
                     }
                 } else {
-                    debugLog("No data returned from OCSP Responder");
+                    if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                        SSLLogger.fine(
+                            "No data returned from OCSP Responder");
+                    }
                 }
             } catch (IOException ioe) {
-                debugLog("Caught exception: " + ioe);
+                if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                    SSLLogger.fine("Caught exception: ", ioe);
+                }
             }
 
             return statInfo;
@@ -626,22 +699,28 @@
             // If no cache lifetime has been set on entries then
             // don't cache this response if there is no nextUpdate field
             if (entry.nextUpdate == null && cacheLifetime == 0) {
-                debugLog("Not caching this OCSP response");
+                if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                    SSLLogger.fine("Not caching this OCSP response");
+                }
             } else {
                 responseCache.put(certId, entry);
-                debugLog("Added response for SN " + certId.getSerialNumber() +
+                if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) {
+                    SSLLogger.fine(
+                        "Added response for SN " +
+                        certId.getSerialNumber() +
                         " to cache");
+                }
             }
         }
 
         /**
          * Determine the delay to use when scheduling the task that will
          * update the OCSP response.  This is the shorter time between the
-         * cache lifetime and the nextUpdate.  If no nextUpdate is present in
-         * the response, then only the cache lifetime is used.
+         * cache lifetime and the nextUpdate.  If no nextUpdate is present
+         * in the response, then only the cache lifetime is used.
          * If cache timeouts are disabled (a zero value) and there's no
-         * nextUpdate, then the entry is not cached and no rescheduling will
-         * take place.
+         * nextUpdate, then the entry is not cached and no rescheduling
+         * will take place.
          *
          * @param nextUpdate a {@code Date} object corresponding to the
          *      next update time from a SingleResponse.
@@ -667,4 +746,218 @@
             return delaySec;
         }
     }
+
+    static final StaplingParameters processStapling(
+            ServerHandshakeContext shc) {
+        StaplingParameters params = null;
+        SSLExtension ext = null;
+        CertStatusRequestType type = null;
+        CertStatusRequest 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 (!shc.sslContext.isStaplingEnabled(false) || shc.isResumption) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Staping disabled or is a resumed session");
+            }
+            return null;
+        }
+
+        // Check if the client has asserted the status_request[_v2] extension(s)
+        Map<SSLExtension, SSLExtension.SSLExtensionSpec> exts =
+                shc.handshakeExtensions;
+        CertStatusRequestSpec statReq = (CertStatusRequestSpec)exts.get(
+                SSLExtension.CH_STATUS_REQUEST);
+        CertStatusRequestV2Spec statReqV2 = (CertStatusRequestV2Spec)
+                exts.get(SSLExtension.CH_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.  Finally, we'll only do this in (D)TLS 1.2 or earlier.
+        if (statReqV2 != null && !shc.negotiatedProtocol.useTLS13PlusSpec()) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {
+                SSLLogger.fine("SH Processing status_request_v2 extension");
+            }
+            // RFC 6961 stapling
+            ext = SSLExtension.CH_STATUS_REQUEST_V2;
+            int ocspIdx = -1;
+            int ocspMultiIdx = -1;
+            CertStatusRequest[] reqItems = statReqV2.certStatusRequests;
+            for (int pos = 0; (pos < reqItems.length &&
+                    (ocspIdx == -1 || ocspMultiIdx == -1)); pos++) {
+                CertStatusRequest item = reqItems[pos];
+                CertStatusRequestType curType =
+                        CertStatusRequestType.valueOf(item.statusType);
+                if (ocspIdx < 0 && curType == CertStatusRequestType.OCSP) {
+                    OCSPStatusRequest ocspReq = (OCSPStatusRequest)item;
+                    // We currently only accept empty responder ID lists
+                    // but may support them in the future
+                    if (ocspReq.responderIds.isEmpty()) {
+                        ocspIdx = pos;
+                    }
+                } else if (ocspMultiIdx < 0 &&
+                        curType == CertStatusRequestType.OCSP_MULTI) {
+                    OCSPStatusRequest ocspReq = (OCSPStatusRequest)item;
+                    // We currently only accept empty responder ID lists
+                    // but may support them in the future
+                    if (ocspReq.responderIds.isEmpty()) {
+                        ocspMultiIdx = pos;
+                    }
+                }
+            }
+            if (ocspMultiIdx >= 0) {
+                req = reqItems[ocspMultiIdx];
+                type = CertStatusRequestType.valueOf(req.statusType);
+            } else if (ocspIdx >= 0) {
+                req = reqItems[ocspIdx];
+                type = CertStatusRequestType.valueOf(req.statusType);
+            } else {
+                if (SSLLogger.isOn &&
+                        SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest("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 ((statReq != null) &&
+                (ext == null || type == null || req == null)) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {
+                SSLLogger.fine("SH Processing status_request extension");
+            }
+            ext = SSLExtension.CH_STATUS_REQUEST;
+            type = CertStatusRequestType.valueOf(
+                    statReq.statusRequest.statusType);
+            if (type == CertStatusRequestType.OCSP) {
+                // If the type is OCSP, then the request is guaranteed
+                // to be OCSPStatusRequest
+                OCSPStatusRequest ocspReq =
+                        (OCSPStatusRequest)statReq.statusRequest;
+                if (ocspReq.responderIds.isEmpty()) {
+                    req = ocspReq;
+                } else {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.finest("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) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("No suitable status_request or " +
+                        "status_request_v2, stapling is disabled");
+            }
+            return null;
+        }
+
+        // Get the cert chain since we'll need it for OCSP checking
+        X509Possession x509Possession = null;
+        for (SSLPossession possession : shc.handshakePossessions) {
+            if (possession instanceof X509Possession) {
+                x509Possession = (X509Possession)possession;
+                break;
+            }
+        }
+
+        if (x509Possession == null) {       // unlikely
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.finest("Warning: no X.509 certificates found.  " +
+                        "Stapling is disabled.");
+            }
+            return null;
+        }
+
+        // Get the OCSP responses from the StatusResponseManager
+        X509Certificate[] certs = x509Possession.popCerts;
+        StatusResponseManager statRespMgr =
+                shc.sslContext.getStatusResponseManager();
+        if (statRespMgr != null) {
+            // For the purposes of the fetch from the SRM, override the
+            // type when it is TLS 1.3 so it always gets responses for
+            // all certs it can.  This should not change the type field
+            // in the StaplingParameters though.
+            CertStatusRequestType fetchType =
+                    shc.negotiatedProtocol.useTLS13PlusSpec() ?
+                    CertStatusRequestType.OCSP_MULTI : type;
+            responses = statRespMgr.get(fetchType, req, certs,
+                    shc.statusRespTimeout, TimeUnit.MILLISECONDS);
+            if (!responses.isEmpty()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest("Response manager returned " +
+                            responses.size() + " entries.");
+                }
+                // If this RFC 6066-style stapling (SSL cert only) then the
+                // response cannot be zero length
+                if (type == CertStatusRequestType.OCSP) {
+                    byte[] respDER = responses.get(certs[0]);
+                    if (respDER == null || respDER.length <= 0) {
+                        if (SSLLogger.isOn &&
+                                SSLLogger.isOn("ssl,handshake")) {
+                            SSLLogger.finest("Warning: Null or zero-length " +
+                                    "response found for leaf certificate. " +
+                                    "Stapling is disabled.");
+                        }
+                        return null;
+                    }
+                }
+                params = new StaplingParameters(ext, type, req, responses);
+            } else {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.finest("Warning: no OCSP responses obtained.  " +
+                            "Stapling is disabled.");
+                }
+            }
+        } else {
+            // This should not happen, but if lazy initialization of the
+            // StatusResponseManager doesn't occur we should turn off stapling.
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.finest("Warning: lazy initialization " +
+                        "of the StatusResponseManager failed.  " +
+                        "Stapling is disabled.");
+            }
+            params = null;
+        }
+
+        return params;
+    }
+
+    /**
+     * Inner class used to hold stapling parameters needed by the handshaker
+     * when stapling is active.
+     */
+    static final class StaplingParameters {
+        final SSLExtension statusRespExt;
+        final CertStatusRequestType statReqType;
+        final CertStatusRequest statReqData;
+        final Map<X509Certificate, byte[]> responseMap;
+
+        StaplingParameters(SSLExtension ext, CertStatusRequestType type,
+                CertStatusRequest req, Map<X509Certificate, byte[]> responses) {
+            statusRespExt = ext;
+            statReqType = type;
+            statReqData = req;
+            responseMap = responses;
+        }
+    }
 }
+
--- a/src/java.base/share/classes/sun/security/ssl/SunJSSE.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SunJSSE.java	Mon Jun 25 13:41:39 2018 -0700
@@ -23,7 +23,6 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.security.*;
@@ -62,7 +61,7 @@
 
     private static String info = "Sun JSSE provider" +
         "(PKCS12, SunX509/PKIX key/trust factories, " +
-        "SSLv3/TLSv1/TLSv1.1/TLSv1.2/DTLSv1.0/DTLSv1.2)";
+        "SSLv3/TLSv1/TLSv1.1/TLSv1.2/TLSv1.3/DTLSv1.0/DTLSv1.2)";
 
     private static String fipsInfo =
         "Sun JSSE provider (FIPS mode, crypto provider ";
@@ -149,7 +148,7 @@
     }
 
     private void registerAlgorithms(final boolean isfips) {
-        AccessController.doPrivileged(new PrivilegedAction<>() {
+        AccessController.doPrivileged(new PrivilegedAction<Object>() {
             @Override
             public Object run() {
                 doRegister(isfips);
@@ -214,6 +213,8 @@
             "sun.security.ssl.SSLContextImpl$TLS11Context");
         put("SSLContext.TLSv1.2",
             "sun.security.ssl.SSLContextImpl$TLS12Context");
+        put("SSLContext.TLSv1.3",
+            "sun.security.ssl.SSLContextImpl$TLS13Context");
         put("SSLContext.TLS",
             "sun.security.ssl.SSLContextImpl$TLSContext");
         if (isfips == false) {
--- a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,13 +25,27 @@
 
 package sun.security.ssl;
 
-import javax.net.ssl.*;
-import java.security.*;
-import java.security.cert.*;
+import java.net.Socket;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
-import java.util.*;
-import java.net.Socket;
-
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
 import javax.security.auth.x500.X500Principal;
 
 
@@ -67,8 +81,6 @@
  */
 final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
 
-    private static final Debug debug = Debug.getInstance("ssl");
-
     private static final String[] STRING0 = new String[0];
 
     /*
@@ -148,14 +160,8 @@
             X509Credentials cred = new X509Credentials((PrivateKey)key,
                 (X509Certificate[])certs);
             credentialsMap.put(alias, cred);
-            if (debug != null && Debug.isOn("keymanager")) {
-                System.out.println("***");
-                System.out.println("found key for : " + alias);
-                for (int i = 0; i < certs.length; i++) {
-                    System.out.println("chain [" + i + "] = "
-                    + certs[i]);
-                }
-                System.out.println("***");
+            if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                SSLLogger.fine("found key for : " + alias, (Object[])certs);
             }
         }
     }
@@ -382,8 +388,8 @@
             if (issuers.length == 0) {
                 // no issuer specified, match all
                 aliases.add(alias);
-                if (debug != null && Debug.isOn("keymanager")) {
-                    System.out.println("matching alias: " + alias);
+                if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                    SSLLogger.fine("matching alias: " + alias);
                 }
             } else {
                 Set<X500Principal> certIssuers =
@@ -391,8 +397,8 @@
                 for (int i = 0; i < x500Issuers.length; i++) {
                     if (certIssuers.contains(issuers[i])) {
                         aliases.add(alias);
-                        if (debug != null && Debug.isOn("keymanager")) {
-                            System.out.println("matching alias: " + alias);
+                        if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                            SSLLogger.fine("matching alias: " + alias);
                         }
                         break;
                     }
--- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,466 +26,1012 @@
 package sun.security.ssl;
 
 import java.io.IOException;
-import java.security.spec.ECGenParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-import java.security.AlgorithmParameters;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
 import java.security.AlgorithmConstraints;
+import java.security.AlgorithmParameters;
 import java.security.CryptoPrimitive;
-import java.security.AccessController;
+import java.security.NoSuchAlgorithmException;
 import java.security.spec.AlgorithmParameterSpec;
-import javax.crypto.spec.DHParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
 import java.util.Map;
-import java.util.ArrayList;
+import javax.crypto.spec.DHParameterSpec;
 import javax.net.ssl.SSLProtocolException;
-
 import sun.security.action.GetPropertyAction;
-
-//
-// Note: Since RFC 7919, the extension's semantics are expanded from
-// "Supported Elliptic Curves" to "Supported Groups".  The enum datatype
-// used in the extension has been renamed from NamedCurve to NamedGroup.
-// Its semantics are likewise expanded from "named curve" to "named group".
-//
-final class SupportedGroupsExtension extends HelloExtension {
-
-    /* Class and subclass dynamic debugging support */
-    private static final Debug debug = Debug.getInstance("ssl");
-
-    private static final int ARBITRARY_PRIME = 0xff01;
-    private static final int ARBITRARY_CHAR2 = 0xff02;
-
-    // cache to speed up the parameters construction
-    private static final Map<NamedGroup,
-                AlgorithmParameters> namedGroupParams = new HashMap<>();
+import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
+import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
 
-    // the supported named groups
-    private static final NamedGroup[] supportedNamedGroups;
+/**
+ * Pack of the "supported_groups" extensions [RFC 4492/7919].
+ */
+final class SupportedGroupsExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHSupportedGroupsProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHSupportedGroupsConsumer();
+    static final SSLStringizer sgsStringizer =
+            new SupportedGroupsStringizer();
 
-    // the named group presented in the extension
-    private final int[] requestedNamedGroupIds;
-
-    static {
-        boolean requireFips = SunJSSE.isFIPS();
+    static final HandshakeProducer eeNetworkProducer =
+            new EESupportedGroupsProducer();
+    static final ExtensionConsumer eeOnLoadConsumer =
+            new EESupportedGroupsConsumer();
 
-        // The value of the System Property defines a list of enabled named
-        // groups in preference order, separated with comma.  For example:
-        //
-        //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
-        //
-        // If the System Property is not defined or the value is empty, the
-        // default groups and preferences will be used.
-        String property = AccessController.doPrivileged(
-                    new GetPropertyAction("jdk.tls.namedGroups"));
-        if (property != null && property.length() != 0) {
-            // remove double quote marks from beginning/end of the property
-            if (property.length() > 1 && property.charAt(0) == '"' &&
-                    property.charAt(property.length() - 1) == '"') {
-                property = property.substring(1, property.length() - 1);
+    /**
+     * The "supported_groups" extension.
+     */
+    static final class SupportedGroupsSpec implements SSLExtensionSpec {
+        final int[] namedGroupsIds;
+
+        private SupportedGroupsSpec(int[] namedGroupsIds) {
+            this.namedGroupsIds = namedGroupsIds;
+        }
+
+        private SupportedGroupsSpec(List<NamedGroup> namedGroups) {
+            this.namedGroupsIds = new int[namedGroups.size()];
+            int i = 0;
+            for (NamedGroup ng : namedGroups) {
+                namedGroupsIds[i++] = ng.id;
             }
         }
 
-        ArrayList<NamedGroup> groupList;
-        if (property != null && property.length() != 0) {   // customized groups
-            String[] groups = property.split(",");
-            groupList = new ArrayList<>(groups.length);
-            for (String group : groups) {
-                group = group.trim();
-                if (!group.isEmpty()) {
-                    NamedGroup namedGroup = NamedGroup.nameOf(group);
-                    if (namedGroup != null &&
-                            (!requireFips || namedGroup.isFips)) {
-                        if (isAvailableGroup(namedGroup)) {
-                            groupList.add(namedGroup);
-                        }
-                    }   // ignore unknown groups
+        private SupportedGroupsSpec(ByteBuffer m) throws IOException  {
+            if (m.remaining() < 2) {      // 2: the length of the list
+                throw new SSLProtocolException(
+                    "Invalid supported_groups extension: insufficient data");
+            }
+
+            byte[] ngs = Record.getBytes16(m);
+            if (m.hasRemaining()) {
+                throw new SSLProtocolException(
+                    "Invalid supported_groups extension: unknown extra data");
+            }
+
+            if ((ngs == null) || (ngs.length == 0) || (ngs.length % 2 != 0)) {
+                throw new SSLProtocolException(
+                    "Invalid supported_groups extension: incomplete data");
+            }
+
+            int[] ids = new int[ngs.length / 2];
+            for (int i = 0, j = 0; i < ngs.length;) {
+                ids[j++] = ((ngs[i++] & 0xFF) << 8) | (ngs[i++] & 0xFF);
+            }
+
+            this.namedGroupsIds = ids;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"versions\": '['{0}']'", Locale.ENGLISH);
+
+            if (namedGroupsIds == null || namedGroupsIds.length == 0) {
+                Object[] messageFields = {
+                        "<no supported named group specified>"
+                    };
+                return messageFormat.format(messageFields);
+            } else {
+                StringBuilder builder = new StringBuilder(512);
+                boolean isFirst = true;
+                for (int ngid : namedGroupsIds) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        builder.append(", ");
+                    }
+
+                    builder.append(NamedGroup.nameOf(ngid));
+                }
+
+                Object[] messageFields = {
+                        builder.toString()
+                    };
+
+                return messageFormat.format(messageFields);
+            }
+        }
+    }
+
+    private static final
+            class SupportedGroupsStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new SupportedGroupsSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    static enum NamedGroupType {
+        NAMED_GROUP_ECDHE,          // Elliptic Curve Groups (ECDHE)
+        NAMED_GROUP_FFDHE,          // Finite Field Groups (DHE)
+        NAMED_GROUP_XDH,            // Finite Field Groups (XDH)
+        NAMED_GROUP_ARBITRARY,      // arbitrary prime and curves (ECDHE)
+        NAMED_GROUP_NONE;           // Not predefined named group
+
+        boolean isSupported(List<CipherSuite> cipherSuites) {
+            for (CipherSuite cs : cipherSuites) {
+                if (cs.keyExchange == null || cs.keyExchange.groupType == this) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    static enum NamedGroup {
+        // Elliptic Curves (RFC 4492)
+        //
+        // See sun.security.util.CurveDB for the OIDs
+        // NIST K-163
+        SECT163_K1  (0x0001, "sect163k1", "1.3.132.0.1", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECT163_R1  (0x0002, "sect163r1", "1.3.132.0.2", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST B-163
+        SECT163_R2  (0x0003, "sect163r2", "1.3.132.0.15", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECT193_R1  (0x0004, "sect193r1", "1.3.132.0.24", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECT193_R2  (0x0005, "sect193r2", "1.3.132.0.25", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST K-233
+        SECT233_K1  (0x0006, "sect233k1", "1.3.132.0.26", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST B-233
+        SECT233_R1  (0x0007, "sect233r1", "1.3.132.0.27", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECT239_K1  (0x0008, "sect239k1", "1.3.132.0.3", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST K-283
+        SECT283_K1  (0x0009, "sect283k1", "1.3.132.0.16", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST B-283
+        SECT283_R1  (0x000A, "sect283r1", "1.3.132.0.17", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST K-409
+        SECT409_K1  (0x000B, "sect409k1", "1.3.132.0.36", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST B-409
+        SECT409_R1  (0x000C, "sect409r1", "1.3.132.0.37", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST K-571
+        SECT571_K1  (0x000D, "sect571k1", "1.3.132.0.38", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST B-571
+        SECT571_R1  (0x000E, "sect571r1", "1.3.132.0.39", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECP160_K1  (0x000F, "secp160k1", "1.3.132.0.9", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECP160_R1  (0x0010, "secp160r1", "1.3.132.0.8", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECP160_R2  (0x0011, "secp160r2", "1.3.132.0.30", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECP192_K1  (0x0012, "secp192k1", "1.3.132.0.31", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST P-192
+        SECP192_R1  (0x0013, "secp192r1", "1.2.840.10045.3.1.1", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECP224_K1  (0x0014, "secp224k1", "1.3.132.0.32", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        // NIST P-224
+        SECP224_R1  (0x0015, "secp224r1", "1.3.132.0.33", true,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        SECP256_K1  (0x0016, "secp256k1", "1.3.132.0.10", false,
+                            ProtocolVersion.PROTOCOLS_TO_12),
+
+        // NIST P-256
+        SECP256_R1  (0x0017, "secp256r1", "1.2.840.10045.3.1.7", true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+
+        // NIST P-384
+        SECP384_R1  (0x0018, "secp384r1", "1.3.132.0.34", true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+
+        // NIST P-521
+        SECP521_R1  (0x0019, "secp521r1", "1.3.132.0.35", true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+
+        // x25519 and x448
+        X25519      (0x001D, "x25519", true, "x25519",
+                            ProtocolVersion.PROTOCOLS_TO_13),
+        X448        (0x001E, "x448", true, "x448",
+                            ProtocolVersion.PROTOCOLS_TO_13),
+
+        // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
+        FFDHE_2048  (0x0100, "ffdhe2048",  true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+        FFDHE_3072  (0x0101, "ffdhe3072",  true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+        FFDHE_4096  (0x0102, "ffdhe4096",  true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+        FFDHE_6144  (0x0103, "ffdhe6144",  true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+        FFDHE_8192  (0x0104, "ffdhe8192",  true,
+                            ProtocolVersion.PROTOCOLS_TO_13),
+
+        // Elliptic Curves (RFC 4492)
+        //
+        // arbitrary prime and characteristic-2 curves
+        ARBITRARY_PRIME  (0xFF01, "arbitrary_explicit_prime_curves",
+                            ProtocolVersion.PROTOCOLS_TO_12),
+        ARBITRARY_CHAR2  (0xFF02, "arbitrary_explicit_char2_curves",
+                            ProtocolVersion.PROTOCOLS_TO_12);
+
+        final int id;               // hash + signature
+        final NamedGroupType type;  // group type
+        final String name;          // literal name
+        final String oid;           // object identifier of the named group
+        final String algorithm;     // signature algorithm
+        final boolean isFips;       // can be used in FIPS mode?
+        final ProtocolVersion[] supportedProtocols;
+
+        // Constructor used for Elliptic Curve Groups (ECDHE)
+        private NamedGroup(int id, String name, String oid, boolean isFips,
+                ProtocolVersion[] supportedProtocols) {
+            this.id = id;
+            this.type = NamedGroupType.NAMED_GROUP_ECDHE;
+            this.name = name;
+            this.oid = oid;
+            this.algorithm = "EC";
+            this.isFips = isFips;
+            this.supportedProtocols = supportedProtocols;
+        }
+
+        // Constructor used for Elliptic Curve Groups (XDH)
+        private NamedGroup(int id, String name,
+                boolean isFips, String algorithm,
+                ProtocolVersion[] supportedProtocols) {
+            this.id = id;
+            this.type = NamedGroupType.NAMED_GROUP_XDH;
+            this.name = name;
+            this.oid = null;
+            this.algorithm = algorithm;
+            this.isFips = isFips;
+            this.supportedProtocols = supportedProtocols;
+        }
+
+        // Constructor used for Finite Field Diffie-Hellman Groups (FFDHE)
+        private NamedGroup(int id, String name, boolean isFips,
+                ProtocolVersion[] supportedProtocols) {
+            this.id = id;
+            this.type = NamedGroupType.NAMED_GROUP_FFDHE;
+            this.name = name;
+            this.oid = null;
+            this.algorithm = "DiffieHellman";
+            this.isFips = isFips;
+            this.supportedProtocols = supportedProtocols;
+        }
+
+        // Constructor used for arbitrary prime and curves (ECDHE)
+        private NamedGroup(int id, String name,
+                ProtocolVersion[] supportedProtocols) {
+            this.id = id;
+            this.type = NamedGroupType.NAMED_GROUP_ARBITRARY;
+            this.name = name;
+            this.oid = null;
+            this.algorithm = "EC";
+            this.isFips = false;
+            this.supportedProtocols = supportedProtocols;
+        }
+
+        static NamedGroup valueOf(int id) {
+            for (NamedGroup group : NamedGroup.values()) {
+                if (group.id == id) {
+                    return group;
+                }
+            }
+
+            return null;
+        }
+
+        static NamedGroup valueOf(ECParameterSpec params) {
+            String oid = JsseJce.getNamedCurveOid(params);
+            if ((oid != null) && (!oid.isEmpty())) {
+                for (NamedGroup group : NamedGroup.values()) {
+                    if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
+                            oid.equals(group.oid)) {
+                        return group;
+                    }
                 }
             }
 
-            if (groupList.isEmpty() && JsseJce.isEcAvailable()) {
-                throw new IllegalArgumentException(
-                    "System property jdk.tls.namedGroups(" + property + ") " +
-                    "contains no supported elliptic curves");
+            return null;
+        }
+
+        static NamedGroup valueOf(DHParameterSpec params) {
+            for (Map.Entry<NamedGroup, AlgorithmParameters> me :
+                    SupportedGroups.namedGroupParams.entrySet()) {
+                NamedGroup ng = me.getKey();
+                if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
+                    continue;
+                }
+
+                DHParameterSpec ngParams = null;
+                AlgorithmParameters aps = me.getValue();
+                try {
+                    ngParams = aps.getParameterSpec(DHParameterSpec.class);
+                } catch (InvalidParameterSpecException ipse) {
+                    // should be unlikely
+                }
+
+                if (ngParams == null) {
+                    continue;
+                }
+
+                if (ngParams.getP().equals(params.getP()) &&
+                        ngParams.getG().equals(params.getG())) {
+                    return ng;
+                }
             }
-        } else {        // default groups
-            NamedGroup[] groups;
-            if (requireFips) {
-                groups = new NamedGroup[] {
-                    // only NIST curves in FIPS mode
-                    NamedGroup.SECP256_R1,
-                    NamedGroup.SECP384_R1,
-                    NamedGroup.SECP521_R1,
-                    NamedGroup.SECT283_K1,
-                    NamedGroup.SECT283_R1,
-                    NamedGroup.SECT409_K1,
-                    NamedGroup.SECT409_R1,
-                    NamedGroup.SECT571_K1,
-                    NamedGroup.SECT571_R1,
+
+            return null;
+        }
+
+        static NamedGroup nameOf(String name) {
+            for (NamedGroup group : NamedGroup.values()) {
+                if (group.name.equals(name)) {
+                    return group;
+                }
+            }
+
+            return null;
+        }
 
-                    // FFDHE 2048
-                    NamedGroup.FFDHE_2048,
-                    NamedGroup.FFDHE_3072,
-                    NamedGroup.FFDHE_4096,
-                    NamedGroup.FFDHE_6144,
-                    NamedGroup.FFDHE_8192,
-                };
-            } else {
-                groups = new NamedGroup[] {
-                    // NIST curves first
-                    NamedGroup.SECP256_R1,
-                    NamedGroup.SECP384_R1,
-                    NamedGroup.SECP521_R1,
-                    NamedGroup.SECT283_K1,
-                    NamedGroup.SECT283_R1,
-                    NamedGroup.SECT409_K1,
-                    NamedGroup.SECT409_R1,
-                    NamedGroup.SECT571_K1,
-                    NamedGroup.SECT571_R1,
+        static String nameOf(int id) {
+            for (NamedGroup group : NamedGroup.values()) {
+                if (group.id == id) {
+                    return group.name;
+                }
+            }
+
+            return "UNDEFINED-NAMED-GROUP(" + id + ")";
+        }
+
+        boolean isAvailable(List<ProtocolVersion> protocolVersions) {
+            for (ProtocolVersion pv : supportedProtocols) {
+                if (protocolVersions.contains(pv)) {
+                    return true;
+                }
+            }
+            return false;
+        }
 
-                    // non-NIST curves
-                    NamedGroup.SECP256_K1,
+        boolean isAvailable(ProtocolVersion protocolVersion) {
+            for (ProtocolVersion pv : supportedProtocols) {
+                if (protocolVersion == pv) {
+                    return true;
+                }
+            }
+            return false;
+        }
 
-                    // FFDHE 2048
-                    NamedGroup.FFDHE_2048,
-                    NamedGroup.FFDHE_3072,
-                    NamedGroup.FFDHE_4096,
-                    NamedGroup.FFDHE_6144,
-                    NamedGroup.FFDHE_8192,
-                };
+        boolean isSupported(List<CipherSuite> cipherSuites) {
+            for (CipherSuite cs : cipherSuites) {
+                boolean isMatch = isAvailable(cs.supportedProtocols);
+                if (isMatch && (cs.keyExchange == null ||
+                        cs.keyExchange.groupType == type)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        // lazy loading of parameters
+        AlgorithmParameters getParameters() {
+            return SupportedGroups.namedGroupParams.get(this);
+        }
+
+        AlgorithmParameterSpec getParameterSpec() {
+            if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                return SupportedGroups.getECGenParamSpec(this);
+            } else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+                return SupportedGroups.getDHParameterSpec(this);
             }
 
-            groupList = new ArrayList<>(groups.length);
-            for (NamedGroup group : groups) {
-                if (isAvailableGroup(group)) {
-                    groupList.add(group);
+            return null;
+        }
+    }
+
+    static class SupportedGroups {
+        // To switch off the supported_groups extension for DHE cipher suite.
+        static final boolean enableFFDHE =
+                Utilities.getBooleanProperty("jsse.enableFFDHE", true);
+
+        // cache to speed up the parameters construction
+        static final Map<NamedGroup,
+                    AlgorithmParameters> namedGroupParams = new HashMap<>();
+
+        // the supported named groups
+        static final NamedGroup[] supportedNamedGroups;
+
+        static {
+            boolean requireFips = SunJSSE.isFIPS();
+
+            // The value of the System Property defines a list of enabled named
+            // groups in preference order, separated with comma.  For example:
+            //
+            //      jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
+            //
+            // If the System Property is not defined or the value is empty, the
+            // default groups and preferences will be used.
+            String property = AccessController.doPrivileged(
+                        new GetPropertyAction("jdk.tls.namedGroups"));
+            if (property != null && property.length() != 0) {
+                // remove double quote marks from beginning/end of the property
+                if (property.length() > 1 && property.charAt(0) == '"' &&
+                        property.charAt(property.length() - 1) == '"') {
+                    property = property.substring(1, property.length() - 1);
+                }
+            }
+
+            ArrayList<NamedGroup> groupList;
+            if (property != null && property.length() != 0) {
+                String[] groups = property.split(",");
+                groupList = new ArrayList<>(groups.length);
+                for (String group : groups) {
+                    group = group.trim();
+                    if (!group.isEmpty()) {
+                        NamedGroup namedGroup = NamedGroup.nameOf(group);
+                        if (namedGroup != null &&
+                                (!requireFips || namedGroup.isFips)) {
+                            if (isAvailableGroup(namedGroup)) {
+                                groupList.add(namedGroup);
+                            }
+                        }   // ignore unknown groups
+                    }
+                }
+
+                if (groupList.isEmpty()) {
+                    throw new IllegalArgumentException(
+                            "System property jdk.tls.namedGroups(" +
+                            property + ") contains no supported named groups");
                 }
+            } else {        // default groups
+                NamedGroup[] groups;
+                if (requireFips) {
+                    groups = new NamedGroup[] {
+                        // only NIST curves in FIPS mode
+                        NamedGroup.SECP256_R1,
+                        NamedGroup.SECP384_R1,
+                        NamedGroup.SECP521_R1,
+                        NamedGroup.SECT283_K1,
+                        NamedGroup.SECT283_R1,
+                        NamedGroup.SECT409_K1,
+                        NamedGroup.SECT409_R1,
+                        NamedGroup.SECT571_K1,
+                        NamedGroup.SECT571_R1,
+
+                        // FFDHE 2048
+                        NamedGroup.FFDHE_2048,
+                        NamedGroup.FFDHE_3072,
+                        NamedGroup.FFDHE_4096,
+                        NamedGroup.FFDHE_6144,
+                        NamedGroup.FFDHE_8192,
+                    };
+                } else {
+                    groups = new NamedGroup[] {
+                        // NIST curves first
+                        NamedGroup.SECP256_R1,
+                        NamedGroup.SECP384_R1,
+                        NamedGroup.SECP521_R1,
+                        NamedGroup.SECT283_K1,
+                        NamedGroup.SECT283_R1,
+                        NamedGroup.SECT409_K1,
+                        NamedGroup.SECT409_R1,
+                        NamedGroup.SECT571_K1,
+                        NamedGroup.SECT571_R1,
+
+                        // non-NIST curves
+                        NamedGroup.SECP256_K1,
+
+                        // FFDHE 2048
+                        NamedGroup.FFDHE_2048,
+                        NamedGroup.FFDHE_3072,
+                        NamedGroup.FFDHE_4096,
+                        NamedGroup.FFDHE_6144,
+                        NamedGroup.FFDHE_8192,
+                    };
+                }
+
+                groupList = new ArrayList<>(groups.length);
+                for (NamedGroup group : groups) {
+                    if (isAvailableGroup(group)) {
+                        groupList.add(group);
+                    }
+                }
+
+                if (groupList.isEmpty() &&
+                        SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.warning("No default named groups");
+                }
+            }
+
+            supportedNamedGroups = new NamedGroup[groupList.size()];
+            int i = 0;
+            for (NamedGroup namedGroup : groupList) {
+                supportedNamedGroups[i++] = namedGroup;
             }
         }
 
-        if (debug != null && groupList.isEmpty()) {
-            Debug.log(
-                "Initialized [jdk.tls.namedGroups|default] list contains " +
-                "no available elliptic curves. " +
-                (property != null ? "(" + property + ")" : "[Default]"));
+        // check whether the group is supported by the underlying providers
+        private static boolean isAvailableGroup(NamedGroup namedGroup) {
+            AlgorithmParameters params = null;
+            AlgorithmParameterSpec spec = null;
+            if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
+                if (namedGroup.oid != null) {
+                    try {
+                        params = JsseJce.getAlgorithmParameters("EC");
+                        spec = new ECGenParameterSpec(namedGroup.oid);
+                    } catch (NoSuchAlgorithmException e) {
+                        return false;
+                    }
+                }
+            } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
+                try {
+                    params = JsseJce.getAlgorithmParameters("DiffieHellman");
+                    spec = getFFDHEDHParameterSpec(namedGroup);
+                } catch (NoSuchAlgorithmException e) {
+                    return false;
+                }
+            }   // Otherwise, unsupported.
+
+            if ((params != null) && (spec != null)) {
+                try {
+                    params.init(spec);
+                } catch (InvalidParameterSpecException e) {
+                    return false;
+                }
+
+                // cache the parameters
+                namedGroupParams.put(namedGroup, params);
+
+                return true;
+            }
+
+            return false;
         }
 
-        supportedNamedGroups = new NamedGroup[groupList.size()];
-        int i = 0;
-        for (NamedGroup namedGroup : groupList) {
-            supportedNamedGroups[i++] = namedGroup;
+        private static DHParameterSpec getFFDHEDHParameterSpec(
+                NamedGroup namedGroup) {
+            DHParameterSpec spec = null;
+            switch (namedGroup) {
+                case FFDHE_2048:
+                    spec = PredefinedDHParameterSpecs.ffdheParams.get(2048);
+                    break;
+                case FFDHE_3072:
+                    spec = PredefinedDHParameterSpecs.ffdheParams.get(3072);
+                    break;
+                case FFDHE_4096:
+                    spec = PredefinedDHParameterSpecs.ffdheParams.get(4096);
+                    break;
+                case FFDHE_6144:
+                    spec = PredefinedDHParameterSpecs.ffdheParams.get(6144);
+                    break;
+                case FFDHE_8192:
+                    spec = PredefinedDHParameterSpecs.ffdheParams.get(8192);
+            }
+
+            return spec;
         }
-    }
 
-    // check whether the group is supported by the underlying providers
-    private static boolean isAvailableGroup(NamedGroup namedGroup) {
-        AlgorithmParameters params = null;
-        AlgorithmParameterSpec spec = null;
-        if ("EC".equals(namedGroup.algorithm)) {
-            if (namedGroup.oid != null) {
-                try {
-                    params = JsseJce.getAlgorithmParameters("EC");
-                    spec = new ECGenParameterSpec(namedGroup.oid);
-                } catch (Exception e) {
-                    return false;
-                }
+        private static DHParameterSpec getPredefinedDHParameterSpec(
+                NamedGroup namedGroup) {
+            DHParameterSpec spec = null;
+            switch (namedGroup) {
+                case FFDHE_2048:
+                    spec = PredefinedDHParameterSpecs.definedParams.get(2048);
+                    break;
+                case FFDHE_3072:
+                    spec = PredefinedDHParameterSpecs.definedParams.get(3072);
+                    break;
+                case FFDHE_4096:
+                    spec = PredefinedDHParameterSpecs.definedParams.get(4096);
+                    break;
+                case FFDHE_6144:
+                    spec = PredefinedDHParameterSpecs.definedParams.get(6144);
+                    break;
+                case FFDHE_8192:
+                    spec = PredefinedDHParameterSpecs.definedParams.get(8192);
             }
-        } else if ("DiffieHellman".equals(namedGroup.algorithm)) {
+
+            return spec;
+        }
+
+        static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) {
+            if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
+                throw new RuntimeException(
+                        "Not a named EC group: " + namedGroup);
+            }
+
+            AlgorithmParameters params = namedGroupParams.get(namedGroup);
             try {
-                params = JsseJce.getAlgorithmParameters("DiffieHellman");
-                spec = getFFDHEDHParameterSpec(namedGroup);
-            } catch (Exception e) {
-                return false;
+                return params.getParameterSpec(ECGenParameterSpec.class);
+            } catch (InvalidParameterSpecException ipse) {
+                // should be unlikely
+                return new ECGenParameterSpec(namedGroup.oid);
             }
         }
 
-        if ((params != null) && (spec != null)) {
+        static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) {
+            if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) {
+                throw new RuntimeException(
+                        "Not a named DH group: " + namedGroup);
+            }
+
+            AlgorithmParameters params = namedGroupParams.get(namedGroup);
             try {
-                params.init(spec);
-            } catch (Exception e) {
+                return params.getParameterSpec(DHParameterSpec.class);
+            } catch (InvalidParameterSpecException ipse) {
+                // should be unlikely
+                return getPredefinedDHParameterSpec(namedGroup);
+            }
+        }
+
+        // Is there any supported group permitted by the constraints?
+        static boolean isActivatable(
+                AlgorithmConstraints constraints, NamedGroupType type) {
+
+            boolean hasFFDHEGroups = false;
+            for (NamedGroup namedGroup : supportedNamedGroups) {
+                if (namedGroup.type == type) {
+                    if (constraints.permits(
+                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                            namedGroup.algorithm,
+                            namedGroupParams.get(namedGroup))) {
+
+                        return true;
+                    }
+
+                    if (!hasFFDHEGroups &&
+                            (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+                        hasFFDHEGroups = true;
+                    }
+                }
+            }
+
+            // For compatibility, if no FFDHE groups are defined, the non-FFDHE
+            // compatible mode (using DHE cipher suite without FFDHE extension)
+            // is allowed.
+            //
+            // Note that the constraints checking on DHE parameters will be
+            // performed during key exchanging in a handshake.
+            return !hasFFDHEGroups && type == NamedGroupType.NAMED_GROUP_FFDHE;
+        }
+
+        // Is the named group permitted by the constraints?
+        static boolean isActivatable(
+                AlgorithmConstraints constraints, NamedGroup namedGroup) {
+            if (!isSupported(namedGroup)) {
                 return false;
             }
 
-            // cache the parameters
-            namedGroupParams.put(namedGroup, params);
+            return constraints.permits(
+                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                            namedGroup.algorithm,
+                            namedGroupParams.get(namedGroup));
+        }
 
-            return true;
+        // Is there any supported group permitted by the constraints?
+        static boolean isSupported(NamedGroup namedGroup) {
+            for (NamedGroup group : supportedNamedGroups) {
+                if (namedGroup.id == group.id) {
+                    return true;
+                }
+            }
+
+            return false;
         }
 
-        return false;
-    }
+        static NamedGroup getPreferredGroup(
+                ProtocolVersion negotiatedProtocol,
+                AlgorithmConstraints constraints, NamedGroupType type,
+                List<NamedGroup> requestedNamedGroups) {
+            for (NamedGroup namedGroup : requestedNamedGroups) {
+                if ((namedGroup.type == type) &&
+                        namedGroup.isAvailable(negotiatedProtocol) &&
+                        constraints.permits(
+                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                                namedGroup.algorithm,
+                                namedGroupParams.get(namedGroup))) {
+                    return namedGroup;
+                }
+            }
 
-    private static DHParameterSpec getFFDHEDHParameterSpec(
-            NamedGroup namedGroup) {
-        DHParameterSpec spec = null;
-        switch (namedGroup) {
-            case FFDHE_2048:
-                spec = PredefinedDHParameterSpecs.ffdheParams.get(2048);
-                break;
-            case FFDHE_3072:
-                spec = PredefinedDHParameterSpecs.ffdheParams.get(3072);
-                break;
-            case FFDHE_4096:
-                spec = PredefinedDHParameterSpecs.ffdheParams.get(4096);
-                break;
-            case FFDHE_6144:
-                spec = PredefinedDHParameterSpecs.ffdheParams.get(6144);
-                break;
-            case FFDHE_8192:
-                spec = PredefinedDHParameterSpecs.ffdheParams.get(8192);
+            return null;
         }
 
-        return spec;
+        static NamedGroup getPreferredGroup(
+                ProtocolVersion negotiatedProtocol,
+                AlgorithmConstraints constraints, NamedGroupType type) {
+            for (NamedGroup namedGroup : supportedNamedGroups) {
+                if ((namedGroup.type == type) &&
+                        namedGroup.isAvailable(negotiatedProtocol) &&
+                        constraints.permits(
+                                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                                namedGroup.algorithm,
+                                namedGroupParams.get(namedGroup))) {
+                    return namedGroup;
+                }
+            }
+
+            return null;
+        }
     }
 
-    private static DHParameterSpec getPredefinedDHParameterSpec(
-            NamedGroup namedGroup) {
-        DHParameterSpec spec = null;
-        switch (namedGroup) {
-            case FFDHE_2048:
-                spec = PredefinedDHParameterSpecs.definedParams.get(2048);
-                break;
-            case FFDHE_3072:
-                spec = PredefinedDHParameterSpecs.definedParams.get(3072);
-                break;
-            case FFDHE_4096:
-                spec = PredefinedDHParameterSpecs.definedParams.get(4096);
-                break;
-            case FFDHE_6144:
-                spec = PredefinedDHParameterSpecs.definedParams.get(6144);
-                break;
-            case FFDHE_8192:
-                spec = PredefinedDHParameterSpecs.definedParams.get(8192);
+    /**
+     * Network data producer of a "supported_groups" extension in
+     * the ClientHello handshake message.
+     */
+    private static final class CHSupportedGroupsProducer
+            extends SupportedGroups implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHSupportedGroupsProducer() {
+            // blank
         }
 
-        return spec;
-    }
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
 
-    private SupportedGroupsExtension(int[] requestedNamedGroupIds) {
-        super(ExtensionType.EXT_SUPPORTED_GROUPS);
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable supported_groups extension");
+                }
+                return null;
+            }
 
-        this.requestedNamedGroupIds = requestedNamedGroupIds;
-    }
+            // Produce the extension.
+            ArrayList<NamedGroup> namedGroups =
+                new ArrayList<>(SupportedGroups.supportedNamedGroups.length);
+            for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
+                if ((!SupportedGroups.enableFFDHE) &&
+                    (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+                    continue;
+                }
 
-    SupportedGroupsExtension(HandshakeInStream s, int len) throws IOException {
-        super(ExtensionType.EXT_SUPPORTED_GROUPS);
+                if (ng.isAvailable(chc.activeProtocols) &&
+                        ng.isSupported(chc.activeCipherSuites) &&
+                        chc.algorithmConstraints.permits(
+                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                            ng.algorithm, namedGroupParams.get(ng))) {
+                    namedGroups.add(ng);
+                } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore inactive or disabled named group: " + ng.name);
+                }
+            }
+
+            if (namedGroups.isEmpty()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("no available named group");
+                }
 
-        int k = s.getInt16();
-        if (((len & 1) != 0) || (k == 0) || (k + 2 != len)) {
-            throw new SSLProtocolException("Invalid " + type + " extension");
-        }
+                return null;
+            }
 
-        // Note: unknown named group will be ignored later.
-        requestedNamedGroupIds = new int[k >> 1];
-        for (int i = 0; i < requestedNamedGroupIds.length; i++) {
-            requestedNamedGroupIds[i] = s.getInt16();
+            int vectorLen = namedGroups.size() << 1;
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (NamedGroup namedGroup : namedGroups) {
+                    Record.putInt16(m, namedGroup.id);
+            }
+
+            // Update the context.
+            chc.clientRequestedNamedGroups =
+                    Collections.<NamedGroup>unmodifiableList(namedGroups);
+            chc.handshakeExtensions.put(CH_SUPPORTED_GROUPS,
+                    new SupportedGroupsSpec(namedGroups));
+
+            return extData;
         }
     }
 
-    // Get a local preferred supported ECDHE group permitted by the constraints.
-    static NamedGroup getPreferredECGroup(AlgorithmConstraints constraints) {
-        for (NamedGroup namedGroup : supportedNamedGroups) {
-            if ((namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
-                constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                    namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
-
-                return namedGroup;
-            }
+    /**
+     * Network data producer of a "supported_groups" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHSupportedGroupsConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHSupportedGroupsConsumer() {
+            // blank
         }
 
-        return null;
-    }
-
-    // Is there any supported group permitted by the constraints?
-    static boolean isActivatable(
-            AlgorithmConstraints constraints, NamedGroupType type) {
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
 
-        boolean hasFFDHEGroups = false;
-        for (NamedGroup namedGroup : supportedNamedGroups) {
-            if (namedGroup.type == type) {
-                if (constraints.permits(
-                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                        namedGroup.algorithm,
-                        namedGroupParams.get(namedGroup))) {
-
-                    return true;
-                }
-
-                if (!hasFFDHEGroups &&
-                        (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
-
-                    hasFFDHEGroups = true;
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable supported_groups extension");
                 }
-            }
-        }
-
-        // For compatibility, if no FFDHE groups are defined, the non-FFDHE
-        // compatible mode (using DHE cipher suite without FFDHE extension)
-        // is allowed.
-        //
-        // Note that the constraints checking on DHE parameters will be
-        // performed during key exchanging in a handshake.
-        if (!hasFFDHEGroups && (type == NamedGroupType.NAMED_GROUP_FFDHE)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    // Create the default supported groups extension.
-    static SupportedGroupsExtension createExtension(
-            AlgorithmConstraints constraints,
-            CipherSuiteList cipherSuites, boolean enableFFDHE) {
-
-        ArrayList<Integer> groupList =
-                new ArrayList<>(supportedNamedGroups.length);
-        for (NamedGroup namedGroup : supportedNamedGroups) {
-            if ((!enableFFDHE) &&
-                (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
-                continue;
+                return;     // ignore the extension
             }
 
-            if (cipherSuites.contains(namedGroup.type) &&
-                constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                    namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
-
-                groupList.add(namedGroup.id);
-            }
-        }
-
-        if (!groupList.isEmpty()) {
-            int[] ids = new int[groupList.size()];
-            int i = 0;
-            for (Integer id : groupList) {
-                ids[i++] = id;
+            // Parse the extension.
+            SupportedGroupsSpec spec;
+            try {
+                spec = new SupportedGroupsSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
             }
 
-            return new SupportedGroupsExtension(ids);
-        }
-
-        return null;
-    }
-
-    // get the preferred activated named group
-    NamedGroup getPreferredGroup(
-            AlgorithmConstraints constraints, NamedGroupType type) {
-
-        for (int groupId : requestedNamedGroupIds) {
-            NamedGroup namedGroup = NamedGroup.valueOf(groupId);
-            if ((namedGroup != null) && (namedGroup.type == type) &&
-                SupportedGroupsExtension.supports(namedGroup) &&
-                constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
-                    namedGroup.algorithm, namedGroupParams.get(namedGroup))) {
-
-                return namedGroup;
+            // Update the context.
+            List<NamedGroup> knownNamedGroups = new LinkedList<>();
+            for (int id : spec.namedGroupsIds) {
+                NamedGroup ng = NamedGroup.valueOf(id);
+                if (ng != null) {
+                    knownNamedGroups.add(ng);
+                }
             }
-        }
-
-        return null;
-    }
 
-    boolean hasFFDHEGroup() {
-        for (int groupId : requestedNamedGroupIds) {
-            /*
-             * [RFC 7919] Codepoints in the "Supported Groups Registry"
-             * with a high byte of 0x01 (that is, between 256 and 511,
-             * inclusive) are set aside for FFDHE groups.
-             */
-            if ((groupId >= 256) && (groupId <= 511)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
+            shc.clientRequestedNamedGroups = knownNamedGroups;
+            shc.handshakeExtensions.put(CH_SUPPORTED_GROUPS, spec);
 
-    boolean contains(int index) {
-        for (int groupId : requestedNamedGroupIds) {
-            if (index == groupId) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    int length() {
-        return 6 + (requestedNamedGroupIds.length << 1);
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        int k = requestedNamedGroupIds.length << 1;
-        s.putInt16(k + 2);
-        s.putInt16(k);
-        for (int groupId : requestedNamedGroupIds) {
-            s.putInt16(groupId);
+            // No impact on session resumption.
         }
     }
 
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Extension " + type + ", group names: {");
-        boolean first = true;
-        for (int groupId : requestedNamedGroupIds) {
-            if (first) {
-                first = false;
-            } else {
-                sb.append(", ");
-            }
-            // first check if it is a known named group, then try other cases.
-            NamedGroup namedGroup = NamedGroup.valueOf(groupId);
-            if (namedGroup != null) {
-                sb.append(namedGroup.name);
-            } else if (groupId == ARBITRARY_PRIME) {
-                sb.append("arbitrary_explicit_prime_curves");
-            } else if (groupId == ARBITRARY_CHAR2) {
-                sb.append("arbitrary_explicit_char2_curves");
-            } else {
-                sb.append("unknown named group " + groupId);
-            }
-        }
-        sb.append("}");
-        return sb.toString();
-    }
+    /**
+     * Network data producer of a "supported_groups" extension in
+     * the EncryptedExtensions handshake message.
+     */
+    private static final class EESupportedGroupsProducer
+            extends SupportedGroups implements HandshakeProducer {
 
-    static boolean supports(NamedGroup namedGroup) {
-        for (NamedGroup group : supportedNamedGroups) {
-            if (namedGroup.id == group.id) {
-                return true;
-            }
+        // Prevent instantiation of this class.
+        private EESupportedGroupsProducer() {
+            // blank
         }
 
-        return false;
-    }
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable supported_groups extension");
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            //
+            // Contains all groups the server supports, regardless of whether
+            // they are currently supported by the client.
+            ArrayList<NamedGroup> namedGroups = new ArrayList<>(
+                    SupportedGroups.supportedNamedGroups.length);
+            for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
+                if ((!SupportedGroups.enableFFDHE) &&
+                    (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) {
+                    continue;
+                }
 
-    static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) {
-        if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
-            throw new RuntimeException("Not a named EC group: " + namedGroup);
-        }
+                if (ng.isAvailable(shc.activeProtocols) &&
+                        ng.isSupported(shc.activeCipherSuites) &&
+                        shc.algorithmConstraints.permits(
+                            EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
+                            ng.algorithm, namedGroupParams.get(ng))) {
+                    namedGroups.add(ng);
+                } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore inactive or disabled named group: " + ng.name);
+                }
+            }
+
+            if (namedGroups.isEmpty()) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning("no available named group");
+                }
 
-        AlgorithmParameters params = namedGroupParams.get(namedGroup);
-        try {
-            return params.getParameterSpec(ECGenParameterSpec.class);
-        } catch (InvalidParameterSpecException ipse) {
-            // should be unlikely
-            return new ECGenParameterSpec(namedGroup.oid);
+                return null;
+            }
+
+            int vectorLen = namedGroups.size() << 1;
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (NamedGroup namedGroup : namedGroups) {
+                    Record.putInt16(m, namedGroup.id);
+            }
+
+            // Update the context.
+            shc.conContext.serverRequestedNamedGroups =
+                    Collections.<NamedGroup>unmodifiableList(namedGroups);
+            SupportedGroupsSpec spec = new SupportedGroupsSpec(namedGroups);
+            shc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec);
+
+            return extData;
         }
     }
 
-    static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) {
-        if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) {
-            throw new RuntimeException("Not a named DH group: " + namedGroup);
+    private static final
+            class EESupportedGroupsConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private EESupportedGroupsConsumer() {
+            // blank
         }
 
-        AlgorithmParameters params = namedGroupParams.get(namedGroup);
-        try {
-            return params.getParameterSpec(DHParameterSpec.class);
-        } catch (InvalidParameterSpecException ipse) {
-            // should be unlikely
-            return getPredefinedDHParameterSpec(namedGroup);
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(EE_SUPPORTED_GROUPS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable supported_groups extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            SupportedGroupsSpec spec;
+            try {
+                spec = new SupportedGroupsSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            List<NamedGroup> knownNamedGroups =
+                    new ArrayList<>(spec.namedGroupsIds.length);
+            for (int id : spec.namedGroupsIds) {
+                NamedGroup ng = NamedGroup.valueOf(id);
+                if (ng != null) {
+                    knownNamedGroups.add(ng);
+                }
+            }
+
+            chc.conContext.serverRequestedNamedGroups = knownNamedGroups;
+            chc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec);
+
+            // No impact on session resumption.
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+import static sun.security.ssl.SSLExtension.CH_SUPPORTED_VERSIONS;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import static sun.security.ssl.SSLExtension.HRR_SUPPORTED_VERSIONS;
+import static sun.security.ssl.SSLExtension.SH_SUPPORTED_VERSIONS;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "supported_versions" extensions.
+ */
+final class SupportedVersionsExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHSupportedVersionsProducer();
+    static final ExtensionConsumer chOnLoadConsumer =
+            new CHSupportedVersionsConsumer();
+    static final SSLStringizer chStringizer =
+            new CHSupportedVersionsStringizer();
+
+    static final HandshakeProducer shNetworkProducer =
+            new SHSupportedVersionsProducer();
+    static final ExtensionConsumer shOnLoadConsumer =
+            new SHSupportedVersionsConsumer();
+    static final SSLStringizer shStringizer =
+            new SHSupportedVersionsStringizer();
+
+    static final HandshakeProducer hrrNetworkProducer =
+            new HRRSupportedVersionsProducer();
+    static final ExtensionConsumer hrrOnLoadConsumer =
+            new HRRSupportedVersionsConsumer();
+    static final HandshakeProducer hrrReproducer =
+            new HRRSupportedVersionsReproducer();
+    static final SSLStringizer hrrStringizer =
+            new SHSupportedVersionsStringizer();
+    /**
+     * The "supported_versions" extension in ClientHello.
+     */
+    static final class CHSupportedVersionsSpec implements SSLExtensionSpec {
+        final int[] requestedProtocols;
+
+        private CHSupportedVersionsSpec(int[] requestedProtocols) {
+            this.requestedProtocols = requestedProtocols;
+        }
+
+        private CHSupportedVersionsSpec(ByteBuffer m) throws IOException  {
+            if (m.remaining() < 3) {        //  1: the length of the list
+                                            // +2: one version at least
+                throw new SSLProtocolException(
+                    "Invalid supported_versions extension: insufficient data");
+            }
+
+            byte[] vbs = Record.getBytes8(m);   // Get the version bytes.
+            if (m.hasRemaining()) {
+                throw new SSLProtocolException(
+                    "Invalid supported_versions extension: unknown extra data");
+            }
+
+            if (vbs == null || vbs.length == 0 || (vbs.length & 0x01) != 0) {
+                throw new SSLProtocolException(
+                    "Invalid supported_versions extension: incomplete data");
+            }
+
+            int[] protocols = new int[vbs.length >> 1];
+            for (int i = 0, j = 0; i < vbs.length;) {
+                byte major = vbs[i++];
+                byte minor = vbs[i++];
+                protocols[j++] = ((major & 0xFF) << 8) | (minor & 0xFF);
+            }
+
+            this.requestedProtocols = protocols;
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"versions\": '['{0}']'", Locale.ENGLISH);
+
+            if (requestedProtocols == null || requestedProtocols.length == 0) {
+                Object[] messageFields = {
+                        "<no supported version specified>"
+                    };
+                return messageFormat.format(messageFields);
+            } else {
+                StringBuilder builder = new StringBuilder(512);
+                boolean isFirst = true;
+                for (int pv : requestedProtocols) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        builder.append(", ");
+                    }
+
+                    builder.append(ProtocolVersion.nameOf(pv));
+                }
+
+                Object[] messageFields = {
+                        builder.toString()
+                    };
+
+                return messageFormat.format(messageFields);
+            }
+        }
+    }
+
+    private static final
+            class CHSupportedVersionsStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new CHSupportedVersionsSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "supported_versions" extension in ClientHello.
+     */
+    private static final
+            class CHSupportedVersionsProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHSupportedVersionsProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " +
+                        CH_SUPPORTED_VERSIONS.name);
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            //
+            // The activated protocols are used as the supported versions.
+            int[] protocols = new int[chc.activeProtocols.size()];
+            int verLen = protocols.length * 2;
+            byte[] extData = new byte[verLen + 1];      // 1: versions length
+            extData[0] = (byte)(verLen & 0xFF);
+            int i = 0, j = 1;
+            for (ProtocolVersion pv : chc.activeProtocols) {
+                protocols[i++] = pv.id;
+                extData[j++] = pv.major;
+                extData[j++] = pv.minor;
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS,
+                    new CHSupportedVersionsSpec(protocols));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "supported_versions" extension in ClientHello.
+     */
+    private static final
+            class CHSupportedVersionsConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHSupportedVersionsConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(CH_SUPPORTED_VERSIONS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " +
+                        CH_SUPPORTED_VERSIONS.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            CHSupportedVersionsSpec spec;
+            try {
+                spec = new CHSupportedVersionsSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, spec);
+
+            // No impact on session resumption.
+            //
+            // Note that the protocol version negotiation happens before the
+            // session resumption negotiation.  And the session resumption
+            // negotiation depends on the negotiated protocol version.
+        }
+    }
+
+    /**
+     * The "supported_versions" extension in ServerHello and HelloRetryRequest.
+     */
+    static final class SHSupportedVersionsSpec implements SSLExtensionSpec {
+        final int selectedVersion;
+
+        private SHSupportedVersionsSpec(ProtocolVersion selectedVersion) {
+            this.selectedVersion = selectedVersion.id;
+        }
+
+        private SHSupportedVersionsSpec(ByteBuffer m) throws IOException  {
+            if (m.remaining() != 2) {       // 2: the selected version
+                throw new SSLProtocolException(
+                    "Invalid supported_versions: insufficient data");
+            }
+
+            byte major = m.get();
+            byte minor = m.get();
+            this.selectedVersion = ((major & 0xFF) << 8) | (minor & 0xFF);
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"selected version\": '['{0}']'", Locale.ENGLISH);
+
+            Object[] messageFields = {
+                    ProtocolVersion.nameOf(selectedVersion)
+                };
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    private static final
+            class SHSupportedVersionsStringizer implements SSLStringizer {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new SHSupportedVersionsSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
+
+    /**
+     * Network data producer of a "supported_versions" extension in ServerHello.
+     */
+    private static final
+            class SHSupportedVersionsProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private SHSupportedVersionsProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // In response to supported_versions request only
+            CHSupportedVersionsSpec svs = (CHSupportedVersionsSpec)
+                    shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS);
+            if (svs == null) {
+                // Unlikely, no key_share extension requested.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.warning(
+                            "Ignore unavailable supported_versions extension");
+                }
+                return null;
+            }
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " +
+                        SH_SUPPORTED_VERSIONS.name);
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            byte[] extData = new byte[2];
+            extData[0] = shc.negotiatedProtocol.major;
+            extData[1] = shc.negotiatedProtocol.minor;
+
+            // Update the context.
+            shc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS,
+                    new SHSupportedVersionsSpec(shc.negotiatedProtocol));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "supported_versions" extension in ServerHello.
+     */
+    private static final
+            class SHSupportedVersionsConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private SHSupportedVersionsConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " +
+                        SH_SUPPORTED_VERSIONS.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            SHSupportedVersionsSpec spec;
+            try {
+                spec = new SHSupportedVersionsSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, spec);
+
+            // No impact on session resumption.
+            //
+            // Note that the protocol version negotiation happens before the
+            // session resumption negotiation.  And the session resumption
+            // negotiation depends on the negotiated protocol version.
+        }
+    }
+
+    /**
+     * Network data producer of a "supported_versions" extension in
+     * HelloRetryRequest.
+     */
+    private static final
+            class HRRSupportedVersionsProducer implements HandshakeProducer {
+
+        // Prevent instantiation of this class.
+        private HRRSupportedVersionsProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " +
+                        HRR_SUPPORTED_VERSIONS.name);
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            byte[] extData = new byte[2];
+            extData[0] = shc.negotiatedProtocol.major;
+            extData[1] = shc.negotiatedProtocol.minor;
+
+            // Update the context.
+            shc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS,
+                    new SHSupportedVersionsSpec(shc.negotiatedProtocol));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "supported_versions" extension in
+     * HelloRetryRequest.
+     */
+    private static final
+            class HRRSupportedVersionsConsumer implements ExtensionConsumer {
+
+        // Prevent instantiation of this class.
+        private HRRSupportedVersionsConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+            // The consuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!chc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable extension: " +
+                        HRR_SUPPORTED_VERSIONS.name);
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            SHSupportedVersionsSpec spec;
+            try {
+                spec = new SHSupportedVersionsSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, spec);
+
+            // No impact on session resumption.
+            //
+            // Note that the protocol version negotiation happens before the
+            // session resumption negotiation.  And the session resumption
+            // negotiation depends on the negotiated protocol version.
+        }
+    }
+
+    /**
+     * Network data producer of a "supported_versions" extension for stateless
+     * HelloRetryRequest reconstruction.
+     */
+    private static final
+            class HRRSupportedVersionsReproducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private HRRSupportedVersionsReproducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            if (!shc.sslConfig.isAvailable(HRR_SUPPORTED_VERSIONS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "[Reproduce] Ignore unavailable extension: " +
+                        HRR_SUPPORTED_VERSIONS.name);
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            byte[] extData = new byte[2];
+            extData[0] = shc.negotiatedProtocol.major;
+            extData[1] = shc.negotiatedProtocol.minor;
+
+            return extData;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.Closeable;
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSocket;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+
+/**
+ * SSL/(D)TLS transportation context.
+ */
+class TransportContext implements ConnectionContext, Closeable {
+    final SSLTransport              transport;
+
+    // registered plaintext consumers
+    final Map<Byte, SSLConsumer>    consumers;
+    final AccessControlContext      acc;
+
+    final SSLContextImpl            sslContext;
+    final SSLConfiguration          sslConfig;
+    final InputRecord               inputRecord;
+    final OutputRecord              outputRecord;
+
+    // connection status
+    boolean                         isUnsureMode;
+    boolean                         isNegotiated = false;
+    boolean                         isBroken = false;
+    boolean                         isInputCloseNotified = false;
+    boolean                         isOutputCloseNotified = false;
+    Exception                       closeReason = null;
+
+    // negotiated security parameters
+    SSLSessionImpl                  conSession;
+    ProtocolVersion                 protocolVersion;
+    String                          applicationProtocol= null;
+
+    // handshake context
+    HandshakeContext                handshakeContext = null;
+
+    // connection reserved status for handshake.
+    boolean                         secureRenegotiation = false;
+    byte[]                          clientVerifyData;
+    byte[]                          serverVerifyData;
+
+    // connection sensitive configuration
+    List<NamedGroup>                serverRequestedNamedGroups;
+
+    CipherSuite cipherSuite;
+    private static final byte[] emptyByteArray = new byte[0];
+
+    // Please never use the transport parameter other than storing a
+    // reference to this object.
+    //
+    // Called by SSLEngineImpl
+    TransportContext(SSLContextImpl sslContext, SSLTransport transport,
+            InputRecord inputRecord, OutputRecord outputRecord) {
+        this(sslContext, transport, new SSLConfiguration(sslContext, true),
+                inputRecord, outputRecord, true);
+    }
+
+    // Please never use the transport parameter other than storing a
+    // reference to this object.
+    //
+    // Called by SSLSocketImpl
+    TransportContext(SSLContextImpl sslContext, SSLTransport transport,
+            InputRecord inputRecord, OutputRecord outputRecord,
+            boolean isClientMode) {
+        this(sslContext, transport,
+                new SSLConfiguration(sslContext, isClientMode),
+                inputRecord, outputRecord, false);
+    }
+
+    // Please never use the transport parameter other than storing a
+    // reference to this object.
+    //
+    // Called by SSLSocketImpl with an existing SSLConfig
+    TransportContext(SSLContextImpl sslContext, SSLTransport transport,
+            SSLConfiguration sslConfig,
+            InputRecord inputRecord, OutputRecord outputRecord) {
+        this(sslContext, transport, (SSLConfiguration)sslConfig.clone(),
+                inputRecord, outputRecord, false);
+    }
+
+    private TransportContext(SSLContextImpl sslContext, SSLTransport transport,
+            SSLConfiguration sslConfig, InputRecord inputRecord,
+            OutputRecord outputRecord, boolean isUnsureMode) {
+        this.transport = transport;
+        this.sslContext = sslContext;
+        this.inputRecord = inputRecord;
+        this.outputRecord = outputRecord;
+        this.sslConfig = sslConfig;
+        if (this.sslConfig.maximumPacketSize == 0) {
+            this.sslConfig.maximumPacketSize = outputRecord.getMaxPacketSize();
+        }
+        this.isUnsureMode = isUnsureMode;
+
+        // initial security parameters
+        this.conSession = SSLSessionImpl.nullSession;
+        this.protocolVersion = this.sslConfig.maximumProtocolVersion;
+        this.clientVerifyData = emptyByteArray;
+        this.serverVerifyData = emptyByteArray;
+
+        this.acc = AccessController.getContext();
+        this.consumers = new HashMap<>();
+    }
+
+    // Dispatch plaintext to a specific consumer.
+    void dispatch(Plaintext plaintext) throws IOException {
+        if (plaintext == null) {
+            return;
+        }
+
+        ContentType ct = ContentType.valueOf(plaintext.contentType);
+        if (ct == null) {
+            fatal(Alert.UNEXPECTED_MESSAGE,
+                "Unknown content type: " + plaintext.contentType);
+            return;
+        }
+
+        switch (ct) {
+            case HANDSHAKE:
+                byte type = HandshakeContext.getHandshakeType(this,
+                        plaintext);
+                if (handshakeContext == null) {
+                    if (type == SSLHandshake.KEY_UPDATE.id ||
+                            type == SSLHandshake.NEW_SESSION_TICKET.id) {
+                        if (isNegotiated &&
+                                protocolVersion.useTLS13PlusSpec()) {
+                            handshakeContext = new PostHandshakeContext(this);
+                        } else {
+                            fatal(Alert.UNEXPECTED_MESSAGE,
+                                    "Unexpected post-handshake message: " +
+                                    SSLHandshake.nameOf(type));
+                        }
+                    } else {
+                        handshakeContext = sslConfig.isClientMode ?
+                                new ClientHandshakeContext(sslContext, this) :
+                                new ServerHandshakeContext(sslContext, this);
+                        outputRecord.initHandshaker();
+                    }
+                }
+                handshakeContext.dispatch(type, plaintext);
+                break;
+            case ALERT:
+                Alert.alertConsumer.consume(this, plaintext.fragment);
+                break;
+            default:
+                SSLConsumer consumer = consumers.get(plaintext.contentType);
+                if (consumer != null) {
+                    consumer.consume(this, plaintext.fragment);
+                } else {
+                    fatal(Alert.UNEXPECTED_MESSAGE,
+                        "Unexpected content: " + plaintext.contentType);
+                }
+        }
+    }
+
+    void kickstart() throws IOException {
+        if (isUnsureMode) {
+            throw new IllegalStateException("Client/Server mode not yet set.");
+        }
+
+        if (outputRecord.isClosed() || inputRecord.isClosed() || isBroken) {
+            if (closeReason != null) {
+                throw new SSLException(
+                        "Cannot kickstart, the connection is broken or closed",
+                        closeReason);
+            } else {
+                throw new SSLException(
+                        "Cannot kickstart, the connection is broken or closed");
+            }
+        }
+
+        // initialize the handshaker if necessary
+        if (handshakeContext == null) {
+            //  TLS1.3 post-handshake
+            if (isNegotiated && protocolVersion.useTLS13PlusSpec()) {
+                handshakeContext = new PostHandshakeContext(this);
+            } else {
+                handshakeContext = sslConfig.isClientMode ?
+                        new ClientHandshakeContext(sslContext, this) :
+                        new ServerHandshakeContext(sslContext, this);
+                outputRecord.initHandshaker();
+            }
+        }
+
+        // kickstart the handshake if needed
+        //
+        // Need no kickstart message on server side unless the connection
+        // has been established.
+        if(isNegotiated || sslConfig.isClientMode) {
+           handshakeContext.kickstart();
+        }
+    }
+
+    void keyUpdate() throws IOException {
+        kickstart();
+    }
+
+    boolean isPostHandshakeContext() {
+        return handshakeContext != null &&
+                (handshakeContext instanceof PostHandshakeContext);
+    }
+
+    // Note: close_notify is delivered as a warning alert.
+    void warning(Alert alert) {
+        // For initial handshaking, don't send a warning alert message to peer
+        // if handshaker has not started.
+        if (isNegotiated || handshakeContext != null) {
+            try {
+                outputRecord.encodeAlert(Alert.Level.WARNING.level, alert.id);
+            } catch (IOException ioe) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.warning(
+                        "Warning: failed to send warning alert " + alert, ioe);
+                }
+            }
+        }
+    }
+
+    void fatal(Alert alert,
+            String diagnostic) throws SSLException {
+        fatal(alert, diagnostic, null);
+    }
+
+    void fatal(Alert alert, Throwable cause) throws SSLException {
+        fatal(alert, null, cause);
+    }
+
+    void fatal(Alert alert,
+            String diagnostic, Throwable cause) throws SSLException {
+        fatal(alert, diagnostic, false, cause);
+    }
+
+    // Note: close_notify is not delivered via fatal() methods.
+    void fatal(Alert alert, String diagnostic,
+            boolean recvFatalAlert, Throwable cause) throws SSLException {
+        // If we've already shutdown because of an error, there is nothing we
+        // can do except rethrow the exception.
+        //
+        // Most exceptions seen here will be SSLExceptions. We may find the
+        // occasional Exception which hasn't been converted to a SSLException,
+        // so we'll do it here.
+        if (closeReason != null) {
+            if (cause == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.warning(
+                            "Closed transport, general or untracked problem");
+                }
+                throw alert.createSSLException(
+                        "Closed transport, general or untracked problem");
+            }
+
+            if (cause instanceof SSLException) {
+                throw (SSLException)cause;
+            } else {    // unlikely, but just in case.
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.warning(
+                            "Closed transport, unexpected rethrowing", cause);
+                }
+                throw alert.createSSLException("Unexpected rethrowing", cause);
+            }
+        }
+
+        // If we have no further information, make a general-purpose
+        // message for folks to see.  We generally have one or the other.
+        if (diagnostic == null) {
+            if (cause == null) {
+                diagnostic = "General/Untracked problem";
+            } else {
+                diagnostic = cause.getMessage();
+            }
+        }
+
+        if (cause == null) {
+            cause = alert.createSSLException(diagnostic);
+        }
+
+        // shutdown the transport
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+            SSLLogger.severe("Fatal (" + alert + "): " + diagnostic, cause);
+        }
+
+        // remember the close reason
+        if (cause instanceof SSLException) {
+            closeReason = (SSLException)cause;
+        } else {
+            // Including RuntimeException, but we'll throw those down below.
+            closeReason = alert.createSSLException(diagnostic, cause);
+        }
+
+        // close inbound
+        try {
+            inputRecord.close();
+        } catch (IOException ioe) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.warning("Fatal: input record closure failed", ioe);
+            }
+        }
+
+        // invalidate the session
+        if (conSession != null) {
+            conSession.invalidate();
+        }
+
+        if (handshakeContext != null &&
+                handshakeContext.handshakeSession != null) {
+            handshakeContext.handshakeSession.invalidate();
+        }
+
+        // send fatal alert
+        //
+        // If we haven't even started handshaking yet, or we are the recipient
+        // of a fatal alert, no need to generate a fatal close alert.
+        if (!recvFatalAlert && !isOutboundDone() && !isBroken &&
+                (isNegotiated || handshakeContext != null)) {
+            try {
+                outputRecord.encodeAlert(Alert.Level.FATAL.level, alert.id);
+            } catch (IOException ioe) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.warning(
+                        "Fatal: failed to send fatal alert " + alert, ioe);
+                }
+            }
+        }
+
+        // close outbound
+        try {
+            outputRecord.close();
+        } catch (IOException ioe) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.warning("Fatal: output record closure failed", ioe);
+            }
+        }
+
+        // terminal handshake context
+        if (handshakeContext != null) {
+            handshakeContext = null;
+        }
+
+        // terminal the transport
+        try {
+            transport.shutdown();
+        } catch (IOException ioe) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.warning("Fatal: transport closure failed", ioe);
+            }
+        } finally {
+            isBroken = true;
+        }
+
+        if (closeReason instanceof SSLException) {
+            throw (SSLException)closeReason;
+        } else {
+            throw (RuntimeException)closeReason;
+        }
+    }
+
+    void setUseClientMode(boolean useClientMode) {
+        /*
+         * If we need to change the client mode and the enabled
+         * protocols and cipher suites haven't specifically been
+         * set by the user, change them to the corresponding
+         * default ones.
+         */
+        if (sslConfig.isClientMode != useClientMode) {
+            // Once handshaking has begun, the mode can not be reset for the
+            // life of this engine.
+            if (handshakeContext != null || isNegotiated) {
+                throw new IllegalArgumentException(
+                    "Cannot change mode after SSL traffic has started");
+            }
+
+            if (sslContext.isDefaultProtocolVesions(
+                    sslConfig.enabledProtocols)) {
+                sslConfig.enabledProtocols =
+                        sslContext.getDefaultProtocolVersions(!useClientMode);
+            }
+
+            if (sslContext.isDefaultCipherSuiteList(
+                    sslConfig.enabledCipherSuites)) {
+                sslConfig.enabledCipherSuites =
+                        sslContext.getDefaultCipherSuites(!useClientMode);
+            }
+
+            sslConfig.isClientMode = useClientMode;
+        }
+
+        isUnsureMode = false;
+    }
+
+    // The OutputRecord is closed and not buffered output record.
+    boolean isOutboundDone() {
+        return outputRecord.isClosed() && outputRecord.isEmpty();
+    }
+
+    // The OutputRecord is closed, but buffered output record may be still
+    // waiting for delivery to the underlying connection.
+    boolean isOutboundClosed() {
+        return outputRecord.isClosed();
+    }
+
+    boolean isInboundDone() {
+        return inputRecord.isClosed();
+    }
+
+    boolean isClosed() {
+        return isOutboundClosed() && isInboundDone();
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (!isOutboundDone()) {
+            closeOutbound();
+        }
+
+        if (!isInboundDone()) {
+            closeInbound();
+        }
+    }
+
+    void closeInbound() {
+        if (isInboundDone()) {
+            return;
+        }
+
+        try {
+            if (isInputCloseNotified) {     // passive close
+                passiveInboundClose();
+            } else {                        // initiative close
+                initiateInboundClose();
+            }
+        } catch (IOException ioe) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.warning("inbound closure failed", ioe);
+            }
+        }
+    }
+
+    void closeOutbound() {
+        if (isOutboundDone()) {
+            return;
+        }
+
+        try {
+             initiateOutboundClose();
+        } catch (IOException ioe) {
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                SSLLogger.warning("outbound closure failed", ioe);
+            }
+        }
+    }
+
+    // Close the connection passively.  The closure could be kickoff by
+    // receiving a close_notify alert or reaching end_of_file of the socket.
+    private void passiveInboundClose() throws IOException {
+        if (!isInboundDone()) {
+            inputRecord.close();
+        }
+
+        // For TLS 1.2 and prior version, it is required to respond with
+        // a close_notify alert of its own and close down the connection
+        // immediately, discarding any pending writes.
+        if (!isOutboundDone() && !isOutputCloseNotified) {
+            try {
+                // send a close_notify alert
+                warning(Alert.CLOSE_NOTIFY);
+            } finally {
+                // any data received after a closure alert is ignored.
+                isOutputCloseNotified = true;
+                outputRecord.close();
+            }
+        }
+
+        transport.shutdown();
+    }
+
+    // Initiate a close by sending a close_notify alert.
+    private void initiateInboundClose() throws IOException {
+        // TLS 1.3 does not define how to initiate and close a TLS connection
+        // gracefully.  We will always send a close_notify alert, and close
+        // the underlying transportation layer if needed.
+        if (!isInboundDone() && !isInputCloseNotified) {
+            try {
+                // send a close_notify alert
+                warning(Alert.CLOSE_NOTIFY);
+            } finally {
+                // any data received after a closure alert is ignored.
+                isInputCloseNotified = true;
+                inputRecord.close();
+            }
+        }
+
+        // For TLS 1.3, input closure is independent from output closure. Both
+        // parties need not wait to receive a "close_notify" alert before
+        // closing their read side of the connection.
+        //
+        // For TLS 1.2 and prior version, it is not required for the initiator
+        // of the close to wait for the responding close_notify alert before
+        // closing the read side of the connection.
+        try {
+            transport.shutdown();
+        } finally {
+            if (!isOutboundDone()) {
+                outputRecord.close();
+            }
+        }
+    }
+
+    // Initiate a close by sending a close_notify alert.
+    private void initiateOutboundClose() throws IOException {
+        if (!isOutboundDone() && !isOutputCloseNotified) {
+            try {     // close outputRecord
+                // send a close_notify alert
+                warning(Alert.CLOSE_NOTIFY);
+            } finally {
+                // any data received after a closure alert is ignored.
+                isOutputCloseNotified = true;
+                outputRecord.close();
+            }
+        }
+
+        // It is not required for the initiator of the close to wait for the
+        // responding close_notify alert before closing the read side of the
+        // connection.  However, if the application protocol using TLS
+        // provides that any data may be carried over the underlying transport
+        // after the TLS connection is closed, the TLS implementation MUST
+        // receive a "close_notify" alert before indicating end-of-data to the
+        // application-layer.
+        try {
+            transport.shutdown();
+        } finally {
+            if (!isInboundDone()) {
+                inputRecord.close();
+            }
+        }
+    }
+
+    // Note; HandshakeStatus.FINISHED status is retrieved in other places.
+    HandshakeStatus getHandshakeStatus() {
+        if (!outputRecord.isEmpty()) {
+            // If no handshaking, special case to wrap alters or
+            // post-handshake messages.
+            return HandshakeStatus.NEED_WRAP;
+        } else if (handshakeContext != null) {
+            if (!handshakeContext.delegatedActions.isEmpty()) {
+                return HandshakeStatus.NEED_TASK;
+            } else if (sslContext.isDTLS() &&
+                    !inputRecord.isEmpty()) {
+                return HandshakeStatus.NEED_UNWRAP_AGAIN;
+            } else {
+                return HandshakeStatus.NEED_UNWRAP;
+            }
+        } else if (isOutboundDone() && !isInboundDone()) {
+            /*
+             * Special case where we're closing, but
+             * still need the close_notify before we
+             * can officially be closed.
+             *
+             * Note isOutboundDone is taken care of by
+             * hasOutboundData() above.
+             */
+            return HandshakeStatus.NEED_UNWRAP;
+        }
+
+        return HandshakeStatus.NOT_HANDSHAKING;
+    }
+
+    HandshakeStatus finishHandshake() {
+        if (protocolVersion.useTLS13PlusSpec()) {
+            outputRecord.tc = this;
+            inputRecord.tc = this;
+            cipherSuite = handshakeContext.negotiatedCipherSuite;
+            inputRecord.readCipher.baseSecret = handshakeContext.baseReadSecret;
+            outputRecord.writeCipher.baseSecret = handshakeContext.baseWriteSecret;
+        }
+
+        handshakeContext = null;
+        outputRecord.handshakeHash.finish();
+        inputRecord.finishHandshake();
+        outputRecord.finishHandshake();
+        isNegotiated = true;
+
+        // Tell folk about handshake completion, but do it in a separate thread.
+        if (transport instanceof SSLSocket &&
+                sslConfig.handshakeListeners != null &&
+                !sslConfig.handshakeListeners.isEmpty()) {
+            HandshakeCompletedEvent hce =
+                new HandshakeCompletedEvent((SSLSocket)transport, conSession);
+            Thread thread = new Thread(
+                null,
+                new NotifyHandshake(sslConfig.handshakeListeners, hce),
+                "HandshakeCompletedNotify-Thread",
+                0,
+                false);
+            thread.start();
+        }
+
+        return HandshakeStatus.FINISHED;
+    }
+
+    HandshakeStatus finishPostHandshake() {
+        handshakeContext = null;
+
+        // Note: May need trigger handshake completion even for post-handshake
+        // authentication in the future.
+
+        return HandshakeStatus.FINISHED;
+    }
+
+    // A separate thread is allocated to deliver handshake completion
+    // events.
+    private static class NotifyHandshake implements Runnable {
+        private final Set<Map.Entry<HandshakeCompletedListener,
+                AccessControlContext>> targets;         // who gets notified
+        private final HandshakeCompletedEvent event;    // the notification
+
+        NotifyHandshake(
+                Map<HandshakeCompletedListener,AccessControlContext> listeners,
+                HandshakeCompletedEvent event) {
+            this.targets = new HashSet<>(listeners.entrySet());     // clone
+            this.event = event;
+        }
+
+        @Override
+        public void run() {
+            // Don't need to synchronize, as it only runs in one thread.
+            for (Map.Entry<HandshakeCompletedListener,
+                    AccessControlContext> entry : targets) {
+                final HandshakeCompletedListener listener = entry.getKey();
+                AccessControlContext acc = entry.getValue();
+                AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                    @Override
+                    public Void run() {
+                        listener.handshakeCompleted(event);
+                        return null;
+                    }
+                }, acc);
+            }
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,18 +25,16 @@
 
 package sun.security.ssl;
 
-import java.util.*;
 import java.io.*;
 import java.security.*;
 import java.security.cert.*;
+import java.util.*;
 import javax.net.ssl.*;
-
+import sun.security.validator.TrustStoreUtil;
 import sun.security.validator.Validator;
-import sun.security.validator.TrustStoreUtil;
 
 abstract class TrustManagerFactoryImpl extends TrustManagerFactorySpi {
 
-    private static final Debug debug = Debug.getInstance("ssl");
     private X509TrustManager trustManager = null;
     private boolean isInitialized = false;
 
@@ -51,26 +49,26 @@
                 trustManager = getInstance(TrustStoreManager.getTrustedCerts());
             } catch (SecurityException se) {
                 // eat security exceptions but report other throwables
-                if (debug != null && Debug.isOn("trustmanager")) {
-                    System.out.println(
-                        "SunX509: skip default keystore: " + se);
+                if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                    SSLLogger.fine(
+                            "SunX509: skip default keystore", se);
                 }
             } catch (Error err) {
-                if (debug != null && Debug.isOn("trustmanager")) {
-                    System.out.println(
-                        "SunX509: skip default keystore: " + err);
+                if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                    SSLLogger.fine(
+                        "SunX509: skip default keystore", err);
                 }
                 throw err;
             } catch (RuntimeException re) {
-                if (debug != null && Debug.isOn("trustmanager")) {
-                    System.out.println(
-                        "SunX509: skip default keystore: " + re);
+                if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                    SSLLogger.fine(
+                        "SunX509: skip default keystor", re);
                 }
                 throw re;
             } catch (Exception e) {
-                if (debug != null && Debug.isOn("trustmanager")) {
-                    System.out.println(
-                        "SunX509: skip default keystore: " + e);
+                if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                    SSLLogger.fine(
+                        "SunX509: skip default keystore", e);
                 }
                 throw new KeyStoreException(
                     "problem accessing trust store", e);
--- a/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,14 +25,11 @@
 
 package sun.security.ssl;
 
+import java.io.*;
 import java.lang.ref.WeakReference;
-import java.io.*;
-import java.util.*;
-
 import java.security.*;
 import java.security.cert.*;
-import java.security.cert.Certificate;
-
+import java.util.*;
 import sun.security.action.*;
 import sun.security.validator.TrustStoreUtil;
 
@@ -41,7 +38,6 @@
  * effectively.
  */
 final class TrustStoreManager {
-    private static final Debug debug = Debug.getInstance("ssl");
 
     // A singleton service to manage the default trusted KeyStores effectively.
     private static final TrustAnchorManager tam = new TrustAnchorManager();
@@ -112,8 +108,8 @@
             this.storeFile = storeFile;
             this.lastModified = lastModified;
 
-            if (debug != null && Debug.isOn("trustmanager")) {
-                System.out.println(
+            if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                SSLLogger.fine(
                     "trustStore is: " + storeName + "\n" +
                     "trustStore type is: " + storeType + "\n" +
                     "trustStore provider is: " + storeProvider + "\n" +
@@ -125,8 +121,10 @@
          * Create an instance of TrustStoreDescriptor for the default
          * trusted KeyStore.
          */
+        @SuppressWarnings("Convert2Lambda")
         static TrustStoreDescriptor createInstance() {
-             return AccessController.doPrivileged(new PrivilegedAction<>() {
+             return AccessController.doPrivileged(
+                    new PrivilegedAction<TrustStoreDescriptor>() {
 
                 @Override
                 public TrustStoreDescriptor run() {
@@ -158,11 +156,11 @@
                             }
 
                             // Not break, the file is inaccessible.
-                            if (debug != null &&
-                                    Debug.isOn("trustmanager")) {
-                                System.out.println(
-                                    "Inaccessible trust store: " +
-                                    storePropName);
+                            if (SSLLogger.isOn &&
+                                    SSLLogger.isOn("trustmanager")) {
+                                SSLLogger.fine(
+                                        "Inaccessible trust store: " +
+                                        storePropName);
                             }
                         }
                     } else {
@@ -267,8 +265,8 @@
             }
 
             // Reload a new key store.
-            if ((debug != null) && Debug.isOn("trustmanager")) {
-                System.out.println("Reload the trust store");
+            if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                SSLLogger.fine("Reload the trust store");
             }
 
             ks = loadKeyStore(descriptor);
@@ -309,20 +307,20 @@
 
             // Reload the trust store if needed.
             if (ks == null) {
-                if ((debug != null) && Debug.isOn("trustmanager")) {
-                    System.out.println("Reload the trust store");
+                if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                    SSLLogger.fine("Reload the trust store");
                 }
                 ks = loadKeyStore(descriptor);
             }
 
             // Reload trust certs from the key store.
-            if ((debug != null) && Debug.isOn("trustmanager")) {
-                System.out.println("Reload trust certs");
+            if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                SSLLogger.fine("Reload trust certs");
             }
 
             certs = loadTrustedCerts(ks);
-            if ((debug != null) && Debug.isOn("trustmanager")) {
-                System.out.println("Reloaded " + certs.size() + " trust certs");
+            if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                SSLLogger.fine("Reloaded " + certs.size() + " trust certs");
             }
 
             // Note that as ks is a local variable, it is not
@@ -333,7 +331,7 @@
         }
 
         /**
-         * Load the KeyStore as described in the specified descriptor.
+         * Load the the KeyStore as described in the specified descriptor.
          */
         private static KeyStore loadKeyStore(
                 TrustStoreDescriptor descriptor) throws Exception {
@@ -341,8 +339,8 @@
                     descriptor.storeFile == null) {
 
                 // No file available, no KeyStore available.
-                if (debug != null && Debug.isOn("trustmanager")) {
-                    System.out.println("No available key store");
+                if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                    SSLLogger.fine("No available key store");
                 }
 
                 return null;
@@ -367,8 +365,8 @@
                     ks.load(fis, password);
                 } catch (FileNotFoundException fnfe) {
                     // No file available, no KeyStore available.
-                    if (debug != null && Debug.isOn("trustmanager")) {
-                        System.out.println(
+                    if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) {
+                        SSLLogger.fine(
                             "Not available key store: " + descriptor.storeName);
                     }
 
--- a/src/java.base/share/classes/sun/security/ssl/UnknownExtension.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2006, 2012, 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.IOException;
-
-final class UnknownExtension extends HelloExtension {
-
-    private final byte[] data;
-
-    UnknownExtension(HandshakeInStream s, int len, ExtensionType type)
-            throws IOException {
-        super(type);
-        data = new byte[len];
-        // s.read() does not handle 0-length arrays.
-        if (len != 0) {
-            s.read(data);
-        }
-    }
-
-    @Override
-    int length() {
-        return 4 + data.length;
-    }
-
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putBytes16(data);
-    }
-
-    @Override
-    public String toString() {
-        return "Unsupported extension " + type + ", data: " +
-            Debug.toString(data);
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/UnknownStatusRequest.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2015, 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.IOException;
-
-final class UnknownStatusRequest implements StatusRequest  {
-
-    private final byte[] data;
-
-    UnknownStatusRequest(HandshakeInStream s, int len) throws IOException {
-        data = new byte[len];
-        if (len > 0) {
-            s.read(data);
-        }
-    }
-
-    UnknownStatusRequest(byte[] requestBytes) {
-        data = requestBytes;
-    }
-
-    @Override
-    public int length() {
-        return data.length;
-    }
-
-    @Override
-    public void send(HandshakeOutStream s) throws IOException {
-        // A raw write of the data
-        s.write(data);
-    }
-
-    @Override
-    public String toString() {
-        return "Unsupported StatusRequest, data: " +
-            Debug.toString(data);
-    }
-}
--- a/src/java.base/share/classes/sun/security/ssl/Utilities.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/Utilities.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,18 +25,21 @@
 
 package sun.security.ssl;
 
-import javax.net.ssl.*;
+import java.math.BigInteger;
 import java.util.*;
+import java.util.regex.Pattern;
+import javax.net.ssl.*;
 import sun.net.util.IPAddressUtil;
+import sun.security.action.GetPropertyAction;
 
 /**
  * A utility class to share the static methods.
  */
 final class Utilities {
-    /**
-     * hex digits
-     */
     static final char[] hexDigits = "0123456789ABCDEF".toCharArray();
+    private static final String indent = "  ";
+    private static final Pattern lineBreakPatern =
+                Pattern.compile("\\r\\n|\\n|\\r");
 
     /**
      * Puts {@code hostname} into the {@code serverNames} list.
@@ -66,9 +69,9 @@
             SNIServerName serverName = sniList.get(i);
             if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
                 sniList.set(i, sniHostName);
-                if (Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                        ", the previous server name in SNI (" + serverName +
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                     SSLLogger.fine(
+                        "the previous server name in SNI (" + serverName +
                         ") was replaced with (" + sniHostName + ")");
                 }
                 reset = true;
@@ -107,9 +110,8 @@
                 sniHostName = new SNIHostName(hostname);
             } catch (IllegalArgumentException iae) {
                 // don't bother to handle illegal host_name
-                if (Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                        ", \"" + hostname + "\" " +
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                     SSLLogger.fine(hostname + "\" " +
                         "is not a legal HostName for  server name indication");
                 }
             }
@@ -117,4 +119,117 @@
 
         return sniHostName;
     }
+
+    /**
+     * Return the value of the boolean System property propName.
+     *
+     * Note use of privileged action. Do NOT make accessible to applications.
+     */
+    static boolean getBooleanProperty(String propName, boolean defaultValue) {
+        // if set, require value of either true or false
+        String b = GetPropertyAction.privilegedGetProperty(propName);
+        if (b == null) {
+            return defaultValue;
+        } else if (b.equalsIgnoreCase("false")) {
+            return false;
+        } else if (b.equalsIgnoreCase("true")) {
+            return true;
+        } else {
+            throw new RuntimeException("Value of " + propName
+                + " must either be 'true' or 'false'");
+        }
+    }
+
+    static String indent(String source) {
+        return Utilities.indent(source, indent);
+    }
+
+    static String indent(String source, String prefix) {
+        StringBuilder builder = new StringBuilder();
+        if (source == null) {
+             builder.append("\n" + prefix + "<blank message>");
+        } else {
+            String[] lines = lineBreakPatern.split(source);
+            boolean isFirst = true;
+            for (String line : lines) {
+                if (isFirst) {
+                    isFirst = false;
+                } else {
+                    builder.append("\n");
+                }
+                builder.append(prefix).append(line);
+            }
+        }
+
+        return builder.toString();
+    }
+
+    static String toHexString(byte b) {
+        return String.valueOf(hexDigits[(b >> 4) & 0x0F]) +
+                String.valueOf(hexDigits[b & 0x0F]);
+    }
+
+    static String byte16HexString(int id) {
+        return "0x" +
+                hexDigits[(id >> 12) & 0x0F] + hexDigits[(id >> 8) & 0x0F] +
+                hexDigits[(id >> 4) & 0x0F] + hexDigits[id & 0x0F];
+    }
+
+    static String toHexString(byte[] bytes) {
+        if (bytes == null || bytes.length == 0) {
+            return "";
+        }
+
+        StringBuilder builder = new StringBuilder(bytes.length * 3);
+        boolean isFirst = true;
+        for (byte b : bytes) {
+            if (isFirst) {
+                isFirst = false;
+            } else {
+                builder.append(' ');
+            }
+
+            builder.append(hexDigits[(b >> 4) & 0x0F]);
+            builder.append(hexDigits[b & 0x0F]);
+        }
+        return builder.toString();
+    }
+
+    static String toHexString(long lv) {
+        StringBuilder builder = new StringBuilder(128);
+
+        boolean isFirst = true;
+        do {
+            if (isFirst) {
+                isFirst = false;
+            } else {
+                builder.append(' ');
+            }
+
+            builder.append(hexDigits[(int)(lv & 0x0F)]);
+            lv >>>= 4;
+            builder.append(hexDigits[(int)(lv & 0x0F)]);
+            lv >>>= 4;
+        } while (lv != 0);
+        builder.reverse();
+
+        return builder.toString();
+    }
+
+    /**
+     * Utility method to convert a BigInteger to a byte array in unsigned
+     * format as needed in the handshake messages. BigInteger uses
+     * 2's complement format, i.e. it prepends an extra zero if the MSB
+     * is set. We remove that.
+     */
+    static byte[] toByteArray(BigInteger bi) {
+        byte[] b = bi.toByteArray();
+        if ((b.length > 1) && (b[0] == 0)) {
+            int n = b.length - 1;
+            byte[] newarray = new byte[n];
+            System.arraycopy(b, 1, newarray, 0, n);
+            b = newarray;
+        }
+        return b;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * 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.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Map;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedKeyManager;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
+import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
+
+enum X509Authentication implements SSLAuthentication {
+    // Require rsaEncryption public key
+    RSA         ("RSA",         new X509PossessionGenerator(
+                                    new String[]{"RSA"})),
+
+    // Require RSASSA-PSS public key
+    RSASSA_PSS  ("RSASSA-PSS",  new X509PossessionGenerator(
+                                    new String[] {"RSASSA-PSS"})),
+
+    // Require rsaEncryption or RSASSA-PSS public key
+    //
+    // Note that this is a specifical scheme for TLS 1.2. (EC)DHE_RSA cipher
+    // suites of TLS 1.2 can use either rsaEncryption or RSASSA-PSS public
+    // key for authentication and handshake.
+    RSA_OR_PSS  ("RSA_OR_PSS",  new X509PossessionGenerator(
+                                    new String[] {"RSA", "RSASSA-PSS"})),
+
+    // Require DSA public key
+    DSA         ("DSA",         new X509PossessionGenerator(
+                                    new String[] {"DSA"})),
+
+    // Require EC public key
+    EC          ("EC",          new X509PossessionGenerator(
+                                    new String[] {"EC"}));
+
+    final String keyType;
+    final SSLPossessionGenerator possessionGenerator;
+
+    X509Authentication(String keyType,
+            SSLPossessionGenerator possessionGenerator) {
+        this.keyType = keyType;
+        this.possessionGenerator = possessionGenerator;
+    }
+
+    static X509Authentication valueOf(SignatureScheme signatureScheme) {
+        for (X509Authentication au: X509Authentication.values()) {
+            if (au.keyType.equals(signatureScheme.keyAlgorithm)) {
+                return au;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public SSLPossession createPossession(HandshakeContext handshakeContext) {
+        return possessionGenerator.createPossession(handshakeContext);
+    }
+
+    @Override
+    public SSLHandshake[] getRelatedHandshakers(
+            HandshakeContext handshakeContext) {
+        if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
+            return new SSLHandshake[] {
+                    SSLHandshake.CERTIFICATE,
+                    SSLHandshake.CERTIFICATE_REQUEST
+                };
+        }   // Otherwise, TLS 1.3 does not use this method.
+
+        return new SSLHandshake[0];
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    @Override
+    public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
+            HandshakeContext handshakeContext) {
+        if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
+            return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[] {
+                    new SimpleImmutableEntry<Byte, HandshakeProducer>(
+                        SSLHandshake.CERTIFICATE.id,
+                        SSLHandshake.CERTIFICATE
+                    )
+                });
+        }   // Otherwise, TLS 1.3 does not use this method.
+
+        return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
+    }
+
+    static final class X509Possession implements SSLPossession {
+        // Proof of possession of the private key corresponding to the public
+        // key for which a certificate is being provided for authentication.
+        final X509Certificate[]   popCerts;
+        final PrivateKey          popPrivateKey;
+
+        X509Possession(PrivateKey popPrivateKey,
+                X509Certificate[] popCerts) {
+            this.popCerts = popCerts;
+            this.popPrivateKey = popPrivateKey;
+        }
+    }
+
+    static final class X509Credentials implements SSLCredentials {
+        final X509Certificate[]   popCerts;
+        final PublicKey           popPublicKey;
+
+        X509Credentials(PublicKey popPublicKey, X509Certificate[] popCerts) {
+            this.popCerts = popCerts;
+            this.popPublicKey = popPublicKey;
+        }
+    }
+
+    private static final
+            class X509PossessionGenerator implements SSLPossessionGenerator {
+        private final String[] keyTypes;
+
+        private X509PossessionGenerator(String[] keyTypes) {
+            this.keyTypes = keyTypes;
+        }
+
+        @Override
+        public SSLPossession createPossession(HandshakeContext context) {
+            if (context.sslConfig.isClientMode) {
+                for (String keyType : keyTypes) {
+                    SSLPossession poss = createClientPossession(
+                            (ClientHandshakeContext)context, keyType);
+                    if (poss != null) {
+                        return poss;
+                    }
+                }
+            } else {
+                for (String keyType : keyTypes) {
+                    SSLPossession poss = createServerPossession(
+                            (ServerHandshakeContext)context, keyType);
+                    if (poss != null) {
+                        return poss;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        // Used by TLS 1.3 only.
+        private SSLPossession createClientPossession(
+                ClientHandshakeContext chc, String keyType) {
+            X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
+            String clientAlias = null;
+            if (chc.conContext.transport instanceof SSLSocketImpl) {
+                clientAlias = km.chooseClientAlias(
+                        new String[] { keyType },
+                        null, (SSLSocket)chc.conContext.transport);
+            } else if (chc.conContext.transport instanceof SSLEngineImpl) {
+                clientAlias = km.chooseEngineClientAlias(
+                        new String[] { keyType },
+                        null, (SSLEngine)chc.conContext.transport);
+            }
+
+            if (clientAlias == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest("No X.509 cert selected for " + keyType);
+                }
+                return null;
+            }
+
+            PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias);
+            if (clientPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest(
+                            clientAlias + " is not a private key entry");
+                }
+                return null;
+            }
+
+            X509Certificate[] clientCerts = km.getCertificateChain(clientAlias);
+            if ((clientCerts == null) || (clientCerts.length == 0)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest(clientAlias +
+                        " is a private key entry with no cert chain stored");
+                }
+                return null;
+            }
+
+            PublicKey clientPublicKey = clientCerts[0].getPublicKey();
+            if ((!clientPrivateKey.getAlgorithm().equals(keyType))
+                    || (!clientPublicKey.getAlgorithm().equals(keyType))) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine(
+                            clientAlias + " private or public key is not of " +
+                            keyType + " algorithm");
+                }
+                return null;
+            }
+
+            return new X509Possession(clientPrivateKey, clientCerts);
+        }
+
+        private SSLPossession createServerPossession(
+                ServerHandshakeContext shc, String keyType) {
+            X509ExtendedKeyManager km = shc.sslContext.getX509KeyManager();
+            String serverAlias = null;
+            if (shc.conContext.transport instanceof SSLSocketImpl) {
+                serverAlias = km.chooseServerAlias(keyType,
+                        null, (SSLSocket)shc.conContext.transport);
+            } else if (shc.conContext.transport instanceof SSLEngineImpl) {
+                serverAlias = km.chooseEngineServerAlias(keyType,
+                        null, (SSLEngine)shc.conContext.transport);
+            }
+
+            if (serverAlias == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest("No X.509 cert selected for " + keyType);
+                }
+                return null;
+            }
+
+            PrivateKey serverPrivateKey = km.getPrivateKey(serverAlias);
+            if (serverPrivateKey == null) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest(
+                            serverAlias + " is not a private key entry");
+                }
+                return null;
+            }
+
+            X509Certificate[] serverCerts = km.getCertificateChain(serverAlias);
+            if ((serverCerts == null) || (serverCerts.length == 0)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.finest(
+                            serverAlias + " is not a certificate entry");
+                }
+                return null;
+            }
+
+            PublicKey serverPublicKey = serverCerts[0].getPublicKey();
+            if ((!serverPrivateKey.getAlgorithm().equals(keyType))
+                    || (!serverPublicKey.getAlgorithm().equals(keyType))) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                    SSLLogger.fine(
+                            serverAlias + " private or public key is not of " +
+                            keyType + " algorithm");
+                }
+                return null;
+            }
+
+            // For ECC certs, check whether we support the EC domain
+            // parameters.  If the client sent a SupportedEllipticCurves
+            // ClientHello extension, check against that too.
+            if (keyType.equals("EC")) {
+                if (!(serverPublicKey instanceof ECPublicKey)) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                        SSLLogger.warning(serverAlias +
+                            " public key is not an instance of ECPublicKey");
+                    }
+                    return null;
+                }
+
+                // For ECC certs, check whether we support the EC domain
+                // parameters. If the client sent a SupportedEllipticCurves
+                // ClientHello extension, check against that too.
+                ECParameterSpec params =
+                        ((ECPublicKey)serverPublicKey).getParams();
+                NamedGroup namedGroup = NamedGroup.valueOf(params);
+                if ((namedGroup == null) ||
+                    (!SupportedGroups.isSupported(namedGroup)) ||
+                    ((shc.clientRequestedNamedGroups != null) &&
+                    !shc.clientRequestedNamedGroups.contains(namedGroup))) {
+
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+                        SSLLogger.warning(
+                            "Unsupported named group (" + namedGroup +
+                            ") used in the " + serverAlias + " certificate");
+                    }
+
+                    return null;
+                }
+            }
+
+            return new X509Possession(serverPrivateKey, serverCerts);
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,18 +26,21 @@
 package sun.security.ssl;
 
 import java.lang.ref.*;
+import java.net.Socket;
+import java.security.AlgorithmConstraints;
+import java.security.KeyStore;
+import java.security.KeyStore.Builder;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.util.*;
-import static java.util.Locale.ENGLISH;
 import java.util.concurrent.atomic.AtomicLong;
-import java.net.Socket;
-
-import java.security.*;
-import java.security.KeyStore.*;
-import java.security.cert.*;
-import java.security.cert.Certificate;
-
 import javax.net.ssl.*;
-
 import sun.security.provider.certpath.AlgorithmChecker;
 import sun.security.validator.Validator;
 
@@ -61,11 +64,6 @@
 final class X509KeyManagerImpl extends X509ExtendedKeyManager
         implements X509KeyManager {
 
-    private static final Debug debug = Debug.getInstance("ssl");
-
-    private static final boolean useDebug =
-                            (debug != null) && Debug.isOn("keymanager");
-
     // for unit testing only, set via privileged reflection
     private static Date verificationDate;
 
@@ -189,9 +187,7 @@
             SSLSession session = sslSocket.getHandshakeSession();
 
             if (session != null) {
-                ProtocolVersion protocolVersion =
-                    ProtocolVersion.valueOf(session.getProtocol());
-                if (protocolVersion.useTLS12PlusSpec()) {
+                if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
                     String[] peerSupportedSignAlgs = null;
 
                     if (session instanceof ExtendedSSLSession) {
@@ -217,9 +213,7 @@
         if (engine != null) {
             SSLSession session = engine.getHandshakeSession();
             if (session != null) {
-                ProtocolVersion protocolVersion =
-                    ProtocolVersion.valueOf(session.getProtocol());
-                if (protocolVersion.useTLS12PlusSpec()) {
+                if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
                     String[] peerSupportedSignAlgs = null;
 
                     if (session instanceof ExtendedSSLSession) {
@@ -297,6 +291,7 @@
         // In TLS 1.2, the signature algorithm  has been obsoleted by the
         // supported_signature_algorithms, and the certificate type no longer
         // restricts the algorithm used to sign the certificate.
+        //
         // However, because we don't support certificate type checking other
         // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the
         // protocol version here.
@@ -328,8 +323,10 @@
                 // Check the signature algorithm of the certificate itself.
                 // Look for the "withRSA" in "SHA1withRSA", etc.
                 X509Certificate issuer = (X509Certificate)chain[0];
-                String sigAlgName = issuer.getSigAlgName().toUpperCase(ENGLISH);
-                String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(ENGLISH);
+                String sigAlgName =
+                        issuer.getSigAlgName().toUpperCase(Locale.ENGLISH);
+                String pattern =
+                        "WITH" + sigKeyAlgorithm.toUpperCase(Locale.ENGLISH);
                 return sigAlgName.contains(pattern);
             }
         }
@@ -388,8 +385,8 @@
                     // if it's a perfect match, return immediately
                     EntryStatus status = results.get(0);
                     if (status.checkResult == CheckResult.OK) {
-                        if (useDebug) {
-                            debug.println("KeyMgr: choosing key: " + status);
+                        if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                            SSLLogger.fine("KeyMgr: choosing key: " + status);
                         }
                         return makeAlias(status);
                     }
@@ -403,16 +400,16 @@
             }
         }
         if (allResults == null) {
-            if (useDebug) {
-                debug.println("KeyMgr: no matching key found");
+            if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                SSLLogger.fine("KeyMgr: no matching key found");
             }
             return null;
         }
         Collections.sort(allResults);
-        if (useDebug) {
-            debug.println("KeyMgr: no good matching key found, "
-                        + "returning best match out of:");
-            debug.println(allResults.toString());
+        if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+            SSLLogger.fine(
+                    "KeyMgr: no good matching key found, "
+                    + "returning best match out of", allResults);
         }
         return makeAlias(allResults.get(0));
     }
@@ -439,7 +436,7 @@
                                     null, null);
                 if (results != null) {
                     if (allResults == null) {
-                        allResults = new ArrayList<EntryStatus>();
+                        allResults = new ArrayList<>();
                     }
                     allResults.addAll(results);
                 }
@@ -448,14 +445,14 @@
             }
         }
         if (allResults == null || allResults.isEmpty()) {
-            if (useDebug) {
-                debug.println("KeyMgr: no matching alias found");
+            if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                SSLLogger.fine("KeyMgr: no matching alias found");
             }
             return null;
         }
         Collections.sort(allResults);
-        if (useDebug) {
-            debug.println("KeyMgr: getting aliases: " + allResults);
+        if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+            SSLLogger.fine("KeyMgr: getting aliases", allResults);
         }
         return toAliases(allResults);
     }
@@ -544,9 +541,10 @@
             return (bit < keyUsage.length) && keyUsage[bit];
         }
 
-        // check if this certificate is appropriate for this type of use
-        // first check extensions, if they match, check expiration
-        // note: we may want to move this code into the sun.security.validator
+        // Check if this certificate is appropriate for this type of use
+        // first check extensions, if they match, check expiration.
+        //
+        // Note: we may want to move this code into the sun.security.validator
         // package
         CheckResult check(X509Certificate cert, Date date,
                 List<SNIServerName> serverNames, String idAlgorithm) {
@@ -570,20 +568,25 @@
                 boolean[] ku = cert.getKeyUsage();
                 if (ku != null) {
                     String algorithm = cert.getPublicKey().getAlgorithm();
-                    boolean kuSignature = getBit(ku, 0);
+                    boolean supportsDigitalSignature = getBit(ku, 0);
                     switch (algorithm) {
                         case "RSA":
                             // require either signature bit
                             // or if server also allow key encipherment bit
-                            if (kuSignature == false) {
-                                if ((this == CLIENT) || (getBit(ku, 2) == false)) {
+                            if (!supportsDigitalSignature) {
+                                if (this == CLIENT || getBit(ku, 2) == false) {
                                     return CheckResult.EXTENSION_MISMATCH;
                                 }
                             }
                             break;
+                        case "RSASSA-PSS":
+                            if (!supportsDigitalSignature && (this == SERVER)) {
+                                return CheckResult.EXTENSION_MISMATCH;
+                            }
+                            break;
                         case "DSA":
                             // require signature bit
-                            if (kuSignature == false) {
+                            if (!supportsDigitalSignature) {
                                 return CheckResult.EXTENSION_MISMATCH;
                             }
                             break;
@@ -595,7 +598,7 @@
                             break;
                         case "EC":
                             // require signature bit
-                            if (kuSignature == false) {
+                            if (!supportsDigitalSignature) {
                                 return CheckResult.EXTENSION_MISMATCH;
                             }
                             // For servers, also require key agreement.
@@ -631,8 +634,9 @@
                                     new SNIHostName(serverName.getEncoded());
                             } catch (IllegalArgumentException iae) {
                                 // unlikely to happen, just in case ...
-                                if (useDebug) {
-                                    debug.println(
+                                if (SSLLogger.isOn &&
+                                        SSLLogger.isOn("keymanager")) {
+                                    SSLLogger.fine(
                                        "Illegal server name: " + serverName);
                                 }
 
@@ -646,11 +650,12 @@
                             X509TrustManagerImpl.checkIdentity(hostname,
                                                         cert, idAlgorithm);
                         } catch (CertificateException e) {
-                            if (useDebug) {
-                                debug.println(
-                                   "Certificate identity does not match " +
-                                   "Server Name Inidication (SNI): " +
-                                   hostname);
+                            if (SSLLogger.isOn &&
+                                    SSLLogger.isOn("keymanager")) {
+                                SSLLogger.fine(
+                                    "Certificate identity does not match " +
+                                    "Server Name Inidication (SNI): " +
+                                    hostname);
                             }
                             return CheckResult.INSENSITIVE;
                         }
@@ -724,7 +729,7 @@
         for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
             String alias = e.nextElement();
             // check if it is a key entry (private key or secret key)
-            if (ks.isKeyEntry(alias) == false) {
+            if (!ks.isKeyEntry(alias)) {
                 continue;
             }
 
@@ -757,8 +762,8 @@
                 j++;
             }
             if (keyIndex == -1) {
-                if (useDebug) {
-                    debug.println("Ignoring alias " + alias
+                if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                    SSLLogger.fine("Ignore alias " + alias
                                 + ": key algorithm does not match");
                 }
                 continue;
@@ -774,9 +779,10 @@
                     }
                 }
                 if (found == false) {
-                    if (useDebug) {
-                        debug.println("Ignoring alias " + alias
-                                    + ": issuers do not match");
+                    if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                        SSLLogger.fine(
+                                "Ignore alias " + alias
+                                + ": issuers do not match");
                     }
                     continue;
                 }
@@ -787,8 +793,8 @@
                     !conformsToAlgorithmConstraints(constraints, chain,
                             checkType.getValidator())) {
 
-                if (useDebug) {
-                    debug.println("Ignoring alias " + alias +
+                if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                    SSLLogger.fine("Ignore alias " + alias +
                             ": certificate list does not conform to " +
                             "algorithm constraints");
                 }
@@ -813,7 +819,7 @@
                 return Collections.singletonList(status);
             } else {
                 if (results == null) {
-                    results = new ArrayList<EntryStatus>();
+                    results = new ArrayList<>();
                 }
                 results.add(status);
             }
@@ -825,14 +831,15 @@
             AlgorithmConstraints constraints, Certificate[] chain,
             String variant) {
 
-        AlgorithmChecker checker = new AlgorithmChecker(constraints, null, variant);
+        AlgorithmChecker checker =
+                new AlgorithmChecker(constraints, null, variant);
         try {
             checker.init(false);
         } catch (CertPathValidatorException cpve) {
             // unlikely to happen
-            if (useDebug) {
-                debug.println(
-                    "Cannot initialize algorithm constraints checker: " + cpve);
+            if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                SSLLogger.fine(
+                    "Cannot initialize algorithm constraints checker", cpve);
             }
 
             return false;
@@ -845,9 +852,9 @@
                 // We don't care about the unresolved critical extensions.
                 checker.check(cert, Collections.<String>emptySet());
             } catch (CertPathValidatorException cpve) {
-                if (useDebug) {
-                    debug.println("Certificate (" + cert +
-                        ") does not conform to algorithm constraints: " + cpve);
+                if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+                    SSLLogger.fine("Certificate does not conform to " +
+                            "algorithm constraints", cert, cpve);
                 }
 
                 return false;
@@ -856,5 +863,4 @@
 
         return true;
     }
-
 }
--- a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,20 +23,16 @@
  * questions.
  */
 
-
 package sun.security.ssl;
 
 import java.net.Socket;
-import javax.net.ssl.SSLSession;
-
-import java.util.*;
 import java.security.*;
 import java.security.cert.*;
+import java.util.*;
 import javax.net.ssl.*;
-
-import sun.security.validator.*;
 import sun.security.util.AnchorCertificates;
 import sun.security.util.HostnameChecker;
+import sun.security.validator.*;
 
 /**
  * This class implements the SunJSSE X.509 trust manager using the internal
@@ -67,8 +63,6 @@
     // the different extension checks. They are initialized lazily on demand.
     private volatile Validator clientValidator, serverValidator;
 
-    private static final Debug debug = Debug.getInstance("ssl");
-
     X509TrustManagerImpl(String validatorType,
             Collection<X509Certificate> trustedCerts) {
 
@@ -81,8 +75,9 @@
 
         this.trustedCerts = trustedCerts;
 
-        if (debug != null && Debug.isOn("trustmanager")) {
-            showTrustedCerts();
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
+            SSLLogger.fine("adding as trusted certificates",
+                    (Object[])trustedCerts.toArray(new X509Certificate[0]));
         }
     }
 
@@ -97,8 +92,9 @@
         trustedCerts = v.getTrustedCertificates();
         serverValidator = v;
 
-        if (debug != null && Debug.isOn("trustmanager")) {
-            showTrustedCerts();
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
+            SSLLogger.fine("adding as trusted certificates",
+                    (Object[])trustedCerts.toArray(new X509Certificate[0]));
         }
     }
 
@@ -202,11 +198,10 @@
             }
 
             // create the algorithm constraints
-            ProtocolVersion protocolVersion =
-                ProtocolVersion.valueOf(session.getProtocol());
             boolean isExtSession = (session instanceof ExtendedSSLSession);
-            AlgorithmConstraints constraints = null;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v && isExtSession) {
+            AlgorithmConstraints constraints;
+            if (isExtSession &&
+                    ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
                 ExtendedSSLSession extSession = (ExtendedSSLSession)session;
                 String[] localSupportedSignAlgs =
                         extSession.getLocalSupportedSignatureAlgorithms();
@@ -228,8 +223,8 @@
 
             // check if EE certificate chains to a public root CA (as
             // pre-installed in cacerts)
-            boolean chainsToPublicCA =
-                AnchorCertificates.contains(trustedChain[trustedChain.length-1]);
+            boolean chainsToPublicCA = AnchorCertificates.contains(
+                    trustedChain[trustedChain.length-1]);
 
             // check endpoint identity
             String identityAlg = sslSocket.getSSLParameters().
@@ -242,9 +237,10 @@
             trustedChain = validate(v, chain, Collections.emptyList(),
                     null, isClient ? null : authType);
         }
-        if (debug != null && Debug.isOn("trustmanager")) {
-            System.out.println("Found trusted certificate:");
-            System.out.println(trustedChain[trustedChain.length - 1]);
+
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
+            SSLLogger.fine("Found trusted certificate",
+                    trustedChain[trustedChain.length - 1]);
         }
     }
 
@@ -260,11 +256,10 @@
             }
 
             // create the algorithm constraints
-            ProtocolVersion protocolVersion =
-                ProtocolVersion.valueOf(session.getProtocol());
             boolean isExtSession = (session instanceof ExtendedSSLSession);
-            AlgorithmConstraints constraints = null;
-            if (protocolVersion.v >= ProtocolVersion.TLS12.v && isExtSession) {
+            AlgorithmConstraints constraints;
+            if (isExtSession &&
+                    ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
                 ExtendedSSLSession extSession = (ExtendedSSLSession)session;
                 String[] localSupportedSignAlgs =
                         extSession.getLocalSupportedSignatureAlgorithms();
@@ -286,8 +281,8 @@
 
             // check if EE certificate chains to a public root CA (as
             // pre-installed in cacerts)
-            boolean chainsToPublicCA =
-                AnchorCertificates.contains(trustedChain[trustedChain.length-1]);
+            boolean chainsToPublicCA = AnchorCertificates.contains(
+                    trustedChain[trustedChain.length-1]);
 
             // check endpoint identity
             String identityAlg = engine.getSSLParameters().
@@ -300,27 +295,10 @@
             trustedChain = validate(v, chain, Collections.emptyList(),
                     null, isClient ? null : authType);
         }
-        if (debug != null && Debug.isOn("trustmanager")) {
-            System.out.println("Found trusted certificate:");
-            System.out.println(trustedChain[trustedChain.length - 1]);
-        }
-    }
 
-    private void showTrustedCerts() {
-        for (X509Certificate cert : trustedCerts) {
-            System.out.println("adding as trusted cert:");
-            System.out.println("  Subject: "
-                                    + cert.getSubjectX500Principal());
-            System.out.println("  Issuer:  "
-                                    + cert.getIssuerX500Principal());
-            System.out.println("  Algorithm: "
-                                    + cert.getPublicKey().getAlgorithm()
-                                    + "; Serial number: 0x"
-                                    + cert.getSerialNumber().toString(16));
-            System.out.println("  Valid from "
-                                    + cert.getNotBefore() + " until "
-                                    + cert.getNotAfter());
-            System.out.println();
+        if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
+            SSLLogger.fine("Found trusted certificate",
+                    trustedChain[trustedChain.length - 1]);
         }
     }
 
@@ -364,8 +342,8 @@
                     hostname = new SNIHostName(sniName.getEncoded());
                 } catch (IllegalArgumentException iae) {
                     // unlikely to happen, just in case ...
-                    if ((debug != null) && Debug.isOn("trustmanager")) {
-                        System.out.println("Illegal server name: " + sniName);
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
+                        SSLLogger.fine("Illegal server name: " + sniName);
                     }
                 }
             }
@@ -491,3 +469,4 @@
         }
     }
 }
+
--- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java	Mon Jun 25 13:41:39 2018 -0700
@@ -35,9 +35,8 @@
 import javax.net.ssl.SNIHostName;
 
 import sun.net.util.IPAddressUtil;
-import sun.security.ssl.ClientKeyExchangeService;
-import sun.security.ssl.Debug;
 import sun.security.x509.X500Name;
+import sun.security.ssl.SSLLogger;
 
 /**
  * Class to check hostnames against the names specified in a certificate as
@@ -60,8 +59,6 @@
     private static final int ALTNAME_DNS = 2;
     private static final int ALTNAME_IP  = 7;
 
-    private static final Debug debug = Debug.getInstance("ssl");
-
     // the algorithm to follow to perform the check. Currently unused.
     private final byte checkType;
 
@@ -107,26 +104,6 @@
     }
 
     /**
-     * Perform the check for Kerberos.
-     */
-    public static boolean match(String expectedName, Principal principal) {
-        String hostName = getServerName(principal);
-        return (expectedName.equalsIgnoreCase(hostName));
-    }
-
-    /**
-     * Return the Server name from Kerberos principal.
-     */
-    public static String getServerName(Principal principal) {
-        ClientKeyExchangeService p =
-                ClientKeyExchangeService.find("KRB5");
-        if (p == null) {
-            throw new AssertionError("Kerberos should have been available");
-        }
-        return p.getServiceHostName(principal);
-    }
-
-    /**
      * Test whether the given hostname looks like a literal IPv4 or IPv6
      * address. The hostname does not need to be a fully qualified name.
      *
@@ -316,9 +293,10 @@
                                               boolean chainsToPublicCA) {
         // not ok if it is a single wildcard character or "*."
         if (template.equals("*") || template.equals("*.")) {
-            if (debug != null) {
-                debug.println("Certificate domain name has illegal single " +
-                              "wildcard character: " + template);
+            if (SSLLogger.isOn) {
+                SSLLogger.fine(
+                    "Certificate domain name has illegal single " +
+                      "wildcard character: " + template);
             }
             return true;
         }
@@ -335,9 +313,10 @@
 
         // not ok if there is no dot after wildcard (ex: "*com")
         if (firstDotIndex == -1) {
-            if (debug != null) {
-                debug.println("Certificate domain name has illegal wildcard, " +
-                              "no dot after wildcard character: " + template);
+            if (SSLLogger.isOn) {
+                SSLLogger.fine(
+                    "Certificate domain name has illegal wildcard, " +
+                    "no dot after wildcard character: " + template);
             }
             return true;
         }
@@ -354,9 +333,10 @@
         if (rd.isPresent()) {
             String wDomain = afterWildcard.substring(firstDotIndex + 1);
             if (rd.get().publicSuffix().equalsIgnoreCase(wDomain)) {
-                if (debug != null) {
-                    debug.println("Certificate domain name has illegal " +
-                                  "wildcard for public suffix: " + template);
+                if (SSLLogger.isOn) {
+                    SSLLogger.fine(
+                        "Certificate domain name has illegal " +
+                        "wildcard for public suffix: " + template);
                 }
                 return true;
             }
--- a/src/java.base/share/conf/security/java.security	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.base/share/conf/security/java.security	Mon Jun 25 13:41:39 2018 -0700
@@ -800,6 +800,40 @@
 #       FFFFFFFF FFFFFFFF, 2}
 
 #
+# TLS key limits on symmetric cryptographic algorithms
+#
+# This security property sets limits on algorithms key usage in TLS 1.3.
+# When the amount of data encrypted exceeds the algorithm value listed below,
+# a KeyUpdate message will trigger a key change.  This is for symmetric ciphers
+# with TLS 1.3 only.
+#
+# The syntax for the property is described below:
+#   KeyLimits:
+#       " KeyLimit { , KeyLimit } "
+#
+#   WeakKeyLimit:
+#       AlgorithmName Action Length
+#
+#   AlgorithmName:
+#       A full algorithm transformation.
+#
+#   Action:
+#       KeyUpdate
+#
+#   Length:
+#       The amount of encrypted data in a session before the Action occurs
+#       This value may be an integer value in bytes, or as a power of two, 2^29.
+#
+#   KeyUpdate:
+#       The TLS 1.3 KeyUpdate handshake process begins when the Length amount
+#       is fulfilled.
+#
+# Note: This property is currently used by OpenJDK's JSSE implementation. It
+# is not guaranteed to be examined and used by other implementations.
+#
+jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37
+
+#
 # Cryptographic Jurisdiction Policy defaults
 #
 # Import and export control rules on cryptographic software vary from
--- a/src/java.naming/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.naming/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -404,27 +404,16 @@
         try {
             HostnameChecker checker = HostnameChecker.getInstance(
                                                 HostnameChecker.TYPE_LDAP);
-            // Use ciphersuite to determine whether Kerberos is active.
-            if (session.getCipherSuite().startsWith("TLS_KRB5")) {
-                Principal principal = getPeerPrincipal(session);
-                if (!HostnameChecker.match(hostname, principal)) {
-                    throw new SSLPeerUnverifiedException(
-                        "hostname of the kerberos principal:" + principal +
-                        " does not match the hostname:" + hostname);
-                }
-            } else { // X.509
-
-                // get the subject's certificate
-                certs = session.getPeerCertificates();
-                X509Certificate peerCert;
-                if (certs[0] instanceof java.security.cert.X509Certificate) {
-                    peerCert = (java.security.cert.X509Certificate) certs[0];
-                } else {
-                    throw new SSLPeerUnverifiedException(
-                            "Received a non X509Certificate from the server");
-                }
-                checker.match(hostname, peerCert);
+            // get the subject's certificate
+            certs = session.getPeerCertificates();
+            X509Certificate peerCert;
+            if (certs[0] instanceof java.security.cert.X509Certificate) {
+                peerCert = (java.security.cert.X509Certificate) certs[0];
+            } else {
+                throw new SSLPeerUnverifiedException(
+                        "Received a non X509Certificate from the server");
             }
+            checker.match(hostname, peerCert);
 
             // no exception means verification passed
             return true;
--- a/src/java.security.jgss/share/classes/module-info.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.security.jgss/share/classes/module-info.java	Mon Jun 25 13:41:39 2018 -0700
@@ -55,7 +55,5 @@
 
     provides java.security.Provider with
         sun.security.jgss.SunProvider;
-    provides sun.security.ssl.ClientKeyExchangeService with
-        sun.security.krb5.internal.ssl.Krb5KeyExchangeService;
 }
 
--- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSCaller.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSCaller.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,8 +34,6 @@
     public static final GSSCaller CALLER_UNKNOWN = new GSSCaller("UNKNOWN");
     public static final GSSCaller CALLER_INITIATE = new GSSCaller("INITIATE");
     public static final GSSCaller CALLER_ACCEPT = new GSSCaller("ACCEPT");
-    public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller("SSL_CLIENT");
-    public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller("SSL_SERVER");
 
     private String name;
     GSSCaller(String s) {
--- a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -109,16 +109,6 @@
                     "com.sun.security.jgss.krb5.accept",
                     "com.sun.security.jgss.accept",
                 };
-            } else if (caller == GSSCaller.CALLER_SSL_CLIENT) {
-                alts = new String[] {
-                    "com.sun.security.jgss.krb5.initiate",
-                    "com.sun.net.ssl.client",
-                };
-            } else if (caller == GSSCaller.CALLER_SSL_SERVER) {
-                alts = new String[] {
-                    "com.sun.security.jgss.krb5.accept",
-                    "com.sun.net.ssl.server",
-                };
             } else if (caller instanceof HttpCaller) {
                 alts = new String[] {
                     "com.sun.security.jgss.krb5.initiate",
@@ -132,14 +122,12 @@
             /*
             switch (caller) {
             case GSSUtil.CALLER_INITIATE:
-            case GSSUtil.CALLER_SSL_CLIENT:
             case GSSUtil.CALLER_HTTP_NEGOTIATE:
                 alts = new String[] {
                     "com.sun.security.jgss." + mechName + ".initiate",
                 };
                 break;
             case GSSUtil.CALLER_ACCEPT:
-            case GSSUtil.CALLER_SSL_SERVER:
                 alts = new String[] {
                     "com.sun.security.jgss." + mechName + ".accept",
                 };
@@ -207,7 +195,6 @@
     }
 
     private static boolean isServerSide (GSSCaller caller) {
-        return GSSCaller.CALLER_ACCEPT == caller ||
-               GSSCaller.CALLER_SSL_SERVER == caller;
+        return GSSCaller.CALLER_ACCEPT == caller;
     }
 }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptedData.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptedData.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -103,7 +103,7 @@
         return new_encryptedData;
     }
 
-     // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
+    // Used by test
     public EncryptedData(
                          int new_eType,
                          Integer new_kvno,
@@ -126,8 +126,7 @@
     }
     */
 
-     // used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv
-     // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret)
+    // used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv
     public EncryptedData(
                          EncryptionKey key,
                          byte[] plaintext,
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-/*
- * Copyright (c) 2003, 2010, 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.krb5.internal.ssl;
-
-import java.io.*;
-import java.security.*;
-import java.util.Arrays;
-
-import javax.net.ssl.*;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeMessage;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is the Kerberos premaster secret in the Kerberos client key
- * exchange message (CLIENT --> SERVER); it holds the
- * Kerberos-encrypted pre-master secret. The secret is encrypted using the
- * Kerberos session key.  The padding and size of the resulting message
- * depends on the session key type, but the pre-master secret is
- * always exactly 48 bytes.
- *
- */
-final class KerberosPreMasterSecret {
-
-    private ProtocolVersion protocolVersion; // preMaster [0,1]
-    private byte[] preMaster;           // 48 bytes
-    private byte[] encrypted;
-
-    /**
-     * Constructor used by client to generate premaster secret.
-     *
-     * Client randomly creates a pre-master secret and encrypts it
-     * using the Kerberos session key; only the server can decrypt
-     * it, using the session key available in the service ticket.
-     *
-     * @param protocolVersion used to set preMaster[0,1]
-     * @param generator random number generator for generating premaster secret
-     * @param sessionKey Kerberos session key for encrypting premaster secret
-     */
-    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
-            SecureRandom generator, EncryptionKey sessionKey) throws IOException {
-
-        if (sessionKey.getEType() ==
-                EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
-            throw new IOException(
-                    "session keys with des3-cbc-hmac-sha1-kd encryption type " +
-                            "are not supported for TLS Kerberos cipher suites");
-        }
-
-        this.protocolVersion = protocolVersion;
-        preMaster = generatePreMaster(generator, protocolVersion);
-
-        // Encrypt premaster secret
-        try {
-            EncryptedData eData = new EncryptedData(sessionKey, preMaster,
-                    KeyUsage.KU_UNKNOWN);
-            encrypted = eData.getBytes();  // not ASN.1 encoded.
-
-        } catch (KrbException e) {
-            throw (SSLKeyException)new SSLKeyException
-                    ("Kerberos premaster secret error").initCause(e);
-        }
-    }
-
-    /*
-     * Constructor used by server to decrypt encrypted premaster secret.
-     * The protocol version in preMaster[0,1] must match either currentVersion
-     * or clientVersion, otherwise, the premaster secret is set to
-     * a random one to foil possible attack.
-     *
-     * @param currentVersion version of protocol being used
-     * @param clientVersion version requested by client
-     * @param generator random number generator used to generate
-     *        bogus premaster secret if premaster secret verification fails
-     * @param input input stream from which to read the encrypted
-     *        premaster secret
-     * @param sessionKey Kerberos session key to be used for decryption
-     */
-    KerberosPreMasterSecret(ProtocolVersion currentVersion,
-            ProtocolVersion clientVersion,
-            SecureRandom generator, byte[] encrypted,
-            EncryptionKey sessionKey) throws IOException {
-
-        if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-            if (encrypted != null) {
-                Debug.println(System.out,
-                        "encrypted premaster secret", encrypted);
-            }
-        }
-
-        if (sessionKey.getEType() ==
-                EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
-            throw new IOException(
-                    "session keys with des3-cbc-hmac-sha1-kd encryption type " +
-                            "are not supported for TLS Kerberos cipher suites");
-        }
-
-        // Decrypt premaster secret
-        try {
-            EncryptedData data = new EncryptedData(sessionKey.getEType(),
-                    null /* optional kvno */, encrypted);
-
-            byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                if (encrypted != null) {
-                    Debug.println(System.out,
-                            "decrypted premaster secret", temp);
-                }
-            }
-
-            // Remove padding bytes after decryption. Only DES and DES3 have
-            // paddings and we don't support DES3 in TLS (see above)
-
-            if (temp.length == 52 &&
-                    data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
-                // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
-                if (paddingByteIs(temp, 52, (byte)4) ||
-                        paddingByteIs(temp, 52, (byte)0)) {
-                    temp = Arrays.copyOf(temp, 48);
-                }
-            } else if (temp.length == 56 &&
-                    data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
-                // For des-cbc-md5, 8 paddings with 0x08, or no padding
-                if (paddingByteIs(temp, 56, (byte)8)) {
-                    temp = Arrays.copyOf(temp, 48);
-                }
-            }
-
-            preMaster = temp;
-
-            protocolVersion = ProtocolVersion.valueOf(preMaster[0],
-                    preMaster[1]);
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                System.out.println("Kerberos PreMasterSecret version: "
-                        + protocolVersion);
-            }
-        } catch (Exception e) {
-            // catch exception & process below
-            preMaster = null;
-            protocolVersion = currentVersion;
-        }
-
-        // check if the premaster secret version is ok
-        // the specification says that it must be the maximum version supported
-        // by the client from its ClientHello message. However, many
-        // old implementations send the negotiated version, so accept both
-        // for SSL v3.0 and TLS v1.0.
-        // NOTE that we may be comparing two unsupported version numbers in
-        // the second case, which is why we cannot use object references
-        // equality in this special case
-        boolean versionMismatch = (protocolVersion.v != clientVersion.v);
-
-        /*
-         * we never checked the client_version in server side
-         * for TLS v1.0 and SSL v3.0. For compatibility, we
-         * maintain this behavior.
-         */
-        if (versionMismatch && (clientVersion.v <= 0x0301)) {
-            versionMismatch = (protocolVersion.v != currentVersion.v);
-        }
-
-        /*
-         * Bogus decrypted ClientKeyExchange? If so, conjure a
-         * a random preMaster secret that will fail later during
-         * Finished message processing. This is a countermeasure against
-         * the "interactive RSA PKCS#1 encryption envelop attack" reported
-         * in June 1998. Preserving the executation path will
-         * mitigate timing attacks and force consistent error handling
-         * that will prevent an attacking client from differentiating
-         * different kinds of decrypted ClientKeyExchange bogosities.
-         */
-        if ((preMaster == null) || (preMaster.length != 48)
-                || versionMismatch) {
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                System.out.println("Kerberos PreMasterSecret error, "
-                        + "generating random secret");
-                if (preMaster != null) {
-                    Debug.println(System.out, "Invalid secret", preMaster);
-                }
-            }
-
-            /*
-             * Randomize the preMaster secret with the
-             * ClientHello.client_version, as will produce invalid master
-             * secret to prevent the attacks.
-             */
-            preMaster = generatePreMaster(generator, clientVersion);
-            protocolVersion = clientVersion;
-        }
-    }
-
-    /**
-     * Checks if all paddings of data are b
-     * @param data the block with padding
-     * @param len length of data, >= 48
-     * @param b expected padding byte
-     */
-    private static boolean paddingByteIs(byte[] data, int len, byte b) {
-        for (int i=48; i<len; i++) {
-            if (data[i] != b) return false;
-        }
-        return true;
-    }
-
-    /*
-     * Used by server to generate premaster secret in case of
-     * problem decoding ticket.
-     *
-     * @param protocolVersion used for preMaster[0,1]
-     * @param generator random number generator to use for generating secret.
-     */
-    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
-            SecureRandom generator) {
-
-        this.protocolVersion = protocolVersion;
-        preMaster = generatePreMaster(generator, protocolVersion);
-    }
-
-    private static byte[] generatePreMaster(SecureRandom rand,
-            ProtocolVersion ver) {
-
-        byte[] pm = new byte[48];
-        rand.nextBytes(pm);
-        pm[0] = ver.major;
-        pm[1] = ver.minor;
-
-        return pm;
-    }
-
-    // Clone not needed; internal use only
-    byte[] getUnencrypted() {
-        return preMaster;
-    }
-
-    // Clone not needed; internal use only
-    byte[] getEncrypted() {
-        return encrypted;
-    }
-}
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,563 +0,0 @@
-/*
- * Copyright (c) 2015, 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.krb5.internal.ssl;
-
-import sun.security.ssl.ClientKeyExchange;
-import sun.security.ssl.Debug;
-import sun.security.ssl.ClientKeyExchangeService;
-import sun.security.ssl.HandshakeOutStream;
-
-import sun.security.jgss.GSSCaller;
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.PrincipalName;
-import sun.security.krb5.internal.EncTicketPart;
-import sun.security.krb5.internal.Ticket;
-import sun.security.krb5.internal.crypto.KeyUsage;
-import sun.security.ssl.ProtocolVersion;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import javax.security.auth.Subject;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.kerberos.KeyTab;
-import javax.security.auth.kerberos.ServicePermission;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.InetAddress;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.security.SecureRandom;
-import java.util.Set;
-
-/**
- * The provider for TLS_KRB_ cipher suites.
- *
- * @since 9
- */
-public class Krb5KeyExchangeService implements ClientKeyExchangeService {
-
-    public static final Debug debug = Debug.getInstance("ssl");
-
-    @Override
-    public String[] supported() {
-        return new String[] { "KRB5", "KRB5_EXPORT" };
-    }
-
-    @Override
-    public Object getServiceCreds(AccessControlContext acc) {
-        try {
-            ServiceCreds serviceCreds = AccessController.doPrivileged(
-                    (PrivilegedExceptionAction<ServiceCreds>)
-                            () -> Krb5Util.getServiceCreds(
-                                    GSSCaller.CALLER_SSL_SERVER, null, acc));
-            if (serviceCreds == null) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Kerberos serviceCreds not available");
-                }
-                return null;
-            }
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Using Kerberos creds");
-            }
-            String serverPrincipal = serviceCreds.getName();
-            if (serverPrincipal != null) {
-                // When service is bound, we check ASAP. Otherwise,
-                // will check after client request is received
-                // in in Kerberos ClientKeyExchange
-                SecurityManager sm = System.getSecurityManager();
-                try {
-                    if (sm != null) {
-                        // Eliminate dependency on ServicePermission
-                        sm.checkPermission(new ServicePermission(
-                                serverPrincipal, "accept"), acc);
-                    }
-                } catch (SecurityException se) {
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println("Permission to access Kerberos"
-                                + " secret key denied");
-                    }
-                    return null;
-                }
-            }
-            return serviceCreds;
-        } catch (PrivilegedActionException e) {
-            // Likely exception here is LoginException
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Attempt to obtain Kerberos key failed: "
-                        + e.toString());
-            }
-            return null;
-        }
-    }
-
-    @Override
-    public String getServiceHostName(Principal principal) {
-        if (principal == null) {
-            return null;
-        }
-        String hostName = null;
-        try {
-            PrincipalName princName =
-                    new PrincipalName(principal.getName(),
-                            PrincipalName.KRB_NT_SRV_HST);
-            String[] nameParts = princName.getNameStrings();
-            if (nameParts.length >= 2) {
-                hostName = nameParts[1];
-            }
-        } catch (Exception e) {
-            // ignore
-        }
-        return hostName;
-    }
-
-
-    @Override
-    public boolean isRelated(boolean isClient,
-            AccessControlContext acc, Principal p) {
-
-        if (p == null) return false;
-        try {
-            Subject subject = AccessController.doPrivileged(
-                    (PrivilegedExceptionAction<Subject>)
-                            () -> Krb5Util.getSubject(
-                                    isClient ? GSSCaller.CALLER_SSL_CLIENT
-                                            : GSSCaller.CALLER_SSL_SERVER,
-                                    acc));
-            if (subject == null) {
-                if (debug != null && Debug.isOn("session")) {
-                    System.out.println("Kerberos credentials are" +
-                            " not present in the current Subject;" +
-                            " check if " +
-                            " javax.security.auth.useSubjectAsCreds" +
-                            " system property has been set to false");
-                }
-                return false;
-            }
-            Set<Principal> principals =
-                    subject.getPrincipals(Principal.class);
-            if (principals.contains(p)) {
-                // bound to this principal
-                return true;
-            } else {
-                if (isClient) {
-                    return false;
-                } else {
-                    for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) {
-                        if (!pc.isBound()) {
-                            return true;
-                        }
-                    }
-                    return false;
-                }
-            }
-        } catch (PrivilegedActionException pae) {
-            if (debug != null && Debug.isOn("session")) {
-                System.out.println("Attempt to obtain" +
-                        " subject failed! " + pae);
-            }
-            return false;
-        }
-
-    }
-
-    public ClientKeyExchange createClientExchange(
-            String serverName, AccessControlContext acc,
-            ProtocolVersion protocolVerson, SecureRandom rand) throws IOException {
-        return new ExchangerImpl(serverName, acc, protocolVerson, rand);
-    }
-
-    public ClientKeyExchange createServerExchange(
-            ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
-            SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
-            AccessControlContext acc, Object serviceCreds) throws IOException {
-        return new ExchangerImpl(protocolVersion, clientVersion, rand,
-                encodedTicket, encrypted, acc, serviceCreds);
-    }
-
-    static class ExchangerImpl extends ClientKeyExchange {
-
-        final private KerberosPreMasterSecret preMaster;
-        final private byte[] encodedTicket;
-        final private KerberosPrincipal peerPrincipal;
-        final private KerberosPrincipal localPrincipal;
-
-        @Override
-        public int messageLength() {
-            return encodedTicket.length + preMaster.getEncrypted().length + 6;
-        }
-
-        @Override
-        public void send(HandshakeOutStream s) throws IOException {
-            s.putBytes16(encodedTicket);
-            s.putBytes16(null);
-            s.putBytes16(preMaster.getEncrypted());
-        }
-
-        @Override
-        public void print(PrintStream s) throws IOException {
-            s.println("*** ClientKeyExchange, Kerberos");
-
-            if (debug != null && Debug.isOn("verbose")) {
-                Debug.println(s, "Kerberos service ticket", encodedTicket);
-                Debug.println(s, "Random Secret", preMaster.getUnencrypted());
-                Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted());
-            }
-        }
-
-        ExchangerImpl(String serverName, AccessControlContext acc,
-                ProtocolVersion protocolVersion, SecureRandom rand) throws IOException {
-
-            // Get service ticket
-            KerberosTicket ticket = getServiceTicket(serverName, acc);
-            encodedTicket = ticket.getEncoded();
-
-            // Record the Kerberos principals
-            peerPrincipal = ticket.getServer();
-            localPrincipal = ticket.getClient();
-
-            // Optional authenticator, encrypted using session key,
-            // currently ignored
-
-            // Generate premaster secret and encrypt it using session key
-            EncryptionKey sessionKey = new EncryptionKey(
-                    ticket.getSessionKeyType(),
-                    ticket.getSessionKey().getEncoded());
-
-            preMaster = new KerberosPreMasterSecret(protocolVersion,
-                    rand, sessionKey);
-        }
-
-        ExchangerImpl(
-                ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand,
-                byte[] encodedTicket, byte[] encrypted,
-                AccessControlContext acc, Object serviceCreds) throws IOException {
-
-            // Read ticket
-            this.encodedTicket = encodedTicket;
-
-            if (debug != null && Debug.isOn("verbose")) {
-                Debug.println(System.out,
-                        "encoded Kerberos service ticket", encodedTicket);
-            }
-
-            EncryptionKey sessionKey = null;
-            KerberosPrincipal tmpPeer = null;
-            KerberosPrincipal tmpLocal = null;
-
-            try {
-                Ticket t = new Ticket(encodedTicket);
-
-                EncryptedData encPart = t.encPart;
-                PrincipalName ticketSname = t.sname;
-
-                final ServiceCreds creds = (ServiceCreds)serviceCreds;
-                final KerberosPrincipal princ =
-                        new KerberosPrincipal(ticketSname.toString());
-
-                // For bound service, permission already checked at setup
-                if (creds.getName() == null) {
-                    SecurityManager sm = System.getSecurityManager();
-                    try {
-                        if (sm != null) {
-                            // Eliminate dependency on ServicePermission
-                            sm.checkPermission(new ServicePermission(
-                                    ticketSname.toString(), "accept"), acc);
-                        }
-                    } catch (SecurityException se) {
-                        serviceCreds = null;
-                        // Do not destroy keys. Will affect Subject
-                        if (debug != null && Debug.isOn("handshake")) {
-                            System.out.println("Permission to access Kerberos"
-                                    + " secret key denied");
-                            se.printStackTrace(System.out);
-                        }
-                        throw new IOException("Kerberos service not allowedy");
-                    }
-                }
-                KerberosKey[] serverKeys = AccessController.doPrivileged(
-                        new PrivilegedAction<KerberosKey[]>() {
-                            @Override
-                            public KerberosKey[] run() {
-                                return creds.getKKeys(princ);
-                            }
-                        });
-                if (serverKeys.length == 0) {
-                    throw new IOException("Found no key for " + princ +
-                            (creds.getName() == null ? "" :
-                                    (", this keytab is for " + creds.getName() + " only")));
-                }
-
-                /*
-                 * permission to access and use the secret key of the Kerberized
-                 * "host" service is done in ServerHandshaker.getKerberosKeys()
-                 * to ensure server has the permission to use the secret key
-                 * before promising the client
-                 */
-
-                // See if we have the right key to decrypt the ticket to get
-                // the session key.
-                int encPartKeyType = encPart.getEType();
-                Integer encPartKeyVersion = encPart.getKeyVersionNumber();
-                KerberosKey dkey = null;
-                try {
-                    dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
-                } catch (KrbException ke) { // a kvno mismatch
-                    throw new IOException(
-                            "Cannot find key matching version number", ke);
-                }
-                if (dkey == null) {
-                    // %%% Should print string repr of etype
-                    throw new IOException("Cannot find key of appropriate type" +
-                            " to decrypt ticket - need etype " + encPartKeyType);
-                }
-
-                EncryptionKey secretKey = new EncryptionKey(
-                        encPartKeyType,
-                        dkey.getEncoded());
-
-                // Decrypt encPart using server's secret key
-                byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
-
-                // Reset data stream after decryption, remove redundant bytes
-                byte[] temp = encPart.reset(bytes);
-                EncTicketPart encTicketPart = new EncTicketPart(temp);
-
-                // Record the Kerberos Principals
-                tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName());
-                tmpLocal = new KerberosPrincipal(ticketSname.getName());
-
-                sessionKey = encTicketPart.key;
-
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("server principal: " + ticketSname);
-                    System.out.println("cname: " + encTicketPart.cname.toString());
-                }
-            } catch (IOException e) {
-                throw e;
-            } catch (Exception e) {
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("KerberosWrapper error getting session key,"
-                            + " generating random secret (" + e.getMessage() + ")");
-                }
-                sessionKey = null;
-            }
-
-            //input.getBytes16();   // XXX Read and ignore authenticator
-
-            if (sessionKey != null) {
-                preMaster = new KerberosPreMasterSecret(protocolVersion,
-                        clientVersion, rand, encrypted, sessionKey);
-            } else {
-                // Generate bogus premaster secret
-                preMaster = new KerberosPreMasterSecret(clientVersion, rand);
-            }
-
-            peerPrincipal = tmpPeer;
-            localPrincipal = tmpLocal;
-        }
-
-        // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
-        private static KerberosTicket getServiceTicket(String serverName,
-                final AccessControlContext acc) throws IOException {
-
-            if ("localhost".equals(serverName) ||
-                    "localhost.localdomain".equals(serverName)) {
-
-                if (debug != null && Debug.isOn("handshake")) {
-                    System.out.println("Get the local hostname");
-                }
-                String localHost = java.security.AccessController.doPrivileged(
-                        new java.security.PrivilegedAction<String>() {
-                            public String run() {
-                                try {
-                                    return InetAddress.getLocalHost().getHostName();
-                                } catch (java.net.UnknownHostException e) {
-                                    if (debug != null && Debug.isOn("handshake")) {
-                                        System.out.println("Warning,"
-                                                + " cannot get the local hostname: "
-                                                + e.getMessage());
-                                    }
-                                    return null;
-                                }
-                            }
-                        });
-                if (localHost != null) {
-                    serverName = localHost;
-                }
-            }
-
-            // Resolve serverName (possibly in IP addr form) to Kerberos principal
-            // name for service with hostname
-            String serviceName = "host/" + serverName;
-            PrincipalName principal;
-            try {
-                principal = new PrincipalName(serviceName,
-                        PrincipalName.KRB_NT_SRV_HST);
-            } catch (SecurityException se) {
-                throw se;
-            } catch (Exception e) {
-                IOException ioe = new IOException("Invalid service principal" +
-                        " name: " + serviceName);
-                ioe.initCause(e);
-                throw ioe;
-            }
-            String realm = principal.getRealmAsString();
-
-            final String serverPrincipal = principal.toString();
-            final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
-            final String clientPrincipal = null;  // use default
-
-
-            // check permission to obtain a service ticket to initiate a
-            // context with the "host" service
-            SecurityManager sm = System.getSecurityManager();
-            if (sm != null) {
-                sm.checkPermission(new ServicePermission(serverPrincipal,
-                        "initiate"), acc);
-            }
-
-            try {
-                KerberosTicket ticket = AccessController.doPrivileged(
-                        new PrivilegedExceptionAction<KerberosTicket>() {
-                            public KerberosTicket run() throws Exception {
-                                return Krb5Util.getTicketFromSubjectAndTgs(
-                                        GSSCaller.CALLER_SSL_CLIENT,
-                                        clientPrincipal, serverPrincipal,
-                                        tgsPrincipal, acc);
-                            }});
-
-                if (ticket == null) {
-                    throw new IOException("Failed to find any kerberos service" +
-                            " ticket for " + serverPrincipal);
-                }
-                return ticket;
-            } catch (PrivilegedActionException e) {
-                IOException ioe = new IOException(
-                        "Attempt to obtain kerberos service ticket for " +
-                                serverPrincipal + " failed!");
-                ioe.initCause(e);
-                throw ioe;
-            }
-        }
-
-        @Override
-        public SecretKey clientKeyExchange() {
-            byte[] secretBytes = preMaster.getUnencrypted();
-            return new SecretKeySpec(secretBytes, "TlsPremasterSecret");
-        }
-
-        @Override
-        public Principal getPeerPrincipal() {
-            return peerPrincipal;
-        }
-
-        @Override
-        public Principal getLocalPrincipal() {
-            return localPrincipal;
-        }
-
-        /**
-         * Determines if a kvno matches another kvno. Used in the method
-         * findKey(etype, version, keys). Always returns true if either input
-         * is null or zero, in case any side does not have kvno info available.
-         *
-         * Note: zero is included because N/A is not a legal value for kvno
-         * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
-         * that the kvno is N/A might be lost when converting between
-         * EncryptionKey and KerberosKey.
-         */
-        private static boolean versionMatches(Integer v1, int v2) {
-            if (v1 == null || v1 == 0 || v2 == 0) {
-                return true;
-            }
-            return v1.equals(v2);
-        }
-
-        private static KerberosKey findKey(int etype, Integer version,
-                KerberosKey[] keys) throws KrbException {
-            int ktype;
-            boolean etypeFound = false;
-
-            // When no matched kvno is found, returns tke key of the same
-            // etype with the highest kvno
-            int kvno_found = 0;
-            KerberosKey key_found = null;
-
-            for (int i = 0; i < keys.length; i++) {
-                ktype = keys[i].getKeyType();
-                if (etype == ktype) {
-                    int kv = keys[i].getVersionNumber();
-                    etypeFound = true;
-                    if (versionMatches(version, kv)) {
-                        return keys[i];
-                    } else if (kv > kvno_found) {
-                        key_found = keys[i];
-                        kvno_found = kv;
-                    }
-                }
-            }
-            // Key not found.
-            // %%% kludge to allow DES keys to be used for diff etypes
-            if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
-                    etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
-                for (int i = 0; i < keys.length; i++) {
-                    ktype = keys[i].getKeyType();
-                    if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
-                            ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
-                        int kv = keys[i].getVersionNumber();
-                        etypeFound = true;
-                        if (versionMatches(version, kv)) {
-                            return new KerberosKey(keys[i].getPrincipal(),
-                                    keys[i].getEncoded(),
-                                    etype,
-                                    kv);
-                        } else if (kv > kvno_found) {
-                            key_found = new KerberosKey(keys[i].getPrincipal(),
-                                    keys[i].getEncoded(),
-                                    etype,
-                                    kv);
-                            kvno_found = kv;
-                        }
-                    }
-                }
-            }
-            if (etypeFound) {
-                return key_found;
-            }
-            return null;
-        }
-    }
-}
--- a/test/jdk/ProblemList.txt	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/ProblemList.txt	Mon Jun 25 13:41:39 2018 -0700
@@ -561,6 +561,22 @@
 
 java/net/DatagramSocket/SendDatagramToBadAddress.java           7143960 macosx-all
 
+java/net/httpclient/ConcurrentResponses.java                    8204977 generic-all
+java/net/httpclient/DigestEchoClientSSL.java                    8204977 generic-all
+java/net/httpclient/EncodedCharsInURI.java                      8204977 generic-all
+java/net/httpclient/FlowAdapterSubscriberTest.java              8204977 generic-all
+java/net/httpclient/ImmutableFlowItems.java                     8204977 generic-all
+java/net/httpclient/ManyRequests.java                           8204977 generic-all
+java/net/httpclient/ManyRequests2.java                          8204977 generic-all
+java/net/httpclient/SplitResponseSSL.java                       8204977 generic-all
+java/net/httpclient/http2/BasicTest.java                        8204977 generic-all
+java/net/httpclient/http2/ContinuationFrameTest.java            8204977 generic-all
+java/net/httpclient/ProxyAuthDisabledSchemesSSL.java            8204977 generic-all
+java/net/httpclient/RequestBodyTest.java                        8204977 generic-all
+java/net/httpclient/SmokeTest.java                              8204977 generic-all
+java/net/httpclient/CancelledResponse.java                      8204977 solaris-all
+java/net/httpclient/InvalidSSLContextTest.java                  8204980 generic-all
+
 ############################################################################
 
 # jdk_nio
--- a/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -67,7 +67,8 @@
     public void performOp(InitialContext ctx) throws NamingException {}
 
     public void handleNamingException(NamingException e, long start, long end) {
-        if (e.getCause() instanceof SocketTimeoutException) {
+        if (e.getCause() instanceof SocketTimeoutException
+                || e.getCause().getCause() instanceof SocketTimeoutException) {
             // SSL connect will timeout via readReply using
             // SocketTimeoutException
             e.printStackTrace();
--- a/test/jdk/java/net/httpclient/MockServer.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/java/net/httpclient/MockServer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -183,7 +183,8 @@
             } catch (IOException |InterruptedException e1) {
                 cleanup();
             } catch (Throwable t) {
-                System.out.println("X: " + t);
+                System.out.println("Exception: " + t);
+                t.printStackTrace();
                 cleanup();
             }
         }
--- a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java	Mon Jun 25 13:41:39 2018 -0700
@@ -55,7 +55,7 @@
 
     @Override
     DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
-        if (needInvalidRecords && (ba.length >= 60) &&
+        if ((ba.length >= 60) &&
                 (ba[0x00] == (byte)0x16) && (ba[0x0D] == (byte)0x01) &&
                 (ba[0x3B] == (byte)0x00) && (ba[0x3C] > 0)) {
 
@@ -64,6 +64,16 @@
             // ba[0x3B]: length of session ID
             // ba[0x3C]: length of cookie
 
+            if (!needInvalidRecords) {
+                // The 2nd ClientHello with cookie.  The 1st one should be
+                // rejected as expected.
+                //
+                // This may happen if the last few bytes of the packet are
+                // for supported_version extension.
+                throw new RuntimeException(
+                    "the crashed handshake message was rejected as expected");
+            }
+
             // ClientHello with cookie
             needInvalidRecords = false;
             System.out.println("invalidate ClientHello message");
--- a/test/jdk/javax/net/ssl/SSLEngine/CheckStatus.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLEngine/CheckStatus.java	Mon Jun 25 13:41:39 2018 -0700
@@ -25,15 +25,16 @@
  * @test
  * @bug 4948079
  * @summary SSLEngineResult needs updating [none yet]
- *
- * This is a simple hack to test a bunch of conditions and check
- * their return codes.
- *
+ * @ignore the dependent implementation details are changed
  * @run main/othervm -Djsse.enableCBCProtection=false CheckStatus
  *
  * @author Brad Wetmore
  */
 
+/*
+ * This is a simple hack to test a bunch of conditions and check
+ * their return codes.
+ */
 import javax.net.ssl.*;
 import javax.net.ssl.SSLEngineResult.*;
 import java.io.*;
--- a/test/jdk/javax/net/ssl/SSLEngine/ConnectionTest.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLEngine/ConnectionTest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -26,17 +26,18 @@
  * @bug 4495742
  * @summary Add non-blocking SSL/TLS functionality, usable with any
  *      I/O abstraction
- *
- * This is a bit hacky, meant to test various conditions.  The main
- * thing I wanted to do with this was to do buffer reads/writes
- * when buffers were not empty.  (buffer.position() = 10)
- * The code could certainly be tightened up a lot.
- *
+ * @ignore the dependent implementation details are changed
  * @author Brad Wetmore
  *
  * @run main/othervm ConnectionTest
  */
 
+/*
+ * This is a bit hacky, meant to test various conditions.  The main
+ * thing I wanted to do with this was to do buffer reads/writes
+ * when buffers were not empty.  (buffer.position() = 10)
+ * The code could certainly be tightened up a lot.
+ */
 import javax.net.ssl.*;
 import javax.net.ssl.SSLEngineResult.*;
 import java.io.*;
--- a/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java	Mon Jun 25 13:41:39 2018 -0700
@@ -25,7 +25,8 @@
  * @test
  * @bug 8133632
  * @summary javax.net.ssl.SSLEngine does not properly handle received
- * SSL fatal alerts
+ *      SSL fatal alerts
+ * @ignore the dependent implementation details are changed
  * @run main/othervm EngineCloseOnAlert
  */
 
--- a/test/jdk/javax/net/ssl/SSLEngine/IllegalHandshakeMessage.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLEngine/IllegalHandshakeMessage.java	Mon Jun 25 13:41:39 2018 -0700
@@ -30,7 +30,7 @@
  * @test
  * @bug 8180643
  * @summary Illegal handshake message
- *
+ * @ignore the dependent implementation details are changed
  * @run main/othervm IllegalHandshakeMessage
  */
 
--- a/test/jdk/javax/net/ssl/SSLEngine/IllegalRecordVersion.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLEngine/IllegalRecordVersion.java	Mon Jun 25 13:41:39 2018 -0700
@@ -28,7 +28,7 @@
  * @test
  * @bug 8042449
  * @summary Issue for negative byte major record version
- *
+ * @ignore the dependent implementation details are changed
  * @run main/othervm IllegalRecordVersion
  */
 
--- a/test/jdk/javax/net/ssl/SSLEngine/LargeBufs.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLEngine/LargeBufs.java	Mon Jun 25 13:41:39 2018 -0700
@@ -45,7 +45,7 @@
 
 public class LargeBufs {
 
-    private static boolean debug = false;
+    private static boolean debug = true;
 
     private SSLContext sslc;
     static private SSLEngine ssle1;     // client
@@ -297,6 +297,11 @@
         } else {
             log("Data transferred cleanly");
         }
+
+        a.position(a.limit());
+        b.position(b.limit());
+        a.limit(a.capacity());
+        b.limit(b.capacity());
     }
 
     private static void log(String str) {
--- a/test/jdk/javax/net/ssl/SSLEngine/NoAuthClientAuth.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLEngine/NoAuthClientAuth.java	Mon Jun 25 13:41:39 2018 -0700
@@ -21,15 +21,19 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test
  * @bug 4495742
  * @summary Demonstrate SSLEngine switch from no client auth to client auth.
- * @run main/othervm NoAuthClientAuth
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
- *
+ * @run main/othervm NoAuthClientAuth SSLv3
+ * @run main/othervm NoAuthClientAuth TLSv1
+ * @run main/othervm NoAuthClientAuth TLSv1.1
+ * @run main/othervm NoAuthClientAuth TLSv1.2
  * @author Brad R. Wetmore
  */
 
@@ -78,6 +82,7 @@
 import java.security.*;
 import java.nio.*;
 
+// Note that this test case depends on JSSE provider implementation details.
 public class NoAuthClientAuth {
 
     /*
@@ -94,7 +99,7 @@
      * including specific handshake messages, and might be best examined
      * after gaining some familiarity with this application.
      */
-    private static boolean debug = false;
+    private static boolean debug = true;
 
     private SSLContext sslc;
 
@@ -128,15 +133,21 @@
     private static String trustFilename =
             System.getProperty("test.src", ".") + "/" + pathToStores +
                 "/" + trustStoreFile;
+    // the specified protocol
+    private static String tlsProtocol;
 
     /*
      * Main entry point for this test.
      */
     public static void main(String args[]) throws Exception {
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
         if (debug) {
             System.setProperty("javax.net.debug", "all");
         }
 
+        tlsProtocol = args[0];
+
         NoAuthClientAuth test = new NoAuthClientAuth();
         test.runTest();
 
@@ -243,8 +254,8 @@
                     for (java.security.cert.Certificate c : certs) {
                         System.out.println(c);
                     }
-                    log("Closing server.");
-                    serverEngine.closeOutbound();
+//                    log("Closing server.");
+//                    serverEngine.closeOutbound();
                 } // nothing.
             }
 
@@ -253,18 +264,30 @@
 
             log("----");
 
-            clientResult = clientEngine.unwrap(sTOc, clientIn);
-            log("client unwrap: ", clientResult);
-            runDelegatedTasks(clientResult, clientEngine);
-            clientIn.clear();
+            if (!clientEngine.isInboundDone()) {
+                clientResult = clientEngine.unwrap(sTOc, clientIn);
+                log("client unwrap: ", clientResult);
+                runDelegatedTasks(clientResult, clientEngine);
+                clientIn.clear();
+                sTOc.compact();
+            } else {
+                sTOc.clear();
+            }
 
-            serverResult = serverEngine.unwrap(cTOs, serverIn);
-            log("server unwrap: ", serverResult);
-            runDelegatedTasks(serverResult, serverEngine);
-            serverIn.clear();
+            if (!serverEngine.isInboundDone()) {
+                serverResult = serverEngine.unwrap(cTOs, serverIn);
+                log("server unwrap: ", serverResult);
+                runDelegatedTasks(serverResult, serverEngine);
+                serverIn.clear();
+                cTOs.compact();
+            } else {
+                cTOs.clear();
+            }
 
-            cTOs.compact();
-            sTOc.compact();
+            if (hsCompleted == 2) {
+                  log("Closing server.");
+                  serverEngine.closeOutbound();
+            }
         }
     }
 
@@ -286,6 +309,7 @@
          */
         clientEngine = sslc.createSSLEngine("client", 80);
         clientEngine.setUseClientMode(true);
+        clientEngine.setEnabledProtocols(new String[] { tlsProtocol });
     }
 
     /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/net/ssl/SSLSession/RenegotiateTLS13.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @run main/othervm -Djavax.net.debug=ssl RenegotiateTLS13
+ */
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+
+public class RenegotiateTLS13 {
+
+    static final String dataString = "This is a test";
+
+    // Run the server as a thread instead of the client
+    static boolean separateServerThread = false;
+
+    static String pathToStores = "../etc";
+    static String keyStoreFile = "keystore";
+    static String trustStoreFile = "truststore";
+    static String passwd = "passphrase";
+
+    // Server ready flag
+    volatile static boolean serverReady = false;
+    // Turn on SSL debugging
+    static boolean debug = false;
+    // Server done flag
+    static boolean done = false;
+
+    // Main server code
+
+    void doServerSide() throws Exception {
+        SSLServerSocketFactory sslssf;
+            sslssf = initContext().getServerSocketFactory();
+        SSLServerSocket sslServerSocket =
+            (SSLServerSocket) sslssf.createServerSocket(serverPort);
+        serverPort = sslServerSocket.getLocalPort();
+
+        serverReady = true;
+
+        SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+
+        DataInputStream sslIS =
+            new DataInputStream(sslSocket.getInputStream());
+        String s = "";
+        while (s.compareTo("done") != 0) {
+            try {
+                s = sslIS.readUTF();
+                System.out.println("Received: " + s);
+            } catch (IOException e) {
+                throw e;
+            }
+        }
+        done = true;
+        sslSocket.close();
+    }
+
+    // Main client code
+    void doClientSide() throws Exception {
+
+        while (!serverReady) {
+            Thread.sleep(5);
+        }
+
+        SSLSocketFactory sslsf;
+        sslsf = initContext().getSocketFactory();
+
+        SSLSocket sslSocket = (SSLSocket)
+            sslsf.createSocket("localhost", serverPort);
+
+        DataOutputStream sslOS =
+            new DataOutputStream(sslSocket.getOutputStream());
+
+        sslOS.writeUTF("With " + dataString);
+        sslOS.writeUTF("With " + dataString);
+        sslOS.writeUTF("With " + dataString);
+
+        sslSocket.startHandshake();
+
+        sslOS.writeUTF("With " + dataString);
+        sslOS.writeUTF("With " + dataString);
+        sslOS.writeUTF("With " + dataString);
+
+        sslSocket.startHandshake();
+
+        sslOS.writeUTF("With " + dataString);
+        sslOS.writeUTF("With " + dataString);
+        sslOS.writeUTF("With " + dataString);
+        sslOS.writeUTF("done");
+
+        while (!done) {
+            Thread.sleep(5);
+        }
+        sslSocket.close();
+    }
+
+    volatile int serverPort = 0;
+
+    volatile Exception serverException = null;
+    volatile Exception clientException = null;
+
+    public static void main(String[] args) throws Exception {
+        String keyFilename =
+            System.getProperty("test.src", "./") + "/" + pathToStores +
+                "/" + keyStoreFile;
+        String trustFilename =
+            System.getProperty("test.src", "./") + "/" + pathToStores +
+                "/" + trustStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+        if (debug)
+            System.setProperty("javax.net.debug", "ssl");
+
+        new RenegotiateTLS13();
+    }
+
+    Thread clientThread = null;
+    Thread serverThread = null;
+
+    /*
+     * Primary constructor, used to drive remainder of the test.
+     *
+     * Fork off the other side, then do your work.
+     */
+    RenegotiateTLS13() throws Exception {
+        try {
+            if (separateServerThread) {
+                startServer(true);
+                startClient(false);
+            } else {
+                startClient(true);
+                startServer(false);
+            }
+        } catch (Exception e) {
+            // swallow for now.  Show later
+        }
+
+        /*
+         * Wait for other side to close down.
+         */
+        if (separateServerThread) {
+            serverThread.join();
+        } else {
+            clientThread.join();
+        }
+
+        /*
+         * When we get here, the test is pretty much over.
+         * Which side threw the error?
+         */
+        Exception local;
+        Exception remote;
+        String whichRemote;
+
+        if (separateServerThread) {
+            remote = serverException;
+            local = clientException;
+            whichRemote = "server";
+        } else {
+            remote = clientException;
+            local = serverException;
+            whichRemote = "client";
+        }
+
+        /*
+         * If both failed, return the curthread's exception, but also
+         * print the remote side Exception
+         */
+        if ((local != null) && (remote != null)) {
+            System.out.println(whichRemote + " also threw:");
+            remote.printStackTrace();
+            System.out.println();
+            throw local;
+        }
+
+        if (remote != null) {
+            throw remote;
+        }
+
+        if (local != null) {
+            throw local;
+        }
+    }
+
+    void startServer(boolean newThread) throws Exception {
+        if (newThread) {
+            serverThread = new Thread() {
+                public void run() {
+                    try {
+                        doServerSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our server thread just died.
+                         *
+                         * Release the client, if not active already...
+                         */
+                        System.err.println("Server died...");
+                        serverReady = true;
+                        serverException = e;
+                    }
+                }
+            };
+            serverThread.start();
+        } else {
+            try {
+                doServerSide();
+            } catch (Exception e) {
+                serverException = e;
+            } finally {
+                serverReady = true;
+            }
+        }
+    }
+
+    void startClient(boolean newThread) throws Exception {
+        if (newThread) {
+            clientThread = new Thread() {
+                public void run() {
+                    try {
+                        doClientSide();
+                    } catch (Exception e) {
+                        /*
+                         * Our client thread just died.
+                         */
+                        System.err.println("Client died...");
+                        clientException = e;
+                    }
+                }
+            };
+            clientThread.start();
+        } else {
+            try {
+                doClientSide();
+            } catch (Exception e) {
+                clientException = e;
+            }
+        }
+    }
+
+    // Initialize context for TLS 1.3
+    SSLContext initContext() throws Exception {
+        System.out.println("Using TLS13");
+        SSLContext sc = SSLContext.getInstance("TLSv1.3");
+        KeyStore ks = KeyStore.getInstance(
+                new File(System.getProperty("javax.net.ssl.keyStore")),
+                passwd.toCharArray());
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(
+                KeyManagerFactory.getDefaultAlgorithm());
+        kmf.init(ks, passwd.toCharArray());
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
+                TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init(ks);
+        sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+        return sc;
+    }
+}
--- a/test/jdk/javax/net/ssl/SSLSession/TestEnabledProtocols.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/SSLSession/TestEnabledProtocols.java	Mon Jun 25 13:41:39 2018 -0700
@@ -234,6 +234,10 @@
                 } catch (java.lang.InterruptedException ie) {
                     // must have been interrupted, no harm
                     break;
+                } catch (SSLException ssle) {
+                    // The client side may have closed the socket.
+                    System.out.println("Server SSLException:");
+                    ssle.printStackTrace(System.out);
                 } catch (Exception e) {
                     System.out.println("Server exception:");
                     e.printStackTrace(System.out);
--- a/test/jdk/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java	Mon Jun 25 13:41:39 2018 -0700
@@ -45,6 +45,7 @@
 import java.util.*;
 import java.net.*;
 import javax.net.ssl.*;
+import java.security.Security;
 
 public class SSLSocketExplorerFailure {
 
@@ -232,6 +233,9 @@
     volatile Exception clientException = null;
 
     public static void main(String[] args) throws Exception {
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+
         String keyFilename =
             System.getProperty("test.src", ".") + "/" + pathToStores +
                 "/" + keyStoreFile;
--- a/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java	Mon Jun 25 13:41:39 2018 -0700
@@ -54,7 +54,8 @@
 import java.security.interfaces.*;
 import java.util.Base64;
 
-
+// Note: this test case works only on TLS 1.2 and prior versions because of
+// the use of MD5withRSA signed certificate.
 public class SSLSocketSNISensitive {
 
     /*
@@ -415,7 +416,7 @@
         TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
         tmf.init(ks);
 
-        SSLContext ctx = SSLContext.getInstance("TLS");
+        SSLContext ctx = SSLContext.getInstance("TLSv1.2");
         KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
         kmf.init(ks, passphrase);
 
--- a/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -109,6 +109,11 @@
     static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
     static int intOcspPort;                 // Port number for intermed. OCSP
 
+    // Extra configuration parameters and constants
+    static final String[] TLS13ONLY = new String[] { "TLSv1.3" };
+    static final String[] TLS12MAX =
+            new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" };
+
     private static final String SIMPLE_WEB_PAGE = "<HTML>\n" +
             "<HEAD><Title>Web Page!</Title></HEAD>\n" +
             "<BODY><H1>Web Page!</H1></BODY>\n</HTML>";
@@ -124,7 +129,7 @@
      */
     public static void main(String[] args) throws Exception {
         if (debug) {
-            System.setProperty("javax.net.debug", "ssl");
+            System.setProperty("javax.net.debug", "ssl:handshake");
         }
 
         System.setProperty("javax.net.ssl.keyStore", "");
@@ -136,7 +141,8 @@
         createPKI();
         utcDateFmt.setTimeZone(TimeZone.getTimeZone("GMT"));
 
-        testPKIXParametersRevEnabled();
+        testPKIXParametersRevEnabled(TLS12MAX);
+        testPKIXParametersRevEnabled(TLS13ONLY);
 
         // shut down the OCSP responders before finishing the test
         intOcsp.stop();
@@ -148,8 +154,10 @@
      * enabled and client-side OCSP disabled.  It will only pass if all
      * stapled responses are present, valid and have a GOOD status.
      */
-    static void testPKIXParametersRevEnabled() throws Exception {
+    static void testPKIXParametersRevEnabled(String[] allowedProts)
+            throws Exception {
         ClientParameters cliParams = new ClientParameters();
+        cliParams.protocols = allowedProts;
         ServerParameters servParams = new ServerParameters();
         serverReady = false;
 
@@ -194,7 +202,7 @@
         // because of the client alert
         if (tr.serverExc instanceof SSLHandshakeException) {
             if (!tr.serverExc.getMessage().contains(
-                    "alert: bad_certificate_status_response")) {
+                    "bad_certificate_status_response")) {
                 throw tr.serverExc;
             }
         }
@@ -333,7 +341,8 @@
             if (contentLength == -1) {
                 contentLength = Integer.MAX_VALUE;
             }
-            byte[] response = new byte[contentLength > 2048 ? 2048 : contentLength];
+            byte[] response = new byte[contentLength > 2048 ? 2048 :
+                contentLength];
             int total = 0;
             while (total < contentLength) {
                 int count = in.read(response, total, response.length - total);
@@ -391,8 +400,8 @@
     /**
      * Checks a validation failure to see if it failed for the reason we think
      * it should.  This comes in as an SSLException of some sort, but it
-     * encapsulates a ValidatorException which in turn encapsulates the
-     * CertPathValidatorException we are interested in.
+     * encapsulates a CertPathValidatorException at some point in the
+     * exception stack.
      *
      * @param e the exception thrown at the top level
      * @param reason the underlying CertPathValidatorException BasicReason
@@ -404,19 +413,31 @@
             BasicReason reason) {
         boolean result = false;
 
-        if (e instanceof SSLException) {
-            Throwable valExc = e.getCause();
-            if (valExc instanceof sun.security.validator.ValidatorException) {
-                Throwable cause = valExc.getCause();
-                if (cause instanceof CertPathValidatorException) {
-                    CertPathValidatorException cpve =
-                            (CertPathValidatorException)cause;
-                    if (cpve.getReason() == reason) {
-                        result = true;
-                    }
-                }
+        // Locate the CertPathValidatorException.  If one
+        // Does not exist, then it's an automatic failure of
+        // the test.
+        Throwable curExc = e;
+        CertPathValidatorException cpve = null;
+        while (curExc != null) {
+            if (curExc instanceof CertPathValidatorException) {
+                cpve = (CertPathValidatorException)curExc;
             }
+            curExc = curExc.getCause();
         }
+
+        // If we get through the loop and cpve is null then we
+        // we didn't find CPVE and this is a failure
+        if (cpve != null) {
+            if (cpve.getReason() == reason) {
+                result = true;
+            } else {
+                System.out.println("CPVE Reason Mismatch: Expected = " +
+                        reason + ", Actual = " + cpve.getReason());
+            }
+        } else {
+            System.out.println("Failed to find an expected CPVE");
+        }
+
         return result;
     }
 
@@ -698,6 +719,8 @@
         boolean enabled = true;
         PKIXBuilderParameters pkixParams = null;
         PKIXRevocationChecker revChecker = null;
+        String[] protocols = null;
+        String[] cipherSuites = null;
 
         ClientParameters() { }
     }
@@ -717,9 +740,19 @@
     static class TestResult {
         Exception serverExc = null;
         Exception clientExc = null;
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Test Result:\n").
+                append("\tServer Exc = ").append(serverExc).append("\n").
+                append("\tClient Exc = ").append(clientExc).append("\n");
+            return sb.toString();
+        }
     }
 
     static class HtucSSLSocketFactory extends SSLSocketFactory {
+        ClientParameters params;
         SSLContext sslc = SSLContext.getInstance("TLS");
 
         HtucSSLSocketFactory(ClientParameters cliParams)
@@ -747,6 +780,7 @@
             }
 
             sslc.init(null, tmf.getTrustManagers(), null);
+            params = cliParams;
         }
 
         @Override
@@ -754,7 +788,7 @@
                 boolean autoClose) throws IOException {
             Socket sock =  sslc.getSocketFactory().createSocket(s, host, port,
                     autoClose);
-            setCiphers(sock);
+            customizeSocket(sock);
             return sock;
         }
 
@@ -762,7 +796,7 @@
         public Socket createSocket(InetAddress host, int port)
                 throws IOException {
             Socket sock = sslc.getSocketFactory().createSocket(host, port);
-            setCiphers(sock);
+            customizeSocket(sock);
             return sock;
         }
 
@@ -771,7 +805,7 @@
                 InetAddress localAddress, int localPort) throws IOException {
             Socket sock = sslc.getSocketFactory().createSocket(host, port,
                     localAddress, localPort);
-            setCiphers(sock);
+            customizeSocket(sock);
             return sock;
         }
 
@@ -779,7 +813,7 @@
         public Socket createSocket(String host, int port)
                 throws IOException {
             Socket sock =  sslc.getSocketFactory().createSocket(host, port);
-            setCiphers(sock);
+            customizeSocket(sock);
             return sock;
         }
 
@@ -789,7 +823,7 @@
                 throws IOException {
             Socket sock =  sslc.getSocketFactory().createSocket(host, port,
                     localAddress, localPort);
-            setCiphers(sock);
+            customizeSocket(sock);
             return sock;
         }
 
@@ -803,10 +837,15 @@
             return sslc.getSupportedSSLParameters().getCipherSuites();
         }
 
-        private static void setCiphers(Socket sock) {
+        private void customizeSocket(Socket sock) {
             if (sock instanceof SSLSocket) {
-                String[] ciphers = { "TLS_RSA_WITH_AES_128_CBC_SHA" };
-                ((SSLSocket)sock).setEnabledCipherSuites(ciphers);
+                SSLSocket sslSock = (SSLSocket)sock;
+                if (params.protocols != null) {
+                    sslSock.setEnabledProtocols(params.protocols);
+                }
+                if (params.cipherSuites != null) {
+                    sslSock.setEnabledCipherSuites(params.cipherSuites);
+                }
             }
         }
     }
--- a/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -106,7 +106,7 @@
      * including specific handshake messages, and might be best examined
      * after gaining some familiarity with this application.
      */
-    private static final boolean debug = false;
+    private static final boolean debug = true;
 
     private SSLEngine clientEngine;     // client Engine
     private ByteBuffer clientOut;       // write side of clientEngine
@@ -142,12 +142,17 @@
     static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
     static int intOcspPort;                 // Port number for intermed. OCSP
 
+    // Extra configuration parameters and constants
+    static final String[] TLS13ONLY = new String[] { "TLSv1.3" };
+    static final String[] TLS12MAX =
+            new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" };
+
     /*
      * Main entry point for this test.
      */
     public static void main(String args[]) throws Exception {
         if (debug) {
-            System.setProperty("javax.net.debug", "ssl");
+            System.setProperty("javax.net.debug", "ssl:handshake");
         }
 
         // Create the PKI we will use for the test and start the OCSP servers
@@ -166,16 +171,23 @@
                                 TimeUnit.HOURS.toMillis(8))));
         intOcsp.updateStatusDb(revInfo);
 
-        SSLEngineWithStapling test = new SSLEngineWithStapling();
-        try {
-            test.runTest();
-            throw new RuntimeException("Expected failure due to revocation " +
-                    "did not occur");
-        } catch (Exception e) {
-            if (!checkClientValidationFailure(e,
-                    CertPathValidatorException.BasicReason.REVOKED)) {
-                System.out.println("*** Didn't find the exception we wanted");
-                throw e;
+        // Create a list of TLS protocol configurations we can use to
+        // drive tests with different handshaking models.
+        List<String[]> allowedProtList = List.of(TLS12MAX, TLS13ONLY);
+
+        for (String[] protocols : allowedProtList) {
+            SSLEngineWithStapling test = new SSLEngineWithStapling();
+            try {
+                test.runTest(protocols);
+                throw new RuntimeException("Expected failure due to " +
+                        "revocation did not occur");
+            } catch (Exception e) {
+                if (!checkClientValidationFailure(e,
+                        CertPathValidatorException.BasicReason.REVOKED)) {
+                    System.out.println(
+                            "*** Didn't find the exception we wanted");
+                    throw e;
+                }
             }
         }
 
@@ -218,10 +230,10 @@
      * One could easily separate these phases into separate
      * sections of code.
      */
-    private void runTest() throws Exception {
+    private void runTest(String[] protocols) throws Exception {
         boolean dataDone = false;
 
-        createSSLEngines();
+        createSSLEngines(protocols);
         createBuffers();
 
         SSLEngineResult clientResult;   // results from client's last operation
@@ -290,7 +302,7 @@
      * Using the SSLContext created during object creation,
      * create/configure the SSLEngines we'll use for this test.
      */
-    private void createSSLEngines() throws Exception {
+    private void createSSLEngines(String[] protocols) throws Exception {
         // Initialize the KeyManager and TrustManager for the server
         KeyManagerFactory servKmf = KeyManagerFactory.getInstance("PKIX");
         servKmf.init(serverKeystore, passwd.toCharArray());
@@ -321,6 +333,7 @@
          * handshake.
          */
         serverEngine = servCtx.createSSLEngine();
+        serverEngine.setEnabledProtocols(protocols);
         serverEngine.setUseClientMode(false);
         serverEngine.setNeedClientAuth(false);
 
@@ -328,6 +341,7 @@
          * Similar to above, but using client mode instead.
          */
         clientEngine = cliCtx.createSSLEngine("client", 80);
+        clientEngine.setEnabledProtocols(protocols);
         clientEngine.setUseClientMode(true);
     }
 
@@ -637,8 +651,8 @@
     /**
      * Checks a validation failure to see if it failed for the reason we think
      * it should.  This comes in as an SSLException of some sort, but it
-     * encapsulates a ValidatorException which in turn encapsulates the
-     * CertPathValidatorException we are interested in.
+     * encapsulates a CertPathValidatorException at some point in the
+     * exception stack.
      *
      * @param e the exception thrown at the top level
      * @param reason the underlying CertPathValidatorException BasicReason
@@ -650,22 +664,31 @@
             CertPathValidatorException.BasicReason reason) {
         boolean result = false;
 
-        if (e instanceof SSLException) {
-            Throwable sslhe = e.getCause();
-            if (sslhe instanceof SSLHandshakeException) {
-                Throwable valExc = sslhe.getCause();
-                if (valExc instanceof sun.security.validator.ValidatorException) {
-                    Throwable cause = valExc.getCause();
-                    if (cause instanceof CertPathValidatorException) {
-                        CertPathValidatorException cpve =
-                                (CertPathValidatorException)cause;
-                        if (cpve.getReason() == reason) {
-                            result = true;
-                        }
-                    }
-                }
+        // Locate the CertPathValidatorException.  If one
+        // Does not exist, then it's an automatic failure of
+        // the test.
+        Throwable curExc = e;
+        CertPathValidatorException cpve = null;
+        while (curExc != null) {
+            if (curExc instanceof CertPathValidatorException) {
+                cpve = (CertPathValidatorException)curExc;
             }
+            curExc = curExc.getCause();
         }
+
+        // If we get through the loop and cpve is null then we
+        // we didn't find CPVE and this is a failure
+        if (cpve != null) {
+            if (cpve.getReason() == reason) {
+                result = true;
+            } else {
+                System.out.println("CPVE Reason Mismatch: Expected = " +
+                        reason + ", Actual = " + cpve.getReason());
+            }
+        } else {
+            System.out.println("Failed to find an expected CPVE");
+        }
+
         return result;
     }
 }
--- a/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java	Mon Jun 25 13:41:39 2018 -0700
@@ -35,6 +35,10 @@
 
 import java.io.*;
 import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import javax.net.ssl.*;
@@ -71,7 +75,7 @@
      */
 
     // Turn on TLS debugging
-    static boolean debug = false;
+    static final boolean debug = false;
 
     /*
      * Should we run the client or server in a separate thread?
@@ -106,6 +110,11 @@
     static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
     static int intOcspPort;                 // Port number for intermed. OCSP
 
+    // Extra configuration parameters and constants
+    static final String[] TLS13ONLY = new String[] { "TLSv1.3" };
+    static final String[] TLS12MAX =
+            new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" };
+
     /*
      * If the client or server is doing some kind of object creation
      * that the other side depends on, and that thread prematurely
@@ -116,20 +125,31 @@
      */
     public static void main(String[] args) throws Exception {
         if (debug) {
-            System.setProperty("javax.net.debug", "ssl");
+            System.setProperty("javax.net.debug", "ssl:handshake");
         }
 
         try {
             // Create the PKI we will use for the test and start the OCSP servers
             createPKI();
 
-            testAllDefault();
-            testPKIXParametersRevEnabled();
-            testRevokedCertificate();
-            testHardFailFallback();
-            testSoftFailFallback();
-            testLatencyNoStaple(false);
-            testLatencyNoStaple(true);
+            testAllDefault(false);
+            testAllDefault(true);
+            testPKIXParametersRevEnabled(false);
+            testPKIXParametersRevEnabled(true);
+            testRevokedCertificate(false);
+            testRevokedCertificate(true);
+            testRevokedIntermediate(false);
+            testRevokedIntermediate(true);
+            testMissingIntermediate(false);
+            testMissingIntermediate(true);
+            testHardFailFallback(false);
+            testHardFailFallback(true);
+            testSoftFailFallback(false);
+            testSoftFailFallback(true);
+            testLatencyNoStaple(false, false);
+            testLatencyNoStaple(false, true);
+            testLatencyNoStaple(true, false);
+            testLatencyNoStaple(true, true);
         } finally {
             // shut down the OCSP responders before finishing the test
             intOcsp.stop();
@@ -140,9 +160,16 @@
     /**
      * Default test using no externally-configured PKIXBuilderParameters
      */
-    static void testAllDefault() throws Exception {
+    static void testAllDefault(boolean isTls13) throws Exception {
         ClientParameters cliParams = new ClientParameters();
         ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
         serverReady = false;
         Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
                 new HashMap<>();
@@ -188,9 +215,16 @@
      * enabled and client-side OCSP disabled.  It will only pass if all
      * stapled responses are present, valid and have a GOOD status.
      */
-    static void testPKIXParametersRevEnabled() throws Exception {
+    static void testPKIXParametersRevEnabled(boolean isTls13) throws Exception {
         ClientParameters cliParams = new ClientParameters();
         ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
         serverReady = false;
 
         System.out.println("=====================================");
@@ -222,9 +256,16 @@
      * pass if the OCSP response is found, since we will check the
      * CertPathValidatorException reason for revoked status.
      */
-    static void testRevokedCertificate() throws Exception {
+    static void testRevokedCertificate(boolean isTls13) throws Exception {
         ClientParameters cliParams = new ClientParameters();
         ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
         serverReady = false;
         Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
                 new HashMap<>();
@@ -242,9 +283,9 @@
                         fiveMinsAgo));
         intOcsp.updateStatusDb(revInfo);
 
-        System.out.println("=======================================");
-        System.out.println("Stapling enabled, default configuration");
-        System.out.println("=======================================");
+        System.out.println("============================================");
+        System.out.println("Stapling enabled, detect revoked certificate");
+        System.out.println("============================================");
 
         cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
                 new X509CertSelector());
@@ -274,13 +315,146 @@
     }
 
     /**
+     * Perform a test where the intermediate CA certificate is revoked and
+     * placed in the TLS handshake.  Client-side OCSP is disabled, so this
+     * test will only pass if the OCSP response for the intermediate CA is
+     * found and placed into the CertificateStatus or Certificate message
+     * (depending on the protocol version) since we will check
+     * the CertPathValidatorException reason for revoked status.
+     */
+    static void testRevokedIntermediate(boolean isTls13) throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
+        serverReady = false;
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+                new HashMap<>();
+
+        // We will prove revocation checking is disabled by marking the SSL
+        // certificate as revoked.  The test would only pass if revocation
+        // checking did not happen.
+        X509Certificate intCACert =
+                (X509Certificate)intKeystore.getCertificate(INT_ALIAS);
+        Date fiveMinsAgo = new Date(System.currentTimeMillis() -
+                TimeUnit.MINUTES.toMillis(5));
+        revInfo.put(intCACert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED,
+                        fiveMinsAgo));
+        rootOcsp.updateStatusDb(revInfo);
+
+        System.out.println("===============================================");
+        System.out.println("Stapling enabled, detect revoked CA certificate");
+        System.out.println("===============================================");
+
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+        Security.setProperty("ocsp.enable", "false");
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (!checkClientValidationFailure(tr.clientExc, BasicReason.REVOKED)) {
+            if (tr.clientExc != null) {
+                throw tr.clientExc;
+            } else {
+                throw new RuntimeException(
+                        "Expected client failure, but the client succeeded");
+            }
+        }
+
+        // Return the ssl certificate to non-revoked status
+        revInfo.put(intCACert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        rootOcsp.updateStatusDb(revInfo);
+
+        System.out.println("                 PASS");
+        System.out.println("=======================================\n");
+    }
+
+    /**
+     * Test a case where OCSP stapling is attempted, but partially occurs
+     * because the root OCSP responder is unreachable.  This should use a
+     * default hard-fail behavior.
+     */
+    static void testMissingIntermediate(boolean isTls13) throws Exception {
+        ClientParameters cliParams = new ClientParameters();
+        ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
+        serverReady = false;
+
+        // Make the OCSP responder reject connections
+        rootOcsp.rejectConnections();
+
+        System.out.println("=======================================");
+        System.out.println("Stapling enbled in client and server,");
+        System.out.println("but root OCSP responder disabled.");
+        System.out.println("PKIXParameters with Revocation checking");
+        System.out.println("enabled.");
+        System.out.println("=======================================");
+
+        Security.setProperty("ocsp.enable", "false");
+        cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
+                new X509CertSelector());
+        cliParams.pkixParams.setRevocationEnabled(true);
+
+        SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
+                servParams);
+        TestResult tr = sslTest.getResult();
+        if (!checkClientValidationFailure(tr.clientExc,
+                BasicReason.UNDETERMINED_REVOCATION_STATUS)) {
+            if (tr.clientExc != null) {
+                throw tr.clientExc;
+            } else {
+                throw new RuntimeException(
+                        "Expected client failure, but the client succeeded");
+            }
+        }
+
+        System.out.println("                 PASS");
+        System.out.println("=======================================\n");
+
+        // Make root OCSP responder accept connections
+        rootOcsp.acceptConnections();
+
+        // Wait 5 seconds for server ready
+        for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) {
+            Thread.sleep(50);
+        }
+        if (!rootOcsp.isServerReady()) {
+            throw new RuntimeException("Root OCSP responder not ready yet");
+        }
+    }
+
+    /**
      * Test a case where client-side stapling is attempted, but does not
      * occur because OCSP responders are unreachable.  This should use a
      * default hard-fail behavior.
      */
-    static void testHardFailFallback() throws Exception {
+    static void testHardFailFallback(boolean isTls13) throws Exception {
         ClientParameters cliParams = new ClientParameters();
         ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
         serverReady = false;
 
         // make OCSP responders reject connections
@@ -320,7 +494,8 @@
         rootOcsp.acceptConnections();
 
         // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) {
+        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() ||
+                !rootOcsp.isServerReady())); i++) {
             Thread.sleep(50);
         }
         if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) {
@@ -333,9 +508,16 @@
      * occur because OCSP responders are unreachable.  Client-side OCSP
      * checking is enabled for this, with SOFT_FAIL.
      */
-    static void testSoftFailFallback() throws Exception {
+    static void testSoftFailFallback(boolean isTls13) throws Exception {
         ClientParameters cliParams = new ClientParameters();
         ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
         serverReady = false;
 
         // make OCSP responders reject connections
@@ -381,7 +563,8 @@
         rootOcsp.acceptConnections();
 
         // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) {
+        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() ||
+                        !rootOcsp.isServerReady())); i++) {
             Thread.sleep(50);
         }
         if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) {
@@ -400,9 +583,17 @@
      * will change the result from the client failing with CPVE (no fallback)
      * to a pass (fallback active).
      */
-    static void testLatencyNoStaple(Boolean fallback) throws Exception {
+    static void testLatencyNoStaple(Boolean fallback, boolean isTls13)
+            throws Exception {
         ClientParameters cliParams = new ClientParameters();
         ServerParameters servParams = new ServerParameters();
+        if (isTls13) {
+            cliParams.protocols = TLS13ONLY;
+            servParams.protocols = TLS13ONLY;
+        } else {
+            cliParams.protocols = TLS12MAX;
+            servParams.protocols = TLS12MAX;
+        }
         serverReady = false;
 
         // Give a 1 second delay before running the test.
@@ -411,7 +602,8 @@
         Thread.sleep(1000);
 
         // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) {
+        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() ||
+                        !rootOcsp.isServerReady())); i++) {
             Thread.sleep(50);
         }
         if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) {
@@ -462,7 +654,8 @@
         Thread.sleep(1000);
 
         // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) {
+        for (int i = 0; (i < 100 && (!intOcsp.isServerReady() ||
+                !rootOcsp.isServerReady())); i++) {
             Thread.sleep(50);
         }
         if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) {
@@ -504,7 +697,8 @@
         SSLContext sslc = SSLContext.getInstance("TLS");
         sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 
-        SSLServerSocketFactory sslssf = sslc.getServerSocketFactory();
+        SSLServerSocketFactory sslssf = new CustomizedServerSocketFactory(sslc,
+                servParams.protocols, servParams.ciphers);
 
         try (SSLServerSocket sslServerSocket =
                 (SSLServerSocket) sslssf.createServerSocket(serverPort)) {
@@ -570,7 +764,8 @@
         SSLContext sslc = SSLContext.getInstance("TLS");
         sslc.init(null, tmf.getTrustManagers(), null);
 
-        SSLSocketFactory sslsf = sslc.getSocketFactory();
+        SSLSocketFactory sslsf = new CustomizedSocketFactory(sslc,
+                cliParams.protocols, cliParams.ciphers);
         try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost",
                 serverPort);
                 InputStream sslIS = sslSocket.getInputStream();
@@ -927,6 +1122,8 @@
         boolean enabled = true;
         PKIXBuilderParameters pkixParams = null;
         PKIXRevocationChecker revChecker = null;
+        String[] protocols = null;
+        String[] ciphers = null;
 
         ClientParameters() { }
     }
@@ -939,10 +1136,161 @@
         String respUri = "";
         boolean respOverride = false;
         boolean ignoreExts = false;
+        String[] protocols = null;
+        String[] ciphers = null;
 
         ServerParameters() { }
     }
 
+    static class CustomizedSocketFactory extends SSLSocketFactory {
+        final SSLContext sslc;
+        final String[] protocols;
+        final String[] cipherSuites;
+
+        CustomizedSocketFactory(SSLContext ctx, String[] prots, String[] suites)
+                throws GeneralSecurityException {
+            super();
+            sslc = (ctx != null) ? ctx : SSLContext.getDefault();
+            protocols = prots;
+            cipherSuites = suites;
+
+            // Create the Trust Manager Factory using the PKIX variant
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+        }
+
+        @Override
+        public Socket createSocket(Socket s, String host, int port,
+                boolean autoClose) throws IOException {
+            Socket sock =  sslc.getSocketFactory().createSocket(s, host, port,
+                    autoClose);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port)
+                throws IOException {
+            Socket sock = sslc.getSocketFactory().createSocket(host, port);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port,
+                InetAddress localAddress, int localPort) throws IOException {
+            Socket sock = sslc.getSocketFactory().createSocket(host, port,
+                    localAddress, localPort);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port)
+                throws IOException {
+            Socket sock =  sslc.getSocketFactory().createSocket(host, port);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port,
+                InetAddress localAddress, int localPort)
+                throws IOException {
+            Socket sock =  sslc.getSocketFactory().createSocket(host, port,
+                    localAddress, localPort);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public String[] getDefaultCipherSuites() {
+            return sslc.getDefaultSSLParameters().getCipherSuites();
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            return sslc.getSupportedSSLParameters().getCipherSuites();
+        }
+
+        private void customizeSocket(Socket sock) {
+            if (sock instanceof SSLSocket) {
+                if (protocols != null) {
+                    ((SSLSocket)sock).setEnabledProtocols(protocols);
+                }
+                if (cipherSuites != null) {
+                    ((SSLSocket)sock).setEnabledCipherSuites(cipherSuites);
+                }
+            }
+        }
+    }
+
+    static class CustomizedServerSocketFactory extends SSLServerSocketFactory {
+        final SSLContext sslc;
+        final String[] protocols;
+        final String[] cipherSuites;
+
+        CustomizedServerSocketFactory(SSLContext ctx, String[] prots, String[] suites)
+                throws GeneralSecurityException {
+            super();
+            sslc = (ctx != null) ? ctx : SSLContext.getDefault();
+            protocols = prots;
+            cipherSuites = suites;
+
+            // Create the Trust Manager Factory using the PKIX variant
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+        }
+
+        @Override
+        public ServerSocket createServerSocket(int port) throws IOException {
+            ServerSocket sock =
+                    sslc.getServerSocketFactory().createServerSocket(port);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public ServerSocket createServerSocket(int port, int backlog)
+                throws IOException {
+            ServerSocket sock =
+                    sslc.getServerSocketFactory().createServerSocket(port,
+                            backlog);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public ServerSocket createServerSocket(int port, int backlog,
+                InetAddress ifAddress) throws IOException {
+            ServerSocket sock =
+                    sslc.getServerSocketFactory().createServerSocket(port,
+                            backlog, ifAddress);
+            customizeSocket(sock);
+            return sock;
+        }
+
+        @Override
+        public String[] getDefaultCipherSuites() {
+            return sslc.getDefaultSSLParameters().getCipherSuites();
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            return sslc.getSupportedSSLParameters().getCipherSuites();
+        }
+
+        private void customizeSocket(ServerSocket sock) {
+            if (sock instanceof SSLServerSocket) {
+                if (protocols != null) {
+                    ((SSLServerSocket)sock).setEnabledProtocols(protocols);
+                }
+                if (cipherSuites != null) {
+                    ((SSLServerSocket)sock).setEnabledCipherSuites(cipherSuites);
+                }
+            }
+        }
+    }
+
+
     static class TestResult {
         Exception serverExc = null;
         Exception clientExc = null;
--- a/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java	Mon Jun 25 13:41:39 2018 -0700
@@ -70,11 +70,16 @@
      */
     private static final boolean debug = false;
 
-    // These two ByteBuffer references will be used to hang onto ClientHello
+    // These four ByteBuffer references will be used to hang onto ClientHello
     // messages with and without the status_request[_v2] extensions.  These
-    // will be used in the server-side stapling tests.
-    private static ByteBuffer cHelloStaple;
-    private static ByteBuffer cHelloNoStaple;
+    // will be used in the server-side stapling tests.  There are two sets,
+    // one for 1.2 and earlier versions of the protocol and one for 1.3
+    // and later versions, since the handshake and extension sets differ
+    // between the two sets.
+    private static ByteBuffer cHello12Staple;
+    private static ByteBuffer cHello12NoStaple;
+    private static ByteBuffer cHello13Staple;
+    private static ByteBuffer cHello13NoStaple;
 
     // The following items are used to set up the keystores.
     private static final String passwd = "passphrase";
@@ -94,6 +99,11 @@
     private static SimpleOCSPServer intOcsp;    // Intermediate CA OCSP server
     private static int intOcspPort;             // Port for intermediate OCSP
 
+    // Extra configuration parameters and constants
+    static final String[] TLS13ONLY = new String[] { "TLSv1.3" };
+    static final String[] TLS12MAX =
+            new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" };
+
     // A few helpful TLS definitions to make it easier
     private static final int HELLO_EXT_STATUS_REQ = 5;
     private static final int HELLO_EXT_STATUS_REQ_V2 = 17;
@@ -103,7 +113,7 @@
      */
     public static void main(String args[]) throws Exception {
         if (debug) {
-            System.setProperty("javax.net.debug", "ssl");
+            System.setProperty("javax.net.debug", "ssl:handshake,verbose");
         }
 
         // Create the PKI we will use for the test and start the OCSP servers
@@ -128,6 +138,7 @@
         System.out.println("=========================================");
         System.out.println("Client Test 1: " +
                 "jdk.tls.client.enableStatusRequestExtension = true");
+        System.out.println("Version = TLS 1.2");
         System.out.println("=========================================");
 
         System.setProperty("jdk.tls.client.enableStatusRequestExtension",
@@ -136,6 +147,7 @@
         ctxStaple.init(null, tmf.getTrustManagers(), null);
         SSLEngine engine = ctxStaple.createSSLEngine();
         engine.setUseClientMode(true);
+        engine.setEnabledProtocols(TLS12MAX);
         SSLSession session = engine.getSession();
         ByteBuffer clientOut = ByteBuffer.wrap("I'm a Client".getBytes());
         ByteBuffer cTOs =
@@ -151,12 +163,13 @@
         cTOs.flip();
         System.out.println(dumpHexBytes(cTOs));
         checkClientHello(cTOs, true, true);
-        cHelloStaple = cTOs;
+        cHello12Staple = cTOs;
 
         // Test with the property set to false
         System.out.println("=========================================");
         System.out.println("Client Test 2: " +
                 "jdk.tls.client.enableStatusRequestExtension = false");
+        System.out.println("Version = TLS 1.2");
         System.out.println("=========================================");
 
         System.setProperty("jdk.tls.client.enableStatusRequestExtension",
@@ -165,6 +178,7 @@
         ctxNoStaple.init(null, tmf.getTrustManagers(), null);
         engine = ctxNoStaple.createSSLEngine();
         engine.setUseClientMode(true);
+        engine.setEnabledProtocols(TLS12MAX);
         session = engine.getSession();
         cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize());
 
@@ -178,7 +192,95 @@
         cTOs.flip();
         System.out.println(dumpHexBytes(cTOs));
         checkClientHello(cTOs, false, false);
-        cHelloNoStaple = cTOs;
+        cHello12NoStaple = cTOs;
+
+        // Turn the property back on to true and test using TLS 1.3
+        System.out.println("=========================================");
+        System.out.println("Client Test 3: " +
+                "jdk.tls.client.enableStatusRequestExtension = true");
+        System.out.println("Version = TLS 1.3");
+        System.out.println("=========================================");
+
+        System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+                "true");
+        ctxStaple = SSLContext.getInstance("TLS");
+        ctxStaple.init(null, tmf.getTrustManagers(), null);
+        engine = ctxStaple.createSSLEngine();
+        engine.setUseClientMode(true);
+        engine.setEnabledProtocols(TLS13ONLY);
+        session = engine.getSession();
+        cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize());
+
+        // Create and check the ClientHello message
+        clientResult = engine.wrap(clientOut, cTOs);
+        log("client wrap: ", clientResult);
+        if (clientResult.getStatus() != SSLEngineResult.Status.OK) {
+            throw new SSLException("Client wrap got status: " +
+                    clientResult.getStatus());
+        }
+        cTOs.flip();
+        System.out.println(dumpHexBytes(cTOs));
+        checkClientHello(cTOs, true, false);
+        cHello13Staple = cTOs;
+
+        // Turn the property off again and test in a TLS 1.3 handshake
+        System.out.println("=========================================");
+        System.out.println("Client Test 4: " +
+                "jdk.tls.client.enableStatusRequestExtension = false");
+        System.out.println("Version = TLS 1.3");
+        System.out.println("=========================================");
+
+        System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+                "false");
+        ctxNoStaple = SSLContext.getInstance("TLS");
+        ctxNoStaple.init(null, tmf.getTrustManagers(), null);
+        engine = ctxNoStaple.createSSLEngine();
+        engine.setUseClientMode(true);
+        engine.setEnabledProtocols(TLS13ONLY);
+        session = engine.getSession();
+        cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize());
+
+        // Create and check the ClientHello message
+        clientResult = engine.wrap(clientOut, cTOs);
+        log("client wrap: ", clientResult);
+        if (clientResult.getStatus() != SSLEngineResult.Status.OK) {
+            throw new SSLException("Client wrap got status: " +
+                    clientResult.getStatus());
+        }
+        cTOs.flip();
+        System.out.println(dumpHexBytes(cTOs));
+        checkClientHello(cTOs, false, false);
+        cHello13NoStaple = cTOs;
+
+        // A TLS 1.3-capable hello, one that is not strictly limited to
+        // the TLS 1.3 protocol should have both status_request and
+        // status_request_v2
+        System.out.println("=========================================");
+        System.out.println("Client Test 5: " +
+                "jdk.tls.client.enableStatusRequestExtension = true");
+        System.out.println("Version = TLS 1.3 capable [default hello]");
+        System.out.println("=========================================");
+
+        System.setProperty("jdk.tls.client.enableStatusRequestExtension",
+                "true");
+        ctxStaple = SSLContext.getInstance("TLS");
+        ctxStaple.init(null, tmf.getTrustManagers(), null);
+        engine = ctxStaple.createSSLEngine();
+        engine.setUseClientMode(true);
+        // Note: Unlike the other tests, there is no explicit protocol setting
+        session = engine.getSession();
+        cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize());
+
+        // Create and check the ClientHello message
+        clientResult = engine.wrap(clientOut, cTOs);
+        log("client wrap: ", clientResult);
+        if (clientResult.getStatus() != SSLEngineResult.Status.OK) {
+            throw new SSLException("Client wrap got status: " +
+                    clientResult.getStatus());
+        }
+        cTOs.flip();
+        System.out.println(dumpHexBytes(cTOs));
+        checkClientHello(cTOs, true, true);
     }
 
     private static void testServerProp() throws Exception {
@@ -189,6 +291,7 @@
         System.out.println("=========================================");
         System.out.println("Server Test 1: " +
                 "jdk.tls.server.enableStatusRequestExtension = true");
+        System.out.println("Version = TLS 1.2");
         System.out.println("=========================================");
 
         System.setProperty("jdk.tls.server.enableStatusRequestExtension",
@@ -197,6 +300,7 @@
         ctxStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
         SSLEngine engine = ctxStaple.createSSLEngine();
         engine.setUseClientMode(false);
+        engine.setEnabledProtocols(TLS12MAX);
         SSLSession session = engine.getSession();
         ByteBuffer serverOut = ByteBuffer.wrap("I'm a Server".getBytes());
         ByteBuffer serverIn =
@@ -205,7 +309,7 @@
                 ByteBuffer.allocateDirect(session.getPacketBufferSize());
 
         // Consume the client hello
-        serverResult = engine.unwrap(cHelloStaple, serverIn);
+        serverResult = engine.unwrap(cHello12Staple, serverIn);
         log("server unwrap: ", serverResult);
         if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
             throw new SSLException("Server unwrap got status: " +
@@ -234,12 +338,13 @@
         checkServerHello(sTOc, false, true);
 
         // Flip the client hello so we can reuse it in the next test.
-        cHelloStaple.flip();
+        cHello12Staple.flip();
 
         // Test with the server-side enable property set to false
         System.out.println("=========================================");
         System.out.println("Server Test 2: " +
                 "jdk.tls.server.enableStatusRequestExtension = false");
+        System.out.println("Version = TLS 1.2");
         System.out.println("=========================================");
 
         System.setProperty("jdk.tls.server.enableStatusRequestExtension",
@@ -248,12 +353,13 @@
         ctxNoStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
         engine = ctxNoStaple.createSSLEngine();
         engine.setUseClientMode(false);
+        engine.setEnabledProtocols(TLS12MAX);
         session = engine.getSession();
         serverIn = ByteBuffer.allocate(session.getApplicationBufferSize() + 50);
         sTOc = ByteBuffer.allocateDirect(session.getPacketBufferSize());
 
         // Consume the client hello
-        serverResult = engine.unwrap(cHelloStaple, serverIn);
+        serverResult = engine.unwrap(cHello12Staple, serverIn);
         log("server unwrap: ", serverResult);
         if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
             throw new SSLException("Server unwrap got status: " +
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/net/ssl/TLSCommon/CipherSuite.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+public enum CipherSuite {
+
+    TLS_AES_256_GCM_SHA384(
+            0x1302, Protocol.TLSV1_3, Protocol.TLSV1_3),
+    TLS_AES_128_GCM_SHA256(
+            0x1301, Protocol.TLSV1_3, Protocol.TLSV1_3),
+    TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
+            0xCCAA, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(
+            0xCCA9, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(
+            0xCCA8, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(
+            0xC032, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(
+            0xC031, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(
+            0xC030, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(
+            0xC02F, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(
+            0xC02E, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(
+            0xC02D, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(
+            0xC02C, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(
+            0xC02B, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(
+            0xC02A, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(
+            0xC029, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(
+            0xC028, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(
+            0xC027, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(
+            0xC026, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(
+            0xC025, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(
+            0xC025, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(
+            0xC024, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(
+            0xC023, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_anon_WITH_AES_256_CBC_SHA(
+            0xC019, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_anon_WITH_AES_128_CBC_SHA(
+            0xC018, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA(
+            0xC017, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_anon_WITH_RC4_128_SHA(
+            0xC016, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_anon_WITH_NULL_SHA(
+            0xC015, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(
+            0xC014, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(
+            0xC013, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA(
+            0xC012, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_RC4_128_SHA(
+            0xC011, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_RSA_WITH_NULL_SHA(
+            0xC010, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(
+            0xC00F, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(
+            0xC00E, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA(
+            0xC00D, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_RC4_128_SHA(
+            0xC00C, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_RSA_WITH_NULL_SHA(
+            0xC00B, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(
+            0xC00A, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(
+            0xC009, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA(
+            0xC008, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA(
+            0xC007, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDHE_ECDSA_WITH_NULL_SHA(
+            0xC006, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA(
+            0xC003, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_RC4_128_SHA(
+            0xC002, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_NULL_SHA(
+            0xC001, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_EMPTY_RENEGOTIATION_INFO_SCSV(
+            0x00FF, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_DH_anon_WITH_AES_256_GCM_SHA384(
+            0x00A7, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DH_anon_WITH_AES_128_GCM_SHA256(
+            0x00A6, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(
+            0x00A3, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(
+            0x00A2, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(
+            0x009F, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(
+            0x009E, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_RSA_WITH_AES_256_GCM_SHA384(
+            0x009D, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_RSA_WITH_AES_128_GCM_SHA256(
+            0x009C, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DH_anon_WITH_AES_256_CBC_SHA256(
+            0x006D, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DH_anon_WITH_AES_128_CBC_SHA256(
+            0x006C, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(
+            0x006B, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(
+            0x006A, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(
+            0x0067, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(
+            0x004C, Protocol.TLSV1, Protocol.TLSV1_2),
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(
+            0x0040, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_RSA_WITH_AES_256_CBC_SHA256(
+            0x003D, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_RSA_WITH_AES_128_CBC_SHA256(
+            0x003C, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_RSA_WITH_NULL_SHA256(
+            0x003B, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_DH_anon_WITH_AES_256_CBC_SHA(
+            0x003A, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA(
+            0x0039, Protocol.TLSV1, Protocol.TLSV1_2),
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA(
+            0x0038, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_RSA_WITH_AES_256_CBC_SHA(
+            0x0035, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_DH_anon_WITH_AES_128_CBC_SHA(
+            0x0034, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA(
+            0x0033, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA(
+            0x0032, Protocol.TLSV1_2, Protocol.TLSV1_2),
+    TLS_RSA_WITH_AES_128_CBC_SHA(
+            0x002F, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_KRB5_WITH_3DES_EDE_CBC_MD5(
+            0x0023, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_KRB5_WITH_DES_CBC_MD5(
+            0x0022, Protocol.SSLV3, Protocol.TLSV1_1),
+    TLS_KRB5_WITH_3DES_EDE_CBC_SHA(
+            0x001F, Protocol.SSLV3, Protocol.TLSV1_2),
+    TLS_KRB5_WITH_DES_CBC_SHA(
+            0x001E, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_DH_anon_WITH_3DES_EDE_CBC_SHA(
+            0x001B, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_DH_anon_WITH_DES_CBC_SHA(
+            0x001A, Protocol.SSLV3, Protocol.TLSV1_1),
+    SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA(
+            0x0019, Protocol.SSLV3, Protocol.TLSV1),
+    SSL_DH_anon_WITH_RC4_128_MD5(
+            0x0018, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_DH_anon_EXPORT_WITH_RC4_40_MD5(
+            0x0017, Protocol.SSLV3, Protocol.TLSV1),
+    SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA(
+            0x0016, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_DHE_RSA_WITH_DES_CBC_SHA(
+            0x0015, Protocol.SSLV3, Protocol.TLSV1_1),
+    SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA(
+            0x0014, Protocol.SSLV3, Protocol.TLSV1),
+    SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA(
+            0x0013, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_DHE_DSS_WITH_DES_CBC_SHA(
+            0x0012, Protocol.SSLV3, Protocol.TLSV1_1),
+    SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(
+            0x0011, Protocol.SSLV3, Protocol.TLSV1),
+    SSL_RSA_WITH_3DES_EDE_CBC_SHA(
+            0x000A, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_RSA_WITH_DES_CBC_SHA(
+            0x0009, Protocol.SSLV3, Protocol.TLSV1_1),
+    SSL_RSA_EXPORT_WITH_DES40_CBC_SHA(
+            0x0008, Protocol.SSLV3, Protocol.TLSV1),
+    SSL_RSA_WITH_RC4_128_SHA(
+            0x0005, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_RSA_WITH_RC4_128_MD5(
+            0x0004, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_RSA_EXPORT_WITH_RC4_40_MD5(
+            0x0003, Protocol.SSLV3, Protocol.TLSV1),
+    SSL_RSA_WITH_NULL_SHA(
+            0x0002, Protocol.SSLV3, Protocol.TLSV1_2),
+    SSL_RSA_WITH_NULL_MD5(
+            0x0001, Protocol.SSLV3, Protocol.TLSV1_2);
+
+    public final int id;
+    public final Protocol startProtocol;
+    public final Protocol endProtocol;
+
+    private CipherSuite(
+            int id,
+            Protocol startProtocol,
+            Protocol endProtocol) {
+        this.id = id;
+        this.startProtocol = startProtocol;
+        this.endProtocol = endProtocol;
+    }
+
+    public boolean supportedByProtocol(Protocol protocol) {
+        return startProtocol.id <= protocol.id
+                && protocol.id <= endProtocol.id;
+    }
+
+    public static CipherSuite cipherSuite(String name) {
+        return CipherSuite.valueOf(CipherSuite.class, name);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/net/ssl/TLSCommon/Protocol.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+public enum Protocol {
+
+    SSLV2HELLO(0x0002, "SSLv2Hello"),
+    SSLV3     (0x0300, "SSLv3"),
+    TLSV1     (0x0301, "TLSv1"),
+    TLSV1_1   (0x0302, "TLSv1.1"),
+    TLSV1_2   (0x0303, "TLSv1.2"),
+    TLSV1_3   (0x0304, "TLSv1.3"),
+
+    DTLS1_3   (0xFEFC, "DTLSv1.3"),
+    DTLS1_2   (0xFEFD, "DTLSv1.2"),
+    DTLS1_0   (0xFEFF, "DTLSv1.0");
+
+    public final int id;
+    public final String name;
+
+    private Protocol(int id, String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+    public String toString() {
+        return name;
+    }
+
+    public static Protocol protocol(String name) {
+        for (Protocol protocol : values()) {
+            if (protocol.name.equals(name)) {
+                return protocol;
+            }
+        }
+
+        return null;
+    }
+}
--- a/test/jdk/javax/net/ssl/TLSCommon/SSLEngineTestCase.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/TLSCommon/SSLEngineTestCase.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -85,6 +85,12 @@
                 SSLEngineTestCase.ENABLED_NON_KRB_NOT_ANON_CIPHERS,
                 "Enabled by default non kerberos not anonymous"),
         /**
+         * Ciphers supported by TLS 1.3 only.
+         */
+        TLS13_CIPHERS(
+                SSLEngineTestCase.TLS13_CIPHERS,
+                "Supported by TLS 1.3 only"),
+        /**
          * Ciphers unsupported by the tested SSLEngine.
          */
         UNSUPPORTED_CIPHERS(SSLEngineTestCase.UNSUPPORTED_CIPHERS,
@@ -174,6 +180,11 @@
     private static final String SERVER_NAME = "service.localhost";
     private static final String SNI_PATTERN = ".*";
 
+    private static final String[] TLS13_CIPHERS = {
+            "TLS_AES_256_GCM_SHA384",
+            "TLS_AES_128_GCM_SHA256"
+    };
+
     private static final String[] SUPPORTED_NON_KRB_CIPHERS;
 
     static {
@@ -183,8 +194,8 @@
             List<String> supportedCiphersList = new LinkedList<>();
             for (String cipher : allSupportedCiphers) {
                 if (!cipher.contains("KRB5")
-                    && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
-
+                        && !isTLS13Cipher(cipher)
+                        && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
                     supportedCiphersList.add(cipher);
                 }
             }
@@ -204,6 +215,7 @@
             List<String> supportedCiphersList = new LinkedList<>();
             for (String cipher : allSupportedCiphers) {
                 if (!cipher.contains("KRB5")
+                        && !isTLS13Cipher(cipher)
                         && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")
                         && !cipher.endsWith("_SHA256")
                         && !cipher.endsWith("_SHA384")) {
@@ -226,7 +238,8 @@
             List<String> supportedCiphersList = new LinkedList<>();
             for (String cipher : allSupportedCiphers) {
                 if (cipher.contains("KRB5")
-                    && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
+                        && !isTLS13Cipher(cipher)
+                        && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
                     supportedCiphersList.add(cipher);
                 }
             }
@@ -246,7 +259,8 @@
             List<String> enabledCiphersList = new LinkedList<>();
             for (String cipher : enabledCiphers) {
                 if (!cipher.contains("anon") && !cipher.contains("KRB5")
-                    && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
+                        && !isTLS13Cipher(cipher)
+                        && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
                     enabledCiphersList.add(cipher);
                 }
             }
@@ -303,6 +317,16 @@
         this.maxPacketSize = 0;
     }
 
+    private static boolean isTLS13Cipher(String cipher) {
+        for (String cipherSuite : TLS13_CIPHERS) {
+            if (cipherSuite.equals(cipher)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * Wraps data with the specified engine.
      *
@@ -392,7 +416,7 @@
         int length = net.remaining();
         System.out.println(wrapper + " wrapped " + length + " bytes.");
         System.out.println(wrapper + " handshake status is "
-                + engine.getHandshakeStatus());
+                + engine.getHandshakeStatus() + " Result is " + r.getStatus());
         if (maxPacketSize < length && maxPacketSize != 0) {
             throw new AssertionError("Handshake wrapped net buffer length "
                     + length + " exceeds maximum packet size "
@@ -480,7 +504,7 @@
         SSLEngineResult r = engine.unwrap(net, app);
         app.flip();
         System.out.println(unwrapper + " handshake status is "
-                + engine.getHandshakeStatus());
+                + engine.getHandshakeStatus() + " Result is " + r.getStatus());
         checkResult(r, wantedStatus);
         if (result != null && result.length > 0) {
             result[0] = r;
@@ -713,8 +737,13 @@
                     case "TLSv1.1":
                         runTests(Ciphers.SUPPORTED_NON_KRB_NON_SHA_CIPHERS);
                         break;
-                    default:
+                    case "DTLSv1.1":
+                    case "TLSv1.2":
                         runTests(Ciphers.SUPPORTED_NON_KRB_CIPHERS);
+                        break;
+                    case "TLSv1.3":
+                        runTests(Ciphers.TLS13_CIPHERS);
+                        break;
                 }
                 break;
             case "krb":
--- a/test/jdk/javax/net/ssl/TLSv12/DisabledShortDSAKeys.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/TLSv12/DisabledShortDSAKeys.java	Mon Jun 25 13:41:39 2018 -0700
@@ -289,8 +289,14 @@
     volatile Exception clientException = null;
 
     public static void main(String[] args) throws Exception {
-        if (debug)
+        Security.setProperty("jdk.certpath.disabledAlgorithms",
+                "DSA keySize < 1024");
+        Security.setProperty("jdk.tls.disabledAlgorithms",
+                "DSA keySize < 1024");
+
+        if (debug) {
             System.setProperty("javax.net.debug", "all");
+        }
 
         /*
          * Get the customized arguments.
--- a/test/jdk/javax/net/ssl/TLSv12/DisabledShortRSAKeys.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/TLSv12/DisabledShortRSAKeys.java	Mon Jun 25 13:41:39 2018 -0700
@@ -238,8 +238,14 @@
     }
 
     public static void main(String[] args) throws Exception {
-        if (debug)
+        Security.setProperty("jdk.certpath.disabledAlgorithms",
+                "RSA keySize < 1024");
+        Security.setProperty("jdk.tls.disabledAlgorithms",
+                "RSA keySize < 1024");
+
+        if (debug) {
             System.setProperty("javax.net.debug", "all");
+        }
 
         /*
          * Get the customized arguments.
--- a/test/jdk/javax/net/ssl/ciphersuites/ECCurvesconstraints.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/ciphersuites/ECCurvesconstraints.java	Mon Jun 25 13:41:39 2018 -0700
@@ -40,6 +40,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.IOException;
 import java.security.KeyStore;
 import java.security.KeyFactory;
 import java.security.cert.Certificate;
@@ -49,7 +50,6 @@
 import java.util.Base64;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocket;
@@ -141,7 +141,7 @@
             sslOS.flush();
 
             throw new Exception("EC curve secp224k1 should be disabled");
-        } catch (SSLHandshakeException she) {
+        } catch (IOException she) {
             // expected exception: no cipher suites in common
             System.out.println("Expected exception: " + she);
         } finally {
@@ -183,7 +183,7 @@
             sslIS.read();
 
             throw new Exception("EC curve secp224k1 should be disabled");
-        } catch (SSLHandshakeException she) {
+        } catch (IOException she) {
             // expected exception: Received fatal alert
             System.out.println("Expected exception: " + she);
         } finally {
--- a/test/jdk/javax/net/ssl/etc/README	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/etc/README	Mon Jun 25 13:41:39 2018 -0700
@@ -80,11 +80,34 @@
   -dname "CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US" \
   -validity 3652 -keypass passphrase -keystore keystore -storepass passphrase
 
+Alias name: dummyecrsa
+--------------------
+Creation date: Apr 13, 2018
+Entry type: PrivateKeyEntry
+Certificate chain length: 2
+Certificate[1]:
+Owner: CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US
+Issuer: CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US
+Serial number: 6f2d1faa
+Valid from: Fri Apr 13 16:20:55 CST 2018 until: Wed Apr 12 16:20:55 CST 2028
+Version: 3
+
+This can be generated by using keytool command:
+% keytool -genkeypair -alias dummyecrsa -keyalg EC -keysize 256 \
+  -keypass passphrase -storepass passphrase -keystore keystore \
+  -dname "CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US"
+% keytool -certreq -alias dummyecrsa -storepass passphrase -keystore keystore \
+  -file ecrsa.csr
+% keytool -gencert -alias dummy -storepass passphrase -keystore keystore \
+  -validity 3652 -infile ecrsa.csr -outfile ecrsa.cer
+% keytool -importcert -alias dummyecrsa -storepass passphrase -keystore keystore \
+  -file ecrsa.cer
+
 
 truststore entries
 ==================
-This key store contains only trusted certificate entries.  The same
-certificates are used in both keystore and truststore.
+This key store contains only trusted certificate entries. The same
+certificates, except dummyecrsa, are used in both keystore and truststore.
 
 
 unknown_keystore
Binary file test/jdk/javax/net/ssl/etc/keystore has changed
Binary file test/jdk/javax/net/ssl/etc/truststore has changed
--- a/test/jdk/javax/net/ssl/sanity/ciphersuites/CipherSuitesInOrder.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/sanity/ciphersuites/CipherSuitesInOrder.java	Mon Jun 25 13:41:39 2018 -0700
@@ -30,6 +30,7 @@
  * @test
  * @bug 7174244
  * @summary NPE in Krb5ProxyImpl.getServerKeys()
+ * @ignore the dependent implementation details are changed
  * @run main/othervm CipherSuitesInOrder
  */
 
--- a/test/jdk/javax/net/ssl/sanity/interop/CipherTest.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/sanity/interop/CipherTest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,7 +21,6 @@
  * questions.
  */
 
-
 import java.io.*;
 import java.net.*;
 import java.util.*;
@@ -29,7 +28,6 @@
 
 import java.security.*;
 import java.security.cert.*;
-import java.security.cert.Certificate;
 
 import javax.net.ssl.*;
 
@@ -103,11 +101,11 @@
 
     public static class TestParameters {
 
-        String cipherSuite;
-        String protocol;
+        CipherSuite cipherSuite;
+        Protocol protocol;
         String clientAuth;
 
-        TestParameters(String cipherSuite, String protocol,
+        TestParameters(CipherSuite cipherSuite, Protocol protocol,
                 String clientAuth) {
             this.cipherSuite = cipherSuite;
             this.protocol = protocol;
@@ -115,7 +113,7 @@
         }
 
         boolean isEnabled() {
-            return TLSCipherStatus.isEnabled(cipherSuite, protocol);
+            return cipherSuite.supportedByProtocol(protocol);
         }
 
         public String toString() {
@@ -125,134 +123,6 @@
             }
             return s;
         }
-
-        static enum TLSCipherStatus {
-            // cipher suites supported since TLS 1.2
-            CS_01("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF),
-            CS_02("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",   0x0303, 0xFFFF),
-            CS_03("TLS_RSA_WITH_AES_256_CBC_SHA256",         0x0303, 0xFFFF),
-            CS_04("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",  0x0303, 0xFFFF),
-            CS_05("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",    0x0303, 0xFFFF),
-            CS_06("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_07("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",     0x0303, 0xFFFF),
-
-            CS_08("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF),
-            CS_09("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",   0x0303, 0xFFFF),
-            CS_10("TLS_RSA_WITH_AES_128_CBC_SHA256",         0x0303, 0xFFFF),
-            CS_11("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",  0x0303, 0xFFFF),
-            CS_12("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",    0x0303, 0xFFFF),
-            CS_13("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_14("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",     0x0303, 0xFFFF),
-
-            CS_15("TLS_DH_anon_WITH_AES_256_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_17("TLS_RSA_WITH_NULL_SHA256",                0x0303, 0xFFFF),
-
-            CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF),
-            CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF),
-            CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",   0x0303, 0xFFFF),
-            CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384",         0x0303, 0xFFFF),
-            CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",  0x0303, 0xFFFF),
-            CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",    0x0303, 0xFFFF),
-            CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",     0x0303, 0xFFFF),
-            CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",     0x0303, 0xFFFF),
-
-            CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",   0x0303, 0xFFFF),
-            CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256",         0x0303, 0xFFFF),
-            CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",  0x0303, 0xFFFF),
-            CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",    0x0303, 0xFFFF),
-            CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",     0x0303, 0xFFFF),
-            CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",     0x0303, 0xFFFF),
-
-            CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384",     0x0303, 0xFFFF),
-            CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256",     0x0303, 0xFFFF),
-
-            // cipher suites obsoleted since TLS 1.2
-            CS_50("SSL_RSA_WITH_DES_CBC_SHA",                0x0000, 0x0303),
-            CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA",            0x0000, 0x0303),
-            CS_52("SSL_DHE_DSS_WITH_DES_CBC_SHA",            0x0000, 0x0303),
-            CS_53("SSL_DH_anon_WITH_DES_CBC_SHA",            0x0000, 0x0303),
-            CS_54("TLS_KRB5_WITH_DES_CBC_SHA",               0x0000, 0x0303),
-            CS_55("TLS_KRB5_WITH_DES_CBC_MD5",               0x0000, 0x0303),
-
-            // cipher suites obsoleted since TLS 1.1
-            CS_60("SSL_RSA_EXPORT_WITH_RC4_40_MD5",          0x0000, 0x0302),
-            CS_61("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",      0x0000, 0x0302),
-            CS_62("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",       0x0000, 0x0302),
-            CS_63("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",   0x0000, 0x0302),
-            CS_64("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",   0x0000, 0x0302),
-            CS_65("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",   0x0000, 0x0302),
-            CS_66("TLS_KRB5_EXPORT_WITH_RC4_40_SHA",         0x0000, 0x0302),
-            CS_67("TLS_KRB5_EXPORT_WITH_RC4_40_MD5",         0x0000, 0x0302),
-            CS_68("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",     0x0000, 0x0302),
-            CS_69("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",     0x0000, 0x0302),
-
-            // ignore TLS_EMPTY_RENEGOTIATION_INFO_SCSV always
-            CS_99("TLS_EMPTY_RENEGOTIATION_INFO_SCSV",       0xFFFF, 0x0000);
-
-            // the cipher suite name
-            final String cipherSuite;
-
-            // supported since protocol version
-            final int supportedSince;
-
-            // obsoleted since protocol version
-            final int obsoletedSince;
-
-            TLSCipherStatus(String cipherSuite,
-                    int supportedSince, int obsoletedSince) {
-                this.cipherSuite = cipherSuite;
-                this.supportedSince = supportedSince;
-                this.obsoletedSince = obsoletedSince;
-            }
-
-            static boolean isEnabled(String cipherSuite, String protocol) {
-                int versionNumber = toVersionNumber(protocol);
-
-                if (versionNumber < 0) {
-                    return true;  // unlikely to happen
-                }
-
-                for (TLSCipherStatus status : TLSCipherStatus.values()) {
-                    if (cipherSuite.equals(status.cipherSuite)) {
-                        if ((versionNumber < status.supportedSince) ||
-                            (versionNumber >= status.obsoletedSince)) {
-                            return false;
-                        }
-
-                        return true;
-                    }
-                }
-
-                return true;
-            }
-
-            private static int toVersionNumber(String protocol) {
-                int versionNumber = -1;
-
-                switch (protocol) {
-                    case "SSLv2Hello":
-                        versionNumber = 0x0002;
-                        break;
-                    case "SSLv3":
-                        versionNumber = 0x0300;
-                        break;
-                    case "TLSv1":
-                        versionNumber = 0x0301;
-                        break;
-                    case "TLSv1.1":
-                        versionNumber = 0x0302;
-                        break;
-                    case "TLSv1.2":
-                        versionNumber = 0x0303;
-                        break;
-                    default:
-                        // unlikely to happen
-                }
-
-                return versionNumber;
-            }
-        }
     }
 
     private List<TestParameters> tests;
@@ -269,11 +139,23 @@
         String[] clientAuths = {null, "RSA", "DSA"};
         tests = new ArrayList<TestParameters>(
             cipherSuites.length * protocols.length * clientAuths.length);
-        for (int i = 0; i < cipherSuites.length; i++) {
-            String cipherSuite = cipherSuites[i];
+        for (int j = 0; j < protocols.length; j++) {
+            String protocol = protocols[j];
+            if (protocol.equals(Protocol.SSLV2HELLO.name)) {
+                System.out.println("Skipping SSLv2Hello protocol");
+                continue;
+            }
 
-            for (int j = 0; j < protocols.length; j++) {
-                String protocol = protocols[j];
+            for (int i = 0; i < cipherSuites.length; i++) {
+                String cipherSuite = cipherSuites[i];
+
+                // skip kerberos cipher suites and TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+                if (cipherSuite.startsWith("TLS_KRB5") || cipherSuite.equals(
+                        CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.name())) {
+                    System.out.println("Skipping unsupported test for " +
+                                        cipherSuite + " of " + protocol);
+                    continue;
+                }
 
                 if (!peerFactory.isSupported(cipherSuite, protocol)) {
                     continue;
@@ -281,13 +163,16 @@
 
                 for (int k = 0; k < clientAuths.length; k++) {
                     String clientAuth = clientAuths[k];
-                    if ((clientAuth != null) &&
-                            (cipherSuite.indexOf("DH_anon") != -1)) {
-                        // no client with anonymous ciphersuites
+                    // no client with anonymous cipher suites;
+                    // TLS 1.3 doesn't support DSA
+                    if ((clientAuth != null && cipherSuite.contains("DH_anon"))
+                            || ("DSA".equals(clientAuth) && "TLSv1.3".equals(protocol))) {
                         continue;
                     }
-                    tests.add(new TestParameters(cipherSuite, protocol,
-                        clientAuth));
+                    tests.add(new TestParameters(
+                            CipherSuite.cipherSuite(cipherSuite),
+                            Protocol.protocol(protocol),
+                            clientAuth));
                 }
             }
         }
@@ -356,7 +241,7 @@
                     // no more tests
                     break;
                 }
-                if (params.isEnabled() == false) {
+                if (!params.isEnabled()) {
                     System.out.println("Skipping disabled test " + params);
                     continue;
                 }
@@ -422,7 +307,7 @@
         }
         PATH = new File(System.getProperty("test.src", "."), relPath);
         CipherTest.peerFactory = peerFactory;
-        System.out.print(
+        System.out.println(
             "Initializing test '" + peerFactory.getName() + "'...");
         secureRandom = new SecureRandom();
         secureRandom.nextInt();
@@ -455,27 +340,12 @@
         abstract Server newServer(CipherTest cipherTest) throws Exception;
 
         boolean isSupported(String cipherSuite, String protocol) {
-            // skip kerberos cipher suites
-            if (cipherSuite.startsWith("TLS_KRB5")) {
-                System.out.println("Skipping unsupported test for " +
-                                    cipherSuite + " of " + protocol);
-                return false;
-            }
-
-            // skip SSLv2Hello protocol
-            if (protocol.equals("SSLv2Hello")) {
-                System.out.println("Skipping unsupported test for " +
-                                    cipherSuite + " of " + protocol);
-                return false;
-            }
-
             // ignore exportable cipher suite for TLSv1.1
-            if (protocol.equals("TLSv1.1")) {
-                if (cipherSuite.indexOf("_EXPORT_WITH") != -1) {
+            if (protocol.equals("TLSv1.1")
+                    && (cipherSuite.indexOf("_EXPORT_WITH") != -1)) {
                     System.out.println("Skipping obsoleted test for " +
                                         cipherSuite + " of " + protocol);
                     return false;
-                }
             }
 
             // ignore obsoleted cipher suite for the specified protocol
--- a/test/jdk/javax/net/ssl/sanity/interop/ClientJSSEServerJSSE.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/sanity/interop/ClientJSSEServerJSSE.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
  * @bug 4496785
  * @summary Verify that all ciphersuites work in all configurations
  * @author Andreas Sterbenz
+ * @library ../../TLSCommon
  * @run main/othervm/timeout=300 ClientJSSEServerJSSE
  */
 import java.security.Security;
--- a/test/jdk/javax/net/ssl/sanity/interop/JSSEClient.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/sanity/interop/JSSEClient.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,16 +21,16 @@
  * questions.
  */
 
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.cert.*;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.security.cert.Certificate;
 
-import javax.net.ssl.*;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
 
 class JSSEClient extends CipherTest.Client {
 
@@ -47,12 +47,15 @@
         SSLSocket socket = null;
         try {
             keyManager.setAuthType(params.clientAuth);
-            sslContext.init(new KeyManager[] {keyManager}, new TrustManager[] {cipherTest.trustManager}, cipherTest.secureRandom);
+            sslContext.init(
+                    new KeyManager[] { keyManager },
+                    new TrustManager[] { CipherTest.trustManager },
+                    CipherTest.secureRandom);
             SSLSocketFactory factory = (SSLSocketFactory)sslContext.getSocketFactory();
-            socket = (SSLSocket)factory.createSocket("127.0.0.1", cipherTest.serverPort);
-            socket.setSoTimeout(cipherTest.TIMEOUT);
-            socket.setEnabledCipherSuites(new String[] {params.cipherSuite});
-            socket.setEnabledProtocols(new String[] {params.protocol});
+            socket = (SSLSocket)factory.createSocket("127.0.0.1", CipherTest.serverPort);
+            socket.setSoTimeout(CipherTest.TIMEOUT);
+            socket.setEnabledCipherSuites(new String[] { params.cipherSuite.name() });
+            socket.setEnabledProtocols(new String[] { params.protocol.name });
             InputStream in = socket.getInputStream();
             OutputStream out = socket.getOutputStream();
             sendRequest(in, out);
@@ -60,12 +63,14 @@
             SSLSession session = socket.getSession();
             session.invalidate();
             String cipherSuite = session.getCipherSuite();
-            if (params.cipherSuite.equals(cipherSuite) == false) {
-                throw new Exception("Negotiated ciphersuite mismatch: " + cipherSuite + " != " + params.cipherSuite);
+            if (!params.cipherSuite.name().equals(cipherSuite)) {
+                throw new Exception("Negotiated ciphersuite mismatch: "
+                        + cipherSuite + " != " + params.cipherSuite);
             }
             String protocol = session.getProtocol();
-            if (params.protocol.equals(protocol) == false) {
-                throw new Exception("Negotiated protocol mismatch: " + protocol + " != " + params.protocol);
+            if (!params.protocol.name.equals(protocol)) {
+                throw new Exception("Negotiated protocol mismatch: " + protocol
+                        + " != " + params.protocol);
             }
             if (cipherSuite.indexOf("DH_anon") == -1) {
                 session.getPeerCertificates();
--- a/test/jdk/javax/net/ssl/sanity/interop/JSSEServer.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/sanity/interop/JSSEServer.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,17 +21,18 @@
  * questions.
  */
 
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import java.util.concurrent.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 
-import java.security.*;
-import java.security.cert.*;
-import java.security.cert.Certificate;
-
-import javax.net.ssl.*;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
 
 class JSSEServer extends CipherTest.Server {
 
@@ -40,23 +41,28 @@
     JSSEServer(CipherTest cipherTest) throws Exception {
         super(cipherTest);
         SSLContext serverContext = SSLContext.getInstance("TLS");
-        serverContext.init(new KeyManager[] {cipherTest.keyManager}, new TrustManager[] {cipherTest.trustManager}, cipherTest.secureRandom);
+        serverContext.init(
+                new KeyManager[] { CipherTest.keyManager },
+                new TrustManager[] { CipherTest.trustManager },
+                CipherTest.secureRandom);
 
-        SSLServerSocketFactory factory = (SSLServerSocketFactory)serverContext.getServerSocketFactory();
-        serverSocket = (SSLServerSocket)factory.createServerSocket(cipherTest.serverPort);
-        cipherTest.serverPort = serverSocket.getLocalPort();
+        SSLServerSocketFactory factory
+                = (SSLServerSocketFactory) serverContext.getServerSocketFactory();
+        serverSocket
+                = (SSLServerSocket) factory.createServerSocket(CipherTest.serverPort);
+        CipherTest.serverPort = serverSocket.getLocalPort();
         serverSocket.setEnabledCipherSuites(factory.getSupportedCipherSuites());
         serverSocket.setWantClientAuth(true);
     }
 
     public void run() {
-        System.out.println("JSSE Server listening on port " + cipherTest.serverPort);
+        System.out.println("JSSE Server listening on port " + CipherTest.serverPort);
         Executor exec = Executors.newFixedThreadPool
                             (cipherTest.THREADS, DaemonThreadFactory.INSTANCE);
         try {
             while (true) {
                 final SSLSocket socket = (SSLSocket)serverSocket.accept();
-                socket.setSoTimeout(cipherTest.TIMEOUT);
+                socket.setSoTimeout(CipherTest.TIMEOUT);
                 Runnable r = new Runnable() {
                     public void run() {
                         try {
--- a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java	Mon Jun 25 13:41:39 2018 -0700
@@ -245,6 +245,8 @@
                 log("\tClosing clientEngine's *OUTBOUND*...");
                 clientEngine.closeOutbound();
                 dataDone = true;
+                log("\tClosing serverEngine's *OUTBOUND*...");
+                serverEngine.closeOutbound();
             }
         }
     }
--- a/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -133,6 +133,7 @@
     protected SSLContext createClientSSLContext() throws Exception {
         return createSSLContext(trustedCertStrs,
                 endEntityCertStrs, endEntityPrivateKeys,
+                endEntityPrivateKeyAlgs,
                 endEntityPrivateKeyNames,
                 getClientContextParameters());
     }
@@ -143,6 +144,7 @@
     protected SSLContext createServerSSLContext() throws Exception {
         return createSSLContext(trustedCertStrs,
                 endEntityCertStrs, endEntityPrivateKeys,
+                endEntityPrivateKeyAlgs,
                 endEntityPrivateKeyNames,
                 getServerContextParameters());
     }
@@ -357,173 +359,235 @@
     private final static String[] trustedCertStrs = {
         // SHA256withECDSA, curve prime256v1
         // Validity
-        //    Not Before: Nov 25 04:19:51 2016 GMT
-        //    Not After : Nov  5 04:19:51 2037 GMT
+        //     Not Before: May 22 07:18:16 2018 GMT
+        //     Not After : May 17 07:18:16 2038 GMT
         // Subject Key Identifier:
-        //    CA:48:E8:00:C1:42:BD:59:9B:79:D9:B4:B4:CE:3F:68:0C:C8:C4:0C
+        //     60:CF:BD:73:FF:FA:1A:30:D2:A4:EC:D3:49:71:46:EF:1A:35:A0:86
         "-----BEGIN CERTIFICATE-----\n" +
-        "MIICHDCCAcGgAwIBAgIJAJtKs6ZEcVjxMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" +
-        "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
-        "ZTAeFw0xNjExMjUwNDE5NTFaFw0zNzExMDUwNDE5NTFaMDsxCzAJBgNVBAYTAlVT\n" +
-        "MQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZjZTBZ\n" +
-        "MBMGByqGSM49AgEGCCqGSM49AwEHA0IABKMO/AFDHZia65RaqMIBX7WBdtzFj8fz\n" +
-        "ggqMADLJhoszS6qfTUDYskETw3uHfB3KAOENsoKX446qFFPuVjxS1aejga0wgaow\n" +
-        "HQYDVR0OBBYEFMpI6ADBQr1Zm3nZtLTOP2gMyMQMMGsGA1UdIwRkMGKAFMpI6ADB\n" +
-        "Qr1Zm3nZtLTOP2gMyMQMoT+kPTA7MQswCQYDVQQGEwJVUzENMAsGA1UEChMESmF2\n" +
-        "YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2WCCQCbSrOmRHFY8TAPBgNV\n" +
-        "HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAKBggqhkjOPQQDAgNJADBGAiEA5cJ/\n" +
-        "jirBbXxzpZ6kdp/Zb/yrIBnr4jiPGJTLgRTb8s4CIQChUDfP1Zqg0qJVfqFNaL4V\n" +
-        "a0EAeJHXGZnvCGGqHzoxkg==\n" +
+        "MIIBvjCCAWOgAwIBAgIJAIvFG6GbTroCMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMDsxCzAJBgNVBAYTAlVT\n" +
+        "MQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZjZTBZ\n" +
+        "MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBz1WeVb6gM2mh85z3QlvaB/l11b5h0v\n" +
+        "LIzmkC3DKlVukZT+ltH2Eq1oEkpXuf7QmbM0ibrUgtjsWH3mULfmcWmjUDBOMB0G\n" +
+        "A1UdDgQWBBRgz71z//oaMNKk7NNJcUbvGjWghjAfBgNVHSMEGDAWgBRgz71z//oa\n" +
+        "MNKk7NNJcUbvGjWghjAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQCG\n" +
+        "6wluh1r2/T6L31mZXRKf9JxeSf9pIzoLj+8xQeUChQIhAJ09wAi1kV8yePLh2FD9\n" +
+        "2YEHlSQUAbwwqCDEVB5KxaqP\n" +
         "-----END CERTIFICATE-----",
+        // -----BEGIN PRIVATE KEY-----
+        // MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/HcHdoLJCdq3haVd
+        // XZTSKP00YzM3xX97l98vGL/RI1KhRANCAAQc9VnlW+oDNpofOc90Jb2gf5ddW+Yd
+        // LyyM5pAtwypVbpGU/pbR9hKtaBJKV7n+0JmzNIm61ILY7Fh95lC35nFp
+        // -----END PRIVATE KEY-----
 
         // SHA256withRSA, 2048 bits
         // Validity
-        //     Not Before: Nov 25 04:20:02 2016 GMT
-        //     Not After : Nov  5 04:20:02 2037 GMT
+        //     Not Before: May 22 07:18:16 2018 GMT
+        //     Not After : May 17 07:18:16 2038 GMT
         // Subject Key Identifier:
-        //     A2:DC:55:38:E4:47:7C:8B:D3:E0:CA:FA:AD:3A:C8:4A:DD:12:A0:8E
+        //     0D:DD:93:C9:FE:4B:BD:35:B7:E8:99:78:90:FB:DB:5A:3D:DB:15:4C
         "-----BEGIN CERTIFICATE-----\n" +
-        "MIIDpzCCAo+gAwIBAgIJAO586A+hYNXaMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" +
-        "BAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2Vy\n" +
-        "aXZjZTAeFw0xNjExMjUwNDIwMDJaFw0zNzExMDUwNDIwMDJaMDsxCzAJBgNVBAYT\n" +
-        "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
-        "ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMm3veDSU4zKXO0aAHos\n" +
-        "cFRXGLBTe+MUJXAtlkNyx7VKaMZNt5wrUuqzyi/r0LFUdRfNCzZf3X8s8HPHQVii\n" +
-        "29tK0y/yeTn4sJTATSmGaAysMJQpKQcfAQ79ItcEGQ721TFQZ3kOBdgp3t/yUYAP\n" +
-        "K2tFze/QbIw72LE52SBnPPPTzyimNw7Ai2MLl4eQlyMkTs7JS07zIiAO5QYbS8s+\n" +
-        "1NW0A3Y+d0B0q8wYEoHGq7QVjOKlSAksfO0tzi4l0Zu6Uf+J5kMAyZ4ZFgEJvGvw\n" +
-        "y/OKJ+soRFH/5cy1SL8B6AWD1y7WXugeeHTHRW1eOjTxqfG1rZqTVd2lfOMER8y1\n" +
-        "bXcCAwEAAaOBrTCBqjAdBgNVHQ4EFgQUotxVOORHfIvT4Mr6rTrISt0SoI4wawYD\n" +
-        "VR0jBGQwYoAUotxVOORHfIvT4Mr6rTrISt0SoI6hP6Q9MDsxCzAJBgNVBAYTAlVT\n" +
-        "MQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZjZYIJ\n" +
-        "AO586A+hYNXaMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3\n" +
-        "DQEBCwUAA4IBAQAtNZJSFeFU6Yid0WSCs2qLAVaTyHsUNSUPUFIIhFAKxdP4DFS0\n" +
-        "+aeOFwdqizAU3kReAYULsfwEBgO51lPBSpB+9coUNQwu7cc8Q5Xqw/llRB0PrINS\n" +
-        "pZl7PW6Ur2ExTBocnUT9A/nhm8iO4PFD/Ly11sf5OdZihhX69NJ2h3a3gcrLjIpO\n" +
-        "L/ewTOgSi5xs+AGGQa+huN3YVL7dh+/rCUvMZVSBX5PloxWS5MMJi0Ui6YjwCFGO\n" +
-        "J4G9m7pI+gJs/x1UG0AgplMF2UCFfiY1SAeE2nKAeOOXAXEwEjFy0ToVTmqXx7fg\n" +
-        "m9YjhuePxlBrd2DF/YW0pc8gmLnrtm4rKPLz\n" +
+        "MIIDSTCCAjGgAwIBAgIJAI4ZF3iy8zG+MA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" +
+        "BAYTAlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2Vy\n" +
+        "aXZjZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMDsxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALpMcY7aWieXDEM1/YJf\n" +
+        "JW27b4nRIFZyEYhEloyGsKTuQiiQjc8cqRZFNXe2vwziDB4IyTEl0Hjl5QF6ZaQE\n" +
+        "huPzzwvQm1pv64KrRXrmj3FisQK8B5OWLty9xp6xDqsaMRoyObLK+oIb20T5fSlE\n" +
+        "evmo1vYjnh8CX0Yzx5Gr5ye6YSEHQvYOWEws8ad17OlyToR2KMeC8w4qo6rs59pW\n" +
+        "g7Mxn9vo22ImDzrtAbTbXbCias3xlE0Bp0h5luyf+5U4UgksoL9B9r2oP4GrLNEV\n" +
+        "oJk57t8lwaR0upiv3CnS8LcJELpegZub5ggqLY8ZPYFQPjlK6IzLOm6rXPgZiZ3m\n" +
+        "RL0CAwEAAaNQME4wHQYDVR0OBBYEFA3dk8n+S701t+iZeJD721o92xVMMB8GA1Ud\n" +
+        "IwQYMBaAFA3dk8n+S701t+iZeJD721o92xVMMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n" +
+        "hvcNAQELBQADggEBAJTRC3rKUUhVH07/1+stUungSYgpM08dY4utJq0BDk36BbmO\n" +
+        "0AnLDMbkwFdHEoqF6hQIfpm7SQTmXk0Fss6Eejm8ynYr6+EXiRAsaXOGOBCzF918\n" +
+        "/RuKOzqABfgSU4UBKECLM5bMfQTL60qx+HdbdVIpnikHZOFfmjCDVxoHsGyXc1LW\n" +
+        "Jhkht8IGOgc4PMGvyzTtRFjz01kvrVQZ75aN2E0GQv6dCxaEY0i3ypSzjUWAKqDh\n" +
+        "3e2OLwUSvumcdaxyCdZAOUsN6pDBQ+8VRG7KxnlRlY1SMEk46QgQYLbPDe/+W/yH\n" +
+        "ca4PejicPeh+9xRAwoTpiE2gulfT7Lm+fVM7Ruc=\n" +
         "-----END CERTIFICATE-----",
+        // -----BEGIN PRIVATE KEY-----
+        // MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC6THGO2lonlwxD
+        // Nf2CXyVtu2+J0SBWchGIRJaMhrCk7kIokI3PHKkWRTV3tr8M4gweCMkxJdB45eUB
+        // emWkBIbj888L0Jtab+uCq0V65o9xYrECvAeTli7cvcaesQ6rGjEaMjmyyvqCG9tE
+        // +X0pRHr5qNb2I54fAl9GM8eRq+cnumEhB0L2DlhMLPGndezpck6EdijHgvMOKqOq
+        // 7OfaVoOzMZ/b6NtiJg867QG0212womrN8ZRNAadIeZbsn/uVOFIJLKC/Qfa9qD+B
+        // qyzRFaCZOe7fJcGkdLqYr9wp0vC3CRC6XoGbm+YIKi2PGT2BUD45SuiMyzpuq1z4
+        // GYmd5kS9AgMBAAECggEAFHSoU2MuWwJ+2jJnb5U66t2V1bAcuOE1g5zkWvG/G5z9
+        // rq6Qo5kmB8f5ovdx6tw3MGUOklLwnRXBG3RxDJ1iokz3AvkY1clMNsDPlDsUrQKF
+        // JSO4QUBQTPSZhnsyfR8XHSU+qJ8Y+ohMfzpVv95BEoCzebtXdVgxVegBlcEmVHo2
+        // kMmkRN+bYNsr8eb2r+b0EpyumS39ZgKYh09+cFb78y3T6IFMGcVJTP6nlGBFkmA/
+        // 25pYeCF2tSki08qtMJZQAvKfw0Kviibk7ZxRbJqmc7B1yfnOEHP6ftjuvKl2+RP/
+        // +5P5f8CfIP6gtA0LwSzAqQX/hfIKrGV5j0pCqrD0kQKBgQDeNR6Xi4sXVq79lihO
+        // a1bSeV7r8yoQrS8x951uO+ox+UIZ1MsAULadl7zB/P0er92p198I9M/0Jth3KBuS
+        // zj45mucvpiiGvmQlMKMEfNq4nN7WHOu55kufPswQB2mR4J3xmwI+4fM/nl1zc82h
+        // De8JSazRldJXNhfx0RGFPmgzbwKBgQDWoVXrXLbCAn41oVnWB8vwY9wjt92ztDqJ
+        // HMFA/SUohjePep9UDq6ooHyAf/Lz6oE5NgeVpPfTDkgvrCFVKnaWdwALbYoKXT2W
+        // 9FlyJox6eQzrtHAacj3HJooXWuXlphKSizntfxj3LtMR9BmrmRJOfK+SxNOVJzW2
+        // +MowT20EkwKBgHmpB8jdZBgxI7o//m2BI5Y1UZ1KE5vx1kc7VXzHXSBjYqeV9FeF
+        // 2ZZLP9POWh/1Fh4pzTmwIDODGT2UPhSQy0zq3O0fwkyT7WzXRknsuiwd53u/dejg
+        // iEL2NPAJvulZ2+AuiHo5Z99LK8tMeidV46xoJDDUIMgTG+UQHNGhK5gNAoGAZn/S
+        // Cn7SgMC0CWSvBHnguULXZO9wH1wZAFYNLL44OqwuaIUFBh2k578M9kkke7woTmwx
+        // HxQTjmWpr6qimIuY6q6WBN8hJ2Xz/d1fwhYKzIp20zHuv5KDUlJjbFfqpsuy3u1C
+        // kts5zwI7pr1ObRbDGVyOdKcu7HI3QtR5qqyjwaUCgYABo7Wq6oHva/9V34+G3Goh
+        // 63bYGUnRw2l5BD11yhQv8XzGGZFqZVincD8gltNThB0Dc/BI+qu3ky4YdgdZJZ7K
+        // z51GQGtaHEbrHS5caV79yQ8QGY5mUVH3E+VXSxuIqb6pZq2DH4sTAEFHyncddmOH
+        // zoXBInYwRG9KE/Bw5elhUw==
+        // -----END PRIVATE KEY-----
 
         // SHA256withDSA, 2048 bits
         // Validity
-        //     Not Before: Nov 25 04:19:56 2016 GMT
-        //     Not After : Nov  5 04:19:56 2037 GMT
+        //     Not Before: May 22 07:18:18 2018 GMT
+        //     Not After : May 17 07:18:18 2038 GMT
         // Subject Key Identifier:
-        //     19:46:10:43:24:6A:A5:14:BE:E2:92:01:79:F0:4C:5F:E1:AE:81:B5
+        //     76:66:9E:F7:3B:DD:45:E5:3B:D9:72:3C:3F:F0:54:39:86:31:26:53
         "-----BEGIN CERTIFICATE-----\n" +
-        "MIIFCzCCBLGgAwIBAgIJAOnEn6YZD/sAMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" +
-        "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
-        "Y2UwHhcNMTYxMTI1MDQxOTU2WhcNMzcxMTA1MDQxOTU2WjA7MQswCQYDVQQGEwJV\n" +
-        "UzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2Uw\n" +
-        "ggNGMIICOQYHKoZIzjgEATCCAiwCggEBAJa17ZYdIChv5yeYuPK3zXxgUEGGsdUD\n" +
-        "AzfQUxtMryCtc3aNgWLxsN1/QYvp9v+hh4twnG20VemCEH9Qlx06Pxg74DwSOA83\n" +
-        "SecO2y7cdgmrHpep9drxKbXVZafwBhbTSwhV+IDO7EO6+LaRvZuya/YOqNIE9ENx\n" +
-        "FVk0NrNsDB6pfDEXZsCZALMN2mcl8KGn1q7vdzJQUEV7F6uLGP33znVfmQyWJH3Y\n" +
-        "W09WVCFXHvDzZHGXDO2O2QwIU1B5AsXnOGeLnKgXzErCoNKwUbVFP0W0OVeJo4pc\n" +
-        "ZfL/8TVELGG90AuuH1V3Gsq6PdzCDPw4Uv/v5m7/6kwXqBQxAJA7jhMCIQCORIGV\n" +
-        "mHy5nBLRhIP4vC7vsTxb4CTApPVmZeL5gTIwtQKCAQB2VZLY22k2+IQM6deRGK3L\n" +
-        "l7tPChGrKnGmTbtUThIza70Sp9DmTBiLzMEY+IgG8kYuT5STVxWjs0cxXCKZGMQW\n" +
-        "tioMtiXPA2M3HA0/8E0mDLSmzb0RAd2xxnDyGsuqo1eVmx7PLjN3bn3EjhD/+j3d\n" +
-        "Jx3ZVScMGyq7sVWppUvpudEXi+2etf6GUHjrcX27juo7u4zQ1ezC/HYG1H+jEFqG\n" +
-        "hdQ6b7H+LBHZH9LegOyIZTMrzAY/TwIr77sXrNJWRoxmDErKB+8bRDybYhNJswlZ\n" +
-        "m0N5YYUlPmepgbl6XzwCv0y0d81h3bayqIPLXEUtRAl9GuM0hNAlA1Y+qSn9xLFY\n" +
-        "A4IBBQACggEAZgWC0uflwqQQP1GRU1tolmFZwyVtKre7SjYgCeQBrOa0Xnj/SLaD\n" +
-        "g1HZ1oH0hccaR/45YouJiCretbbsQ77KouldGSGqTHJgRL75Y2z5uvxa60+YxZ0Z\n" +
-        "v8xvZnj4seyOjgJLxSSYSPl5n/F70RaNiCLVz/kGe6OQ8KoAeQjdDTOHXCegO9KX\n" +
-        "tvhM7EaYc8CII9OIR7S7PXJW0hgLKynZcu/Unh02aM0ABh/uLmw1+tvo8e8KTp98\n" +
-        "NKYSVf6kV3/ya58n4h64UbIYL08JoKUM/5SFETcKAZTU0YKZbpWTM79oJMr8oYVk\n" +
-        "P9jKitNsXq0Xkzt5dSO0kfu/kM7zpnaFsqOBrTCBqjAdBgNVHQ4EFgQUGUYQQyRq\n" +
-        "pRS+4pIBefBMX+GugbUwawYDVR0jBGQwYoAUGUYQQyRqpRS+4pIBefBMX+GugbWh\n" +
-        "P6Q9MDsxCzAJBgNVBAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5K\n" +
-        "U1NFIFRlc3QgU2VyaXZjZYIJAOnEn6YZD/sAMA8GA1UdEwEB/wQFMAMBAf8wCwYD\n" +
-        "VR0PBAQDAgEGMAsGCWCGSAFlAwQDAgNHADBEAiAwBafz5RRR9nc4cCYoYuBlT/D9\n" +
-        "9eayhkjhBY/zYunypwIgNp/JnFR88/T4hh36QfSKBGXId9RBCM6uaOkOKnEGkps=\n" +
+        "MIIErjCCBFSgAwIBAgIJAOktYLNCbr02MAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" +
+        "EwJVUzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+        "Y2UwHhcNMTgwNTIyMDcxODE4WhcNMzgwNTE3MDcxODE4WjA7MQswCQYDVQQGEwJV\n" +
+        "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Uw\n" +
+        "ggNHMIICOQYHKoZIzjgEATCCAiwCggEBAO5GyPhSm0ze3LSu+gicdULLj05iOfTL\n" +
+        "UvZQ29sYz41zmqrLBQbdKiHqgJu2Re9sgTb5suLNjF047TOLPnU3jhPtWm2X8Xzi\n" +
+        "VGIcHym/Q/MeZxStt/88seqroI3WOKzIML2GcrishT+lcGrtH36Tf1+ue2Snn3PS\n" +
+        "WyxygNqPjllP5uUjYmFLvAf4QLMldkd/D2VxcwsHjB8y5iUZsXezc/LEhRZS/02m\n" +
+        "ivqlRw3AMkq/OVe/ZtxFWsP0nsfxEGdZuaUFpppGfixxFvymrB3+J51cTt+pZBDq\n" +
+        "D2y0DYfc+88iCs4jwHTfcDIpLb538HBjBj2rEgtQESQmB0ooD/+wsPsCIQC1bYch\n" +
+        "gElNtDYL3FgpLgNSUYp7gIWv9ehaC7LO2z7biQKCAQBitvFOnDkUja8NAF7lDpOV\n" +
+        "b5ipQ8SicBLW3kQamxhyuyxgZyy/PojZ/oPorkqW/T/A0rhnG6MssEpAtdiwVB+c\n" +
+        "rBYGo3bcwmExJhdOJ6dYuKFppPWhCwKMHs9npK+lqBMl8l5j58xlcFeC7ZfGf8GY\n" +
+        "GkhFW0c44vEQhMMbac6ZTTP4mw+1t7xJfmDMlLEyIpTXaAAk8uoVLWzQWnR40sHi\n" +
+        "ybvS0u3JxQkb7/y8tOOZu8qlz/YOS7lQ6UxUGX27Ce1E0+agfPphetoRAlS1cezq\n" +
+        "Wa7r64Ga0nkj1kwkcRqjgTiJx0NwnUXr78VAXFhVF95+O3lfqhvdtEGtkhDGPg7N\n" +
+        "A4IBBgACggEBAMmSHQK0w2i+iqUjOPzn0yNEZrzepLlLeQ1tqtn0xnlv5vBAeefD\n" +
+        "Pm9dd3tZOjufVWP7hhEz8xPobb1CS4e3vuQiv5UBfhdPL3f3l9T7JMAKPH6C9Vve\n" +
+        "OQXE5eGqbjsySbcmseHoYUt1WCSnSda1opX8zchX04e7DhGfE2/L9flpYEoSt8lI\n" +
+        "vMNjgOwvKdW3yvPt1/eBBHYNFG5gWPv/Q5KoyCtHS03uqGm4rNc/wZTIEEfd66C+\n" +
+        "QRaUltjOaHmtwOdDHaNqwhYZSVOip+Mo+TfyzHFREcdHLapo7ZXqbdYkRGxRR3d+\n" +
+        "3DfHaraJO0OKoYlPkr3JMvM/MSGR9AnZOcejUDBOMB0GA1UdDgQWBBR2Zp73O91F\n" +
+        "5TvZcjw/8FQ5hjEmUzAfBgNVHSMEGDAWgBR2Zp73O91F5TvZcjw/8FQ5hjEmUzAM\n" +
+        "BgNVHRMEBTADAQH/MAsGCWCGSAFlAwQDAgNHADBEAiBzriYE41M2y9Hy5ppkL0Qn\n" +
+        "dIlNc8JhXT/PHW7GDtViagIgMko8Qoj9gDGPK3+O9E8DC3wGiiF9CObM4LN387ok\n" +
+        "J+g=\n" +
         "-----END CERTIFICATE-----"
+        // -----BEGIN PRIVATE KEY-----
+        // MIICZQIBADCCAjkGByqGSM44BAEwggIsAoIBAQDuRsj4UptM3ty0rvoInHVCy49O
+        // Yjn0y1L2UNvbGM+Nc5qqywUG3Soh6oCbtkXvbIE2+bLizYxdOO0ziz51N44T7Vpt
+        // l/F84lRiHB8pv0PzHmcUrbf/PLHqq6CN1jisyDC9hnK4rIU/pXBq7R9+k39frntk
+        // p59z0lsscoDaj45ZT+blI2JhS7wH+ECzJXZHfw9lcXMLB4wfMuYlGbF3s3PyxIUW
+        // Uv9Npor6pUcNwDJKvzlXv2bcRVrD9J7H8RBnWbmlBaaaRn4scRb8pqwd/iedXE7f
+        // qWQQ6g9stA2H3PvPIgrOI8B033AyKS2+d/BwYwY9qxILUBEkJgdKKA//sLD7AiEA
+        // tW2HIYBJTbQ2C9xYKS4DUlGKe4CFr/XoWguyzts+24kCggEAYrbxTpw5FI2vDQBe
+        // 5Q6TlW+YqUPEonAS1t5EGpsYcrssYGcsvz6I2f6D6K5Klv0/wNK4ZxujLLBKQLXY
+        // sFQfnKwWBqN23MJhMSYXTienWLihaaT1oQsCjB7PZ6SvpagTJfJeY+fMZXBXgu2X
+        // xn/BmBpIRVtHOOLxEITDG2nOmU0z+JsPtbe8SX5gzJSxMiKU12gAJPLqFS1s0Fp0
+        // eNLB4sm70tLtycUJG+/8vLTjmbvKpc/2Dku5UOlMVBl9uwntRNPmoHz6YXraEQJU
+        // tXHs6lmu6+uBmtJ5I9ZMJHEao4E4icdDcJ1F6+/FQFxYVRfefjt5X6ob3bRBrZIQ
+        // xj4OzQQjAiEAsceWOM8do4etxp2zgnoNXV8PUUyqWhz1+0srcKV7FR4=
+        // -----END PRIVATE KEY-----
         };
 
     // End entity certificate.
     private final static String[] endEntityCertStrs = {
         // SHA256withECDSA, curve prime256v1
         // Validity
-        //     Not Before: Nov 25 04:19:51 2016 GMT
-        //     Not After : Aug 12 04:19:51 2036 GMT
+        //     Not Before: May 22 07:18:16 2018 GMT
+        //     Not After : May 17 07:18:16 2038 GMT
         // Authority Key Identifier:
-        //     CA:48:E8:00:C1:42:BD:59:9B:79:D9:B4:B4:CE:3F:68:0C:C8:C4:0C
+        //     60:CF:BD:73:FF:FA:1A:30:D2:A4:EC:D3:49:71:46:EF:1A:35:A0:86
         "-----BEGIN CERTIFICATE-----\n" +
-        "MIIB1zCCAXygAwIBAgIJAPFq2QL/nUNZMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" +
-        "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
-        "ZTAeFw0xNjExMjUwNDE5NTFaFw0zNjA4MTIwNDE5NTFaMFUxCzAJBgNVBAYTAlVT\n" +
+        "MIIBqjCCAVCgAwIBAgIJAPLY8qZjgNRAMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMFUxCzAJBgNVBAYTAlVT\n" +
         "MQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZjZTEY\n" +
         "MBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n" +
-        "QgAE4yvRGVvy9iVATyuHPJVdX6+lh/GLm/sRJ5qLT/3PVFOoNIvlEVNiARo7xhyj\n" +
-        "2p6bnf32gNg5Ye+QCw20VUv9E6NPME0wCwYDVR0PBAQDAgPoMB0GA1UdDgQWBBSO\n" +
-        "hHlHZQp9hyBfSGTSQWeszqMXejAfBgNVHSMEGDAWgBTKSOgAwUK9WZt52bS0zj9o\n" +
-        "DMjEDDAKBggqhkjOPQQDAgNJADBGAiEAu3t6cvFglBAZfkhZlEwB04ZjUFqyfiRj\n" +
-        "4Hr275E4ZoQCIQDUEonJHlmA19J6oobfR5lYsmoqPm1r0DPm/IiNNKGKKA==\n" +
+        "QgAEb+9n05qfXnfHUb0xtQJNS4JeSi6IjOfW5NqchvKnfJey9VkJzR7QHLuOESdf\n" +
+        "xlR7q8YIWgih3iWLGfB+wxHiOqMjMCEwHwYDVR0jBBgwFoAUYM+9c//6GjDSpOzT\n" +
+        "SXFG7xo1oIYwCgYIKoZIzj0EAwIDSAAwRQIgWpRegWXMheiD3qFdd8kMdrkLxRbq\n" +
+        "1zj8nQMEwFTUjjQCIQDRIrAjZX+YXHN9b0SoWWLPUq0HmiFIi8RwMnO//wJIGQ==\n" +
         "-----END CERTIFICATE-----",
 
         // SHA256withRSA, 2048 bits
         // Validity
-        //     Not Before: Nov 25 04:20:02 2016 GMT
-        //     Not After : Aug 12 04:20:02 2036 GMT
+        //     Not Before: May 22 07:18:16 2018 GMT
+        //     Not After : May 17 07:18:16 2038 GMT
         // Authority Key Identifier:
-        //     A2:DC:55:38:E4:47:7C:8B:D3:E0:CA:FA:AD:3A:C8:4A:DD:12:A0:8E
+        //     0D:DD:93:C9:FE:4B:BD:35:B7:E8:99:78:90:FB:DB:5A:3D:DB:15:4C
         "-----BEGIN CERTIFICATE-----\n" +
-        "MIIDdjCCAl6gAwIBAgIJAJDcIGOlAmBmMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" +
-        "BAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2Vy\n" +
-        "aXZjZTAeFw0xNjExMjUwNDIwMDJaFw0zNjA4MTIwNDIwMDJaMFUxCzAJBgNVBAYT\n" +
+        "MIIDNjCCAh6gAwIBAgIJAO2+yPcFryUTMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" +
+        "BAYTAlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2Vy\n" +
+        "aXZjZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMFUxCzAJBgNVBAYT\n" +
         "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
         "ZTEYMBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOC\n" +
-        "AQ8AMIIBCgKCAQEAp0dHrifTg2aY0sH03f2JjK2fW4DL6gLDKq0YirsNE07z54LF\n" +
-        "IeeDio49XwPjB7OpbUTC1hf/YKZ7XiRWyPa1rYozZ88emhZt+cpkyKz+nmW4avlA\n" +
-        "WnrV+gx4+bU9T+WuBWdAraBcq27Y1I26yfCEtC8k3+O0sdlHbhasF+BUDmX/n4+n\n" +
-        "ifJdbNm5eSDx8eFYHFTdjhAud3An2X6QD9WWSoJpPdDi4utHhFAlxW6osjJxsAPv\n" +
-        "zo8YsqmpCMjZcEks4ZsqiZKKiWUWUAjCcbMerDPDX29fyeo348uztvJsmNRzfcwl\n" +
-        "FtwxpYdxuZqwHi2QoNaQTGXjjbZFmjn7qEkjXwIDAQABo2MwYTALBgNVHQ8EBAMC\n" +
-        "A+gwHQYDVR0OBBYEFP+henfufE6Znr60lRkmayadVdxnMB8GA1UdIwQYMBaAFKLc\n" +
-        "VTjkR3yL0+DK+q06yErdEqCOMBIGA1UdEQEB/wQIMAaHBH8AAAEwDQYJKoZIhvcN\n" +
-        "AQELBQADggEBAK56pV2XoAIkrHFTCkWtYX518nuvkzN6a6BqPKALQlmlbJnq/lhV\n" +
-        "tPQx79b0j7tow28l2ze/3M0hRb5Ft/d/7mITZNMR+0owk4U51AU2NacRt7fpoxu5\n" +
-        "wX3hTa4VgX2+BAXeoWF+Yzy6Jj5gAVmSLzBnkTUH0d+EyL1pp+DFE3QdvZqf3+nP\n" +
-        "zkxz15h3iW8FwI+7/19MX2j2XB/sG8mJpqoszWw8lM4qCa2eWyCbqSHhPi+/+rGg\n" +
-        "dDG5uzZeOC845GEH2T3tHDA+F3WwcZG/W+4RR6ZaaHlqPKKMcwFL73YbsqdCiKBv\n" +
-        "p6sXrhIiP0oXImRBRLDlidj5TIOLfAtNM9A=\n" +
+        "AQ8AMIIBCgKCAQEAszfBobWfZIp8AgC6PiWDDavP65mSvgCXUGxACbxVNAfkLhNR\n" +
+        "QOsHriRB3X1Q3nvO9PetC6wKlvE9jlnDDj7D+1j1r1CHO7ms1fq8rfcQYdkanDtu\n" +
+        "4AlHo8v+SSWX16MIXFRYDj2VVHmyPtgbltcg4zGAuwT746FdLI94uXjJjq1IOr/v\n" +
+        "0VIlwE5ORWH5Xc+5Tj+oFWK0E4a4GHDgtKKhn2m72hN56/GkPKGkguP5NRS1qYYV\n" +
+        "/EFkdyQMOV8J1M7HaicSft4OL6eKjTrgo93+kHk+tv0Dc6cpVBnalX3TorG8QI6B\n" +
+        "cHj1XQd78oAlAC+/jF4pc0mwi0un49kdK9gRfQIDAQABoyMwITAfBgNVHSMEGDAW\n" +
+        "gBQN3ZPJ/ku9NbfomXiQ+9taPdsVTDANBgkqhkiG9w0BAQsFAAOCAQEApXS0nKwm\n" +
+        "Kp8gpmO2yG1rpd1+2wBABiMU4JZaTqmma24DQ3RzyS+V2TeRb29dl5oTUEm98uc0\n" +
+        "GPZvhK8z5RFr4YE17dc04nI/VaNDCw4y1NALXGs+AHkjoPjLyGbWpi1S+gfq2sNB\n" +
+        "Ekkjp6COb/cb9yiFXOGVls7UOIjnVZVd0r7KaPFjZhYh82/f4PA/A1SnIKd1+nfH\n" +
+        "2yk7mSJNC7Z3qIVDL8MM/jBVwiC3uNe5GPB2uwhd7k5LGAVN3j4HQQGB0Sz+VC1h\n" +
+        "92oi6xDa+YBva2fvHuCd8P50DDjxmp9CemC7rnZ5j8egj88w14X44Xjb/Fd/ApG9\n" +
+        "e57NnbT7KM+Grw==\n" +
+        "-----END CERTIFICATE-----",
+
+        // SHA256withRSA, curv prime256v1
+        // Validity
+        //     Not Before: May 22 07:18:16 2018 GMT
+        //     Not After : May 21 07:18:16 2028 GMT
+        // Authority Key Identifier:
+        //     0D:DD:93:C9:FE:4B:BD:35:B7:E8:99:78:90:FB:DB:5A:3D:DB:15:4C
+        "-----BEGIN CERTIFICATE-----\n" +
+        "MIICazCCAVOgAwIBAgIJAO2+yPcFryUUMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" +
+        "BAYTAlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2Vy\n" +
+        "aXZjZTAeFw0xODA1MjIwNzE4MTZaFw0yODA1MjEwNzE4MTZaMFUxCzAJBgNVBAYT\n" +
+        "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" +
+        "ZTEYMBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0D\n" +
+        "AQcDQgAE59MERNTlVZ1eeps8Z3Oue5ZkgQdPtD+WIE6tj3PbIKpxGPDxvfNP959A\n" +
+        "yQjEK/ehWQVrCMmNoEkIzY+IIBgB06MjMCEwHwYDVR0jBBgwFoAUDd2Tyf5LvTW3\n" +
+        "6Jl4kPvbWj3bFUwwDQYJKoZIhvcNAQELBQADggEBAFOTVEqs70ykhZiIdrEsF1Ra\n" +
+        "I3B2rLvwXZk52uSltk2/bzVvewA577ZCoxQ1pL7ynkisPfBN1uVYtHjM1VA3RC+4\n" +
+        "+TAK78dnI7otYjWoHp5rvs4l6c/IbOspS290IlNuDUxMErEm5wxIwj+Aukx/1y68\n" +
+        "hOyCvHBLMY2c1LskH1MMBbDuS1aI+lnGpToi+MoYObxGcV458vxuT8+wwV8Fkpvd\n" +
+        "ll8IIFmeNPRv+1E+lXbES6CSNCVaZ/lFhPgdgYKleN7sfspiz50DG4dqafuEAaX5\n" +
+        "xaK1NWXJxTRz0ROH/IUziyuDW6jphrlgit4+3NCzp6vP9hAJQ8Vhcj0n15BKHIQ=\n" +
         "-----END CERTIFICATE-----",
 
         // SHA256withDSA, 2048 bits
         // Validity
-        //    Not Before: Nov 25 04:19:56 2016 GMT
-        //    Not After : Aug 12 04:19:56 2036 GMT
+        //     Not Before: May 22 07:18:20 2018 GMT
+        //     Not After : May 17 07:18:20 2038 GMT
         // Authority Key Identifier:
-        //     19:46:10:43:24:6A:A5:14:BE:E2:92:01:79:F0:4C:5F:E1:AE:81:B5
+        //     76:66:9E:F7:3B:DD:45:E5:3B:D9:72:3C:3F:F0:54:39:86:31:26:53
         "-----BEGIN CERTIFICATE-----\n" +
-        "MIIE2jCCBICgAwIBAgIJAONcI1oba9V9MAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" +
-        "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
-        "Y2UwHhcNMTYxMTI1MDQxOTU2WhcNMzYwODEyMDQxOTU2WjBVMQswCQYDVQQGEwJV\n" +
+        "MIIEnDCCBEGgAwIBAgIJAP/jh1qVhNVjMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" +
+        "EwJVUzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+        "Y2UwHhcNMTgwNTIyMDcxODIwWhcNMzgwNTE3MDcxODIwWjBVMQswCQYDVQQGEwJV\n" +
         "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" +
-        "GDAWBgNVBAMMD1JlZ3Jlc3Npb24gVGVzdDCCA0YwggI5BgcqhkjOOAQBMIICLAKC\n" +
-        "AQEAlrXtlh0gKG/nJ5i48rfNfGBQQYax1QMDN9BTG0yvIK1zdo2BYvGw3X9Bi+n2\n" +
-        "/6GHi3CcbbRV6YIQf1CXHTo/GDvgPBI4DzdJ5w7bLtx2Casel6n12vEptdVlp/AG\n" +
-        "FtNLCFX4gM7sQ7r4tpG9m7Jr9g6o0gT0Q3EVWTQ2s2wMHql8MRdmwJkAsw3aZyXw\n" +
-        "oafWru93MlBQRXsXq4sY/ffOdV+ZDJYkfdhbT1ZUIVce8PNkcZcM7Y7ZDAhTUHkC\n" +
-        "xec4Z4ucqBfMSsKg0rBRtUU/RbQ5V4mjilxl8v/xNUQsYb3QC64fVXcayro93MIM\n" +
-        "/DhS/+/mbv/qTBeoFDEAkDuOEwIhAI5EgZWYfLmcEtGEg/i8Lu+xPFvgJMCk9WZl\n" +
-        "4vmBMjC1AoIBAHZVktjbaTb4hAzp15EYrcuXu08KEasqcaZNu1ROEjNrvRKn0OZM\n" +
-        "GIvMwRj4iAbyRi5PlJNXFaOzRzFcIpkYxBa2Kgy2Jc8DYzccDT/wTSYMtKbNvREB\n" +
-        "3bHGcPIay6qjV5WbHs8uM3dufcSOEP/6Pd0nHdlVJwwbKruxVamlS+m50ReL7Z61\n" +
-        "/oZQeOtxfbuO6ju7jNDV7ML8dgbUf6MQWoaF1Dpvsf4sEdkf0t6A7IhlMyvMBj9P\n" +
-        "Aivvuxes0lZGjGYMSsoH7xtEPJtiE0mzCVmbQ3lhhSU+Z6mBuXpfPAK/TLR3zWHd\n" +
-        "trKog8tcRS1ECX0a4zSE0CUDVj6pKf3EsVgDggEFAAKCAQBEGmdP55PyE3M+Q3fU\n" +
-        "dCGq0sbKw/04xPVhaNYRnRKNR82n+wb8bMCI1vvFqXy1BB6svti4mTHbQZ8+bQXm\n" +
-        "gyce67uYMwIa5BIk6omNGCeW/kd4ruPgyFxeb6O/Y/7w6AWyRmQttlxRA5M5OhSC\n" +
-        "tVS4oVC1KK1EfHAUh7mu8S8GrWJoJAWA3PM97Oy/HSGCEUl6HGEu1m7FHPhOKeYG\n" +
-        "cLkSaov5cbCYO76smHchI+tdUciVqeL3YKQdS+KAzsQoeAZIu/WpbaI1V+5/rSG1\n" +
-        "I94uBITLCjlJfJZ1aredCDrRXOFH7qgSBhM8/WzwFpFCnnpbSKMgrcrKubsFmW9E\n" +
-        "jQhXo2MwYTALBgNVHQ8EBAMCA+gwHQYDVR0OBBYEFNA9PhQOjB+05fxxXPNqe0OT\n" +
-        "doCjMB8GA1UdIwQYMBaAFBlGEEMkaqUUvuKSAXnwTF/hroG1MBIGA1UdEQEB/wQI\n" +
-        "MAaHBH8AAAEwCwYJYIZIAWUDBAMCA0cAMEQCIE0LM2sZi+L8tjH9sgjLEwJmYZvO\n" +
-        "yqNfQnXrkTCb+MLMAiBZLaRTVJrOW3edQjum+SonKKuiN22bRclO6pGuNRCtng==\n" +
+        "GDAWBgNVBAMMD1JlZ3Jlc3Npb24gVGVzdDCCA0cwggI6BgcqhkjOOAQBMIICLQKC\n" +
+        "AQEAmlavgoJrMcjqWRVcDE2dmWAPREgnzQvneEDef68cprDzjSwvOs5QeFyx75ib\n" +
+        "ado1e6jO/rW1prCGWHDD1oA/Tn4Pk3vu0nUxzvl1qATc+aJbpUU5Op0bvp6LbCsQ\n" +
+        "QslV9FeRh7Eb7bP6gpc/kHCBzEgC1VCK7prccXWy+t6SMOHbND3h+UbckfSaUuaV\n" +
+        "sVJNTD1D6GElfRj4Nmz1BGPfSYvKorwNZEU3gXwFgtDoAcGx7tcyClLpDHfqRfw/\n" +
+        "7yiqLyeiP7D4hl5lMNouJWDlAdMFp0FMgS3s9VDFinIcr6VtBWMTG7+4+czHAB+3\n" +
+        "fvrwlqNzhBn3uFHrekN/w8fNxwIhAJo7Sae1za7IMW0Q6hE5B4b+s2B/FaKPoA4E\n" +
+        "jtZu13B9AoIBAQCOZqLMKfvqZWUgT0PQ3QjR7dAFdd06I9Y3+TOQzZk1+j+vw/6E\n" +
+        "X4vFItX4gihb/u5Q9CdmpwhVGi7bvo+7+/IKeTgoQ6f5+PSug7SrWWUQ5sPwaZui\n" +
+        "zXZJ5nTeZDucFc2yFx0wgnjbPwiUxZklOT7xGiOMtzOTa2koCz5KuIBL+/wPKKxm\n" +
+        "ypo9VoY9xfbdU6LMXZv/lpD5XTM9rYHr/vUTNkukvV6Hpm0YMEWhVZKUJiqCqTqG\n" +
+        "XHaleOxSw6uQWB/+TznifcC7gB48UOQjCqOKf5VuwQneJLhlhU/jhRV3xtr+hLZa\n" +
+        "hW1wYhVi8cjLDrZFKlgEQqhB4crnJU0mJY+tA4IBBQACggEAID0ezl00/X8mv7eb\n" +
+        "bzovum1+DEEP7FM57k6HZEG2N3ve4CW+0m9Cd+cWPz8wkZ+M0j/Eqa6F0IdbkXEc\n" +
+        "Q7CuzvUyJ57xQ3L/WCgXsiS+Bh8O4Mz7GwW22CGmHqafbVv+hKBfr8MkskO6GJUt\n" +
+        "SUF/CVLzB4gMIvZMH26tBP2xK+i7FeEK9kT+nGdzQSZBAhFYpEVCBplHZO24/OYq\n" +
+        "1DNoU327nUuXIhmsfA8N0PjiWbIZIjTPwBGr9H0LpATI7DIDNcvRRvtROP+pBU9y\n" +
+        "fuykPkptg9C0rCM9t06bukpOSaEz/2VIQdLE8fHYFA6pHZ6CIc2+5cfvMgTPhcjz\n" +
+        "W2jCt6MjMCEwHwYDVR0jBBgwFoAUdmae9zvdReU72XI8P/BUOYYxJlMwCwYJYIZI\n" +
+        "AWUDBAMCA0gAMEUCIQCeI5fN08b9BpOaHdc3zQNGjp24FOL/RxlBLeBAorswJgIg\n" +
+        "JEZ8DhYxQy1O7mmZ2UIT7op6epWMB4dENjs0qWPmcKo=\n" +
         "-----END CERTIFICATE-----"
         };
 
@@ -532,63 +596,79 @@
         //
         // EC private key related to cert endEntityCertStrs[0].
         //
-        "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGAy4Pxrd2keM7AdP\n" +
-        "VNUMEO5iO681v4/tstVGfdXkCTuhRANCAATjK9EZW/L2JUBPK4c8lV1fr6WH8Yub\n" +
-        "+xEnmotP/c9UU6g0i+URU2IBGjvGHKPanpud/faA2Dlh75ALDbRVS/0T",
+        "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgn5K03bpTLjEtFQRa\n" +
+        "JUtx22gtmGEvvSUSQdimhGthdtihRANCAARv72fTmp9ed8dRvTG1Ak1Lgl5KLoiM\n" +
+        "59bk2pyG8qd8l7L1WQnNHtAcu44RJ1/GVHurxghaCKHeJYsZ8H7DEeI6",
 
         //
         // RSA private key related to cert endEntityCertStrs[1].
         //
-        "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnR0euJ9ODZpjS\n" +
-        "wfTd/YmMrZ9bgMvqAsMqrRiKuw0TTvPngsUh54OKjj1fA+MHs6ltRMLWF/9gpnte\n" +
-        "JFbI9rWtijNnzx6aFm35ymTIrP6eZbhq+UBaetX6DHj5tT1P5a4FZ0CtoFyrbtjU\n" +
-        "jbrJ8IS0LyTf47Sx2UduFqwX4FQOZf+fj6eJ8l1s2bl5IPHx4VgcVN2OEC53cCfZ\n" +
-        "fpAP1ZZKgmk90OLi60eEUCXFbqiyMnGwA+/OjxiyqakIyNlwSSzhmyqJkoqJZRZQ\n" +
-        "CMJxsx6sM8Nfb1/J6jfjy7O28myY1HN9zCUW3DGlh3G5mrAeLZCg1pBMZeONtkWa\n" +
-        "OfuoSSNfAgMBAAECggEAWnAHKPkPObN2XDvQj1RL0WrtBSOVG2dy7Ne4tQh8ATxm\n" +
-        "UXw56CKq03YjaANJ8xgHObQ7QlSnFTHs8PDkmrIHd1OIh09LVDNcMfhilLwyzKBi\n" +
-        "HDO1vzU6Cn5DyX1bMJ8UfodcSIKyl1zOjdwyaItIs8HpRcJuJtk57SME18PIrh9H\n" +
-        "EWchWSxTvPvKDY2bhb4vBMgVPfTQO3yc8gY/1J5vKPqDpyEuCGjV13zd/AoL/u5A\n" +
-        "sG10hQ2veJ9KAn1xwPwEoAkCdNLL8vPB1rCbeMZNamqHZRihfoOgDnnJIf3FtUFF\n" +
-        "8bS2FM2kGQR+05SZdhBmJl0obPrbBWil/2vxqeFrgQKBgQDZl1yQsFss2BKK2VAh\n" +
-        "9PKc8tU1v6RpHQZhJEDSct2slHQS5DF6bWl5kJiPevXSvcnjgoPamZf7Joii+Rds\n" +
-        "3xtPQmRw2vQmXBQzQS1YoRuh4hUlkdFWCjUNNg1kvipue6FO4PVg3ViP7llN8PXK\n" +
-        "rSpVxn0a36UiN9jN2zMOUs6tJwKBgQDEzlqa7DghMli7TUdL44uvD2/ceiHqHMMw\n" +
-        "5eLsEHeRVn/EVU99beKw/dAOGwRssTpCd9h7fwzQ2503/Qb/Goe0nKE7+xvt3/sE\n" +
-        "n2Y8Qfv1W1+hGb2qU2jhQaR5bZrLZp0+BgRuQ4ttpYvzopYe4FLZWhDBA0zsGyu0\n" +
-        "nCi7lUSrCQKBgGeGYW8hyS9r2l6fiEWvsiLEUnbRKFsuiRN82S6HojpzI0q9sWDL\n" +
-        "X6yMBFn3qa/LxpttRGikPTAsJERN+Tw+ZlLuhrU/J3x8wMumDfomJOx/kYofd5bV\n" +
-        "ImqXtgWhiLSqM5RA6d5dUb6hK3Iu2/LDMuo+ltVLZNkD8y32RbNh6J1vAoGAbLqQ\n" +
-        "pgyhSf3Vtc0Q+aVB87p0k3tKJ1wynl4zSzYhyMLgHakAHIzL8/qVqmVUwXP8euJZ\n" +
-        "UIk1nGHobxk0d1XB6Y+rKEcn+/iFZt1ljx7pQ3ly0L824NXqGKC6bHeYUI1li/Gp\n" +
-        "Gv3oFvCh7D1D8NUAEKLIpMndAohUUhkAC/qAkHkCgYEAzSIarDNquayV+umC1SXm\n" +
-        "Zo6XLuzWjudLxPd2lyCfwR2aRKlrb+5OFYErX+RSLyCJmaqVZMyXP09PBIvNXu2Z\n" +
-        "+gbx5WUC+kA+6zdKEPXowei6i6EHMXYT2AL7395ZbPajZjsCduE3WuUztuHrhtMm\n" +
-        "JI+k1o4rCnSLlX4gWdN1oTs=",
+        "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzN8GhtZ9kinwC\n" +
+        "ALo+JYMNq8/rmZK+AJdQbEAJvFU0B+QuE1FA6weuJEHdfVDee870960LrAqW8T2O\n" +
+        "WcMOPsP7WPWvUIc7uazV+ryt9xBh2RqcO27gCUejy/5JJZfXowhcVFgOPZVUebI+\n" +
+        "2BuW1yDjMYC7BPvjoV0sj3i5eMmOrUg6v+/RUiXATk5FYfldz7lOP6gVYrQThrgY\n" +
+        "cOC0oqGfabvaE3nr8aQ8oaSC4/k1FLWphhX8QWR3JAw5XwnUzsdqJxJ+3g4vp4qN\n" +
+        "OuCj3f6QeT62/QNzpylUGdqVfdOisbxAjoFwePVdB3vygCUAL7+MXilzSbCLS6fj\n" +
+        "2R0r2BF9AgMBAAECggEASIkPkMCuw4WdTT44IwERus3IOIYOs2IP3BgEDyyvm4B6\n" +
+        "JP/iihDWKfA4zEl1Gqcni1RXMHswSglXra682J4kui02Ov+vzEeJIY37Ibn2YnP5\n" +
+        "ZjRT2s9GtI/S2o4hl8A/mQb2IMViFC+xKehTukhV4j5d6NPKk0XzLR7gcMjnYxwn\n" +
+        "l21fS6D2oM1xRG/di7sL+uLF8EXLRzfiWDNi12uQv4nwtxPKvuKhH6yzHt7YqMH0\n" +
+        "46pmDKDaxV4w1JdycjCb6NrCJOYZygoQobuZqOQ30UZoZsPJrtovkncFr1e+lNcO\n" +
+        "+aWDfOLCtTH046dEQh5oCShyXMybNlry/QHsOtHOwQKBgQDh2iIjs+FPpQy7Z3EX\n" +
+        "DGEvHYqPjrYO9an2KSRr1m9gzRlWYxKY46WmPKwjMerYtra0GP+TBHrgxsfO8tD2\n" +
+        "wUAII6sd1qup0a/Sutgf2JxVilLykd0+Ge4/Cs51tCdJ8EqDV2B6WhTewOY2EGvg\n" +
+        "JiKYkeNwgRX/9M9CFSAMAk0hUQKBgQDLJAartL3DoGUPjYtpJnfgGM23yAGl6G5r\n" +
+        "NSXDn80BiYIC1p0bG3N0xm3yAjqOtJAUj9jZbvDNbCe3GJfLARMr23legX4tRrgZ\n" +
+        "nEdKnAFKAKL01oM+A5/lHdkwaZI9yyv+hgSVdYzUjB8rDmzeVQzo1BT7vXypt2yV\n" +
+        "6O1OnUpCbQKBgA/0rzDChopv6KRcvHqaX0tK1P0rYeVQqb9ATNhpf9jg5Idb3HZ8\n" +
+        "rrk91BNwdVz2G5ZBpdynFl9G69rNAMJOCM4KZw5mmh4XOEq09Ivba8AHU7DbaTv3\n" +
+        "7QL7KnbaUWRB26HHzIMYVh0el6T+KADf8NXCiMTr+bfpfbL3dxoiF3zhAoGAbCJD\n" +
+        "Qse1dBs/cKYCHfkSOsI5T6kx52Tw0jS6Y4X/FOBjyqr/elyEexbdk8PH9Ar931Qr\n" +
+        "NKMvn8oA4iA/PRrXX7M2yi3YQrWwbkGYWYjtzrzEAdzmg+5eARKAeJrZ8/bg9l3U\n" +
+        "ttKaItJsDPlizn8rngy3FsJpR9aSAMK6/+wOiYkCgYEA1tZkI1rD1W9NYZtbI9BE\n" +
+        "qlJVFi2PBOJMKNuWdouPX3HLQ72GJSQff2BFzLTELjweVVJ0SvY4IipzpQOHQOBy\n" +
+        "5qh/p6izXJZh3IHtvwVBjHoEVplg1b2+I5e3jDCfqnwcQw82dW5SxOJMg1h/BD0I\n" +
+        "qAL3go42DYeYhu/WnECMeis=",
 
         //
-        // DSA private key related to cert endEntityCertStrs[2].
+        // EC private key related to cert endEntityCertStrs[2].
+        //
+        "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGVc7hICpmp91jbYe\n" +
+        "nrr8nYHD37RZP3VENY+szuA7WjuhRANCAATn0wRE1OVVnV56mzxnc657lmSBB0+0\n" +
+        "P5YgTq2Pc9sgqnEY8PG980/3n0DJCMQr96FZBWsIyY2gSQjNj4ggGAHT",
+
+        //
+        // DSA private key related to cert endEntityCertStrs[3].
         //
-        "MIICZAIBADCCAjkGByqGSM44BAEwggIsAoIBAQCWte2WHSAob+cnmLjyt818YFBB\n" +
-        "hrHVAwM30FMbTK8grXN2jYFi8bDdf0GL6fb/oYeLcJxttFXpghB/UJcdOj8YO+A8\n" +
-        "EjgPN0nnDtsu3HYJqx6XqfXa8Sm11WWn8AYW00sIVfiAzuxDuvi2kb2bsmv2DqjS\n" +
-        "BPRDcRVZNDazbAweqXwxF2bAmQCzDdpnJfChp9au73cyUFBFexerixj99851X5kM\n" +
-        "liR92FtPVlQhVx7w82RxlwztjtkMCFNQeQLF5zhni5yoF8xKwqDSsFG1RT9FtDlX\n" +
-        "iaOKXGXy//E1RCxhvdALrh9VdxrKuj3cwgz8OFL/7+Zu/+pMF6gUMQCQO44TAiEA\n" +
-        "jkSBlZh8uZwS0YSD+Lwu77E8W+AkwKT1ZmXi+YEyMLUCggEAdlWS2NtpNviEDOnX\n" +
-        "kRity5e7TwoRqypxpk27VE4SM2u9EqfQ5kwYi8zBGPiIBvJGLk+Uk1cVo7NHMVwi\n" +
-        "mRjEFrYqDLYlzwNjNxwNP/BNJgy0ps29EQHdscZw8hrLqqNXlZsezy4zd259xI4Q\n" +
-        "//o93Scd2VUnDBsqu7FVqaVL6bnRF4vtnrX+hlB463F9u47qO7uM0NXswvx2BtR/\n" +
-        "oxBahoXUOm+x/iwR2R/S3oDsiGUzK8wGP08CK++7F6zSVkaMZgxKygfvG0Q8m2IT\n" +
-        "SbMJWZtDeWGFJT5nqYG5el88Ar9MtHfNYd22sqiDy1xFLUQJfRrjNITQJQNWPqkp\n" +
-        "/cSxWAQiAiAKHYbYwEy0XS9J0MeKQmqPswn0nCJKvH+esfMKkZvV3w=="
+        "MIICZQIBADCCAjoGByqGSM44BAEwggItAoIBAQCaVq+CgmsxyOpZFVwMTZ2ZYA9E\n" +
+        "SCfNC+d4QN5/rxymsPONLC86zlB4XLHvmJtp2jV7qM7+tbWmsIZYcMPWgD9Ofg+T\n" +
+        "e+7SdTHO+XWoBNz5olulRTk6nRu+notsKxBCyVX0V5GHsRvts/qClz+QcIHMSALV\n" +
+        "UIrumtxxdbL63pIw4ds0PeH5RtyR9JpS5pWxUk1MPUPoYSV9GPg2bPUEY99Ji8qi\n" +
+        "vA1kRTeBfAWC0OgBwbHu1zIKUukMd+pF/D/vKKovJ6I/sPiGXmUw2i4lYOUB0wWn\n" +
+        "QUyBLez1UMWKchyvpW0FYxMbv7j5zMcAH7d++vCWo3OEGfe4Uet6Q3/Dx83HAiEA\n" +
+        "mjtJp7XNrsgxbRDqETkHhv6zYH8Voo+gDgSO1m7XcH0CggEBAI5moswp++plZSBP\n" +
+        "Q9DdCNHt0AV13Toj1jf5M5DNmTX6P6/D/oRfi8Ui1fiCKFv+7lD0J2anCFUaLtu+\n" +
+        "j7v78gp5OChDp/n49K6DtKtZZRDmw/Bpm6LNdknmdN5kO5wVzbIXHTCCeNs/CJTF\n" +
+        "mSU5PvEaI4y3M5NraSgLPkq4gEv7/A8orGbKmj1Whj3F9t1Tosxdm/+WkPldMz2t\n" +
+        "gev+9RM2S6S9XoembRgwRaFVkpQmKoKpOoZcdqV47FLDq5BYH/5POeJ9wLuAHjxQ\n" +
+        "5CMKo4p/lW7BCd4kuGWFT+OFFXfG2v6EtlqFbXBiFWLxyMsOtkUqWARCqEHhyucl\n" +
+        "TSYlj60EIgIgLfA75+8KcKxdN8mr6gzGjQe7jPFGG42Ejhd7Q2F4wuw="
+        };
+
+    // Private key algorithm of endEntityPrivateKeys.
+    private final static String[] endEntityPrivateKeyAlgs = {
+        "EC",
+        "RSA",
+        "EC",
+        "DSA",
         };
 
     // Private key names of endEntityPrivateKeys.
     private final static String[] endEntityPrivateKeyNames = {
-        "EC",
-        "RSA",
-        "DSA",
+        "ecdsa",
+        "rsa",
+        "ec-rsa",
+        "dsa",
         };
 
     /*
@@ -599,6 +679,7 @@
             String[] keyMaterialCerts,
             String[] keyMaterialKeys,
             String[] keyMaterialKeyAlgs,
+            String[] keyMaterialKeyNames,
             ContextParameters params) throws Exception {
 
         KeyStore ts = null;     // trust store
@@ -665,7 +746,7 @@
                 Certificate[] chain = new Certificate[] { keyCert };
 
                 // import the key entry.
-                ks.setKeyEntry("cert-" + keyMaterialKeyAlgs[i],
+                ks.setKeyEntry("cert-" + keyMaterialKeyNames[i],
                         priKey, passphrase, chain);
             }
         }
--- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ReadTimeout.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ReadTimeout.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -175,9 +175,9 @@
 
                 throw new Exception(
                         "system property timeout configuration does not work");
-            } catch (SocketTimeoutException stex) {
+            } catch (SSLException | SocketTimeoutException ex) {
                 System.out.println("Got expected timeout exception for " +
-                        "system property timeout configuration: " + stex);
+                        "system property timeout configuration: " + getCause(ex));
             } finally {
                 done();
                 http.disconnect();
@@ -196,9 +196,9 @@
 
                 throw new Exception(
                         "HttpsURLConnection.setReadTimeout() does not work");
-            } catch (SocketTimeoutException stex) {
+            } catch (SSLException | SocketTimeoutException ex) {
                 System.out.println("Got expected timeout exception for " +
-                        "HttpsURLConnection.setReadTimeout(): " + stex);
+                        "HttpsURLConnection.setReadTimeout(): " + getCause(ex));
             } finally {
                 done();
                 http.disconnect();
@@ -208,6 +208,20 @@
         }
     }
 
+    private Exception getCause(Exception ex) {
+        Exception cause = null;
+        if (ex instanceof SSLException) {
+            cause = (Exception) ex.getCause();
+            if (!(cause instanceof SocketTimeoutException)) {
+                throw new RuntimeException("Unexpected cause", cause);
+            }
+        } else {
+            cause = ex;
+        }
+
+        return cause;
+    }
+
     static class NameVerifier implements HostnameVerifier {
         public boolean verify(String hostname, SSLSession session) {
             return true;
--- a/test/jdk/sun/security/ec/TestEC.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ec/TestEC.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,7 @@
  * @library ../pkcs11/ec
  * @library ../pkcs11/sslecc
  * @library ../../../java/security/testlibrary
+ * @library ../../../javax/net/ssl/TLSCommon
  * @modules jdk.crypto.cryptoki/sun.security.pkcs11.wrapper
  * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1" TestEC
  * @run main/othervm/java.security.policy=TestEC.policy -Djdk.tls.namedGroups="secp256r1,sect193r1" TestEC
--- a/test/jdk/sun/security/krb5/auto/SSL.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-/*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-/*
- * @test
- * @bug 6894643 6913636 8005523 8025123 8194486
- * @summary Test JSSE Kerberos ciphersuite
- * @library /test/lib
- * @run main jdk.test.lib.FileInstaller TestHosts TestHosts
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_WITH_RC4_128_SHA
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_WITH_RC4_128_SHA unbound
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_WITH_RC4_128_SHA unbound sni
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_WITH_3DES_EDE_CBC_SHA
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_WITH_3DES_EDE_CBC_MD5
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_WITH_DES_CBC_SHA
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_WITH_DES_CBC_MD5
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_EXPORT_WITH_RC4_40_SHA
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_EXPORT_WITH_RC4_40_MD5
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL
- *      TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5
- */
-import java.io.*;
-import java.security.Permission;
-import javax.net.ssl.*;
-import java.security.Principal;
-import java.security.Security;
-import java.util.Date;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Locale;
-import javax.security.auth.kerberos.ServicePermission;
-import sun.security.jgss.GSSUtil;
-import sun.security.krb5.PrincipalName;
-import sun.security.krb5.internal.ktab.KeyTab;
-
-public class SSL extends SecurityManager {
-
-    private static String krb5Cipher;
-    private static final int LOOP_LIMIT = 3;
-    private static int loopCount = 0;
-    private static volatile String server;
-    private static volatile int port;
-    private static String sniHostname = null;
-    private static String sniMatcherPattern = null;
-
-    private static String permChecks = "";
-
-    // 0-Not started, 1-Start OK, 2-Failure
-    private static volatile int serverState = 0;
-
-    @Override
-    public void checkPermission(Permission perm, Object context) {
-        checkPermission(perm);
-    }
-
-    public void checkPermission(Permission perm) {
-        if (!(perm instanceof ServicePermission)) {
-            return;
-        }
-        ServicePermission p = (ServicePermission)perm;
-        // ServicePermissions required to create GSSName are ignored
-        if (!p.getActions().isEmpty()) {
-            permChecks = permChecks
-                + p.getActions().toUpperCase(Locale.US).charAt(0);
-        }
-    }
-
-    public static void main(String[] args) throws Exception {
-        // reset the security property to make sure that the algorithms
-        // and keys used in this test are not disabled.
-        Security.setProperty("jdk.tls.disabledAlgorithms", "");
-
-        krb5Cipher = args[0];
-
-        boolean unbound = args.length > 1;
-
-        // Workaround for JDK-8161101, reference the class before
-        // SecurityManager is set.
-        System.out.println("Touching " + ServicePermission.class);
-
-        System.setSecurityManager(new SSL());
-
-        KDC kdc = KDC.create(OneKDC.REALM);
-        server = "host." + OneKDC.REALM_LOWER_CASE;
-
-        if (args.length > 2) {
-            sniHostname = "test." + server;
-            sniMatcherPattern = ".*";
-        }
-
-        kdc.addPrincipal(OneKDC.USER, OneKDC.PASS);
-        kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM);
-        KDC.saveConfig(OneKDC.KRB5_CONF, kdc);
-        System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF);
-
-        // Add 3 versions of keys into keytab
-        KeyTab ktab = KeyTab.create(OneKDC.KTAB);
-        String serviceName = null;
-        if (sniHostname != null) {
-            serviceName = "host/" + sniHostname;
-        } else {
-            serviceName = "host/" + server;
-        }
-        PrincipalName service = new PrincipalName(
-            serviceName, PrincipalName.KRB_NT_SRV_HST);
-        ktab.addEntry(service, "pass1".toCharArray(), 1, true);
-        ktab.addEntry(service, "pass2".toCharArray(), 2, true);
-        ktab.addEntry(service, "pass3".toCharArray(), 3, true);
-        ktab.save();
-
-        // and use the middle one as the real key
-        kdc.addPrincipal(serviceName, "pass2".toCharArray());
-
-
-        // JAAS config entry name ssl
-        System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF);
-        File f = new File(OneKDC.JAAS_CONF);
-        FileOutputStream fos = new FileOutputStream(f);
-        fos.write((
-                "ssl {\n" +
-                "    com.sun.security.auth.module.Krb5LoginModule required\n" +
-                (unbound ?
-                    "    principal=*\n" :
-                    "    principal=\"" + serviceName + "\"\n") +
-                "    useKeyTab=true\n" +
-                "    keyTab=" + OneKDC.KTAB + "\n" +
-                "    isInitiator=false\n" +
-                "    storeKey=true;\n};\n"
-                ).getBytes());
-        fos.close();
-
-        Context c;
-        final Context s = Context.fromJAAS("ssl");
-
-        s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
-
-        Thread server = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    s.doAs(new JsseServerAction(), null);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    serverState = 2;
-                }
-            }
-        });
-        server.setDaemon(true);
-        server.start();
-
-        while (serverState == 0) {
-            Thread.sleep(50);
-        }
-
-        if (serverState == 2) {
-            throw new Exception("Server already failed");
-        }
-
-        c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
-        c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
-        c.doAs(new JsseClientAction(), null);
-
-        // Add another version of key, make sure it can be loaded
-        Thread.sleep(2000);
-        ktab = KeyTab.getInstance(OneKDC.KTAB);
-        ktab.addEntry(service, "pass4".toCharArray(), 4, true);
-        ktab.save();
-        kdc.addPrincipal(serviceName, "pass4".toCharArray());
-
-        c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
-        c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
-        c.doAs(new JsseClientAction(), null);
-
-        // Permission checking check. Please note this is highly
-        // implementation related.
-        if (unbound) {
-            // For unbound, server does not know what name to check.
-            // Client checks "initiate", then server gets the name
-            // and checks "accept". Second connection resume.
-            if (!permChecks.equals("IA")) {
-                throw new Exception(permChecks);
-            }
-        } else {
-            // For bound, JAAS checks "accept" once. Server checks again,
-            // client then checks "initiate". Second connection resume.
-            if (!permChecks.equals("AAI")) {
-                throw new Exception(permChecks);
-            }
-        }
-    }
-
-    // Following codes copied from
-    // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/lab/part2.html#JSSE
-    private static class JsseClientAction implements Action {
-        public byte[] run(Context s, byte[] input) throws Exception {
-            SSLSocketFactory sslsf =
-                (SSLSocketFactory) SSLSocketFactory.getDefault();
-            System.out.println("Connecting " + server + ":" + port);
-            SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port);
-
-            // Enable only a KRB5 cipher suite.
-            String enabledSuites[] = {krb5Cipher};
-            sslSocket.setEnabledCipherSuites(enabledSuites);
-            // Should check for exception if enabledSuites is not supported
-
-            if (sniHostname != null) {
-                List<SNIServerName> serverNames = new ArrayList<>();
-                serverNames.add(new SNIHostName(sniHostname));
-                SSLParameters params = sslSocket.getSSLParameters();
-                params.setServerNames(serverNames);
-                sslSocket.setSSLParameters(params);
-            }
-
-            BufferedReader in = new BufferedReader(new InputStreamReader(
-                sslSocket.getInputStream()));
-            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
-                sslSocket.getOutputStream()));
-
-            String outStr = "Hello There!\n";
-            out.write(outStr);
-            out.flush();
-            System.out.print("Sending " + outStr);
-
-            String inStr = in.readLine();
-            System.out.println("Received " + inStr);
-
-            String cipherSuiteChosen = sslSocket.getSession().getCipherSuite();
-            System.out.println("Cipher suite in use: " + cipherSuiteChosen);
-            Principal self = sslSocket.getSession().getLocalPrincipal();
-            System.out.println("I am: " + self.toString());
-            Principal peer = sslSocket.getSession().getPeerPrincipal();
-            System.out.println("Server is: " + peer.toString());
-
-            sslSocket.close();
-            // This line should not be needed. It's the server's duty to
-            // forget the old key
-            //sslSocket.getSession().invalidate();
-            return null;
-        }
-    }
-
-    private static class JsseServerAction implements Action {
-        public byte[] run(Context s, byte[] input) throws Exception {
-            SSLServerSocketFactory sslssf =
-                (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
-            SSLServerSocket sslServerSocket =
-                (SSLServerSocket) sslssf.createServerSocket(0); // any port
-            port = sslServerSocket.getLocalPort();
-            System.out.println("Listening on " + port);
-            serverState = 1;
-
-            // Enable only a KRB5 cipher suite.
-            String enabledSuites[] = {krb5Cipher};
-            sslServerSocket.setEnabledCipherSuites(enabledSuites);
-            // Should check for exception if enabledSuites is not supported
-
-            if (sniMatcherPattern != null) {
-                List<SNIMatcher> matchers = new ArrayList<>();
-                matchers.add(SNIHostName.createSNIMatcher(sniMatcherPattern));
-                SSLParameters params = sslServerSocket.getSSLParameters();
-                params.setSNIMatchers(matchers);
-                sslServerSocket.setSSLParameters(params);
-            }
-
-            while (loopCount++ < LOOP_LIMIT) {
-                System.out.println("Waiting for incoming connection...");
-
-                SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
-
-                System.out.println("Got connection from client "
-                    + sslSocket.getInetAddress());
-
-                BufferedReader in = new BufferedReader(new InputStreamReader(
-                    sslSocket.getInputStream()));
-                BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
-                    sslSocket.getOutputStream()));
-
-                String inStr = in.readLine();
-                System.out.println("Received " + inStr);
-
-                String outStr = inStr + " " + new Date().toString() + "\n";
-                out.write(outStr);
-                System.out.println("Sending " + outStr);
-                out.flush();
-
-                String cipherSuiteChosen =
-                    sslSocket.getSession().getCipherSuite();
-                System.out.println("Cipher suite in use: " + cipherSuiteChosen);
-                Principal self = sslSocket.getSession().getLocalPrincipal();
-                System.out.println("I am: " + self.toString());
-                Principal peer = sslSocket.getSession().getPeerPrincipal();
-                System.out.println("Client is: " + peer.toString());
-
-                sslSocket.close();
-            }
-            return null;
-        }
-    }
-}
--- a/test/jdk/sun/security/krb5/auto/SSLwithPerms.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-/*
- * @test
- * @bug 8038089 8194486
- * @summary TLS optional support for Kerberos cipher suites needs to be re-examined
- * @library ../../../../java/security/testlibrary/ /test/lib
- * @run main jdk.test.lib.FileInstaller TestHosts TestHosts
- * @run main/othervm -Djdk.net.hosts.file=TestHosts SSLwithPerms
- */
-import java.io.*;
-import javax.net.ssl.*;
-import javax.security.auth.AuthPermission;
-import javax.security.auth.kerberos.ServicePermission;
-import java.net.SocketPermission;
-import java.nio.ByteBuffer;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.Principal;
-import java.security.Security;
-import java.security.SecurityPermission;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.PropertyPermission;
-
-import sun.security.jgss.GSSUtil;
-
-public class SSLwithPerms {
-
-    static String KRB5_CONF = "krb5.conf";
-    static String JAAS_CONF = "jaas.conf";
-    static String REALM = "REALM";
-    static String KTAB = "ktab";
-    static String HOST = "host." + REALM.toLowerCase(Locale.US);
-    static String SERVER = "host/" + HOST;
-    static String USER = "user";
-    static char[] PASS = "password".toCharArray();
-
-    public static void main(String[] args) throws Exception {
-
-        Security.setProperty("jdk.tls.disabledAlgorithms", "");
-        if (args.length == 0) {
-            KDC kdc = KDC.create(REALM, HOST, 0, true);
-
-            kdc.addPrincipal(USER, PASS);
-            kdc.addPrincipalRandKey("krbtgt/" + REALM);
-            kdc.addPrincipalRandKey(SERVER);
-            KDC.saveConfig(KRB5_CONF, kdc);
-            kdc.writeKtab(KTAB);
-
-            File f = new File(JAAS_CONF);
-            FileOutputStream fos = new FileOutputStream(f);
-            fos.write((
-                    "ssl {\n" +
-                            "    com.sun.security.auth.module.Krb5LoginModule required\n" +
-                            "    principal=\"" + SERVER + "\"\n" +
-                            "    useKeyTab=true\n" +
-                            "    keyTab=" + KTAB + "\n" +
-                            "    isInitiator=false\n" +
-                            "    storeKey=true;\n};\n"
-            ).getBytes());
-            fos.close();
-
-            String hostsFileName = System.getProperty("test.src", ".") + "/TestHosts";
-
-            Proc pc = Proc.create("SSLwithPerms")
-                    .args("client")
-                    .inheritIO()
-                    .prop("java.security.manager", "")
-                    .prop("java.security.krb5.conf", KRB5_CONF)
-                    .prop("jdk.net.hosts.file", hostsFileName)
-                    .prop("javax.net.ssl", "handshake")
-                    .prop("sun.security.krb5.debug", "true")
-                    .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
-                    .perm(new java.util.PropertyPermission("user.name", "read"))
-                    .perm(new PropertyPermission("sun.security.krb5.principal", "read"))
-                    .perm(new FilePermission("port", "read"))
-                    .perm(new FilePermission(hostsFileName, "read"))
-                    .perm(new FilePermission(KTAB, "read"))
-                    .perm(new AuthPermission("modifyPrincipals"))
-                    .perm(new AuthPermission("modifyPrivateCredentials"))
-                    .perm(new AuthPermission("doAs"))
-                    .perm(new SocketPermission("127.0.0.1", "connect"))
-                    .perm(new ServicePermission("host/host.realm@REALM", "initiate"))
-                    .start();
-
-            Proc ps = Proc.create("SSLwithPerms")
-                    .args("server")
-                    .inheritIO()
-                    .prop("java.security.manager", "")
-                    .prop("java.security.krb5.conf", KRB5_CONF)
-                    .prop("java.security.auth.login.config", JAAS_CONF)
-                    .prop("jdk.net.hosts.file", hostsFileName)
-                    .prop("javax.net.ssl", "handshake")
-                    .prop("sun.security.krb5.debug", "true")
-                    .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
-                    .perm(new AuthPermission("createLoginContext.ssl"))
-                    .perm(new AuthPermission("doAs"))
-                    .perm(new FilePermission(hostsFileName, "read"))
-                    .perm(new FilePermission("port", "write"))
-                    .perm(new SocketPermission("127.0.0.1", "accept"))
-                    .perm(new ServicePermission("host/host.realm@REALM", "accept"))
-                    .start();
-
-            if (pc.waitFor() != 0) {
-                throw new Exception();
-            }
-            if (ps.waitFor() != 0) {
-                throw new Exception();
-            }
-        } else if (args[0].equals("client")) {
-            Context c;
-            c = Context.fromUserPass(USER, PASS, false);
-            c.doAs(new JsseClientAction(), null);
-        } else if (args[0].equals("server")) {
-            final Context s = Context.fromJAAS("ssl");
-            s.doAs(new JsseServerAction(), null);
-        }
-    }
-
-    private static class JsseClientAction implements Action {
-        public byte[] run(Context s, byte[] input) throws Exception {
-            SSLSocketFactory sslsf =
-                (SSLSocketFactory) SSLSocketFactory.getDefault();
-            while (!Files.exists(Paths.get("port"))) {
-                Thread.sleep(100);
-            }
-            int port = ByteBuffer.allocate(4)
-                    .put(Files.readAllBytes(Paths.get("port"))).getInt(0);
-            System.out.println("Connecting " + SERVER + ":" + port);
-            SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(HOST, port);
-
-            // Enable only a KRB5 cipher suite.
-            String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
-            sslSocket.setEnabledCipherSuites(enabledSuites);
-
-            SSLParameters params = sslSocket.getSSLParameters();
-            params.setServerNames(Collections.singletonList(new SNIHostName(HOST)));
-            sslSocket.setSSLParameters(params);
-
-            BufferedReader in = new BufferedReader(new InputStreamReader(
-                sslSocket.getInputStream()));
-            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
-                sslSocket.getOutputStream()));
-
-            String outStr = "Hello There!\n";
-            out.write(outStr);
-            out.flush();
-            System.out.print("Sending " + outStr);
-
-            String inStr = in.readLine();
-            System.out.println("Received " + inStr);
-
-            String cipherSuiteChosen = sslSocket.getSession().getCipherSuite();
-            System.out.println("Cipher suite in use: " + cipherSuiteChosen);
-            Principal self = sslSocket.getSession().getLocalPrincipal();
-            System.out.println("I am: " + self.toString());
-            Principal peer = sslSocket.getSession().getPeerPrincipal();
-            System.out.println("Server is: " + peer.toString());
-
-            sslSocket.close();
-            return null;
-        }
-    }
-
-    private static class JsseServerAction implements Action {
-        public byte[] run(Context s, byte[] input) throws Exception {
-            SSLServerSocketFactory sslssf =
-                (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
-            SSLServerSocket sslServerSocket =
-                (SSLServerSocket) sslssf.createServerSocket(0); // any port
-            int port = sslServerSocket.getLocalPort();
-            System.out.println("Listening on " + port);
-
-            String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
-            sslServerSocket.setEnabledCipherSuites(enabledSuites);
-
-            Files.write(Paths.get("port"), ByteBuffer.allocate(4).putInt(port).array());
-            System.out.println("Waiting for incoming connection...");
-
-            SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
-
-            System.out.println("Got connection from client "
-                + sslSocket.getInetAddress());
-
-            BufferedReader in = new BufferedReader(new InputStreamReader(
-                sslSocket.getInputStream()));
-            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
-                sslSocket.getOutputStream()));
-
-            String inStr = in.readLine();
-            System.out.println("Received " + inStr);
-
-            String outStr = inStr + " " + new Date().toString() + "\n";
-            out.write(outStr);
-            System.out.println("Sending " + outStr);
-            out.flush();
-
-            String cipherSuiteChosen =
-                sslSocket.getSession().getCipherSuite();
-            System.out.println("Cipher suite in use: " + cipherSuiteChosen);
-            Principal self = sslSocket.getSession().getLocalPrincipal();
-            System.out.println("I am: " + self.toString());
-            Principal peer = sslSocket.getSession().getPeerPrincipal();
-            System.out.println("Client is: " + peer.toString());
-
-            sslSocket.close();
-            return null;
-        }
-    }
-}
--- a/test/jdk/sun/security/krb5/auto/UnboundSSL.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivilegedActionException;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.security.auth.login.LoginException;
-
-/*
- * @test
- * @bug 8025123 8194486
- * @summary Checks if an unbound server can handle connections
- *          only for allowed service principals
- * @library /test/lib
- * @run main jdk.test.lib.FileInstaller TestHosts TestHosts
- * @run main/othervm/policy=unbound.ssl.policy -Djdk.net.hosts.file=TestHosts
- *      UnboundSSL unbound.ssl.jaas.conf server_star
- * @run main/othervm/policy=unbound.ssl.policy -Djdk.net.hosts.file=TestHosts
- *      UnboundSSL unbound.ssl.jaas.conf server_multiple_principals
- */
-public class UnboundSSL {
-
-    public static void main(String[] args) throws IOException,
-            NoSuchAlgorithmException,LoginException, PrivilegedActionException,
-            InterruptedException {
-        UnboundSSL test = new UnboundSSL();
-        test.start(args[0], args[1]);
-    }
-
-    private void start(String jaacConfigFile, String serverJaasConfig)
-            throws IOException, NoSuchAlgorithmException,LoginException,
-            PrivilegedActionException, InterruptedException {
-
-        // define principals
-        String service1host = "service1." + UnboundSSLUtils.HOST;
-        String service2host = "service2." + UnboundSSLUtils.HOST;
-        String service3host = "service3." + UnboundSSLUtils.HOST;
-        String service1Principal = "host/" + service1host + "@"
-                + UnboundSSLUtils.REALM;
-        String service2Principal = "host/" + service2host + "@"
-                + UnboundSSLUtils.REALM;
-        String service3Principal = "host/" + service3host + "@"
-                + UnboundSSLUtils.REALM;
-
-        Map<String, String> principals = new HashMap<>();
-        principals.put(UnboundSSLUtils.USER_PRINCIPAL,
-                UnboundSSLUtils.USER_PASSWORD);
-        principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, null);
-        principals.put(service1Principal, null);
-        principals.put(service2Principal, null);
-        principals.put(service3Principal, null);
-
-        System.setProperty("java.security.krb5.conf",
-               UnboundSSLUtils.KRB5_CONF_FILENAME);
-
-        // start a local KDC instance
-        KDC.startKDC(UnboundSSLUtils.HOST, UnboundSSLUtils.KRB5_CONF_FILENAME,
-                UnboundSSLUtils.REALM, principals,
-                UnboundSSLUtils.KTAB_FILENAME, KDC.KtabMode.APPEND);
-
-        System.setProperty("java.security.auth.login.config",
-                UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile);
-        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
-
-        try (final SSLEchoServer server = SSLEchoServer.init(
-                UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) {
-
-            // start a server instance
-            UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig);
-
-            // wait for the server is ready
-            while (!server.isReady()) {
-                Thread.sleep(UnboundSSLUtils.DELAY);
-            }
-
-            int port = server.getPort();
-
-            // run clients
-
-            // the server should have a permission to handle a request
-            // with this service principal (there should be an appropriate
-            // javax.security.auth.kerberos.ServicePermission in policy file)
-            System.out.println("Connect: SNI hostname = " + service1host
-                    + ", successful connection is expected");
-            SSLClient.init(UnboundSSLUtils.HOST, port,
-                    UnboundSSLUtils.TLS_KRB5_FILTER, service1host).connect();
-
-            // the server should NOT have a permission to handle a request
-            // with this service principal (there should be an appropriate
-            // javax.security.auth.kerberos.ServicePermission in policy file)
-            // handshake failures is expected
-            System.out.println("Connect: SNI hostname = " + service2host
-                    + ", connection failure is expected");
-            try {
-                SSLClient.init(UnboundSSLUtils.HOST, port,
-                        UnboundSSLUtils.TLS_KRB5_FILTER, service2host)
-                            .connect();
-                throw new RuntimeException("Test failed: "
-                        + "expected IOException not thrown");
-            } catch (IOException e) {
-                System.out.println("Expected exception: " + e);
-            }
-
-            // the server should have a permission to handle a request
-            // with this service principal (there should be an appropriate
-            // javax.security.auth.kerberos.ServicePermission in policy file)
-            System.out.println("Connect: SNI hostname = " + service3host
-                    + ", successful connection is expected");
-            SSLClient.init(UnboundSSLUtils.HOST, port,
-                    UnboundSSLUtils.TLS_KRB5_FILTER, service3host).connect();
-        }
-
-        System.out.println("Test passed");
-    }
-}
--- a/test/jdk/sun/security/krb5/auto/UnboundSSLMultipleKeys.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivilegedActionException;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.security.auth.login.LoginException;
-
-/*
- * @test
- * @bug 8025123 8194486
- * @summary Checks if an unbound server pick up a correct key from keytab
- * @library /test/lib
- * @run main jdk.test.lib.FileInstaller TestHosts TestHosts
- * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLMultipleKeys
- *                              unbound.ssl.jaas.conf server_star
- * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLMultipleKeys
- *                              unbound.ssl.jaas.conf server_multiple_principals
- */
-public class UnboundSSLMultipleKeys {
-
-    public static void main(String[] args)
-            throws IOException, NoSuchAlgorithmException, LoginException,
-            PrivilegedActionException, InterruptedException {
-        UnboundSSLMultipleKeys test = new UnboundSSLMultipleKeys();
-        test.start(args[0], args[1]);
-    }
-
-    private void start(String jaacConfigFile, String serverJaasConfig)
-            throws IOException, NoSuchAlgorithmException, LoginException,
-            PrivilegedActionException, InterruptedException {
-
-        // define service principals
-        String service1host = "service1." + UnboundSSLUtils.HOST;
-        String service2host = "service2." + UnboundSSLUtils.HOST;
-        String service3host = "service3." + UnboundSSLUtils.HOST;
-        String service1Principal = "host/" + service1host + "@"
-                + UnboundSSLUtils.REALM;
-        String service2Principal = "host/" + service2host + "@"
-                + UnboundSSLUtils.REALM;
-        String service3Principal = "host/" + service3host + "@"
-                + UnboundSSLUtils.REALM;
-
-        Map<String, String> principals = new HashMap<>();
-        principals.put(UnboundSSLUtils.USER_PRINCIPAL,
-                UnboundSSLUtils.USER_PASSWORD);
-        principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, "pass");
-        principals.put(service1Principal, "pass0");
-        principals.put(service1Principal, "pass1");
-        principals.put(service1Principal, "pass2");
-        principals.put(service2Principal, "pass");
-        principals.put(service3Principal, "pass");
-
-        System.setProperty("java.security.krb5.conf",
-                UnboundSSLUtils.KRB5_CONF_FILENAME);
-
-        /*
-         * Start a local KDC instance
-         *
-         * Keytab file contains 3 keys (with different KVNO) for service1
-         * principal, but password for only one key is the same with the record
-         * for service1 principal in KDC.
-         */
-        KDC.startKDC(UnboundSSLUtils.HOST, UnboundSSLUtils.KRB5_CONF_FILENAME,
-                UnboundSSLUtils.REALM, principals,
-                UnboundSSLUtils.KTAB_FILENAME, KDC.KtabMode.APPEND);
-
-        System.setProperty("java.security.auth.login.config",
-                UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile);
-        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
-
-        // start an SSL server instance
-        try (SSLEchoServer server = SSLEchoServer.init(
-                UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) {
-
-            UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig);
-
-            //  wait for the server is ready
-            while (!server.isReady()) {
-                Thread.sleep(UnboundSSLUtils.DELAY);
-            }
-
-            // run a client
-            System.out.println("Successful connection is expected");
-            SSLClient.init(UnboundSSLUtils.HOST, server.getPort(),
-                    UnboundSSLUtils.TLS_KRB5_FILTER, service1host).connect();
-        }
-
-        System.out.println("Test passed");
-    }
-
-}
--- a/test/jdk/sun/security/krb5/auto/UnboundSSLPrincipalProperty.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivilegedActionException;
-import java.util.HashMap;
-import java.util.Map;
-import javax.security.auth.login.LoginException;
-
-/*
- * @test
- * @bug 8025123 8194486
- * @summary Checks if an unbound server uses a service principal
- *          from sun.security.krb5.principal system property if specified
- * @library /test/lib
- * @run main jdk.test.lib.FileInstaller TestHosts TestHosts
- * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLPrincipalProperty
- *                              unbound.ssl.jaas.conf server_star
- * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLPrincipalProperty
- *                              unbound.ssl.jaas.conf server_multiple_principals
- */
-public class UnboundSSLPrincipalProperty {
-
-    public static void main(String[] args) throws IOException,
-            NoSuchAlgorithmException,LoginException, PrivilegedActionException,
-            InterruptedException {
-        UnboundSSLPrincipalProperty test = new UnboundSSLPrincipalProperty();
-        test.start(args[0], args[1]);
-    }
-
-    public void start(String jaacConfigFile, String serverJaasConfig)
-            throws IOException, NoSuchAlgorithmException,LoginException,
-            PrivilegedActionException, InterruptedException {
-
-        // define principals
-        String service1host = "service1." + UnboundSSLUtils.HOST;
-        String service3host = "service3." + UnboundSSLUtils.HOST;
-        String service1Principal = "host/" + service1host + "@"
-                + UnboundSSLUtils.REALM;
-        String service3Principal = "host/" + service3host
-                + "@" + UnboundSSLUtils.REALM;
-
-        Map<String, String> principals = new HashMap<>();
-        principals.put(UnboundSSLUtils.USER_PRINCIPAL,
-                UnboundSSLUtils.USER_PASSWORD);
-        principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, null);
-        principals.put(service1Principal, null);
-        principals.put(service3Principal, null);
-
-        System.setProperty("java.security.krb5.conf",
-                UnboundSSLUtils.KRB5_CONF_FILENAME);
-
-        // start a local KDC instance
-        KDC.startKDC(UnboundSSLUtils.HOST, UnboundSSLUtils.KRB5_CONF_FILENAME,
-                UnboundSSLUtils.REALM, principals,
-                UnboundSSLUtils.KTAB_FILENAME, KDC.KtabMode.APPEND);
-
-        System.setProperty("java.security.auth.login.config",
-                UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile);
-        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
-
-        // start an SSL server instance
-        try (final SSLEchoServer server = SSLEchoServer.init(
-                UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) {
-
-            // specify a service principal for the server
-            System.setProperty("sun.security.krb5.principal",
-                    service3Principal);
-
-            UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig);
-
-            // wait for the server is ready
-            while (!server.isReady()) {
-                Thread.sleep(UnboundSSLUtils.DELAY);
-            }
-
-            int port = server.getPort();
-
-            // connetion failure is expected
-            // since service3 principal was specified to use by the server
-            System.out.println("Connect: SNI hostname = " + service1host
-                    + ", connection failure is expected");
-            try {
-                SSLClient.init(UnboundSSLUtils.HOST, port,
-                        UnboundSSLUtils.TLS_KRB5_FILTER, service1host)
-                            .connect();
-                throw new RuntimeException("Test failed: "
-                        + "expected IOException not thrown");
-            } catch (IOException e) {
-                System.out.println("Expected exception: " + e);
-            }
-
-            System.out.println("Connect: SNI hostname = " + service3host
-                    + ", successful connection is expected");
-            SSLClient.init(UnboundSSLUtils.HOST, port,
-                    UnboundSSLUtils.TLS_KRB5_FILTER, service3host).connect();
-        }
-
-        System.out.println("Test passed");
-    }
-}
--- a/test/jdk/sun/security/krb5/auto/UnboundSSLUtils.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,292 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- * 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.
- */
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.SNIHostName;
-import javax.net.ssl.SNIMatcher;
-import javax.net.ssl.SNIServerName;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-/*
- * Helper class for unbound krb5 tests.
- */
-class UnboundSSLUtils {
-
-    static final String KTAB_FILENAME = "krb5.keytab.data";
-    static final String HOST = "localhost";
-    static final String REALM = "TEST.REALM";
-    static final String KRBTGT_PRINCIPAL = "krbtgt/" + REALM;
-    static final String TEST_SRC = System.getProperty("test.src", ".");
-    static final String TLS_KRB5_FILTER = "TLS_KRB5";
-    static final String USER = "USER";
-    static final String USER_PASSWORD = "password";
-    static final String FS = System.getProperty("file.separator");
-    static final String SNI_PATTERN = ".*";
-    static final String USER_PRINCIPAL = USER + "@" + REALM;
-    static final String KRB5_CONF_FILENAME = "krb5.conf";
-    static final int DELAY = 1000;
-
-   static String[] filterStringArray(String[] src, String filter) {
-        return Arrays.stream(src).filter((item) -> item.startsWith(filter))
-                .toArray(size -> new String[size]);
-    }
-
-    /*
-     * The method does JAAS login,
-     * and runs an SSL server in the JAAS context.
-     */
-    static void startServerWithJaas(final SSLEchoServer server,
-            String config) throws LoginException, PrivilegedActionException {
-        LoginContext context = new LoginContext(config);
-        context.login();
-        System.out.println("Server: successful authentication");
-        Subject.doAs(context.getSubject(),
-                (PrivilegedExceptionAction<Object>) () -> {
-            SSLEchoServer.startServer(server);
-            return null;
-        });
-    }
-
-}
-
-class SSLClient {
-
-    private final static byte[][] arrays = {
-        new byte[] {-1, 0, 2},
-        new byte[] {}
-    };
-
-    private final SSLSocket socket;
-
-    private SSLClient(SSLSocket socket) {
-        this.socket = socket;
-    }
-
-    void connect() throws IOException {
-        System.out.println("Client: connect to server");
-        try (BufferedInputStream bis = new BufferedInputStream(
-                        socket.getInputStream());
-                BufferedOutputStream bos = new BufferedOutputStream(
-                        socket.getOutputStream())) {
-
-            for (byte[] bytes : arrays) {
-                System.out.println("Client: send byte array: "
-                        + Arrays.toString(bytes));
-
-                bos.write(bytes);
-                bos.flush();
-
-                byte[] recieved = new byte[bytes.length];
-                int read = bis.read(recieved, 0, bytes.length);
-                if (read < 0) {
-                    throw new IOException("Client: couldn't read a response");
-                }
-
-                System.out.println("Client: recieved byte array: "
-                        + Arrays.toString(recieved));
-
-                if (!Arrays.equals(bytes, recieved)) {
-                    throw new IOException("Client: sent byte array "
-                                + "is not equal with recieved byte array");
-                }
-            }
-            socket.getSession().invalidate();
-        } finally {
-            if (!socket.isClosed()) {
-                socket.close();
-            }
-        }
-    }
-
-    static SSLClient init(String host, int port, String cipherSuiteFilter,
-            String sniHostName) throws NoSuchAlgorithmException, IOException {
-        SSLContext sslContext = SSLContext.getDefault();
-        SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
-        SSLSocket socket = (SSLSocket) ssf.createSocket(host, port);
-        SSLParameters params = new SSLParameters();
-
-        if (cipherSuiteFilter != null) {
-            String[] cipherSuites = UnboundSSLUtils.filterStringArray(
-                    ssf.getSupportedCipherSuites(), cipherSuiteFilter);
-            System.out.println("Client: enabled cipher suites: "
-                    + Arrays.toString(cipherSuites));
-            params.setCipherSuites(cipherSuites);
-        }
-
-        if (sniHostName != null) {
-            System.out.println("Client: set SNI hostname: " + sniHostName);
-            SNIHostName serverName = new SNIHostName(sniHostName);
-            List<SNIServerName> serverNames = new ArrayList<>();
-            serverNames.add(serverName);
-            params.setServerNames(serverNames);
-        }
-
-        socket.setSSLParameters(params);
-
-        return new SSLClient(socket);
-    }
-
-}
-
-class SSLEchoServer implements Runnable, AutoCloseable {
-
-    private final SSLServerSocket ssocket;
-    private volatile boolean stopped = false;
-    private volatile boolean ready = false;
-
-    /*
-     * Starts the server in a separate thread.
-     */
-    static void startServer(SSLEchoServer server) {
-        Thread serverThread = new Thread(server, "SSL echo server thread");
-        serverThread.setDaemon(true);
-        serverThread.start();
-    }
-
-    private SSLEchoServer(SSLServerSocket ssocket) {
-        this.ssocket = ssocket;
-    }
-
-    /*
-     * Main server loop.
-     */
-    @Override
-    public void run() {
-        System.out.println("Server: started");
-        while (!stopped) {
-            ready = true;
-            try (SSLSocket socket = (SSLSocket) ssocket.accept()) {
-                System.out.println("Server: client connection accepted");
-                try (
-                    BufferedInputStream bis = new BufferedInputStream(
-                            socket.getInputStream());
-                    BufferedOutputStream bos = new BufferedOutputStream(
-                            socket.getOutputStream())
-                ) {
-                    byte[] buffer = new byte[1024];
-                    int read;
-                    while ((read = bis.read(buffer)) > 0) {
-                        bos.write(buffer, 0, read);
-                        System.out.println("Server: recieved " + read
-                                + " bytes: "
-                                + Arrays.toString(Arrays.copyOf(buffer, read)));
-                        bos.flush();
-                    }
-                }
-            } catch (IOException e) {
-                if (stopped) {
-                    // stopped == true means that stop() method was called,
-                    // so just ignore the exception, and finish the loop
-                    break;
-                }
-                System.out.println("Server: couldn't accept client connection: "
-                        + e);
-            }
-        }
-        System.out.println("Server: finished");
-    }
-
-    boolean isReady() {
-        return ready;
-    }
-
-    void stop() {
-        stopped = true;
-        ready = false;
-
-        // close the server socket to interupt accept() method
-        try {
-            if (!ssocket.isClosed()) {
-                ssocket.close();
-            }
-        } catch (IOException e) {
-            throw new RuntimeException("Unexpected exception: " + e);
-        }
-    }
-
-    @Override
-    public void close() {
-        stop();
-    }
-
-    int getPort() {
-        return ssocket.getLocalPort();
-    }
-
-    /*
-     * Creates server instance.
-     *
-     * @param cipherSuiteFilter Filter for enabled cipher suites
-     * @param sniMatcherPattern Pattern for SNI server hame
-     */
-    static SSLEchoServer init(String cipherSuiteFilter,
-            String sniPattern) throws NoSuchAlgorithmException, IOException {
-        SSLContext context = SSLContext.getDefault();
-        SSLServerSocketFactory ssf =
-                (SSLServerSocketFactory) context.getServerSocketFactory();
-        SSLServerSocket ssocket =
-                (SSLServerSocket) ssf.createServerSocket(0);
-
-        // specify enabled cipher suites
-        if (cipherSuiteFilter != null) {
-            String[] ciphersuites = UnboundSSLUtils.filterStringArray(
-                    ssf.getSupportedCipherSuites(), cipherSuiteFilter);
-            System.out.println("Server: enabled cipher suites: "
-                    + Arrays.toString(ciphersuites));
-            ssocket.setEnabledCipherSuites(ciphersuites);
-        }
-
-        // specify SNI matcher pattern
-        if (sniPattern != null) {
-            System.out.println("Server: set SNI matcher: " + sniPattern);
-            SNIMatcher matcher = SNIHostName.createSNIMatcher(sniPattern);
-            List<SNIMatcher> matchers = new ArrayList<>();
-            matchers.add(matcher);
-            SSLParameters params = ssocket.getSSLParameters();
-            params.setSNIMatchers(matchers);
-            ssocket.setSSLParameters(params);
-        }
-
-        return new SSLEchoServer(ssocket);
-    }
-
-}
-
--- a/test/jdk/sun/security/krb5/auto/unbound.ssl.jaas.conf	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-com.sun.net.ssl.client {
-    com.sun.security.auth.module.Krb5LoginModule required
-    principal="USER@TEST.REALM"
-    doNotPrompt=true
-    useKeyTab=true
-    keyTab="krb5.keytab.data";
-};
-
-server_star {
-    com.sun.security.auth.module.Krb5LoginModule required
-    principal="*"
-    isInitiator=false
-    useKeyTab=true
-    keyTab="krb5.keytab.data"
-    storeKey=true;
-};
-
-server_multiple_principals {
-    com.sun.security.auth.module.Krb5LoginModule required
-    principal="host/service1.localhost@TEST.REALM"
-    isInitiator=false
-    useKeyTab=true
-    keyTab="krb5.keytab.data"
-    storeKey=true;
-
-    com.sun.security.auth.module.Krb5LoginModule required
-    principal="host/service2.localhost@TEST.REALM"
-    isInitiator=false
-    useKeyTab=true
-    keyTab="krb5.keytab.data"
-    storeKey=true;
-
-    com.sun.security.auth.module.Krb5LoginModule required
-    principal="host/service3.localhost@TEST.REALM"
-    isInitiator=false
-    useKeyTab=true
-    keyTab="krb5.keytab.data"
-    storeKey=true;
-};
-
--- a/test/jdk/sun/security/krb5/auto/unbound.ssl.policy	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-grant {
-    permission java.util.PropertyPermission "*", "read,write";
-    permission java.net.SocketPermission "*:*", "listen,resolve,accept,connect";
-    permission java.io.FilePermission "/-", "read";
-    permission java.io.FilePermission "*", "read,write,delete";
-    permission java.lang.RuntimePermission "accessDeclaredMembers";
-    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
-    permission java.lang.RuntimePermission "accessClassInPackage.*";
-    permission javax.security.auth.AuthPermission "doAs";
-    permission javax.security.auth.AuthPermission "getSubject";
-    permission javax.security.auth.AuthPermission
-                    "createLoginContext.server_star";
-    permission javax.security.auth.AuthPermission
-                    "createLoginContext.server_multiple_principals";
-    permission javax.security.auth.AuthPermission "modifyPrincipals";
-    permission javax.security.auth.PrivateCredentialPermission "javax.security.auth.kerberos.KeyTab java.security.Principal \"krb5.keytab.data\"", "read";
-
-    // clients have a permission to use all service principals
-    permission javax.security.auth.kerberos.ServicePermission "*", "initiate";
-
-    // server has a service permission
-    // to accept only service1 and service3 principals
-    permission javax.security.auth.kerberos.ServicePermission
-                    "host/service1.localhost@TEST.REALM", "accept";
-    permission javax.security.auth.kerberos.ServicePermission
-                    "host/service3.localhost@TEST.REALM", "accept";
-};
--- a/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java	Mon Jun 25 13:41:39 2018 -0700
@@ -197,6 +197,9 @@
     }
 
     public static void main(String[] args) throws Exception {
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+
         // Get the customized arguments.
         parseArguments(args);
         main(new ClientAuth());
--- a/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.sh	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.sh	Mon Jun 25 13:41:39 2018 -0700
@@ -55,46 +55,46 @@
     ARCH=`isainfo`
     case "$ARCH" in
       sparc* )
-	FS="/"
-	PS=":"
-	CP="${FS}bin${FS}cp"
-	CHMOD="${FS}bin${FS}chmod"
-	;;
+    FS="/"
+    PS=":"
+    CP="${FS}bin${FS}cp"
+    CHMOD="${FS}bin${FS}chmod"
+    ;;
       i[3-6]86 )
-	FS="/"
-	PS=":"
-	CP="${FS}bin${FS}cp"
-	CHMOD="${FS}bin${FS}chmod"
-	;;
+    FS="/"
+    PS=":"
+    CP="${FS}bin${FS}cp"
+    CHMOD="${FS}bin${FS}chmod"
+    ;;
       amd64* )
-	FS="/"
-	PS=":"
-	CP="${FS}bin${FS}cp"
-	CHMOD="${FS}bin${FS}chmod"
-	;;
+    FS="/"
+    PS=":"
+    CP="${FS}bin${FS}cp"
+    CHMOD="${FS}bin${FS}chmod"
+    ;;
       * )
 #     ?itanium? )
 #     amd64* )
-	echo "Unsupported System: Solaris ${ARCH}"
-	exit 0;
-	;;
+    echo "Unsupported System: Solaris ${ARCH}"
+    exit 0;
+    ;;
     esac
     ;;
   Linux )
     ARCH=`uname -m`
     case "$ARCH" in
       i[3-6]86 )
-	FS="/"
-	PS=":"
-	CP="${FS}bin${FS}cp"
-	CHMOD="${FS}bin${FS}chmod"
-	;;
+    FS="/"
+    PS=":"
+    CP="${FS}bin${FS}cp"
+    CHMOD="${FS}bin${FS}chmod"
+    ;;
       * )
 #     ia64 )
 #     x86_64 )
-	echo "Unsupported System: Linux ${ARCH}"
-	exit 0;
-	;;
+    echo "Unsupported System: Linux ${ARCH}"
+    exit 0;
+    ;;
     esac
     ;;
   Windows* )
@@ -126,23 +126,44 @@
 
 # compile test
 ${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
-	-classpath ${TESTSRC} \
-	-d ${TESTCLASSES} \
-	${TESTSRC}${FS}ClientAuth.java \
-	${TESTSRC}${FS}..${FS}PKCS11Test.java
+    -classpath ${TESTSRC} \
+    -d ${TESTCLASSES} \
+    ${TESTSRC}${FS}ClientAuth.java \
+    ${TESTSRC}${FS}..${FS}PKCS11Test.java
 
 # run test
-echo "Run ClientAuth ..."
+echo "Run ClientAuth TLSv1 ..."
 ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \
-	-classpath ${TESTCLASSES} \
-	-DDIR=${TESTSRC}${FS}ClientAuthData${FS} \
-	-DCUSTOM_DB_DIR=${TESTCLASSES} \
-	-DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \
-	-DNO_DEFAULT=true \
-	-DNO_DEIMOS=true \
-	-Dtest.src=${TESTSRC} \
-	-Dtest.classes=${TESTCLASSES} \
-	ClientAuth
+    -classpath ${TESTCLASSES} \
+    -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \
+    -DCUSTOM_DB_DIR=${TESTCLASSES} \
+    -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \
+    -DNO_DEFAULT=true \
+    -DNO_DEIMOS=true \
+    -Dtest.src=${TESTSRC} \
+    -Dtest.classes=${TESTCLASSES} \
+    ClientAuth TLSv1
+
+# save error status
+status=$?
+
+# return if failed
+if [ "${status}" != "0" ] ; then
+    exit $status
+fi
+
+# run test
+echo "Run ClientAuth TLSv1.1 ..."
+${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \
+    -classpath ${TESTCLASSES} \
+    -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \
+    -DCUSTOM_DB_DIR=${TESTCLASSES} \
+    -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \
+    -DNO_DEFAULT=true \
+    -DNO_DEIMOS=true \
+    -Dtest.src=${TESTSRC} \
+    -Dtest.classes=${TESTCLASSES} \
+    ClientAuth TLSv1.1
 
 # save error status
 status=$?
@@ -155,15 +176,15 @@
 # run test with specified TLS protocol and cipher suite
 echo "Run ClientAuth TLSv1.2 TLS_DHE_RSA_WITH_AES_128_CBC_SHA"
 ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \
-	-classpath ${TESTCLASSES} \
-	-DDIR=${TESTSRC}${FS}ClientAuthData${FS} \
-	-DCUSTOM_DB_DIR=${TESTCLASSES} \
-	-DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \
-	-DNO_DEFAULT=true \
-	-DNO_DEIMOS=true \
-	-Dtest.src=${TESTSRC} \
-	-Dtest.classes=${TESTCLASSES} \
-	ClientAuth TLSv1.2 TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+    -classpath ${TESTCLASSES} \
+    -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \
+    -DCUSTOM_DB_DIR=${TESTCLASSES} \
+    -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \
+    -DNO_DEFAULT=true \
+    -DNO_DEIMOS=true \
+    -Dtest.src=${TESTSRC} \
+    -Dtest.classes=${TESTCLASSES} \
+    ClientAuth TLSv1.2 TLS_DHE_RSA_WITH_AES_128_CBC_SHA
 
 # save error status
 status=$?
--- a/test/jdk/sun/security/pkcs11/sslecc/CipherTest.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/pkcs11/sslecc/CipherTest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -105,11 +105,11 @@
 
     public static class TestParameters {
 
-        String cipherSuite;
-        String protocol;
+        CipherSuite cipherSuite;
+        Protocol protocol;
         String clientAuth;
 
-        TestParameters(String cipherSuite, String protocol,
+        TestParameters(CipherSuite cipherSuite, Protocol protocol,
                 String clientAuth) {
             this.cipherSuite = cipherSuite;
             this.protocol = protocol;
@@ -117,7 +117,7 @@
         }
 
         boolean isEnabled() {
-            return TLSCipherStatus.isEnabled(cipherSuite, protocol);
+            return cipherSuite.supportedByProtocol(protocol);
         }
 
         @Override
@@ -128,134 +128,6 @@
             }
             return s;
         }
-
-        static enum TLSCipherStatus {
-            // cipher suites supported since TLS 1.2
-            CS_01("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF),
-            CS_02("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",   0x0303, 0xFFFF),
-            CS_03("TLS_RSA_WITH_AES_256_CBC_SHA256",         0x0303, 0xFFFF),
-            CS_04("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",  0x0303, 0xFFFF),
-            CS_05("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",    0x0303, 0xFFFF),
-            CS_06("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_07("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",     0x0303, 0xFFFF),
-
-            CS_08("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF),
-            CS_09("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",   0x0303, 0xFFFF),
-            CS_10("TLS_RSA_WITH_AES_128_CBC_SHA256",         0x0303, 0xFFFF),
-            CS_11("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",  0x0303, 0xFFFF),
-            CS_12("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",    0x0303, 0xFFFF),
-            CS_13("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_14("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",     0x0303, 0xFFFF),
-
-            CS_15("TLS_DH_anon_WITH_AES_256_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256",     0x0303, 0xFFFF),
-            CS_17("TLS_RSA_WITH_NULL_SHA256",                0x0303, 0xFFFF),
-
-            CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF),
-            CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF),
-            CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",   0x0303, 0xFFFF),
-            CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384",         0x0303, 0xFFFF),
-            CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",  0x0303, 0xFFFF),
-            CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",    0x0303, 0xFFFF),
-            CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",     0x0303, 0xFFFF),
-            CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",     0x0303, 0xFFFF),
-
-            CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",   0x0303, 0xFFFF),
-            CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256",         0x0303, 0xFFFF),
-            CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",  0x0303, 0xFFFF),
-            CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",    0x0303, 0xFFFF),
-            CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",     0x0303, 0xFFFF),
-            CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",     0x0303, 0xFFFF),
-
-            CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384",     0x0303, 0xFFFF),
-            CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256",     0x0303, 0xFFFF),
-
-            // cipher suites obsoleted since TLS 1.2
-            CS_50("SSL_RSA_WITH_DES_CBC_SHA",                0x0000, 0x0303),
-            CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA",            0x0000, 0x0303),
-            CS_52("SSL_DHE_DSS_WITH_DES_CBC_SHA",            0x0000, 0x0303),
-            CS_53("SSL_DH_anon_WITH_DES_CBC_SHA",            0x0000, 0x0303),
-            CS_54("TLS_KRB5_WITH_DES_CBC_SHA",               0x0000, 0x0303),
-            CS_55("TLS_KRB5_WITH_DES_CBC_MD5",               0x0000, 0x0303),
-
-            // cipher suites obsoleted since TLS 1.1
-            CS_60("SSL_RSA_EXPORT_WITH_RC4_40_MD5",          0x0000, 0x0302),
-            CS_61("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",      0x0000, 0x0302),
-            CS_62("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",       0x0000, 0x0302),
-            CS_63("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",   0x0000, 0x0302),
-            CS_64("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",   0x0000, 0x0302),
-            CS_65("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",   0x0000, 0x0302),
-            CS_66("TLS_KRB5_EXPORT_WITH_RC4_40_SHA",         0x0000, 0x0302),
-            CS_67("TLS_KRB5_EXPORT_WITH_RC4_40_MD5",         0x0000, 0x0302),
-            CS_68("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",     0x0000, 0x0302),
-            CS_69("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",     0x0000, 0x0302),
-
-            // ignore TLS_EMPTY_RENEGOTIATION_INFO_SCSV always
-            CS_99("TLS_EMPTY_RENEGOTIATION_INFO_SCSV",       0xFFFF, 0x0000);
-
-            // the cipher suite name
-            final String cipherSuite;
-
-            // supported since protocol version
-            final int supportedSince;
-
-            // obsoleted since protocol version
-            final int obsoletedSince;
-
-            TLSCipherStatus(String cipherSuite,
-                    int supportedSince, int obsoletedSince) {
-                this.cipherSuite = cipherSuite;
-                this.supportedSince = supportedSince;
-                this.obsoletedSince = obsoletedSince;
-            }
-
-            static boolean isEnabled(String cipherSuite, String protocol) {
-                int versionNumber = toVersionNumber(protocol);
-
-                if (versionNumber < 0) {
-                    return true;  // unlikely to happen
-                }
-
-                for (TLSCipherStatus status : TLSCipherStatus.values()) {
-                    if (cipherSuite.equals(status.cipherSuite)) {
-                        if ((versionNumber < status.supportedSince) ||
-                            (versionNumber >= status.obsoletedSince)) {
-                            return false;
-                        }
-
-                        return true;
-                    }
-                }
-
-                return true;
-            }
-
-            private static int toVersionNumber(String protocol) {
-                int versionNumber = -1;
-
-                switch (protocol) {
-                    case "SSLv2Hello":
-                        versionNumber = 0x0002;
-                        break;
-                    case "SSLv3":
-                        versionNumber = 0x0300;
-                        break;
-                    case "TLSv1":
-                        versionNumber = 0x0301;
-                        break;
-                    case "TLSv1.1":
-                        versionNumber = 0x0302;
-                        break;
-                    case "TLSv1.2":
-                        versionNumber = 0x0303;
-                        break;
-                    default:
-                        // unlikely to happen
-                }
-
-                return versionNumber;
-            }
-        }
     }
 
     private List<TestParameters> tests;
@@ -283,14 +155,21 @@
 
                 for (int k = 0; k < clientAuths.length; k++) {
                     String clientAuth = clientAuths[k];
-                    if ((clientAuth != null) &&
-                            (cipherSuite.indexOf("DH_anon") != -1)) {
-                        // no client with anonymous ciphersuites
+                    // no client with anonymous cipher suites.
+                    // TLS_EMPTY_RENEGOTIATION_INFO_SCSV always be skipped.
+                    // TLS 1.3 is skipped due to the signature algorithm,
+                    // exactly MD5withRSA, in the certificates is not allowed.
+                    if ((clientAuth != null && cipherSuite.contains("DH_anon")
+                            || cipherSuite.equals(
+                                    CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.name())
+                            || "TLSv1.3".equals(protocol))) {
                         continue;
                     }
 
-                    tests.add(new TestParameters(cipherSuite, protocol,
-                        clientAuth));
+                    tests.add(new TestParameters(
+                            CipherSuite.cipherSuite(cipherSuite),
+                            Protocol.protocol(protocol),
+                            clientAuth));
                 }
             }
         }
--- a/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,7 @@
  * @bug 6405536
  * @summary Verify that all ciphersuites work (incl. ECC using NSS crypto)
  * @author Andreas Sterbenz
- * @library ..
+ * @library .. ../../../../javax/net/ssl/TLSCommon
  * @library ../../../../java/security/testlibrary
  * @modules jdk.crypto.cryptoki
  * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1"
--- a/test/jdk/sun/security/pkcs11/sslecc/JSSEClient.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/pkcs11/sslecc/JSSEClient.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -67,8 +67,8 @@
             }
 
             socket.setSoTimeout(CipherTest.TIMEOUT);
-            socket.setEnabledCipherSuites(new String[] {params.cipherSuite});
-            socket.setEnabledProtocols(new String[] {params.protocol});
+            socket.setEnabledCipherSuites(new String[] {params.cipherSuite.name()});
+            socket.setEnabledProtocols(new String[] {params.protocol.name});
             InputStream in = socket.getInputStream();
             OutputStream out = socket.getOutputStream();
             sendRequest(in, out);
@@ -76,11 +76,11 @@
             SSLSession session = socket.getSession();
             session.invalidate();
             String cipherSuite = session.getCipherSuite();
-            if (params.cipherSuite.equals(cipherSuite) == false) {
+            if (!params.cipherSuite.name().equals(cipherSuite)) {
                 throw new Exception("Negotiated ciphersuite mismatch: " + cipherSuite + " != " + params.cipherSuite);
             }
             String protocol = session.getProtocol();
-            if (params.protocol.equals(protocol) == false) {
+            if (!params.protocol.name.equals(protocol)) {
                 throw new Exception("Negotiated protocol mismatch: " + protocol + " != " + params.protocol);
             }
             if (cipherSuite.indexOf("DH_anon") == -1) {
--- a/test/jdk/sun/security/ssl/AppOutputStream/NoExceptionOnClose.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/AppOutputStream/NoExceptionOnClose.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,14 +21,16 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test 1.3 01/03/08
  * @bug 4378397
  * @summary  JSSE socket output stream doesn't throw after socket is closed
  * @run main/othervm NoExceptionOnClose
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
  * @author Jaya Hangal
  */
 
@@ -152,11 +154,11 @@
         try {
             sslOS.write(22);
             sslOS.flush();
-        } catch (SocketException socketClosed) {
+        } catch (SSLException | SocketException socketClosed) {
                 System.out.println("Received \"" + socketClosed.getMessage()
                         + "\" exception as expected");
                 isSocketClosedThrown = true;
-          }
+        }
         if (!isSocketClosedThrown) {
                 throw new Exception("No Exception thrown on write() after"
                                 + " close()");
--- a/test/jdk/sun/security/ssl/CipherSuite/SSL_NULL.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/CipherSuite/SSL_NULL.java	Mon Jun 25 13:41:39 2018 -0700
@@ -24,7 +24,8 @@
 /**
  * @test
  * @bug 4854838
- * @summary Verify that SSL_NULL_WITH_NULL_NULL is returned as ciphersuite if the handshake fails
+ * @summary Verify that SSL_NULL_WITH_NULL_NULL is returned as ciphersuite
+ *      if the handshake fails
  * @author Andreas Sterbenz
  */
 
@@ -34,7 +35,6 @@
 import javax.net.ssl.*;
 
 public class SSL_NULL {
-
     private static volatile Boolean result;
 
     public static void main(String[] args) throws Exception {
@@ -71,15 +71,18 @@
             SSLSocketFactory.getDefault().createSocket(
                 "localhost", serverSocket.getLocalPort());
         socket.setEnabledCipherSuites(
-            new String[] { "SSL_RSA_WITH_RC4_128_MD5" });
+            new String[] { "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" });
         try {
             OutputStream out = socket.getOutputStream();
             out.write(0);
             out.flush();
             throw new RuntimeException("No exception received");
         } catch (SSLHandshakeException e) {
+            System.out.println("Expected handshake exception: " + e);
         }
+
         System.out.println("client: " + socket.getSession().getCipherSuite());
+
         // wait for other thread to set result
         while (result == null) {
             Thread.sleep(50);
--- a/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java	Mon Jun 25 13:41:39 2018 -0700
@@ -74,7 +74,6 @@
 import java.nio.*;
 import java.util.List;
 import java.util.ArrayList;
-import sun.security.ssl.ProtocolVersion;
 
 public class LengthCheckTest {
 
@@ -155,6 +154,7 @@
     private static final int TLS_ALERT_UNEXPECTED_MSG = 0x0A;
     private static final int TLS_ALERT_HANDSHAKE_FAILURE = 0x28;
     private static final int TLS_ALERT_INTERNAL_ERROR = 0x50;
+    private static final int TLS_ALERT_ILLEGAL_PARAMETER = 0x2F;
 
     public interface HandshakeTest {
         void execTest() throws Exception;
@@ -268,24 +268,24 @@
                 runDelegatedTasks(serverResult, serverEngine);
                 sTOc.flip();
                 dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
+
+                // We expect to see the server generate an alert here
+                serverResult = serverEngine.wrap(serverOut, sTOc);
+                log("server wrap: ", serverResult);
+                runDelegatedTasks(serverResult, serverEngine);
+                sTOc.flip();
+                dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
             } catch (SSLProtocolException ssle) {
                 log("Received expected SSLProtocolException: " + ssle);
                 gotException = true;
             }
 
-            // We expect to see the server generate an alert here
-            serverResult = serverEngine.wrap(serverOut, sTOc);
-            log("server wrap: ", serverResult);
-            runDelegatedTasks(serverResult, serverEngine);
-            sTOc.flip();
-            dumpByteBuffer("SERVER-TO-CLIENT", sTOc);
-
             // At this point we can verify that both an exception
             // was thrown and the proper action (a TLS alert) was
             // sent back to the client.
             if (gotException == false ||
                 !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
-                        TLS_ALERT_UNEXPECTED_MSG)) {
+                        TLS_ALERT_ILLEGAL_PARAMETER)) {
                 throw new SSLException(
                     "Server failed to throw Alert:fatal:internal_error");
             }
@@ -783,10 +783,9 @@
             int ver_major = Byte.toUnsignedInt(bBuf.get());
             int ver_minor = Byte.toUnsignedInt(bBuf.get());
             int recLen = Short.toUnsignedInt(bBuf.getShort());
-            ProtocolVersion pv = ProtocolVersion.valueOf(ver_major, ver_minor);
 
             log("===== " + header + " (" + tlsRecType(type) + " / " +
-                pv + " / " + bufLen + " bytes) =====");
+                ver_major + "." + ver_minor + " / " + bufLen + " bytes) =====");
             bBuf.reset();
             for (int i = 0; i < bufLen; i++) {
                 if (i != 0 && i % 16 == 0) {
--- a/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.sh	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.sh	Mon Jun 25 13:41:39 2018 -0700
@@ -1,7 +1,7 @@
 #! /bin/sh
 
 #
-# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -54,7 +54,7 @@
 ${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \
     ${TESTSRC}${FS}DebugReportsOneExtraByte.java
 
-STRING='main, WRITE: TLSv1 Application Data, length = 8'
+STRING='WRITE: TLS10 application_data, length = 8'
 
 echo "Examining debug output for the string:"
 echo "${STRING}"
--- a/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- * 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.
- */
-
-/**
- * @test
- * @bug 8080535
- * @summary Expected size of Character.UnicodeBlock.map is not optimal
- * @library /lib/testlibrary
- * @modules java.base/java.util:open
- *          java.base/sun.security.ssl:open
- * @build jdk.testlibrary.OptimalCapacity
- * @run main OptimalListSize
- */
-
-import jdk.testlibrary.OptimalCapacity;
-
-public class OptimalListSize {
-    public static void main(String[] args) throws Throwable {
-        OptimalCapacity.ofArrayList(
-                Class.forName("sun.security.ssl.ExtensionType"),
-                "knownExtensions", 16);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSDefaultProtocols.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @summary Test jdk.tls.client.protocols with DTLS
+ * @run main/othervm -Djdk.tls.client.protocols="DTLSv1.0"
+ *   CustomizedDTLSDefaultProtocols
+*/
+
+
+import java.security.Security;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+
+public class CustomizedDTLSDefaultProtocols {
+
+    enum ContextVersion {
+        TLS_CV_01("DTLS",
+                new String[] {"DTLSv1.0"}),
+        TLS_CV_02("DTLSv1.0",
+                new String[] {"DTLSv1.0"}),
+        TLS_CV_03("DTLSv1.2",
+                new String[] {"DTLSv1.0", "DTLSv1.2"});
+
+        final String contextVersion;
+        final String[] enabledProtocols;
+        final static String[] supportedProtocols = new String[] {
+                "DTLSv1.0", "DTLSv1.2"};
+
+        ContextVersion(String contextVersion, String[] enabledProtocols) {
+            this.contextVersion = contextVersion;
+            this.enabledProtocols = enabledProtocols;
+        }
+    }
+
+    private static boolean checkProtocols(String[] target, String[] expected) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No protocols");
+            success = false;
+        }
+
+        if (!protocolEquals(target, expected)) {
+            System.out.println("\tError: Expected to get protocols " +
+                    Arrays.toString(expected));
+            success = false;
+        }
+        System.out.println("\t  Protocols found " + Arrays.toString(target));
+
+        return success;
+    }
+
+    private static boolean protocolEquals(
+            String[] actualProtocols,
+            String[] expectedProtocols) {
+        if (actualProtocols.length != expectedProtocols.length) {
+            return false;
+        }
+
+        Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols));
+        for (String actual : actualProtocols) {
+            if (set.add(actual)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean checkCipherSuites(String[] target) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No cipher suites");
+            success = false;
+        }
+
+        return success;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // reset the security property to make sure that the algorithms
+        // and keys used in this test are not disabled.
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+        boolean failed = false;
+        for (ContextVersion cv : ContextVersion.values()) {
+            System.out.println("Checking SSLContext of " + cv.contextVersion);
+            SSLContext context = SSLContext.getInstance(cv.contextVersion);
+
+            // Default SSLContext is initialized automatically.
+            if (!cv.contextVersion.equals("Default")) {
+                // Use default TK, KM and random.
+                context.init((KeyManager[])null, (TrustManager[])null, null);
+            }
+
+            //
+            // Check SSLContext
+            //
+            // Check default SSLParameters of SSLContext
+            System.out.println("\tChecking default SSLParameters");
+            SSLParameters parameters = context.getDefaultSSLParameters();
+
+            String[] protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            String[] ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            // Check supported SSLParameters of SSLContext
+            System.out.println("\tChecking supported SSLParameters");
+            parameters = context.getSupportedSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLEngine
+            //
+            // Check SSLParameters of SSLEngine
+            System.out.println();
+            System.out.println("\tChecking SSLEngine of this SSLContext");
+            System.out.println("\tChecking SSLEngine.getSSLParameters()");
+            SSLEngine engine = context.createSSLEngine();
+            engine.setUseClientMode(true);
+            parameters = engine.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = engine.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = engine.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = engine.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = engine.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLSocket
+            //
+            // Check SSLParameters of SSLSocket
+            System.out.println();
+            System.out.println("\tChecking SSLSocket of this SSLContext");
+            System.out.println("\tChecking SSLSocket.getSSLParameters()");
+            SocketFactory fac = context.getSocketFactory();
+            SSLSocket socket = (SSLSocket)fac.createSocket();
+            parameters = socket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = socket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = socket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = socket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = socket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLServerSocket
+            //
+            // Check SSLParameters of SSLServerSocket
+            System.out.println();
+            System.out.println("\tChecking SSLServerSocket of this SSLContext");
+            System.out.println("\tChecking SSLServerSocket.getSSLParameters()");
+            SSLServerSocketFactory sf = context.getServerSocketFactory();
+            SSLServerSocket ssocket = (SSLServerSocket)sf.createServerSocket();
+            parameters = ssocket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = ssocket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = ssocket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = ssocket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = ssocket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+        }
+
+        if (failed) {
+            throw new Exception("Run into problems, see log for more details");
+        } else {
+            System.out.println("\t... Success");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSServerDefaultProtocols.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @summary Test jdk.tls.server.protocols with DTLS
+ * @run main/othervm -Djdk.tls.server.protocols="DTLSv1.0"
+ *      CustomizedDTLSServerDefaultProtocols
+ */
+
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+
+public class CustomizedDTLSServerDefaultProtocols {
+
+    final static String[] supportedProtocols = new String[]{
+            "DTLSv1.0", "DTLSv1.2"};
+
+    enum ContextVersion {
+        TLS_CV_01("DTLS",
+                new String[]{"DTLSv1.0"},
+                supportedProtocols),
+        TLS_CV_02("DTLSv1.0",
+                supportedProtocols,
+                new String[]{"DTLSv1.0"}),
+        TLS_CV_03("DTLS1.2",
+                supportedProtocols,
+                supportedProtocols);
+
+        final String contextVersion;
+        final String[] serverEnabledProtocols;
+        final String[] clientEnabledProtocols;
+
+        ContextVersion(String contextVersion, String[] serverEnabledProtocols,
+                String[] clientEnabledProtocols) {
+            this.contextVersion = contextVersion;
+            this.serverEnabledProtocols = serverEnabledProtocols;
+            this.clientEnabledProtocols = clientEnabledProtocols;
+        }
+    }
+
+    private static boolean checkProtocols(String[] target, String[] expected) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No protocols");
+            success = false;
+        }
+
+        if (!protocolEquals(target, expected)) {
+            System.out.println("\tError: Expected to get protocols " +
+                    Arrays.toString(expected));
+            success = false;
+        }
+        System.out.println("\t  Protocols found " + Arrays.toString(target));
+        return success;
+    }
+
+    private static boolean protocolEquals(
+            String[] actualProtocols,
+            String[] expectedProtocols) {
+        if (actualProtocols.length != expectedProtocols.length) {
+            return false;
+        }
+
+        Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols));
+        for (String actual : actualProtocols) {
+            if (set.add(actual)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean checkCipherSuites(String[] target) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No cipher suites");
+            success = false;
+        }
+
+        return success;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // reset the security property to make sure that the algorithms
+        // and keys used in this test are not disabled.
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        System.out.println("jdk.tls.client.protocols = " +
+                System.getProperty("jdk.tls.client.protocols"));
+        System.out.println("jdk.tls.server.protocols = "+
+                System.getProperty("jdk.tls.server.protocols"));
+        Test();
+    }
+
+    static void Test() throws Exception {
+        boolean failed = false;
+
+        SSLContext context;
+        for (ContextVersion cv : ContextVersion.values()) {
+            System.out.println("Checking SSLContext of " + cv.contextVersion);
+            try {
+                context = SSLContext.getInstance(cv.contextVersion);
+            } catch (NoSuchAlgorithmException e) {
+                if (cv.contextVersion.compareToIgnoreCase("DTLS1.2") == 0) {
+                    System.out.println("Exception expected: " + e.getMessage());
+                    continue;
+                }
+                throw e;
+            }
+            // Default SSLContext is initialized automatically.
+            if (!cv.contextVersion.equals("Default")) {
+                // Use default TK, KM and random.
+                context.init(null, null, null);
+            }
+
+            //
+            // Check SSLContext
+            //
+            // Check default SSLParameters of SSLContext
+            System.out.println("\tChecking default SSLParameters");
+            SSLParameters parameters = context.getDefaultSSLParameters();
+
+            String[] protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            String[] ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            // Check supported SSLParameters of SSLContext
+            System.out.println("\tChecking supported SSLParameters");
+            parameters = context.getSupportedSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLEngine
+            //
+            // Check SSLParameters of SSLEngine
+            System.out.println();
+            System.out.println("\tChecking SSLEngine of this SSLContext");
+            System.out.println("\tChecking SSLEngine.getSSLParameters()");
+            SSLEngine engine = context.createSSLEngine();
+            engine.setUseClientMode(true);
+            parameters = engine.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = engine.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = engine.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = engine.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = engine.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLSocket
+            //
+            // Check SSLParameters of SSLSocket
+            System.out.println();
+            System.out.println("\tChecking SSLSocket of this SSLContext");
+            System.out.println("\tChecking SSLSocket.getSSLParameters()");
+            SocketFactory fac = context.getSocketFactory();
+            SSLSocket socket = (SSLSocket) fac.createSocket();
+            parameters = socket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLSocket.getEnabledProtocols()");
+            protocols = socket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            System.out.println("\tChecking SSLSocket.getEnabledCipherSuites()");
+            ciphers = socket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLSocket.getSupportedProtocols()");
+            protocols = socket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLSocket.getSupportedCipherSuites()");
+            ciphers = socket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLServerSocket
+            //
+            // Check SSLParameters of SSLServerSocket
+            System.out.println();
+            System.out.println("\tChecking SSLServerSocket of this SSLContext");
+            System.out.println("\tChecking SSLServerSocket.getSSLParameters()");
+            SSLServerSocketFactory sf = context.getServerSocketFactory();
+            SSLServerSocket ssocket = (SSLServerSocket) sf.createServerSocket();
+            parameters = ssocket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.serverEnabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = ssocket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.serverEnabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = ssocket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = ssocket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = ssocket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            if (failed) {
+                throw new Exception("Run into problems, see log for more details");
+            } else {
+                System.out.println("\t... Success");
+            }
+        }
+    }
+}
--- a/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDefaultProtocols.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDefaultProtocols.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,13 +32,23 @@
  *      CustomizedDefaultProtocols
  */
 
-import javax.net.*;
-import javax.net.ssl.*;
+import java.security.Security;
 import java.util.Arrays;
-import java.security.Security;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
 
 public class CustomizedDefaultProtocols {
-    static enum ContextVersion {
+    enum ContextVersion {
         TLS_CV_01("SSL",
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1"}),
         TLS_CV_02("TLS",
@@ -51,13 +61,15 @@
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1"}),
         TLS_CV_06("TLSv1.2",
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}),
-        TLS_CV_07("Default",
+        TLS_CV_07("TLSv1.3",
+                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
+        TLS_CV_08("Default",
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1"});
 
         final String contextVersion;
         final String[] enabledProtocols;
         final static String[] supportedProtocols = new String[] {
-                "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"};
+                "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"};
 
         ContextVersion(String contextVersion, String[] enabledProtocols) {
             this.contextVersion = contextVersion;
@@ -72,17 +84,33 @@
             success = false;
         }
 
-        if (!Arrays.equals(target, expected)) {
+        if (!protocolEquals(target, expected)) {
             System.out.println("\tError: Expected to get protocols " +
                     Arrays.toString(expected));
-            System.out.println("\tError: The actual protocols " +
-                    Arrays.toString(target));
             success = false;
         }
+        System.out.println("\t  Protocols found " + Arrays.toString(target));
 
         return success;
     }
 
+    private static boolean protocolEquals(
+            String[] actualProtocols,
+            String[] expectedProtocols) {
+        if (actualProtocols.length != expectedProtocols.length) {
+            return false;
+        }
+
+        Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols));
+        for (String actual : actualProtocols) {
+            if (set.add(actual)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     private static boolean checkCipherSuites(String[] target) {
         boolean success = true;
         if (target.length == 0) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedServerDefaultProtocols.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @summary Test jdk.tls.server.protocols with TLS
+ * @run main/othervm -Djdk.tls.server.protocols="SSLv3,TLSv1,TLSv1.1"
+ *      CustomizedServerDefaultProtocols
+ */
+
+import java.security.Security;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+
+public class CustomizedServerDefaultProtocols {
+
+    final static String[] supportedProtocols = new String[]{
+            "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"};
+
+    enum ContextVersion {
+        TLS_CV_01("SSL",
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1"},
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
+        TLS_CV_02("TLS",
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1"},
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
+        TLS_CV_03("SSLv3",
+                supportedProtocols,
+                new String[]{"SSLv3", "TLSv1"}),
+        TLS_CV_04("TLSv1",
+                supportedProtocols,
+                new String[]{"SSLv3", "TLSv1"}),
+        TLS_CV_05("TLSv1.1",
+                supportedProtocols,
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1"}),
+        TLS_CV_06("TLSv1.2",
+                supportedProtocols,
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}),
+        TLS_CV_07("TLSv1.3",
+                supportedProtocols,
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
+        TLS_CV_08("Default",
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1"},
+                new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"});
+
+        final String contextVersion;
+        final String[] serverEnabledProtocols;
+        final String[] clientEnabledProtocols;
+
+        ContextVersion(String contextVersion, String[] serverEnabledProtocols,
+                String[] clientEnabledProtocols) {
+            this.contextVersion = contextVersion;
+            this.serverEnabledProtocols = serverEnabledProtocols;
+            this.clientEnabledProtocols = clientEnabledProtocols;
+        }
+    }
+
+    private static boolean checkProtocols(String[] target, String[] expected) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No protocols");
+            success = false;
+        }
+
+        if (!protocolEquals(target, expected)) {
+            System.out.println("\tError: Expected to get protocols " +
+                    Arrays.toString(expected));
+            success = false;
+        }
+        System.out.println("\t  Protocols found " + Arrays.toString(target));
+        return success;
+    }
+
+    private static boolean protocolEquals(
+            String[] actualProtocols,
+            String[] expectedProtocols) {
+        if (actualProtocols.length != expectedProtocols.length) {
+            return false;
+        }
+
+        Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols));
+        for (String actual : actualProtocols) {
+            if (set.add(actual)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean checkCipherSuites(String[] target) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No cipher suites");
+            success = false;
+        }
+
+        return success;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // reset the security property to make sure that the algorithms
+        // and keys used in this test are not disabled.
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+        System.out.println("jdk.tls.client.protocols = " +
+                System.getProperty("jdk.tls.client.protocols"));
+        System.out.println("jdk.tls.server.protocols = "+
+                System.getProperty("jdk.tls.server.protocols"));
+        Test();
+    }
+
+    static void Test() throws Exception {
+        boolean failed = false;
+
+        for (ContextVersion cv : ContextVersion.values()) {
+            System.out.println("Checking SSLContext of " + cv.contextVersion);
+            SSLContext context = SSLContext.getInstance(cv.contextVersion);
+
+            // Default SSLContext is initialized automatically.
+            if (!cv.contextVersion.equals("Default")) {
+                // Use default TK, KM and random.
+                context.init(null, null, null);
+            }
+
+            //
+            // Check SSLContext
+            //
+            // Check default SSLParameters of SSLContext
+            System.out.println("\tChecking default SSLParameters");
+            SSLParameters parameters = context.getDefaultSSLParameters();
+
+            String[] protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            String[] ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            // Check supported SSLParameters of SSLContext
+            System.out.println("\tChecking supported SSLParameters");
+            parameters = context.getSupportedSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLEngine
+            //
+            // Check SSLParameters of SSLEngine
+            System.out.println();
+            System.out.println("\tChecking SSLEngine of this SSLContext");
+            System.out.println("\tChecking SSLEngine.getSSLParameters()");
+            SSLEngine engine = context.createSSLEngine();
+            engine.setUseClientMode(true);
+            parameters = engine.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = engine.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = engine.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = engine.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = engine.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLSocket
+            //
+            // Check SSLParameters of SSLSocket
+            System.out.println();
+            System.out.println("\tChecking SSLSocket of this SSLContext");
+            System.out.println("\tChecking SSLSocket.getSSLParameters()");
+            SocketFactory fac = context.getSocketFactory();
+            SSLSocket socket = (SSLSocket) fac.createSocket();
+            parameters = socket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLSocket.getEnabledProtocols()");
+            protocols = socket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.clientEnabledProtocols);
+
+            System.out.println("\tChecking SSLSocket.getEnabledCipherSuites()");
+            ciphers = socket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLSocket.getSupportedProtocols()");
+            protocols = socket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLSocket.getSupportedCipherSuites()");
+            ciphers = socket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLServerSocket
+            //
+            // Check SSLParameters of SSLServerSocket
+            System.out.println();
+            System.out.println("\tChecking SSLServerSocket of this SSLContext");
+            System.out.println("\tChecking SSLServerSocket.getSSLParameters()");
+            SSLServerSocketFactory sf = context.getServerSocketFactory();
+            SSLServerSocket ssocket = (SSLServerSocket) sf.createServerSocket();
+            parameters = ssocket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.serverEnabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = ssocket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.serverEnabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = ssocket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = ssocket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = ssocket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            if (failed) {
+                throw new Exception("Run into problems, see log for more details");
+            } else {
+                System.out.println("\t... Success");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/DefaultDTLSEnabledProtocols.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @summary Test jdk.tls.client.protocols with DTLS
+ * @run main/othervm DefaultDTLSEnabledProtocols
+ */
+
+import java.security.Security;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+
+public class DefaultDTLSEnabledProtocols {
+    static enum ContextVersion {
+        TLS_CV_01("DTLS",
+                new String[] {"DTLSv1.0", "DTLSv1.2"}),
+        TLS_CV_02("DTLSv1.0",
+                new String[] {"DTLSv1.0"}),
+        TLS_CV_03("DTLSv1.2",
+                new String[] {"DTLSv1.0", "DTLSv1.2"});
+
+        final String contextVersion;
+        final String[] enabledProtocols;
+        final static String[] supportedProtocols = new String[] {
+                "DTLSv1.0", "DTLSv1.2"};
+
+        ContextVersion(String contextVersion, String[] enabledProtocols) {
+            this.contextVersion = contextVersion;
+            this.enabledProtocols = enabledProtocols;
+        }
+    }
+
+    private static boolean checkProtocols(String[] target, String[] expected) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No protocols");
+            success = false;
+        }
+
+        if (!protocolEquals(target, expected)) {
+            System.out.println("\tError: Expected to get protocols " +
+                    Arrays.toString(expected));
+            success = false;
+        }
+        System.out.println("\t  Protocols found " + Arrays.toString(target));
+
+        return success;
+    }
+
+    private static boolean protocolEquals(
+            String[] actualProtocols,
+            String[] expectedProtocols) {
+        if (actualProtocols.length != expectedProtocols.length) {
+            return false;
+        }
+
+        Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols));
+        for (String actual : actualProtocols) {
+            if (set.add(actual)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean checkCipherSuites(String[] target) {
+        boolean success = true;
+        if (target.length == 0) {
+            System.out.println("\tError: No cipher suites");
+            success = false;
+        }
+
+        return success;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // reset the security property to make sure that the algorithms
+        // and keys used in this test are not disabled.
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+        boolean failed = false;
+        for (ContextVersion cv : ContextVersion.values()) {
+            System.out.println("Checking SSLContext of " + cv.contextVersion);
+            SSLContext context = SSLContext.getInstance(cv.contextVersion);
+
+            // Default SSLContext is initialized automatically.
+            if (!cv.contextVersion.equals("Default")) {
+                // Use default TK, KM and random.
+                context.init((KeyManager[])null, (TrustManager[])null, null);
+            }
+
+            //
+            // Check SSLContext
+            //
+            // Check default SSLParameters of SSLContext
+            System.out.println("\tChecking default SSLParameters");
+            SSLParameters parameters = context.getDefaultSSLParameters();
+
+            String[] protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            String[] ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            // Check supported SSLParameters of SSLContext
+            System.out.println("\tChecking supported SSLParameters");
+            parameters = context.getSupportedSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLEngine
+            //
+            // Check SSLParameters of SSLEngine
+            System.out.println();
+            System.out.println("\tChecking SSLEngine of this SSLContext");
+            System.out.println("\tChecking SSLEngine.getSSLParameters()");
+            SSLEngine engine = context.createSSLEngine();
+            engine.setUseClientMode(true);
+            parameters = engine.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = engine.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = engine.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = engine.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = engine.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLSocket
+            //
+            // Check SSLParameters of SSLSocket
+            System.out.println();
+            System.out.println("\tChecking SSLSocket of this SSLContext");
+            System.out.println("\tChecking SSLSocket.getSSLParameters()");
+            SocketFactory fac = context.getSocketFactory();
+            SSLSocket socket = (SSLSocket)fac.createSocket();
+            parameters = socket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = socket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.enabledProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = socket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = socket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = socket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            //
+            // Check SSLServerSocket
+            //
+            // Check SSLParameters of SSLServerSocket
+            System.out.println();
+            System.out.println("\tChecking SSLServerSocket of this SSLContext");
+            System.out.println("\tChecking SSLServerSocket.getSSLParameters()");
+            SSLServerSocketFactory sf = context.getServerSocketFactory();
+            SSLServerSocket ssocket = (SSLServerSocket)sf.createServerSocket();
+            parameters = ssocket.getSSLParameters();
+
+            protocols = parameters.getProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            ciphers = parameters.getCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getEnabledProtocols()");
+            protocols = ssocket.getEnabledProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()");
+            ciphers = ssocket.getEnabledCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+
+            System.out.println("\tChecking SSLEngine.getSupportedProtocols()");
+            protocols = ssocket.getSupportedProtocols();
+            failed |= !checkProtocols(protocols, cv.supportedProtocols);
+
+            System.out.println(
+                    "\tChecking SSLEngine.getSupportedCipherSuites()");
+            ciphers = ssocket.getSupportedCipherSuites();
+            failed |= !checkCipherSuites(ciphers);
+        }
+
+        if (failed) {
+            throw new Exception("Run into problems, see log for more details");
+        } else {
+            System.out.println("\t... Success");
+        }
+    }
+}
--- a/test/jdk/sun/security/ssl/SSLContextImpl/DefaultEnabledProtocols.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/DefaultEnabledProtocols.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,17 +31,27 @@
  * @run main/othervm DefaultEnabledProtocols
  */
 
-import javax.net.*;
-import javax.net.ssl.*;
+import java.security.Security;
 import java.util.Arrays;
-import java.security.Security;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
 
 public class DefaultEnabledProtocols {
-    static enum ContextVersion {
+    enum ContextVersion {
         TLS_CV_01("SSL",
-                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}),
+                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
         TLS_CV_02("TLS",
-                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}),
+                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
         TLS_CV_03("SSLv3",
                 new String[] {"SSLv3", "TLSv1"}),
         TLS_CV_04("TLSv1",
@@ -50,13 +60,15 @@
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1"}),
         TLS_CV_06("TLSv1.2",
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}),
-        TLS_CV_07("Default",
-                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"});
+        TLS_CV_07("TLSv1.3",
+                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
+        TLS_CV_08("Default",
+                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"});
 
         final String contextVersion;
         final String[] enabledProtocols;
         final static String[] supportedProtocols = new String[] {
-                "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"};
+                "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"};
 
         ContextVersion(String contextVersion, String[] enabledProtocols) {
             this.contextVersion = contextVersion;
@@ -71,17 +83,33 @@
             success = false;
         }
 
-        if (!Arrays.equals(target, expected)) {
+        if (!protocolEquals(target, expected)) {
             System.out.println("\tError: Expected to get protocols " +
                     Arrays.toString(expected));
-            System.out.println("\tError: The actual protocols " +
-                    Arrays.toString(target));
             success = false;
         }
+        System.out.println("\t  Protocols found " + Arrays.toString(target));
 
         return success;
     }
 
+    private static boolean protocolEquals(
+            String[] actualProtocols,
+            String[] expectedProtocols) {
+        if (actualProtocols.length != expectedProtocols.length) {
+            return false;
+        }
+
+        Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols));
+        for (String actual : actualProtocols) {
+            if (set.add(actual)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     private static boolean checkCipherSuites(String[] target) {
         boolean success = true;
         if (target.length == 0) {
--- a/test/jdk/sun/security/ssl/SSLContextImpl/NoOldVersionContext.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/NoOldVersionContext.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,10 +32,20 @@
  *      NoOldVersionContext
  */
 
-import javax.net.*;
-import javax.net.ssl.*;
+import java.security.Security;
 import java.util.Arrays;
-import java.security.Security;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
 
 public class NoOldVersionContext {
     static enum ContextVersion {
@@ -51,13 +61,15 @@
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1"}),
         TLS_CV_06("TLSv1.2",
                 new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}),
-        TLS_CV_07("Default",
+        TLS_CV_07("TLSv1.3",
+                new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}),
+        TLS_CV_08("Default",
                 new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
 
         final String contextVersion;
         final String[] enabledProtocols;
         final static String[] supportedProtocols = new String[] {
-                "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"};
+                "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"};
 
         ContextVersion(String contextVersion, String[] enabledProtocols) {
             this.contextVersion = contextVersion;
@@ -72,7 +84,7 @@
             success = false;
         }
 
-        if (!Arrays.equals(target, expected)) {
+        if (!protocolEquals(target, expected)) {
             System.out.println("\tError: Expected to get protocols " +
                     Arrays.toString(expected));
             System.out.println("\tError: The actual protocols " +
@@ -83,6 +95,23 @@
         return success;
     }
 
+    private static boolean protocolEquals(
+            String[] actualProtocols,
+            String[] expectedProtocols) {
+        if (actualProtocols.length != expectedProtocols.length) {
+            return false;
+        }
+
+        Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols));
+        for (String actual : actualProtocols) {
+            if (set.add(actual)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     private static boolean checkCipherSuites(String[] target) {
         boolean success = true;
         if (target.length == 0) {
--- a/test/jdk/sun/security/ssl/SSLContextImpl/TrustTrustedCert.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLContextImpl/TrustTrustedCert.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -131,12 +131,10 @@
             sslIS.read();
             sslOS.write('A');
             sslOS.flush();
-        } catch (SSLHandshakeException e) {
-            if (expectFail && !e.toString().contains("certificate_unknown")) {
-                throw new RuntimeException(
-                        "Expected to see certificate_unknown in exception output",
-                        e);
-            }
+        } catch (SSLException ssle) {
+            if (!expectFail) {
+                throw ssle;
+            }   // Otherwise, ignore.
         }
     }
 
@@ -158,12 +156,15 @@
             sslOS.flush();
             sslIS.read();
         } catch (SSLHandshakeException e) {
+            if (expectFail) {
             // focus on the CertPathValidatorException
-            Throwable t = e.getCause().getCause();
-            if ((t == null)
-                    || (expectFail && !t.toString().contains("MD5withRSA"))) {
-                throw new RuntimeException(
+                Throwable t = e.getCause().getCause();
+                if (t == null || !t.toString().contains("MD5withRSA")) {
+                    throw new RuntimeException(
                         "Expected to see MD5withRSA in exception output", t);
+                }
+            } else {
+                throw e;
             }
         }
     }
--- a/test/jdk/sun/security/ssl/SSLEngineImpl/CloseEngineException.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLEngineImpl/CloseEngineException.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -45,9 +45,10 @@
 import java.security.*;
 import java.nio.*;
 
+// Note that this test case depends on JSSE provider implementation details.
 public class CloseEngineException {
 
-    private static boolean debug = false;
+    private static boolean debug = true;
 
     private SSLContext sslc;
     private SSLEngine ssle1;    // client
@@ -94,43 +95,53 @@
         SSLEngineResult result1;        // ssle1's results from last operation
         SSLEngineResult result2;        // ssle2's results from last operation
 
-        while (!isEngineClosed(ssle1) || !isEngineClosed(ssle2)) {
+        while (!isEngineClosed(ssle1) && !isEngineClosed(ssle2)) {
 
             log("================");
 
-            result1 = ssle1.wrap(appOut1, oneToTwo);
-            result2 = ssle2.wrap(appOut2, twoToOne);
+            if (!isEngineClosed(ssle1)) {
+                result1 = ssle1.wrap(appOut1, oneToTwo);
+                runDelegatedTasks(result1, ssle1);
 
-            log("wrap1:  " + result1);
-            log("oneToTwo  = " + oneToTwo);
-            log("");
+                log("wrap1:  " + result1);
+                log("oneToTwo  = " + oneToTwo);
+                log("");
 
-            log("wrap2:  " + result2);
-            log("twoToOne  = " + twoToOne);
+                oneToTwo.flip();
+            }
+            if (!isEngineClosed(ssle2)) {
+                result2 = ssle2.wrap(appOut2, twoToOne);
+                runDelegatedTasks(result2, ssle2);
 
-            runDelegatedTasks(result1, ssle1);
-            runDelegatedTasks(result2, ssle2);
+                log("wrap2:  " + result2);
+                log("twoToOne  = " + twoToOne);
 
-            oneToTwo.flip();
-            twoToOne.flip();
+                twoToOne.flip();
+            }
 
             log("----");
 
-            result1 = ssle1.unwrap(twoToOne, appIn1);
-            result2 = ssle2.unwrap(oneToTwo, appIn2);
+            if (!isEngineClosed(ssle1) && !dataDone) {
+            log("--");
+                result1 = ssle1.unwrap(twoToOne, appIn1);
+                runDelegatedTasks(result1, ssle1);
 
-            log("unwrap1: " + result1);
-            log("twoToOne  = " + twoToOne);
-            log("");
+                log("unwrap1: " + result1);
+                log("twoToOne  = " + twoToOne);
+                log("");
 
-            log("unwrap2: " + result2);
-            log("oneToTwo  = " + oneToTwo);
+                twoToOne.compact();
+            }
+            if (!isEngineClosed(ssle2)) {
+            log("---");
+                result2 = ssle2.unwrap(oneToTwo, appIn2);
+                runDelegatedTasks(result2, ssle2);
 
-            runDelegatedTasks(result1, ssle1);
-            runDelegatedTasks(result2, ssle2);
+                log("unwrap2: " + result2);
+                log("oneToTwo  = " + oneToTwo);
 
-            oneToTwo.compact();
-            twoToOne.compact();
+                oneToTwo.compact();
+            }
 
             /*
              * If we've transfered all the data between app1 and app2,
@@ -154,7 +165,7 @@
                     throw new Exception(
                         "TEST FAILED:  didn't throw Exception");
                 } catch (SSLException e) {
-                    System.out.println("PARTIAL PASS");
+                    System.err.println("PARTIAL PASS");
                 }
             }
         }
@@ -167,7 +178,7 @@
             throw new Exception(
                 "TEST FAILED:  didn't throw Exception");
         } catch (SSLException e) {
-            System.out.println("TEST PASSED");
+            System.err.println("TEST PASSED");
         }
     }
 
@@ -181,7 +192,7 @@
 
         test.runTest();
 
-        System.out.println("Test Passed.");
+        System.err.println("Test Passed.");
     }
 
     /*
@@ -277,7 +288,7 @@
 
     private static void log(String str) {
         if (debug) {
-            System.out.println(str);
+            System.err.println(str);
         }
     }
 }
--- a/test/jdk/sun/security/ssl/SSLEngineImpl/CloseInboundException.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,275 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, 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.
- *
- * 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.
- */
-
-//
-// SunJSSE does not support dynamic system properties, no way to re-use
-// system properties in samevm/agentvm mode.
-//
-
-/*
- * @test
- * @bug 4931274
- * @summary closeInbound does not signal when a close_notify has not
- *              been received.
- * @run main/othervm CloseInboundException
- * @author Brad Wetmore
- */
-
-import javax.net.ssl.*;
-import javax.net.ssl.SSLEngineResult.*;
-import java.io.*;
-import java.security.*;
-import java.nio.*;
-
-public class CloseInboundException {
-
-    private SSLEngine ssle1;    // client
-    private SSLEngine ssle2;    // server
-
-    SSLEngineResult result1;    // ssle1's results from last operation
-    SSLEngineResult result2;    // ssle2's results from last operation
-
-    private static String pathToStores = "../../../../javax/net/ssl/etc";
-    private static String keyStoreFile = "keystore";
-    private static String trustStoreFile = "truststore";
-    private static String passwd = "passphrase";
-
-    private static String keyFilename =
-            System.getProperty("test.src", "./") + "/" + pathToStores +
-                "/" + keyStoreFile;
-    private static String trustFilename =
-            System.getProperty("test.src", "./") + "/" + pathToStores +
-                "/" + trustStoreFile;
-
-    private ByteBuffer appOut1;         // write side of ssle1
-    private ByteBuffer appIn1;          // read side of ssle1
-    private ByteBuffer appOut2;         // write side of ssle2
-    private ByteBuffer appIn2;          // read side of ssle2
-
-    private ByteBuffer oneToTwo;        // "reliable" transport ssle1->ssle2
-    private ByteBuffer twoToOne;        // "reliable" transport ssle2->ssle1
-
-    /*
-     * Majority of the test case is here, setup is done below.
-     */
-    private void runTest(boolean inboundClose) throws Exception {
-
-        boolean done = false;
-
-        while (!isEngineClosed(ssle1) || !isEngineClosed(ssle2)) {
-
-            System.out.println("================");
-
-            result1 = ssle1.wrap(appOut1, oneToTwo);
-            result2 = ssle2.wrap(appOut2, twoToOne);
-
-            System.out.println("wrap1 = " + result1);
-            System.out.println("oneToTwo  = " + oneToTwo);
-
-            System.out.println("wrap2 = " + result2);
-            System.out.println("twoToOne  = " + twoToOne);
-
-            runDelegatedTasks(result1, ssle1);
-            runDelegatedTasks(result2, ssle2);
-
-            oneToTwo.flip();
-            twoToOne.flip();
-
-            System.out.println("----");
-            result1 = ssle1.unwrap(twoToOne, appIn1);
-
-            if (done && inboundClose) {
-                try {
-                    result2 = ssle2.unwrap(oneToTwo, appIn2);
-                    throw new Exception("Didn't throw Exception");
-                } catch (SSLException e) {
-                    System.out.println("Caught proper exception\n" + e);
-                    return;
-                }
-            } else {
-                result2 = ssle2.unwrap(oneToTwo, appIn2);
-            }
-
-            System.out.println("unwrap1 = " + result1);
-            System.out.println("twoToOne  = " + twoToOne);
-
-            System.out.println("unwrap2 = " + result2);
-            System.out.println("oneToTwo  = " + oneToTwo);
-
-            runDelegatedTasks(result1, ssle1);
-            runDelegatedTasks(result2, ssle2);
-
-            oneToTwo.compact();
-            twoToOne.compact();
-
-            /*
-             * If we've transfered all the data between app1 and app2,
-             * we try to close and see what that gets us.
-             */
-            if (!done && (appOut1.limit() == appIn2.position()) &&
-                (appOut2.limit() == appIn1.position())) {
-
-                if (inboundClose) {
-                    try {
-                        System.out.println("Closing ssle1's *INBOUND*...");
-                        ssle1.closeInbound();
-                        throw new Exception("closeInbound didn't throw");
-                    } catch (SSLException e) {
-                        System.out.println("Caught closeInbound exc properly");
-                        checkStatus();
-                        /*
-                         * Let the message processing continue to
-                         * handle the alert.
-                         */
-                    }
-                    done = true;
-                } else {
-                    done = true;
-                    System.out.println("Closing ssle1's *OUTBOUND*...");
-                    ssle1.closeOutbound();
-                }
-            }
-        }
-    }
-
-    /*
-     * Check to see if the close generated a close_notify message,
-     * that the result status is sane, and that close again doesn't
-     * generate a new exception.
-     *
-     * We'll consume the wrapped data when we loop back around.
-     */
-    private void checkStatus() throws Exception {
-        System.out.println("\nCalling last wrap");
-        int pos = oneToTwo.position();
-
-        result1 = ssle1.wrap(appOut1, oneToTwo);
-        System.out.println("result1 = " + result1);
-
-        if ((pos >= oneToTwo.position()) ||
-                !result1.getStatus().equals(Status.CLOSED) ||
-                !result1.getHandshakeStatus().equals(
-                    HandshakeStatus.NOT_HANDSHAKING) ||
-                !ssle1.isOutboundDone() ||
-                !ssle1.isInboundDone()) {
-            throw new Exception(result1.toString());
-        }
-        System.out.println("Make sure we don't throw a second SSLException.");
-        ssle1.closeInbound();
-    }
-
-    public static void main(String args[]) throws Exception {
-
-        CloseInboundException test;
-
-        test = new CloseInboundException();
-        test.runTest(false);
-
-        test = new CloseInboundException();
-        test.runTest(true);
-        System.out.println("Test PASSED!!!");
-    }
-
-    /*
-     * **********************************************************
-     * Majority of the test case is above, below is just setup stuff
-     * **********************************************************
-     */
-
-    public CloseInboundException() throws Exception {
-
-        SSLContext sslc = getSSLContext(keyFilename, trustFilename);
-
-        ssle1 = sslc.createSSLEngine("host1", 1);
-        ssle1.setUseClientMode(true);
-
-        ssle2 = sslc.createSSLEngine("host2", 2);
-        ssle2.setUseClientMode(false);
-
-        createBuffers();
-    }
-
-    /*
-     * Create an initialized SSLContext to use for this test.
-     */
-    private SSLContext getSSLContext(String keyFile, String trustFile)
-            throws Exception {
-
-        KeyStore ks = KeyStore.getInstance("JKS");
-        KeyStore ts = KeyStore.getInstance("JKS");
-
-        char[] passphrase = "passphrase".toCharArray();
-
-        ks.load(new FileInputStream(keyFile), passphrase);
-        ts.load(new FileInputStream(trustFile), passphrase);
-
-        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
-        kmf.init(ks, passphrase);
-
-        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
-        tmf.init(ts);
-
-        SSLContext sslCtx = SSLContext.getInstance("TLS");
-
-        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
-
-        return sslCtx;
-    }
-
-    private void createBuffers() {
-        // Size the buffers as appropriate.
-        SSLSession session = ssle1.getSession();
-        int appBufferMax = session.getApplicationBufferSize();
-        int netBufferMax = session.getPacketBufferSize();
-
-        appIn1 = ByteBuffer.allocateDirect(appBufferMax + 50);
-        appIn2 = ByteBuffer.allocateDirect(appBufferMax + 50);
-
-        oneToTwo = ByteBuffer.allocateDirect(netBufferMax);
-        twoToOne = ByteBuffer.allocateDirect(netBufferMax);
-
-        appOut1 = ByteBuffer.wrap("Hi Engine2, I'm SSLEngine1".getBytes());
-        appOut2 = ByteBuffer.wrap("Hello Engine1, I'm SSLEngine2".getBytes());
-
-        System.out.println("AppOut1 = " + appOut1);
-        System.out.println("AppOut2 = " + appOut2);
-        System.out.println();
-    }
-
-    private static void runDelegatedTasks(SSLEngineResult result,
-            SSLEngine engine) throws Exception {
-
-        if (result.getHandshakeStatus().equals(HandshakeStatus.NEED_TASK)) {
-            Runnable runnable;
-            while ((runnable = engine.getDelegatedTask()) != null) {
-                System.out.println("running delegated task...");
-                runnable.run();
-            }
-        }
-    }
-
-    private static boolean isEngineClosed(SSLEngine engine) {
-        return (engine.isOutboundDone() && engine.isInboundDone());
-    }
-
-}
--- a/test/jdk/sun/security/ssl/SSLEngineImpl/EngineEnforceUseClientMode.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLEngineImpl/EngineEnforceUseClientMode.java	Mon Jun 25 13:41:39 2018 -0700
@@ -107,10 +107,9 @@
                 "wrap():  Didn't catch the exception properly");
         } catch (IllegalStateException e) {
             System.out.println("Caught the correct exception.");
-            ssle3.wrap(appOut1, oneToTwo);
             oneToTwo.flip();
             if (oneToTwo.hasRemaining()) {
-                throw new Exception("wrap1 generated data");
+                throw new Exception("wrap generated data");
             }
             oneToTwo.clear();
         }
@@ -122,12 +121,11 @@
                 "unwrap():  Didn't catch the exception properly");
         } catch (IllegalStateException e) {
             System.out.println("Caught the correct exception.");
-            ssle4.wrap(appOut1, oneToTwo);
-            oneToTwo.flip();
-            if (oneToTwo.hasRemaining()) {
-                throw new Exception("wrap2 generated data");
+            appIn1.flip();
+            if (appIn1.hasRemaining()) {
+                throw new Exception("unwrap generated data");
             }
-            oneToTwo.clear();
+            appIn1.clear();
         }
 
         try {
@@ -137,12 +135,6 @@
                 "unwrap():  Didn't catch the exception properly");
         } catch (IllegalStateException e) {
             System.out.println("Caught the correct exception.");
-            ssle5.wrap(appOut1, oneToTwo);
-            oneToTwo.flip();
-            if (oneToTwo.hasRemaining()) {
-                throw new Exception("wrap3 generated data");
-            }
-            oneToTwo.clear();
         }
 
         boolean dataDone = false;
@@ -200,7 +192,7 @@
 
                 System.out.println("Try changing modes...");
                 try {
-                    ssle2.setUseClientMode(false);
+                    ssle2.setUseClientMode(true);
                     throw new RuntimeException(
                         "setUseClientMode():  " +
                         "Didn't catch the exception properly");
@@ -311,8 +303,10 @@
             log("Data transferred cleanly");
         }
 
-        a.clear();
-        b.clear();
+        a.position(a.limit());
+        b.position(b.limit());
+        a.limit(a.capacity());
+        b.limit(b.capacity());
     }
 
     private static void log(String str) {
--- a/test/jdk/sun/security/ssl/SSLEngineImpl/RehandshakeFinished.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLEngineImpl/RehandshakeFinished.java	Mon Jun 25 13:41:39 2018 -0700
@@ -160,7 +160,7 @@
                 TrustManagerFactory.getInstance("SunX509");
             tmf.init(ts);
 
-            SSLContext sslCtx = SSLContext.getInstance("TLS");
+            SSLContext sslCtx = SSLContext.getInstance("TLSv1.2");
             sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
             sslc = sslCtx;
         } catch (Exception e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8164879
+ * @library /lib/testlibrary ../../
+ * @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property
+ * start a new handshake sequence to renegotiate the symmetric key with an
+ * SSLSocket connection.  This test verifies the handshake method was called
+ * via debugging info.  It does not verify the renegotiation was successful
+ * as that is very hard.
+ *
+ * @run main SSLEngineKeyLimit 0 server AES/GCM/NoPadding keyupdate 1050000
+ * @run main SSLEngineKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22
+ */
+
+/*
+ * This test runs in another process so we can monitor the debug
+ * results.  The OutputAnalyzer must see correct debug output to return a
+ * success.
+ */
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.File;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.Utils;
+import jdk.testlibrary.OutputAnalyzer;
+
+public class SSLEngineKeyLimit {
+
+    SSLEngine eng;
+    static ByteBuffer cTos;
+    static ByteBuffer sToc;
+    static ByteBuffer outdata;
+    ByteBuffer buf;
+    static boolean ready = false;
+
+    static String pathToStores = "../../../../javax/net/ssl/etc/";
+    static String keyStoreFile = "keystore";
+    static String passwd = "passphrase";
+    static String keyFilename;
+    static int dataLen = 10240;
+    static boolean serverwrite = true;
+    int totalDataLen = 0;
+    static boolean sc = true;
+    int delay = 1;
+    static boolean readdone = false;
+
+    // Turn on debugging
+    static boolean debug = false;
+
+    SSLEngineKeyLimit() {
+        buf = ByteBuffer.allocate(dataLen*4);
+    }
+
+    /**
+     * args should have two values:  server|client, <limit size>
+     * Prepending 'p' is for internal use only.
+     */
+    public static void main(String args[]) throws Exception {
+
+        for (int i = 0; i < args.length; i++) {
+            System.out.print(" " + args[i]);
+        }
+        System.out.println();
+        if (args[0].compareTo("p") != 0) {
+            boolean expectedFail = (Integer.parseInt(args[0]) == 1);
+            if (expectedFail) {
+                System.out.println("Test expected to not find updated msg");
+            }
+
+            // Write security property file to overwrite default
+            File f = new File("keyusage."+ System.nanoTime());
+            PrintWriter p = new PrintWriter(f);
+            p.write("jdk.tls.keyLimits=");
+            for (int i = 2; i < args.length; i++) {
+                p.write(" "+ args[i]);
+            }
+            p.close();
+
+            System.setProperty("test.java.opts",
+                    "-Dtest.src=" + System.getProperty("test.src") +
+                            " -Dtest.jdk=" + System.getProperty("test.jdk") +
+                            " -Djavax.net.debug=ssl,handshake" +
+                            " -Djava.security.properties=" + f.getName());
+
+            System.out.println("test.java.opts: " +
+                    System.getProperty("test.java.opts"));
+
+            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
+                    Utils.addTestJavaOpts("SSLEngineKeyLimit", "p", args[1]));
+
+            OutputAnalyzer output = ProcessTools.executeProcess(pb);
+            try {
+                if (expectedFail) {
+                    output.shouldNotContain("KeyUpdate: write key updated");
+                    output.shouldNotContain("KeyUpdate: read key updated");
+                } else {
+                    output.shouldContain("KeyUpdate: triggered, read side");
+                    output.shouldContain("KeyUpdate: triggered, write side");
+                    output.shouldContain("KeyUpdate: write key updated");
+                    output.shouldContain("KeyUpdate: read key updated");
+                }
+            } catch (Exception e) {
+                throw e;
+            } finally {
+                System.out.println("-- BEGIN Stdout:");
+                System.out.println(output.getStdout());
+                System.out.println("-- END Stdout");
+                System.out.println("-- BEGIN Stderr:");
+                System.out.println(output.getStderr());
+                System.out.println("-- END Stderr");
+            }
+            return;
+        }
+
+        if (args[0].compareTo("p") != 0) {
+            throw new Exception ("Tried to run outside of a spawned process");
+        }
+
+        if (args[1].compareTo("client") == 0) {
+            serverwrite = false;
+        }
+
+        cTos = ByteBuffer.allocateDirect(dataLen*4);
+        keyFilename =
+            System.getProperty("test.src", "./") + "/" + pathToStores +
+                "/" + keyStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+
+        sToc = ByteBuffer.allocateDirect(dataLen*4);
+        outdata = ByteBuffer.allocateDirect(dataLen);
+
+        byte[] data  = new byte[dataLen];
+        Arrays.fill(data, (byte)0x0A);
+        outdata.put(data);
+        outdata.flip();
+        cTos.clear();
+        sToc.clear();
+
+        Thread ts = new Thread(serverwrite ? new Client() : new Server());
+        ts.start();
+        (serverwrite ? new Server() : new Client()).run();
+        ts.interrupt();
+        ts.join();
+    }
+
+    private static void doTask(SSLEngineResult result,
+            SSLEngine engine) throws Exception {
+
+        if (result.getHandshakeStatus() ==
+                SSLEngineResult.HandshakeStatus.NEED_TASK) {
+            Runnable runnable;
+            while ((runnable = engine.getDelegatedTask()) != null) {
+                print("\trunning delegated task...");
+                runnable.run();
+            }
+            SSLEngineResult.HandshakeStatus hsStatus =
+                    engine.getHandshakeStatus();
+            if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+                throw new Exception(
+                    "handshake shouldn't need additional tasks");
+            }
+            print("\tnew HandshakeStatus: " + hsStatus);
+        }
+    }
+
+    static void print(String s) {
+        if (debug) {
+            System.out.println(s);
+        }
+    }
+
+    static void log(String s, SSLEngineResult r) {
+        if (!debug) {
+            return;
+        }
+        System.out.println(s + ": " +
+                r.getStatus() + "/" + r.getHandshakeStatus()+ " " +
+                r.bytesConsumed() + "/" + r.bytesProduced() + " ");
+
+    }
+
+    void write() throws Exception {
+        int i = 0;
+        SSLEngineResult r;
+        boolean again = true;
+
+        while (!ready) {
+            Thread.sleep(delay);
+        }
+        print("Write-side. ");
+
+        while (i++ < 150) {
+            while (sc) {
+                if (readdone) {
+                    return;
+                }
+                Thread.sleep(delay);
+            }
+
+            outdata.rewind();
+            print("write wrap");
+
+            while (true) {
+                r = eng.wrap(outdata, getWriteBuf());
+                log("write wrap", r);
+                if (debug && r.getStatus() != SSLEngineResult.Status.OK) {
+                    print("outdata pos: " + outdata.position() +
+                            " rem: " + outdata.remaining() +
+                            " lim: " + outdata.limit() +
+                            " cap: " + outdata.capacity());
+                    print("writebuf pos: " + getWriteBuf().position() +
+                            " rem: " + getWriteBuf().remaining() +
+                            " lim: " + getWriteBuf().limit() +
+                            " cap: " + getWriteBuf().capacity());
+                }
+                if (again && r.getStatus() == SSLEngineResult.Status.OK &&
+                        r.getHandshakeStatus() ==
+                                SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+                    print("again");
+                    again = false;
+                    continue;
+                }
+                break;
+            }
+            doTask(r, eng);
+            getWriteBuf().flip();
+            sc = true;
+            while (sc) {
+                if (readdone) {
+                    return;
+                }
+                Thread.sleep(delay);
+            }
+
+            while (true) {
+                buf.clear();
+                r = eng.unwrap(getReadBuf(), buf);
+                log("write unwrap", r);
+                if (debug && r.getStatus() != SSLEngineResult.Status.OK) {
+                    print("buf pos: " + buf.position() +
+                            " rem: " + buf.remaining() +
+                            " lim: " + buf.limit() +
+                            " cap: " + buf.capacity());
+                    print("readbuf pos: " + getReadBuf().position() +
+                            " rem: " + getReadBuf().remaining() +
+                            " lim: " + getReadBuf().limit() +
+                            " cap:"  + getReadBuf().capacity());
+                }
+                break;
+            }
+            doTask(r, eng);
+            getReadBuf().compact();
+            print("compacted readbuf pos: " + getReadBuf().position() +
+                    " rem: " + getReadBuf().remaining() +
+                    " lim: " + getReadBuf().limit() +
+                    " cap: " + getReadBuf().capacity());
+            sc = true;
+        }
+    }
+
+    void read() throws Exception {
+        byte b = 0x0B;
+        ByteBuffer buf2 = ByteBuffer.allocateDirect(dataLen);
+        SSLEngineResult r = null;
+        boolean exit, again = true;
+
+        while (eng == null) {
+            Thread.sleep(delay);
+        }
+
+        try {
+            System.out.println("connected");
+            print("entering read loop");
+            ready = true;
+            while (true) {
+
+                while (!sc) {
+                    Thread.sleep(delay);
+                }
+
+                print("read wrap");
+                exit = false;
+                while (!exit) {
+                    buf2.put(b);
+                    buf2.flip();
+                    r = eng.wrap(buf2, getWriteBuf());
+                    log("read wrap", r);
+                    if (debug) {
+                             // && r.getStatus() != SSLEngineResult.Status.OK) {
+                        print("buf2 pos: " + buf2.position() +
+                                " rem: " + buf2.remaining() +
+                                " cap: " + buf2.capacity());
+                        print("writebuf pos: " + getWriteBuf().position() +
+                                " rem: " + getWriteBuf().remaining() +
+                                " cap: " + getWriteBuf().capacity());
+                    }
+                    if (again && r.getStatus() == SSLEngineResult.Status.OK &&
+                            r.getHandshakeStatus() ==
+                                SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+                        buf2.compact();
+                        again = false;
+                        continue;
+                    }
+                    exit = true;
+                }
+                doTask(r, eng);
+                buf2.clear();
+                getWriteBuf().flip();
+
+                sc = false;
+
+                while (!sc) {
+                    Thread.sleep(delay);
+                }
+
+                while (true) {
+                        buf.clear();
+                        r = eng.unwrap(getReadBuf(), buf);
+                        log("read unwrap", r);
+                        if (debug &&
+                                r.getStatus() != SSLEngineResult.Status.OK) {
+                            print("buf pos " + buf.position() +
+                                    " rem: " + buf.remaining() +
+                                    " lim: " + buf.limit() +
+                                    " cap: " + buf.capacity());
+                            print("readbuf pos: " + getReadBuf().position() +
+                                    " rem: " + getReadBuf().remaining() +
+                                    " lim: " + getReadBuf().limit() +
+                                    " cap: " + getReadBuf().capacity());
+                            doTask(r, eng);
+                        }
+
+                    if (again && r.getStatus() == SSLEngineResult.Status.OK &&
+                            r.getHandshakeStatus() ==
+                                SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+                        buf.clear();
+                        print("again");
+                        again = false;
+                        continue;
+
+                    }
+                    break;
+                }
+                buf.clear();
+                getReadBuf().compact();
+
+                totalDataLen += r.bytesProduced();
+                sc = false;
+            }
+        } catch (Exception e) {
+            sc = false;
+            readdone = true;
+            System.out.println(e.getMessage());
+            e.printStackTrace();
+            System.out.println("Total data read = " + totalDataLen);
+        }
+    }
+
+    ByteBuffer getReadBuf() {
+        return null;
+    }
+
+    ByteBuffer getWriteBuf() {
+        return null;
+    }
+
+
+    SSLContext initContext() throws Exception {
+        SSLContext sc = SSLContext.getInstance("TLSv1.3");
+        KeyStore ks = KeyStore.getInstance(
+                new File(System.getProperty("javax.net.ssl.keyStore")),
+                passwd.toCharArray());
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(
+                KeyManagerFactory.getDefaultAlgorithm());
+        kmf.init(ks, passwd.toCharArray());
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
+                TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init(ks);
+        sc.init(kmf.getKeyManagers(),
+                tmf.getTrustManagers(), new SecureRandom());
+        return sc;
+    }
+
+    static class Server extends SSLEngineKeyLimit implements Runnable {
+        Server() throws Exception {
+            super();
+            eng = initContext().createSSLEngine();
+            eng.setUseClientMode(false);
+            eng.setNeedClientAuth(true);
+        }
+
+        public void run() {
+            try {
+                if (serverwrite) {
+                    write();
+                } else {
+                    read();
+                }
+
+            } catch (Exception e) {
+                System.out.println("server: " + e.getMessage());
+                e.printStackTrace();
+            }
+            System.out.println("Server closed");
+        }
+
+        @Override
+        ByteBuffer getWriteBuf() {
+            return sToc;
+        }
+        @Override
+        ByteBuffer getReadBuf() {
+            return cTos;
+        }
+    }
+
+
+    static class Client extends SSLEngineKeyLimit implements Runnable {
+        Client() throws Exception {
+            super();
+            eng = initContext().createSSLEngine();
+            eng.setUseClientMode(true);
+        }
+
+        public void run() {
+            try {
+                if (!serverwrite) {
+                    write();
+                } else {
+                    read();
+                }
+            } catch (Exception e) {
+                System.out.println("client: " + e.getMessage());
+                e.printStackTrace();
+            }
+            System.out.println("Client closed");
+        }
+        @Override
+        ByteBuffer getWriteBuf() {
+            return cTos;
+        }
+        @Override
+        ByteBuffer getReadBuf() {
+            return sToc;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLEngineImpl/TLS13BeginHandshake.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test SSLEngine.begineHandshake() triggers a KeyUpdate handshake
+ * in TLSv1.3
+ * @run main/othervm TLS13BeginHandshake
+ */
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+
+public class TLS13BeginHandshake {
+    static String pathToStores =
+            System.getProperty("test.src") + "/../../../../javax/net/ssl/etc/";
+    static String keyStoreFile = "keystore";
+    static String passwd = "passphrase";
+
+    private SSLEngine serverEngine, clientEngine;
+    SSLEngineResult clientResult, serverResult;
+    private ByteBuffer clientOut, clientIn;
+    private ByteBuffer serverOut, serverIn;
+    private ByteBuffer cTOs,sTOc;
+
+    public static void main(String args[]) throws Exception{
+        new TLS13BeginHandshake().runDemo();
+    }
+
+    private void runDemo() throws Exception {
+        int done = 0;
+
+        createSSLEngines();
+        createBuffers();
+
+        while (!isEngineClosed(clientEngine) || !isEngineClosed(serverEngine)) {
+
+            System.out.println("================");
+            clientResult = clientEngine.wrap(clientOut, cTOs);
+            System.out.println("client wrap: " + clientResult);
+            runDelegatedTasks(clientResult, clientEngine);
+            serverResult = serverEngine.wrap(serverOut, sTOc);
+            System.out.println("server wrap: " + serverResult);
+            runDelegatedTasks(serverResult, serverEngine);
+
+            cTOs.flip();
+            sTOc.flip();
+
+            System.out.println("----");
+            clientResult = clientEngine.unwrap(sTOc, clientIn);
+            System.out.println("client unwrap: " + clientResult);
+            if (clientResult.getStatus() == SSLEngineResult.Status.CLOSED) {
+                break;
+            }            runDelegatedTasks(clientResult, clientEngine);
+            serverResult = serverEngine.unwrap(cTOs, serverIn);
+            System.out.println("server unwrap: " + serverResult);
+            runDelegatedTasks(serverResult, serverEngine);
+
+            cTOs.compact();
+            sTOc.compact();
+
+            //System.err.println("so limit="+serverOut.limit()+" so pos="+serverOut.position());
+            //System.out.println("bf ctos limit="+cTOs.limit()+" pos="+cTOs.position()+" cap="+cTOs.capacity());
+            //System.out.println("bf stoc limit="+sTOc.limit()+" pos="+sTOc.position()+" cap="+sTOc.capacity());
+            if (done < 2  && (clientOut.limit() == serverIn.position()) &&
+                    (serverOut.limit() == clientIn.position())) {
+
+                if (done == 0) {
+                    checkTransfer(serverOut, clientIn);
+                    checkTransfer(clientOut, serverIn);
+                    clientEngine.beginHandshake();
+                    done++;
+                    continue;
+                }
+
+                checkTransfer(serverOut, clientIn);
+                checkTransfer(clientOut, serverIn);
+                System.out.println("\tClosing...");
+                clientEngine.closeOutbound();
+                done++;
+                continue;
+            }
+        }
+
+    }
+
+    private static boolean isEngineClosed(SSLEngine engine) {
+        if (engine.isInboundDone())
+            System.out.println("inbound closed");
+        if (engine.isOutboundDone())
+            System.out.println("outbound closed");
+        return (engine.isOutboundDone() && engine.isInboundDone());
+    }
+
+    private static void checkTransfer(ByteBuffer a, ByteBuffer b)
+            throws Exception {
+        a.flip();
+        b.flip();
+
+        if (!a.equals(b)) {
+            throw new Exception("Data didn't transfer cleanly");
+        } else {
+            System.out.println("\tData transferred cleanly");
+        }
+
+        a.compact();
+        b.compact();
+
+    }
+    private void createBuffers() {
+        SSLSession session = clientEngine.getSession();
+        int appBufferMax = session.getApplicationBufferSize();
+        int netBufferMax = session.getPacketBufferSize();
+
+        clientIn = ByteBuffer.allocate(appBufferMax + 50);
+        serverIn = ByteBuffer.allocate(appBufferMax + 50);
+
+        cTOs = ByteBuffer.allocateDirect(netBufferMax);
+        sTOc = ByteBuffer.allocateDirect(netBufferMax);
+
+        clientOut = ByteBuffer.wrap("client".getBytes());
+        serverOut = ByteBuffer.wrap("server".getBytes());
+    }
+
+    private void createSSLEngines() throws Exception {
+        serverEngine = initContext().createSSLEngine();
+        serverEngine.setUseClientMode(false);
+        serverEngine.setNeedClientAuth(true);
+
+        clientEngine = initContext().createSSLEngine("client", 80);
+        clientEngine.setUseClientMode(true);
+    }
+
+    private SSLContext initContext() throws Exception {
+        SSLContext sc = SSLContext.getInstance("TLSv1.3");
+        KeyStore ks = KeyStore.getInstance(new File(pathToStores + keyStoreFile),
+                passwd.toCharArray());
+        KeyManagerFactory kmf =
+                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        kmf.init(ks, passwd.toCharArray());
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init(ks);
+        sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+        return sc;
+    }
+
+    private static void runDelegatedTasks(SSLEngineResult result,
+            SSLEngine engine) throws Exception {
+
+        if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+            Runnable runnable;
+            while ((runnable = engine.getDelegatedTask()) != null) {
+                runnable.run();
+            }
+            HandshakeStatus hsStatus = engine.getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_TASK) {
+                throw new Exception(
+                    "handshake shouldn't need additional tasks");
+            }
+        }
+    }
+}
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/AsyncSSLSocketClose.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/AsyncSSLSocketClose.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,6 @@
 // SunJSSE does not support dynamic system properties, no way to re-use
 // system properties in samevm/agentvm mode.
 //
-// The test may timeout occasionally on heavy loaded system because
-// there are lot of TLS transactions involved. Frequent timeout(s) should
-// be analyzed further.
-//
 
 /*
  * @test
@@ -40,19 +36,23 @@
 
 import javax.net.ssl.*;
 import java.io.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
-public class AsyncSSLSocketClose implements Runnable
-{
+public class AsyncSSLSocketClose implements Runnable {
     SSLSocket socket;
     SSLServerSocket ss;
 
+    // Is the socket ready to close?
+    private final CountDownLatch closeCondition = new CountDownLatch(1);
+
     // Where do we find the keystores?
     static String pathToStores = "../../../../javax/net/ssl/etc";
     static String keyStoreFile = "keystore";
     static String trustStoreFile = "truststore";
     static String passwd = "passphrase";
 
-    public static void main(String[] args) {
+    public static void main(String[] args) throws Exception {
         String keyFilename =
             System.getProperty("test.src", "./") + "/" + pathToStores +
                 "/" + keyStoreFile;
@@ -68,58 +68,68 @@
         new AsyncSSLSocketClose();
     }
 
-    public AsyncSSLSocketClose() {
-        try {
-            SSLServerSocketFactory sslssf =
+    public AsyncSSLSocketClose() throws Exception {
+        SSLServerSocketFactory sslssf =
                 (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
-            ss = (SSLServerSocket) sslssf.createServerSocket(0);
-
-            SSLSocketFactory sslsf =
-                (SSLSocketFactory)SSLSocketFactory.getDefault();
-            socket = (SSLSocket)sslsf.createSocket("localhost",
-                                                        ss.getLocalPort());
-            SSLSocket serverSoc = (SSLSocket) ss.accept();
-            ss.close();
+        ss = (SSLServerSocket) sslssf.createServerSocket(0);
 
-            (new Thread(this)).start();
-            serverSoc.startHandshake();
-
-            try {
-                Thread.sleep(5000);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
+        SSLSocketFactory sslsf =
+            (SSLSocketFactory)SSLSocketFactory.getDefault();
+        socket = (SSLSocket)sslsf.createSocket("localhost", ss.getLocalPort());
+        SSLSocket serverSoc = (SSLSocket)ss.accept();
+        ss.close();
 
-            socket.setSoLinger(true, 10);
-            System.out.println("Calling Socket.close");
-            socket.close();
-            System.out.println("ssl socket get closed");
-            System.out.flush();
+        (new Thread(this)).start();
+        serverSoc.startHandshake();
 
-        } catch (IOException e) {
-            e.printStackTrace();
+        boolean closeIsReady = closeCondition.await(90L, TimeUnit.SECONDS);
+        if (!closeIsReady) {
+            System.out.println(
+                    "Ignore, the closure is not ready yet in 90 seconds.");
+            return;
         }
 
+        socket.setSoLinger(true, 10);
+        System.out.println("Calling Socket.close");
+        socket.close();
+        System.out.println("ssl socket get closed");
+        System.out.flush();
     }
 
     // block in write
     public void run() {
+        byte[] ba = new byte[1024];
+        for (int i = 0; i < ba.length; i++) {
+            ba[i] = 0x7A;
+        }
+
         try {
-            byte[] ba = new byte[1024];
-            for (int i=0; i<ba.length; i++)
-                ba[i] = 0x7A;
-
             OutputStream os = socket.getOutputStream();
             int count = 0;
+
+            // 1st round write
+            count += ba.length;
+            System.out.println(count + " bytes to be written");
+            os.write(ba);
+            System.out.println(count + " bytes written");
+
+            // Signal, ready to close.
+            closeCondition.countDown();
+
+            // write more
             while (true) {
                 count += ba.length;
                 System.out.println(count + " bytes to be written");
                 os.write(ba);
                 System.out.println(count + " bytes written");
             }
-        } catch (IOException e) {
-            e.printStackTrace();
+        } catch (Exception e) {
+            if (socket.isClosed() || socket.isOutputShutdown()) {
+                System.out.println("interrupted, the socket is closed");
+            } else {
+                throw new RuntimeException("interrupted?", e);
+            }
         }
     }
+}
 
-}
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/ClientTimeout.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/ClientTimeout.java	Mon Jun 25 13:41:39 2018 -0700
@@ -21,14 +21,15 @@
  * questions.
  */
 
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
 /*
  * @test
  * @bug 4836493
+ * @ignore need further evaluation
  * @summary Socket timeouts for SSLSockets causes data corruption.
  * @run main/othervm ClientTimeout
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
  */
 
 import java.io.*;
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java	Mon Jun 25 13:41:39 2018 -0700
@@ -21,19 +21,25 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test
  * @bug 4403428
  * @summary Invalidating JSSE session on server causes SSLProtocolException
- * @run main/othervm InvalidateServerSessionRenegotiate
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
+ * @run main/othervm InvalidateServerSessionRenegotiate SSLv3
+ * @run main/othervm InvalidateServerSessionRenegotiate TLSv1
+ * @run main/othervm InvalidateServerSessionRenegotiate TLSv1.1
+ * @run main/othervm InvalidateServerSessionRenegotiate TLSv1.2
  * @author Brad Wetmore
  */
 
 import java.io.*;
 import java.net.*;
+import java.security.Security;
 import javax.net.ssl.*;
 
 public class InvalidateServerSessionRenegotiate implements
@@ -157,6 +163,7 @@
             (SSLSocketFactory) SSLSocketFactory.getDefault();
         SSLSocket sslSocket = (SSLSocket)
             sslsf.createSocket("localhost", serverPort);
+        sslSocket.setEnabledProtocols(new String[] { tlsProtocol });
 
         InputStream sslIS = sslSocket.getInputStream();
         OutputStream sslOS = sslSocket.getOutputStream();
@@ -187,6 +194,9 @@
     volatile Exception serverException = null;
     volatile Exception clientException = null;
 
+    // the specified protocol
+    private static String tlsProtocol;
+
     public static void main(String[] args) throws Exception {
         String keyFilename =
             System.getProperty("test.src", "./") + "/" + pathToStores +
@@ -200,8 +210,13 @@
         System.setProperty("javax.net.ssl.trustStore", trustFilename);
         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
 
-        if (debug)
+        if (debug) {
             System.setProperty("javax.net.debug", "all");
+        }
+
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+        tlsProtocol = args[0];
 
         /*
          * Start the tests.
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java	Mon Jun 25 13:41:39 2018 -0700
@@ -28,12 +28,19 @@
  * @test
  * @bug 7188658
  * @summary Add possibility to disable client initiated renegotiation
- * @run main/othervm
- *      -Djdk.tls.rejectClientInitiatedRenegotiation=true NoImpactServerRenego
+ * @run main/othervm  -Djdk.tls.rejectClientInitiatedRenegotiation=true
+ *      NoImpactServerRenego SSLv3
+ * @run main/othervm  -Djdk.tls.rejectClientInitiatedRenegotiation=true
+ *      NoImpactServerRenego TLSv1
+ * @run main/othervm  -Djdk.tls.rejectClientInitiatedRenegotiation=true
+ *      NoImpactServerRenego TLSv1.1
+ * @run main/othervm  -Djdk.tls.rejectClientInitiatedRenegotiation=true
+ *      NoImpactServerRenego TLSv1.2
  */
 
 import java.io.*;
 import java.net.*;
+import java.security.Security;
 import javax.net.ssl.*;
 
 public class NoImpactServerRenego implements
@@ -157,6 +164,7 @@
             (SSLSocketFactory) SSLSocketFactory.getDefault();
         SSLSocket sslSocket = (SSLSocket)
             sslsf.createSocket("localhost", serverPort);
+        sslSocket.setEnabledProtocols(new String[] { tlsProtocol });
 
         InputStream sslIS = sslSocket.getInputStream();
         OutputStream sslOS = sslSocket.getOutputStream();
@@ -187,6 +195,9 @@
     volatile Exception serverException = null;
     volatile Exception clientException = null;
 
+    // the specified protocol
+    private static String tlsProtocol;
+
     public static void main(String[] args) throws Exception {
         String keyFilename =
             System.getProperty("test.src", "./") + "/" + pathToStores +
@@ -200,8 +211,13 @@
         System.setProperty("javax.net.ssl.trustStore", trustFilename);
         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
 
-        if (debug)
+        if (debug) {
             System.setProperty("javax.net.debug", "all");
+        }
+
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+        tlsProtocol = args[0];
 
         /*
          * Start the tests.
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,14 +21,18 @@
  * questions.
  */
 
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
 /*
  * @test
  * @bug 4404399
+ * @ignore this test does not work any more as the TLS spec changes the
+ *         behaviors of close_notify.
  * @summary When a layered SSL socket is closed, it should wait for close_notify
  * @run main/othervm NonAutoClose
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
  * @author Brad Wetmore
  */
 
@@ -72,7 +76,7 @@
      * Turn on SSL debugging?
      */
     private final static boolean DEBUG = false;
-    private final static boolean VERBOSE = false;
+    private final static boolean VERBOSE = true;
     private final static int NUM_ITERATIONS  = 10;
     private final static int PLAIN_SERVER_VAL = 1;
     private final static int PLAIN_CLIENT_VAL = 2;
@@ -90,7 +94,7 @@
 
     void expectValue(int got, int expected, String msg) throws IOException {
         if (VERBOSE) {
-            System.out.println(msg + ": read (" + got + ")");
+            System.err.println(msg + ": read (" + got + ")");
         }
         if (got != expected) {
             throw new IOException(msg + ": read (" + got
@@ -108,7 +112,7 @@
 
      void doServerSide() throws Exception {
         if (VERBOSE) {
-            System.out.println("Starting server");
+            System.err.println("Starting server");
         }
 
         /*
@@ -137,8 +141,8 @@
 
         for (int i = 1; i <= NUM_ITERATIONS; i++) {
             if (VERBOSE) {
-                System.out.println("=================================");
-                System.out.println("Server Iteration #" + i);
+                System.err.println("=================================");
+                System.err.println("Server Iteration #" + i);
             }
 
             SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket,
@@ -158,7 +162,7 @@
             ssls.close();
 
             if (VERBOSE) {
-                System.out.println("TLS socket is closed");
+                System.err.println("TLS socket is closed");
             }
         }
 
@@ -172,7 +176,7 @@
         plainSocket.close();
 
         if (VERBOSE) {
-            System.out.println("Server plain socket is closed");
+            System.err.println("Server plain socket is closed");
         }
     }
 
@@ -191,7 +195,7 @@
         }
 
         if (VERBOSE) {
-            System.out.println("Starting client");
+            System.err.println("Starting client");
         }
 
         /*
@@ -211,8 +215,8 @@
 
         for (int i = 1; i <= NUM_ITERATIONS; i++) {
             if (VERBOSE) {
-                System.out.println("===================================");
-                System.out.println("Client Iteration #" + i);
+                System.err.println("===================================");
+                System.err.println("Client Iteration #" + i);
               }
 
             SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket,
@@ -233,7 +237,7 @@
             ssls.close();
 
             if (VERBOSE) {
-                System.out.println("Client TLS socket is closed");
+                System.err.println("Client TLS socket is closed");
             }
         }
 
@@ -247,7 +251,7 @@
         plainSocket.close();
 
         if (VERBOSE) {
-            System.out.println("Client plain socket is closed");
+            System.err.println("Client plain socket is closed");
         }
     }
 
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/RejectClientRenego.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/RejectClientRenego.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,12 +28,19 @@
  * @test
  * @bug 7188658
  * @summary Add possibility to disable client initiated renegotiation
- * @run main/othervm RejectClientRenego true
- * @run main/othervm RejectClientRenego false
+ * @run main/othervm RejectClientRenego true SSLv3
+ * @run main/othervm RejectClientRenego false SSLv3
+ * @run main/othervm RejectClientRenego true TLSv1
+ * @run main/othervm RejectClientRenego false TLSv1
+ * @run main/othervm RejectClientRenego true TLSv1.1
+ * @run main/othervm RejectClientRenego false TLSv1.1
+ * @run main/othervm RejectClientRenego true TLSv1.2
+ * @run main/othervm RejectClientRenego false TLSv1.2
  */
 
 import java.io.*;
 import java.net.*;
+import java.security.Security;
 import javax.net.ssl.*;
 
 public class RejectClientRenego implements
@@ -113,6 +120,7 @@
         serverReady = true;
 
         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+        sslSocket.setEnabledProtocols(new String[] { tlsProtocol });
         sslSocket.addHandshakeCompletedListener(this);
         InputStream sslIS = sslSocket.getInputStream();
         OutputStream sslOS = sslSocket.getOutputStream();
@@ -157,6 +165,7 @@
             (SSLSocketFactory) SSLSocketFactory.getDefault();
         SSLSocket sslSocket = (SSLSocket)
             sslsf.createSocket("localhost", serverPort);
+        sslSocket.setEnabledProtocols(new String[] { tlsProtocol });
 
         InputStream sslIS = sslSocket.getInputStream();
         OutputStream sslOS = sslSocket.getOutputStream();
@@ -202,6 +211,9 @@
     // Is it abbreviated handshake?
     private static boolean isAbbreviated = false;
 
+    // the specified protocol
+    private static String tlsProtocol;
+
     public static void main(String[] args) throws Exception {
         String keyFilename =
             System.getProperty("test.src", "./") + "/" + pathToStores +
@@ -219,14 +231,19 @@
         System.setProperty(
             "jdk.tls.rejectClientInitiatedRenegotiation", "true");
 
-        if (debug)
+        if (debug) {
             System.setProperty("javax.net.debug", "all");
+        }
+
+        Security.setProperty("jdk.tls.disabledAlgorithms", "");
 
         // Is it abbreviated handshake?
         if ("true".equals(args[0])) {
             isAbbreviated = true;
         }
 
+        tlsProtocol = args[1];
+
         /*
          * Start the tests.
          */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8164879
+ * @library /lib/testlibrary ../../
+ * @modules java.base/sun.security.util
+ * @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property
+ * @run main SSLSocketKeyLimit 0 server AES/GCM/NoPadding keyupdate 1000000
+ * @run main SSLSocketKeyLimit 0 client AES/GCM/NoPadding keyupdate 1000000
+ * @run main SSLSocketKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22
+ */
+
+ /**
+  * Verify AES/GCM's limits set in the jdk.tls.keyLimits property
+  * start a new handshake sequence to renegotiate the symmetric key with an
+  * SSLSocket connection.  This test verifies the handshake method was called
+  * via debugging info.  It does not verify the renegotiation was successful
+  * as that is very hard.
+  */
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.Utils;
+import jdk.testlibrary.OutputAnalyzer;
+import sun.security.util.HexDumpEncoder;
+
+public class SSLSocketKeyLimit {
+    SSLSocket socket;
+    private InputStream in;
+    private OutputStream out;
+
+    static boolean serverReady = false;
+    static int serverPort = 0;
+
+    static String pathToStores = "../../../../javax/net/ssl/etc/";
+    static String keyStoreFile = "keystore";
+    static String passwd = "passphrase";
+    static int dataLen = 10240;
+    static byte[] data  = new byte[dataLen];
+    static boolean serverwrite = true;
+    int totalDataLen = 0;
+    static boolean done = false;
+
+    SSLSocketKeyLimit() {
+    }
+
+    SSLContext initContext() throws Exception {
+        SSLContext sc = SSLContext.getInstance("TLSv1.3");
+        KeyStore ks = KeyStore.getInstance(
+                new File(System.getProperty("javax.net.ssl.keyStore")),
+                passwd.toCharArray());
+        KeyManagerFactory kmf =
+                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        kmf.init(ks, passwd.toCharArray());
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init(ks);
+        sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+        return sc;
+    }
+
+    /**
+     * args should have two values:  server|client, <limit size>
+     * Prepending 'p' is for internal use only.
+     */
+    public static void main(String args[]) throws Exception {
+        if (args[0].compareTo("p") != 0) {
+
+            boolean expectedFail = (Integer.parseInt(args[0]) == 1);
+            if (expectedFail) {
+                System.out.println("Test expected to not find updated msg");
+            }
+            //Write security property file to overwrite default
+            File f = new File("keyusage."+ System.nanoTime());
+            PrintWriter p = new PrintWriter(f);
+            p.write("jdk.tls.keyLimits=");
+            for (int i = 2; i < args.length; i++) {
+                p.write(" "+ args[i]);
+            }
+            p.close();
+            System.out.println("Keyusage path = " + f.getAbsolutePath());
+            System.setProperty("test.java.opts",
+                    "-Dtest.src=" + System.getProperty("test.src") +
+                            " -Dtest.jdk=" + System.getProperty("test.jdk") +
+                            " -Djavax.net.debug=ssl,handshake " +
+                            " -Djava.security.properties=" + f.getName());
+
+            System.out.println("test.java.opts: " +
+                    System.getProperty("test.java.opts"));
+
+            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
+                    Utils.addTestJavaOpts("SSLSocketKeyLimit", "p", args[1]));
+
+            OutputAnalyzer output = ProcessTools.executeProcess(pb);
+            try {
+                if (expectedFail) {
+                    output.shouldNotContain("KeyUpdate: write key updated");
+                    output.shouldNotContain("KeyUpdate: read key updated");
+                } else {
+                    output.shouldContain("KeyUpdate: triggered, read side");
+                    output.shouldContain("KeyUpdate: triggered, write side");
+                    output.shouldContain("KeyUpdate: write key updated");
+                    output.shouldContain("KeyUpdate: read key updated");
+                }
+            } catch (Exception e) {
+                throw e;
+            } finally {
+                System.out.println("-- BEGIN Stdout:");
+                System.out.println(output.getStdout());
+                System.out.println("-- END Stdout");
+                System.out.println("-- BEGIN Stderr:");
+                System.out.println(output.getStderr());
+                System.out.println("-- END Stderr");
+            }
+            return;
+        }
+
+        if (args.length > 0 && args[0].compareToIgnoreCase("client") == 0) {
+            serverwrite = false;
+        }
+
+        String keyFilename =
+            System.getProperty("test.src", "./") + "/" + pathToStores +
+                "/" + keyStoreFile;
+
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+
+        Arrays.fill(data, (byte)0x0A);
+        Thread ts = new Thread(new Server());
+
+        ts.start();
+        while (!serverReady) {
+            Thread.sleep(100);
+        }
+        new Client().run();
+        ts.join(10000);  // 10sec
+        System.exit(0);
+    }
+
+    void write(SSLSocket s) throws Exception {
+        int i = 0;
+        in = s.getInputStream();
+        out = s.getOutputStream();
+        while (i++ < 150) {
+            out.write(data, 0, dataLen);
+            System.out.print("W");
+            in.readNBytes(1);
+            System.out.print("R");
+        }
+        out.write(0x0D);
+        out.flush();
+
+        // Let read side all the data
+        while (!done) {
+            Thread.sleep(100);
+        }
+        out.close();
+        in.close();
+    }
+
+
+    void read(SSLSocket s) throws Exception {
+        byte[] buf = new byte[dataLen];
+        int len;
+        byte i = 0;
+        try {
+            System.out.println("Server: connected " + s.getSession().getCipherSuite());
+            in = s.getInputStream();
+            out = s.getOutputStream();
+            while (true) {
+                len = in.read(buf, 0, dataLen);
+                System.out.print("r");
+                out.write(i++);
+                System.out.print("w");
+                for (byte b: buf) {
+                    if (b == 0x0A || b == 0x0D) {
+                        continue;
+                    }
+                    System.out.println("\nData invalid: " + new HexDumpEncoder().encode(buf));
+                    break;
+                }
+
+                if (len > 0 && buf[len-1] == 0x0D) {
+                    System.out.println("got end byte");
+                    break;
+                }
+                totalDataLen += len;
+            }
+        } catch (Exception e) {
+            System.out.println("\n"  + e.getMessage());
+            e.printStackTrace();
+        } finally {
+            // Tell write side that we are done reading
+            out.close();
+            in.close();
+            done = true;
+        }
+        System.out.println("\nTotalDataLen = " + totalDataLen);
+    }
+
+    static class Server extends SSLSocketKeyLimit implements Runnable {
+        private SSLServerSocketFactory ssf;
+        private SSLServerSocket ss;
+        Server() {
+            super();
+            try {
+                ssf = initContext().getServerSocketFactory();
+                ss = (SSLServerSocket) ssf.createServerSocket(serverPort);
+                serverPort = ss.getLocalPort();
+            } catch (Exception e) {
+                System.out.println("server: " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+
+        public void run() {
+            try {
+                serverReady = true;
+                System.out.println("Server waiting... port: " + serverPort);
+                socket = (SSLSocket) ss.accept();
+                if (serverwrite) {
+                    write(socket);
+                } else {
+                    read(socket);
+                }
+
+                socket.close();
+            } catch (Exception e) {
+                System.out.println("server: " + e.getMessage());
+                e.printStackTrace();
+            }
+            System.out.println("Server closed");
+        }
+    }
+
+
+    static class Client extends SSLSocketKeyLimit implements Runnable {
+        private SSLSocketFactory sf;
+
+        Client() {
+            super();
+        }
+
+        public void run() {
+            try {
+                sf = initContext().getSocketFactory();
+                System.out.println("Client: connecting... port: " + serverPort);
+                socket = (SSLSocket)sf.createSocket("localhost", serverPort);
+                System.out.println("Client: connected." + socket.getSession().getCipherSuite());
+
+                // Opposite of what the server does
+                if (!serverwrite) {
+                    write(socket);
+                } else {
+                    read(socket);
+                }
+
+            } catch (Exception e) {
+                System.err.println("client: " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+    }
+}
--- a/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java	Mon Jun 25 13:41:39 2018 -0700
@@ -21,15 +21,17 @@
  * questions.
  */
 
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
 /*
  * @test
  * @bug 6223624
+ * @ignore this test does not grant to work.  The handshake may have completed
+ *        when getSession() return.  Please update or remove this test case.
  * @summary SSLSocket.setUseClientMode() fails to throw expected
  *        IllegalArgumentException
  * @run main/othervm SetClientMode
- *
- *     SunJSSE does not support dynamic system properties, no way to re-use
- *     system properties in samevm/agentvm mode.
  */
 
 /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8046321
+ * @library ../../../../java/security/testlibrary
+ * @build CertificateBuilder SimpleOCSPServer
+ * @run main/othervm -Djavax.net.debug=ssl:respmgr java.base/sun.security.ssl.StatusResponseManagerTests
+ * @summary OCSP Stapling for TLS
+ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/Stapling/TEST.properties	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,6 @@
+modules = \
+    java.base/sun.security.provider.certpath \
+    java.base/sun.security.util \
+    java.base/sun.security.x509 \
+    java.base/sun.security.ssl
+bootclasspath.dirs=.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2015, 2016, 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.
+ *
+ * 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.IOException;
+import java.math.BigInteger;
+import java.security.cert.*;
+import java.util.*;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.util.concurrent.TimeUnit;
+
+import sun.security.testlibrary.SimpleOCSPServer;
+import sun.security.testlibrary.CertificateBuilder;
+
+import static sun.security.ssl.CertStatusExtension.*;
+
+/*
+ * Checks that the hash value for a certificate's issuer name is generated
+ * correctly. Requires any certificate that is not self-signed.
+ *
+ * NOTE: this test uses Sun private classes which are subject to change.
+ */
+public class StatusResponseManagerTests {
+
+    private static final boolean debug = true;
+    private static final boolean ocspDebug = false;
+
+    // PKI components we will need for this test
+    static String passwd = "passphrase";
+    static String ROOT_ALIAS = "root";
+    static String INT_ALIAS = "intermediate";
+    static String SSL_ALIAS = "ssl";
+    static KeyStore rootKeystore;           // Root CA Keystore
+    static KeyStore intKeystore;            // Intermediate CA Keystore
+    static KeyStore serverKeystore;         // SSL Server Keystore
+    static KeyStore trustStore;             // SSL Client trust store
+    static X509Certificate rootCert;
+    static X509Certificate intCert;
+    static X509Certificate sslCert;
+    static SimpleOCSPServer rootOcsp;       // Root CA OCSP Responder
+    static int rootOcspPort;                // Port number for root OCSP
+    static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
+    static int intOcspPort;                 // Port number for intermed. OCSP
+
+    static X509Certificate[] chain;
+
+    public static void main(String[] args) throws Exception {
+        Map<String, TestCase> testList =
+                new LinkedHashMap<String, TestCase>() {{
+            put("Basic OCSP fetch test", testOcspFetch);
+            put("Clear StatusResponseManager cache", testClearSRM);
+            put("Basic OCSP_MULTI fetch test", testOcspMultiFetch);
+            put("Test Cache Expiration", testCacheExpiry);
+        }};
+
+        // Create the CAs and OCSP responders
+        createPKI();
+
+        // Grab the certificates and make a chain we can reuse for tests
+        sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
+        intCert = (X509Certificate)intKeystore.getCertificate(INT_ALIAS);
+        rootCert = (X509Certificate)rootKeystore.getCertificate(ROOT_ALIAS);
+        chain = new X509Certificate[3];
+        chain[0] = sslCert;
+        chain[1] = intCert;
+        chain[2] = rootCert;
+
+        runTests(testList);
+
+        intOcsp.stop();
+        rootOcsp.stop();
+    }
+
+    // Test a simple RFC 6066 server-side fetch
+    public static final TestCase testOcspFetch = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP;
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                Map<X509Certificate, byte[]> responseMap = srm.get(
+                        CertStatusRequestType.OCSP, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be one entry in the returned map and
+                // one entry in the cache when the operation is complete.
+                if (responseMap.size() != 1) {
+                    message = "Incorrect number of responses: expected 1, got "
+                            + responseMap.size();
+                } else if (!responseMap.containsKey(sslCert)) {
+                    message = "Response map key is incorrect, expected " +
+                            sslCert.getSubjectX500Principal().toString();
+                } else if (srm.size() != 1) {
+                    message = "Incorrect number of cache entries: " +
+                            "expected 1, got " + srm.size();
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test clearing the StatusResponseManager cache.
+    public static final TestCase testClearSRM = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP_MULTI;
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                srm.get(CertStatusRequestType.OCSP_MULTI, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be two entries in the returned map and
+                // two entries in the cache when the operation is complete.
+                if (srm.size() != 2) {
+                    message = "Incorrect number of responses: expected 2, got "
+                            + srm.size();
+                } else {
+                    // Next, clear the SRM, then check the size again
+                    srm.clear();
+                    if (srm.size() != 0) {
+                        message = "Incorrect number of responses: expected 0," +
+                                " got " + srm.size();
+                    } else {
+                        pass = Boolean.TRUE;
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test a simple RFC 6961 server-side fetch
+    public static final TestCase testOcspMultiFetch = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP_MULTI;
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                Map<X509Certificate, byte[]> responseMap = srm.get(
+                        CertStatusRequestType.OCSP_MULTI, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be two entries in the returned map and
+                // two entries in the cache when the operation is complete.
+                if (responseMap.size() != 2) {
+                    message = "Incorrect number of responses: expected 2, got "
+                            + responseMap.size();
+                } else if (!responseMap.containsKey(sslCert) ||
+                        !responseMap.containsKey(intCert)) {
+                    message = "Response map keys are incorrect, expected " +
+                            sslCert.getSubjectX500Principal().toString() +
+                            " and " +
+                            intCert.getSubjectX500Principal().toString();
+                } else if (srm.size() != 2) {
+                    message = "Incorrect number of cache entries: " +
+                            "expected 2, got " + srm.size();
+                } else {
+                    pass = Boolean.TRUE;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    // Test cache expiration
+    public static final TestCase testCacheExpiry = new TestCase() {
+        @Override
+        public Map.Entry<Boolean, String> runTest() {
+            // For this test, we will set the cache expiry to 5 seconds
+            System.setProperty("jdk.tls.stapling.cacheLifetime", "5");
+            StatusResponseManager srm = new StatusResponseManager();
+            Boolean pass = Boolean.FALSE;
+            String message = null;
+            CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP_MULTI;
+
+            try {
+                // Get OCSP responses for non-root certs in the chain
+                srm.get(CertStatusRequestType.OCSP_MULTI, oReq, chain, 5000,
+                        TimeUnit.MILLISECONDS);
+
+                // There should be two entries in the returned map and
+                // two entries in the cache when the operation is complete.
+                if (srm.size() != 2) {
+                    message = "Incorrect number of responses: expected 2, got "
+                            + srm.size();
+                } else {
+                    // Next, wait for more than 5 seconds so the responses
+                    // in the SRM will expire.
+                    Thread.sleep(7000);
+                    if (srm.size() != 0) {
+                        message = "Incorrect number of responses: expected 0," +
+                                " got " + srm.size();
+                    } else {
+                        pass = Boolean.TRUE;
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace(System.out);
+                message = e.getClass().getName();
+            }
+
+            // Set the cache lifetime back to the default
+            System.setProperty("jdk.tls.stapling.cacheLifetime", "");
+            return new AbstractMap.SimpleEntry<>(pass, message);
+        }
+    };
+
+    /**
+     * Creates the PKI components necessary for this test, including
+     * Root CA, Intermediate CA and SSL server certificates, the keystores
+     * for each entity, a client trust store, and starts the OCSP responders.
+     */
+    private static void createPKI() throws Exception {
+        CertificateBuilder cbld = new CertificateBuilder();
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(2048);
+        KeyStore.Builder keyStoreBuilder =
+                KeyStore.Builder.newInstance("PKCS12", null,
+                        new KeyStore.PasswordProtection(passwd.toCharArray()));
+
+        // Generate Root, IntCA, EE keys
+        KeyPair rootCaKP = keyGen.genKeyPair();
+        log("Generated Root CA KeyPair");
+        KeyPair intCaKP = keyGen.genKeyPair();
+        log("Generated Intermediate CA KeyPair");
+        KeyPair sslKP = keyGen.genKeyPair();
+        log("Generated SSL Cert KeyPair");
+
+        // Set up the Root CA Cert
+        cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
+        cbld.setPublicKey(rootCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("1"));
+        // Make a 3 year validity starting from 60 days ago
+        long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
+        long end = start + TimeUnit.DAYS.toMillis(1085);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        // Make our Root CA Cert!
+        X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Root CA Created:\n" + certInfo(rootCert));
+
+        // Now build a keystore and add the keys and cert
+        rootKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] rootChain = {rootCert};
+        rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
+                passwd.toCharArray(), rootChain);
+
+        // Now fire up the OCSP responder
+        rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
+        rootOcsp.enableLog(ocspDebug);
+        rootOcsp.setNextUpdateInterval(3600);
+        rootOcsp.start();
+
+        // Wait 5 seconds for server ready
+        for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) {
+            Thread.sleep(50);
+        }
+        if (!rootOcsp.isServerReady()) {
+            throw new RuntimeException("Server not ready yet");
+        }
+
+        rootOcspPort = rootOcsp.getPort();
+        String rootRespURI = "http://localhost:" + rootOcspPort;
+        log("Root OCSP Responder URI is " + rootRespURI);
+
+        // Now that we have the root keystore and OCSP responder we can
+        // create our intermediate CA.
+        cbld.reset();
+        cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
+        cbld.setPublicKey(intCaKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("100"));
+        // Make a 2 year validity starting from 30 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
+        end = start + TimeUnit.DAYS.toMillis(730);
+        cbld.setValidity(new Date(start), new Date(end));
+        addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
+        addCommonCAExts(cbld);
+        cbld.addAIAExt(Collections.singletonList(rootRespURI));
+        // Make our Intermediate CA Cert!
+        X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("Intermediate CA Created:\n" + certInfo(intCaCert));
+
+        // Provide intermediate CA cert revocation info to the Root CA
+        // OCSP responder.
+        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
+            new HashMap<>();
+        revInfo.put(intCaCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        rootOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        intKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] intChain = {intCaCert, rootCert};
+        intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
+                passwd.toCharArray(), intChain);
+        intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // Now fire up the Intermediate CA OCSP responder
+        intOcsp = new SimpleOCSPServer(intKeystore, passwd,
+                INT_ALIAS, null);
+        intOcsp.enableLog(ocspDebug);
+        intOcsp.setNextUpdateInterval(3600);
+        intOcsp.start();
+
+        // Wait 5 seconds for server ready
+        for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) {
+            Thread.sleep(50);
+        }
+        if (!intOcsp.isServerReady()) {
+            throw new RuntimeException("Server not ready yet");
+        }
+
+        intOcspPort = intOcsp.getPort();
+        String intCaRespURI = "http://localhost:" + intOcspPort;
+        log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
+
+        // Last but not least, let's make our SSLCert and add it to its own
+        // Keystore
+        cbld.reset();
+        cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
+        cbld.setPublicKey(sslKP.getPublic());
+        cbld.setSerialNumber(new BigInteger("4096"));
+        // Make a 1 year validity starting from 7 days ago
+        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
+        end = start + TimeUnit.DAYS.toMillis(365);
+        cbld.setValidity(new Date(start), new Date(end));
+
+        // Add extensions
+        addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
+        boolean[] kuBits = {true, false, true, false, false, false,
+            false, false, false};
+        cbld.addKeyUsageExt(kuBits);
+        List<String> ekuOids = new ArrayList<>();
+        ekuOids.add("1.3.6.1.5.5.7.3.1");
+        ekuOids.add("1.3.6.1.5.5.7.3.2");
+        cbld.addExtendedKeyUsageExt(ekuOids);
+        cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
+        cbld.addAIAExt(Collections.singletonList(intCaRespURI));
+        // Make our SSL Server Cert!
+        X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
+                "SHA256withRSA");
+        log("SSL Certificate Created:\n" + certInfo(sslCert));
+
+        // Provide SSL server cert revocation info to the Intermeidate CA
+        // OCSP responder.
+        revInfo = new HashMap<>();
+        revInfo.put(sslCert.getSerialNumber(),
+                new SimpleOCSPServer.CertStatusInfo(
+                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
+        intOcsp.updateStatusDb(revInfo);
+
+        // Now build a keystore and add the keys, chain and root cert as a TA
+        serverKeystore = keyStoreBuilder.getKeyStore();
+        Certificate[] sslChain = {sslCert, intCaCert, rootCert};
+        serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
+                passwd.toCharArray(), sslChain);
+        serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
+
+        // And finally a Trust Store for the client
+        trustStore = keyStoreBuilder.getKeyStore();
+        trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
+    }
+
+    private static void addCommonExts(CertificateBuilder cbld,
+            PublicKey subjKey, PublicKey authKey) throws IOException {
+        cbld.addSubjectKeyIdExt(subjKey);
+        cbld.addAuthorityKeyIdExt(authKey);
+    }
+
+    private static void addCommonCAExts(CertificateBuilder cbld)
+            throws IOException {
+        cbld.addBasicConstraintsExt(true, true, -1);
+        // Set key usage bits for digitalSignature, keyCertSign and cRLSign
+        boolean[] kuBitSettings = {true, false, false, false, false, true,
+            true, false, false};
+        cbld.addKeyUsageExt(kuBitSettings);
+    }
+
+    /**
+     * Helper routine that dumps only a few cert fields rather than
+     * the whole toString() output.
+     *
+     * @param cert An X509Certificate to be displayed
+     *
+     * @return The {@link String} output of the issuer, subject and
+     * serial number
+     */
+    private static String certInfo(X509Certificate cert) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
+                append("\n");
+        sb.append("Subject: ").append(cert.getSubjectX500Principal()).
+                append("\n");
+        sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
+        return sb.toString();
+    }
+
+    /**
+     * Log a message on stdout
+     *
+     * @param message The message to log
+     */
+    private static void log(String message) {
+        if (debug) {
+            System.out.println(message);
+        }
+    }
+
+    public static void runTests(Map<String, TestCase> testList) {
+        int testNo = 0;
+        int numberFailed = 0;
+        Map.Entry<Boolean, String> result;
+
+        System.out.println("============ Tests ============");
+        for (String testName : testList.keySet()) {
+            System.out.println("Test " + ++testNo + ": " + testName);
+            result = testList.get(testName).runTest();
+            System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL"));
+            System.out.println(" " +
+                    (result.getValue() != null ? result.getValue() : ""));
+            System.out.println("-------------------------------------------");
+            if (!result.getKey()) {
+                numberFailed++;
+            }
+        }
+
+        System.out.println("End Results: " + (testList.size() - numberFailed) +
+                " Passed" + ", " + numberFailed + " Failed.");
+        if (numberFailed > 0) {
+            throw new RuntimeException(
+                    "One or more tests failed, see test output for details");
+        }
+    }
+
+    public interface TestCase {
+        Map.Entry<Boolean, String> runTest();
+    }
+}
--- a/test/jdk/sun/security/ssl/StatusStapling/RunStatReqSelect.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2016, 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.
- *
- * 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.
- */
-
-/*
- * @test
- * @bug 8132943
- * @library ../../../../java/security/testlibrary
- * @build CertificateBuilder SimpleOCSPServer
- * @run main/othervm java.base/sun.security.ssl.StatusReqSelection
- * @summary ServerHandshaker may select non-empty OCSPStatusRequest
- *          structures when Responder ID selection is not supported
- */
-
--- a/test/jdk/sun/security/ssl/StatusStapling/TEST.properties	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-modules = \
-    java.base/sun.security.provider.certpath \
-    java.base/sun.security.util \
-    java.base/sun.security.x509 \
-    java.base/sun.security.ssl
-bootclasspath.dirs=.
--- a/test/jdk/sun/security/ssl/StatusStapling/TestRun.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.
- *
- * 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.
- */
-
-/*
- * @test
- * @bug 8046321
- * @library ../../../../java/security/testlibrary
- * @build CertificateBuilder SimpleOCSPServer
- * @run main/othervm java.base/sun.security.ssl.CertStatusReqExtensionTests
- * @run main/othervm java.base/sun.security.ssl.CertStatusReqItemV2Tests
- * @run main/othervm java.base/sun.security.ssl.CertStatusReqListV2ExtensionTests
- * @run main/othervm java.base/sun.security.ssl.OCSPStatusRequestTests
- * @run main/othervm -Djavax.net.debug=ssl:respmgr java.base/sun.security.ssl.StatusResponseManagerTests
- * @summary OCSP Stapling for TLS
- */
-
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/BogusStatusRequest.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- * 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.IOException;
-
-final class BogusStatusRequest implements StatusRequest {
-    BogusStatusRequest() { }
-
-    @Override
-    public int length() { return 0; }
-
-    @Override
-    public void send(HandshakeOutStream s) throws IOException { }
-
-    @Override
-    public String toString() {
-        return "BogusStatusRequest";
-    }
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqExtensionTests.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,338 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.
- *
- * 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.IOException;
-import java.util.*;
-import java.nio.ByteBuffer;
-
-/*
- * Checks that the hash value for a certificate's issuer name is generated
- * correctly. Requires any certificate that is not self-signed.
- *
- * NOTE: this test uses Sun private classes which are subject to change.
- */
-public class CertStatusReqExtensionTests {
-
-    private static final boolean debug = false;
-
-    // Default status_request extension (type = ocsp, OCSPStatusRequest
-    // with no responder IDs or extensions
-    private static final byte[] CSRE_DEF_OSR = {1, 0, 0, 0, 0};
-
-    // A status_request extension using a user-defined type (0xFF) and
-    // an underlying no-Responder ID/no-extension OCSPStatusRequest
-    private static final byte[] CSRE_TYPE_FF = {-1, 0, 0, 0, 0};
-
-    // A CertStatusReqExtension with 5 ResponderIds and 1 Extension
-    private static final byte[] CSRE_REQ_RID_EXTS = {
-           1,    0,  -13,    0,   59,  -95,   57,   48,
-          55,   49,   16,   48,   14,    6,    3,   85,
-           4,   10,   19,    7,   83,  111,  109,  101,
-          73,  110,   99,   49,   16,   48,   14,    6,
-           3,   85,    4,   11,   19,    7,   83,  111,
-         109,  101,   80,   75,   73,   49,   17,   48,
-          15,    6,    3,   85,    4,    3,   19,    8,
-          83,  111,  109,  101,   79,   67,   83,   80,
-           0,   68,  -95,   66,   48,   64,   49,   13,
-          48,   11,    6,    3,   85,    4,   10,   19,
-           4,   79,  104,   77,  121,   49,   14,   48,
-          12,    6,    3,   85,    4,   11,   19,    5,
-          66,  101,   97,  114,  115,   49,   15,   48,
-          13,    6,    3,   85,    4,   11,   19,    6,
-          84,  105,  103,  101,  114,  115,   49,   14,
-          48,   12,    6,    3,   85,    4,    3,   19,
-           5,   76,  105,  111,  110,  115,    0,   58,
-         -95,   56,   48,   54,   49,   16,   48,   14,
-           6,    3,   85,    4,   10,   19,    7,   67,
-         111,  109,  112,   97,  110,  121,   49,   13,
-          48,   11,    6,    3,   85,    4,   11,   19,
-           4,   87,  101,  115,  116,   49,   19,   48,
-          17,    6,    3,   85,    4,    3,   19,   10,
-          82,  101,  115,  112,  111,  110,  100,  101,
-         114,   49,    0,   24,  -94,   22,    4,   20,
-         -67,  -36,  114,  121,   92,  -79,  116,   -1,
-         102, -107,    7,  -21,   18, -113,   64,   76,
-          96,   -7,  -66,  -63,    0,   24,  -94,   22,
-           4,   20,  -51,  -69,  107,  -82,  -39,  -87,
-          45,   25,   41,   28,  -76,  -68,  -11, -110,
-         -94,  -97,   62,   47,   58, -125,    0,   51,
-          48,   49,   48,   47,    6,    9,   43,    6,
-           1,    5,    5,    7,   48,    1,    2,    4,
-          34,    4,   32,  -26,  -81, -120,  -61, -127,
-         -79,    0,  -39,  -54,   49,    3,  -51,  -57,
-         -85,   19, -126,   94,   -2,   21,   26,   98,
-           6,  105,  -35,  -37,  -29,  -73,  101,   53,
-          44,   15,  -19
-    };
-
-    public static void main(String[] args) throws Exception {
-        Map<String, TestCase> testList =
-                new LinkedHashMap<String, TestCase>() {{
-            put("CTOR (default)", testCtorDefault);
-            put("CTOR (int, StatusRequest)", testCtorStatReqs);
-            put("CTOR (HandshakeInStream, length, getReqType, getRequest)",
-                    testCtorInStream);
-        }};
-
-        TestUtils.runTests(testList);
-    }
-
-    public static final TestCase testCtorDefault = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                CertStatusReqExtension csreDef = new CertStatusReqExtension();
-                HandshakeOutStream hsout =
-                        new HandshakeOutStream(null);
-                csreDef.send(hsout);
-                TestUtils.valueCheck(wrapExtData(null), hsout.toByteArray());
-
-                // The length should be 4 (2 bytes for the type, 2 for the
-                // encoding of zero-length
-                if (csreDef.length() != 4) {
-                    throw new RuntimeException("Incorrect length from " +
-                            "default object.  Expected 4, got " +
-                            csreDef.length());
-                }
-
-                // Since there's no data, there are no status_type or request
-                // data fields defined.  Both should return null in this case
-                if (csreDef.getType() != null) {
-                    throw new RuntimeException("Default CSRE returned " +
-                            "non-null status_type");
-                } else if (csreDef.getRequest() != null) {
-                    throw new RuntimeException("Default CSRE returned " +
-                            "non-null request object");
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    public static final TestCase testCtorStatReqs = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                HandshakeOutStream hsout =
-                        new HandshakeOutStream(null);
-                StatusRequest basicStatReq = new OCSPStatusRequest();
-
-                // Create an extension using a default-style OCSPStatusRequest
-                // (no responder IDs, no extensions).
-                CertStatusReqExtension csre1 = new CertStatusReqExtension(
-                        StatusRequestType.OCSP, basicStatReq);
-                csre1.send(hsout);
-                TestUtils.valueCheck(wrapExtData(CSRE_DEF_OSR),
-                        hsout.toByteArray());
-                hsout.reset();
-
-                // Create the extension using a StatusRequestType not already
-                // instantiated as a static StatusRequestType
-                // (e.g. OCSP/OCSP_MULTI)
-                CertStatusReqExtension csre2 =
-                        new CertStatusReqExtension(StatusRequestType.get(-1),
-                                basicStatReq);
-                csre2.send(hsout);
-                TestUtils.valueCheck(wrapExtData(CSRE_TYPE_FF),
-                        hsout.toByteArray());
-
-                // Create the extension using a StatusRequest that
-                // does not match the status_type field
-                // This should throw an IllegalArgumentException
-                try {
-                    CertStatusReqExtension csreBadRequest =
-                            new CertStatusReqExtension(StatusRequestType.OCSP,
-                                    new BogusStatusRequest());
-                    throw new RuntimeException("Constructor accepted a " +
-                            "StatusRequest that is inconsistent with " +
-                            "the status_type");
-                } catch (IllegalArgumentException iae) { }
-
-                // We don't allow a null value for the StatusRequestType
-                // parameter in this constructor.
-                try {
-                    CertStatusReqExtension csreBadRequest =
-                            new CertStatusReqExtension(null, basicStatReq);
-                    throw new RuntimeException("Constructor accepted a " +
-                            "null StatusRequestType");
-                } catch (NullPointerException npe) { }
-
-                // We also don't allow a null value for the StatusRequest
-                // parameter in this constructor.
-                try {
-                    CertStatusReqExtension csreBadRequest =
-                            new CertStatusReqExtension(StatusRequestType.OCSP,
-                                    null);
-                    throw new RuntimeException("Constructor accepted a " +
-                            "null StatusRequest");
-                } catch (NullPointerException npe) { }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the constructor that builds the ob ject using data from
-    // a HandshakeInStream
-    // This also tests the length, getReqType and getRequest methods
-    public static final TestCase testCtorInStream = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            OCSPStatusRequest osr;
-
-            try {
-                // To simulate the extension coming in a ServerHello, the
-                // type and length would already be read by HelloExtensions
-                // and there is no extension data
-                HandshakeInStream hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(new byte[0]));
-                CertStatusReqExtension csre =
-                        new CertStatusReqExtension(hsis, hsis.available());
-                // Verify length/type/request
-                if (csre.length() != 4) {
-                     throw new RuntimeException("Invalid length: received " +
-                            csre.length() + ", expected 4");
-                } else if (csre.getType() != null) {
-                    throw new RuntimeException("Non-null type from default " +
-                            "extension");
-                } else if (csre.getRequest() != null) {
-                    throw new RuntimeException("Non-null request from default " +
-                            "extension");
-                }
-
-                // Try the an extension with a default OCSPStatusRequest
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(CSRE_DEF_OSR));
-                csre = new CertStatusReqExtension(hsis, hsis.available());
-                if (csre.length() != (CSRE_DEF_OSR.length + 4)) {
-                    throw new RuntimeException("Invalid length: received " +
-                            csre.length() + ", expected " +
-                            CSRE_DEF_OSR.length + 4);
-                } else if (!csre.getType().equals(StatusRequestType.OCSP)) {
-                    throw new RuntimeException("Unknown status_type: " +
-                            String.format("0x%02X", csre.getType().id));
-                } else {
-                    osr = (OCSPStatusRequest)csre.getRequest();
-                    if (!osr.getResponderIds().isEmpty() ||
-                            !osr.getExtensions().isEmpty()) {
-                        throw new RuntimeException("Non-default " +
-                                "OCSPStatusRequest found in extension");
-                    }
-                }
-
-                // Try with a non-default extension
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(CSRE_REQ_RID_EXTS));
-                csre = new CertStatusReqExtension(hsis, hsis.available());
-                if (csre.length() != (CSRE_REQ_RID_EXTS.length + 4)) {
-                    throw new RuntimeException("Invalid length: received " +
-                            csre.length() + ", expected " +
-                            CSRE_REQ_RID_EXTS.length + 4);
-                } else if (!(csre.getType().equals(StatusRequestType.OCSP))) {
-                    throw new RuntimeException("Unknown status_type: " +
-                            String.format("0x%02X", csre.getType().id));
-                } else {
-                    osr = (OCSPStatusRequest)csre.getRequest();
-                    if (osr.getResponderIds().size() != 5 ||
-                            osr.getExtensions().size() != 1) {
-                        throw new RuntimeException("Incorrect number of " +
-                                "ResponderIds or Extensions found in " +
-                                "OCSPStatusRequest");
-                    }
-                }
-
-                // Create a CSRE that asserts status_request and has the
-                // proper length, but really is a bunch of random junk inside
-                // In this case, it will create an UnknownStatusRequest to
-                // handle the unparseable data.
-                byte[] junkData = new byte[48];
-                Random r = new Random(System.currentTimeMillis());
-                r.nextBytes(junkData);
-                junkData[0] = 7;        // Ensure it isn't a valid status_type
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(junkData));
-                csre = new CertStatusReqExtension(hsis, hsis.available());
-                StatusRequest sr = csre.getRequest();
-                if (!(sr instanceof UnknownStatusRequest)) {
-                    throw new RuntimeException("Expected returned status " +
-                            "request to be of type UnknownStatusRequest but " +
-                            "received " + sr.getClass().getName());
-                } else if (csre.length() != (junkData.length + 4)) {
-                    throw new RuntimeException("Invalid length: received " +
-                            csre.length() + ", expected " +
-                            junkData.length + 4);
-                }
-
-                // Set the leading byte to 1 (OCSP type) and run again
-                // It should pass the argument check and fail trying to parse
-                // the underlying StatusRequest.
-                junkData[0] = (byte)StatusRequestType.OCSP.id;
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(junkData));
-                try {
-                    csre = new CertStatusReqExtension(hsis, hsis.available());
-                    throw new RuntimeException("Expected CTOR exception did " +
-                            "not occur");
-                } catch (IOException ioe) { }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Take CSRE extension data and add extension type and length decorations
-    private static byte[] wrapExtData(byte[] extData) {
-        int bufferLen = (extData != null ? extData.length : 0) + 4;
-        ByteBuffer bb = ByteBuffer.allocate(bufferLen);
-        bb.putShort((short)ExtensionType.EXT_STATUS_REQUEST.id);
-        bb.putShort((short)(extData != null ? extData.length: 0));
-        if (extData != null) {
-            bb.put(extData);
-        }
-        return bb.array();
-    }
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqItemV2Tests.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,455 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.
- *
- * 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.security.cert.*;
-import java.util.*;
-import java.nio.ByteBuffer;
-import javax.net.ssl.SSLException;
-import javax.security.auth.x500.X500Principal;
-import sun.security.provider.certpath.ResponderId;
-import sun.security.provider.certpath.OCSPNonceExtension;
-
-/*
- * Checks that the hash value for a certificate's issuer name is generated
- * correctly. Requires any certificate that is not self-signed.
- *
- * NOTE: this test uses Sun private classes which are subject to change.
- */
-public class CertStatusReqItemV2Tests {
-
-    private static final boolean debug = false;
-
-    private static final byte[] DEF_CSRIV2_OCSP_MULTI_BYTES = {
-           2,    0,    4,    0,    0,    0,    0
-    };
-
-    private static final byte[] DEF_CSRIV2_OCSP_BYTES = {
-           1,    0,    4,    0,    0,    0,    0
-    };
-
-    // This is a CSRIV2 (ocsp_multi) that has a single
-    // responder ID and no extensions.
-    private static final byte[] CSRIV2_1RID = {
-            2,    0,   32,     0,   28,    0,   26,  -95,
-           24,   48,   22,    49,   20,   48,   18,    6,
-            3,   85,    4,     3,   19,   11,   79,   67,
-           83,   80,   32,    83,  105,  103,  110,  101,
-          114,    0 ,   0
-    };
-
-    // This is a CSRIV2 (ocsp_multi) that has a single
-    // responder ID and no extensions.  The request_length
-    // field is too short in this case.
-    private static final byte[] CSRIV2_LENGTH_TOO_SHORT = {
-            2,    0,   27,     0,   28,    0,   26,  -95,
-           24,   48,   22,    49,   20,   48,   18,    6,
-            3,   85,    4,     3,   19,   11,   79,   67,
-           83,   80,   32,    83,  105,  103,  110,  101,
-          114,    0 ,   0
-    };
-
-    // This is a CSRIV2 (ocsp_multi) that has a single
-    // responder ID and no extensions.  The request_length
-    // field is too long in this case.
-    private static final byte[] CSRIV2_LENGTH_TOO_LONG = {
-            2,    0,   54,     0,   28,    0,   26,  -95,
-           24,   48,   22,    49,   20,   48,   18,    6,
-            3,   85,    4,     3,   19,   11,   79,   67,
-           83,   80,   32,    83,  105,  103,  110,  101,
-          114,    0 ,   0
-    };
-
-    // A CSRIV2 (ocsp) with one Responder ID (byName: CN=OCSP Signer)
-    // and a nonce extension (32 bytes).
-    private static final byte[] CSRIV2_OCSP_1RID_1EXT = {
-            1,    0,   83,    0,   28,    0,   26,  -95,
-           24,   48,   22,   49,   20,   48,   18,    6,
-            3,   85,    4,    3,   19,   11,   79,   67,
-           83,   80,   32,   83,  105,  103,  110,  101,
-          114,    0,   51,   48,   49,   48,   47,    6,
-            9,   43,    6,    1,    5,    5,    7,   48,
-            1,    2,    4,   34,    4,   32,  -34,  -83,
-          -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
-          -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
-          -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
-          -66,  -17,  -34,  -83,  -66,  -17
-    };
-
-    public static void main(String[] args) throws Exception {
-        Map<String, TestCase> testList =
-                new LinkedHashMap<String, TestCase>() {{
-            put("CTOR (Default)", testCtorTypeStatReq);
-            put("CTOR (Byte array)", testCtorByteArray);
-            put("CTOR (invalid lengths)", testCtorInvalidLengths);
-        }};
-
-        TestUtils.runTests(testList);
-    }
-
-    public static final TestCase testCtorTypeStatReq = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                // Attempt to create CSRIv2 objects using null pointers
-                // for either parameter.  In either case NPE should be thrown
-                CertStatusReqItemV2 csriNull;
-                try {
-                    csriNull = new CertStatusReqItemV2(null,
-                            new OCSPStatusRequest());
-                    throw new RuntimeException("Did not catch expected NPE " +
-                            "for null status_type parameter");
-                } catch (NullPointerException npe) { }
-
-                try {
-                    csriNull = new CertStatusReqItemV2(StatusRequestType.OCSP,
-                            null);
-                    throw new RuntimeException("Did not catch expected NPE " +
-                            "for null StatusRequest parameter");
-                } catch (NullPointerException npe) { }
-
-                // Create an "ocsp_multi" type request using a default
-                // (no Responder IDs, no Extensions) OCSPStatusRequest
-                CertStatusReqItemV2 csriMulti =
-                        new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                                new OCSPStatusRequest());
-                HandshakeOutStream hsout = new HandshakeOutStream(null);
-                csriMulti.send(hsout);
-                TestUtils.valueCheck(DEF_CSRIV2_OCSP_MULTI_BYTES,
-                        hsout.toByteArray());
-                hsout.reset();
-
-                // Create an "ocsp" type request using a default
-                // (no Responder IDs, no Extensions) OCSPStatusRequest
-                CertStatusReqItemV2 csriSingle =
-                        new CertStatusReqItemV2(StatusRequestType.OCSP,
-                                new OCSPStatusRequest(new LinkedList<>(),
-                                        new LinkedList<>()));
-                csriSingle.send(hsout);
-                TestUtils.valueCheck(DEF_CSRIV2_OCSP_BYTES,
-                        hsout.toByteArray());
-
-                // Create the CertStatusRequestItemV2 with a user-defined
-                // StatusRequestType value
-                CertStatusReqItemV2 csriNine =
-                        new CertStatusReqItemV2(StatusRequestType.get(9),
-                                new OCSPStatusRequest(null, null));
-                if (csriNine.getType().id != 9) {
-                    throw new RuntimeException("Expected status_type = 9, " +
-                            "got " + csriNine.getType().id);
-                } else {
-                    StatusRequest sr = csriNine.getRequest();
-                    if (!(sr instanceof OCSPStatusRequest)) {
-                        throw new RuntimeException("Expected " +
-                                "OCSPStatusRequest, got " +
-                                sr.getClass().getName());
-                    }
-                }
-
-                // Create the CertStatusRequestItemV2 with a StatusRequest
-                // that does not match the status_type argument.
-                // We expect IllegalArgumentException in this case.
-                try {
-                    CertStatusReqItemV2 csriBadSR = new CertStatusReqItemV2(
-                            StatusRequestType.OCSP_MULTI,
-                            new BogusStatusRequest());
-                    throw new RuntimeException("Constructor accepted a " +
-                            "StatusRequest that is inconsistent with " +
-                            "the status_type");
-                } catch (IllegalArgumentException iae) {
-                    // The expected result...nothing to do here
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the constructor form that takes the data from a byte array
-    public static final TestCase testCtorByteArray = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                StatusRequestType sType;
-                StatusRequest sReq;
-                ResponderId checkRid =
-                        new ResponderId(new X500Principal("CN=OCSP Signer"));
-                Extension checkExt = new OCSPNonceExtension(32);
-
-                CertStatusReqItemV2 csriv =
-                        new CertStatusReqItemV2(CSRIV2_OCSP_1RID_1EXT);
-                sType = csriv.getType();
-                if (sType != StatusRequestType.OCSP) {
-                    throw new RuntimeException("Unexpected StatusRequestType " +
-                            sType.getClass().getName());
-                }
-
-                sReq = csriv.getRequest();
-                if (sReq instanceof OCSPStatusRequest) {
-                    OCSPStatusRequest osr = (OCSPStatusRequest)sReq;
-                    List<ResponderId> ridList = osr.getResponderIds();
-                    List<Extension> extList = osr.getExtensions();
-
-                    if (ridList.size() != 1 || !ridList.contains(checkRid)) {
-                        throw new RuntimeException("Responder list mismatch");
-                    } else if (extList.size() !=  1 ||
-                            !extList.get(0).getId().equals(checkExt.getId())) {
-                        throw new RuntimeException("Extension list mismatch");
-                    }
-                } else {
-                    throw new RuntimeException("Expected OCSPStatusRequest " +
-                            "from decoded bytes, got " +
-                            sReq.getClass().getName());
-                }
-
-                // Create a CSRIV2 out of random data.  A non-OCSP/OCSP_MULTI
-                // type will be forcibly set and the outer length field will
-                // be correct.
-                // The constructor should create a StatusRequestType object
-                // and an UnknownStatusRequest object consisting of the
-                // data segment.
-                byte[] junkData = new byte[48];
-                Random r = new Random(System.currentTimeMillis());
-                r.nextBytes(junkData);
-                junkData[0] = 7;        // status_type = 7
-                junkData[1] = 0;
-                junkData[2] = 45;       // request_length = 45
-                csriv = new CertStatusReqItemV2(junkData);
-
-                sType = csriv.getType();
-                sReq = csriv.getRequest();
-                if (sType.id != junkData[0]) {
-                    throw new RuntimeException("StatusRequestType mismatch: " +
-                            "expected 7, got " + sType.id);
-                }
-                if (sReq instanceof UnknownStatusRequest) {
-                    // Verify the underlying StatusRequest bytes have been
-                    // preserved correctly.
-                    HandshakeOutStream hsout = new HandshakeOutStream(null);
-                    sReq.send(hsout);
-                    byte[] srDataOut = hsout.toByteArray();
-                    TestUtils.valueCheck(srDataOut, junkData, 0, 3,
-                            srDataOut.length);
-                } else {
-                    throw new RuntimeException("StatusRequest mismatch: " +
-                            "expected UnknownStatusRequest, got " +
-                            sReq.getClass().getName());
-                }
-
-                // Test the parsing of the default OCSP/OCSP_MULTI extensions
-                // and make sure the underlying StatusRequestType and
-                // StatusRequest objects are correct.
-                csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_MULTI_BYTES);
-                sType = csriv.getType();
-                sReq = csriv.getRequest();
-                if (sType != StatusRequestType.OCSP_MULTI) {
-                    throw new RuntimeException("StatusRequestType mismatch: " +
-                            "expected OCSP_MULTI (2), got " + sType.id);
-                }
-                if (!(sReq instanceof OCSPStatusRequest)) {
-                    throw new RuntimeException("StatusRequest mismatch: " +
-                            "expected OCSPStatusRequest, got " +
-                            sReq.getClass().getName());
-                }
-
-                csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_BYTES);
-                sType = csriv.getType();
-                sReq = csriv.getRequest();
-                if (sType != StatusRequestType.OCSP) {
-                    throw new RuntimeException("StatusRequestType mismatch: " +
-                            "expected OCSP (1), got " + sType.id);
-                }
-                if (!(sReq instanceof OCSPStatusRequest)) {
-                    throw new RuntimeException("StatusRequest mismatch: " +
-                            "expected OCSPStatusRequest, got " +
-                            sReq.getClass().getName());
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    public static final TestCase testCtorInvalidLengths = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                try {
-                    CertStatusReqItemV2 csriTooShort =
-                            new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_SHORT);
-                    throw new RuntimeException("Expected exception not thrown");
-                } catch (SSLException ssle) { }
-
-                try {
-                    CertStatusReqItemV2 csriTooLong =
-                            new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_LONG);
-                    throw new RuntimeException("Expected exception not thrown");
-                } catch (SSLException ssle) { }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the constructor form that takes the data from HandshakeInputStream
-    public static final TestCase testCtorInputStream = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                StatusRequestType sType;
-                StatusRequest sReq;
-                ResponderId checkRid =
-                        new ResponderId(new X500Principal("CN=OCSP Signer"));
-                Extension checkExt = new OCSPNonceExtension(32);
-
-                HandshakeInStream hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(CSRIV2_OCSP_1RID_1EXT));
-                CertStatusReqItemV2 csriv = new CertStatusReqItemV2(hsis);
-                sType = csriv.getType();
-                if (sType != StatusRequestType.OCSP) {
-                    throw new RuntimeException("Unexpected StatusRequestType " +
-                            sType.getClass().getName());
-                }
-
-                sReq = csriv.getRequest();
-                if (sReq instanceof OCSPStatusRequest) {
-                    OCSPStatusRequest osr = (OCSPStatusRequest)sReq;
-                    List<ResponderId> ridList = osr.getResponderIds();
-                    List<Extension> extList = osr.getExtensions();
-
-                    if (ridList.size() != 1 || !ridList.contains(checkRid)) {
-                        throw new RuntimeException("Responder list mismatch");
-                    } else if (extList.size() !=  1 ||
-                            !extList.get(0).getId().equals(checkExt.getId())) {
-                        throw new RuntimeException("Extension list mismatch");
-                    }
-                } else {
-                    throw new RuntimeException("Expected OCSPStatusRequest " +
-                            "from decoded bytes, got " +
-                            sReq.getClass().getName());
-                }
-
-                // Create a CSRIV2 out of random data.  A non-OCSP/OCSP_MULTI
-                // type will be forcibly set and the outer length field will
-                // be correct.
-                // The constructor should create a StatusRequestType object
-                // and an UnknownStatusRequest object consisting of the
-                // data segment.
-                byte[] junkData = new byte[48];
-                Random r = new Random(System.currentTimeMillis());
-                r.nextBytes(junkData);
-                junkData[0] = 7;        // status_type = 7
-                junkData[1] = 0;
-                junkData[2] = 45;       // request_length = 45
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(junkData));
-                csriv = new CertStatusReqItemV2(hsis);
-
-                sType = csriv.getType();
-                sReq = csriv.getRequest();
-                if (sType.id != junkData[0]) {
-                    throw new RuntimeException("StatusRequestType mismatch: " +
-                            "expected 7, got " + sType.id);
-                }
-                if (sReq instanceof UnknownStatusRequest) {
-                    // Verify the underlying StatusRequest bytes have been
-                    // preserved correctly.
-                    HandshakeOutStream hsout = new HandshakeOutStream(null);
-                    sReq.send(hsout);
-                    byte[] srDataOut = hsout.toByteArray();
-                    TestUtils.valueCheck(srDataOut, junkData, 0, 3,
-                            srDataOut.length);
-                } else {
-                    throw new RuntimeException("StatusRequest mismatch: " +
-                            "expected UnknownStatusRequest, got " +
-                            sReq.getClass().getName());
-                }
-
-                // Test the parsing of the default OCSP/OCSP_MULTI extensions
-                // and make sure the underlying StatusRequestType and
-                // StatusRequest objects are correct.
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(
-                        ByteBuffer.wrap(DEF_CSRIV2_OCSP_MULTI_BYTES));
-                csriv = new CertStatusReqItemV2(hsis);
-                sType = csriv.getType();
-                sReq = csriv.getRequest();
-                if (sType != StatusRequestType.OCSP_MULTI) {
-                    throw new RuntimeException("StatusRequestType mismatch: " +
-                            "expected OCSP_MULTI (2), got " + sType.id);
-                }
-                if (!(sReq instanceof OCSPStatusRequest)) {
-                    throw new RuntimeException("StatusRequest mismatch: " +
-                            "expected OCSPStatusRequest, got " +
-                            sReq.getClass().getName());
-                }
-
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(DEF_CSRIV2_OCSP_BYTES));
-                csriv = new CertStatusReqItemV2(hsis);
-                sType = csriv.getType();
-                sReq = csriv.getRequest();
-                if (sType != StatusRequestType.OCSP) {
-                    throw new RuntimeException("StatusRequestType mismatch: " +
-                            "expected OCSP (1), got " + sType.id);
-                }
-                if (!(sReq instanceof OCSPStatusRequest)) {
-                    throw new RuntimeException("StatusRequest mismatch: " +
-                            "expected OCSPStatusRequest, got " +
-                            sReq.getClass().getName());
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqListV2ExtensionTests.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,405 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.
- *
- * 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.IOException;
-import java.util.*;
-import java.nio.ByteBuffer;
-import javax.net.ssl.*;
-
-/*
- * Checks that the hash value for a certificate's issuer name is generated
- * correctly. Requires any certificate that is not self-signed.
- *
- * NOTE: this test uses Sun private classes which are subject to change.
- */
-public class CertStatusReqListV2ExtensionTests {
-
-    private static final boolean debug = false;
-
-    // Default status_request_v2 extension with two items
-    // 1. Type = ocsp_multi, OCSPStatusRequest is default
-    // 2. Type = ocsp, OCSPStatusRequest is default
-    private static final byte[] CSRLV2_DEF = {
-           0,   14,    2,    0,    4,    0,    0,    0,
-           0,    1,    0,    4,    0,    0,    0,    0
-    };
-
-    // A status_request_v2 where the item list length is
-    // longer than the provided data
-    private static final byte[] CSRLV2_LEN_TOO_LONG = {
-           0,   18,    2,    0,    4,    0,    0,    0,
-           0,    1,    0,    4,    0,    0,    0,    0
-    };
-
-    // A status_request_v2 where the item list length is
-    // shorter than the provided data
-    private static final byte[] CSRLV2_LEN_TOO_SHORT = {
-           0,   11,    2,    0,    4,    0,    0,    0,
-           0,    1,    0,    4,    0,    0,    0,    0
-    };
-
-    // A status_request_v2 extension with a zero-length
-    // certificate_status_req_list (not allowed by the spec)
-    private static final byte[] CSRLV2_INVALID_ZEROLEN = {0, 0};
-
-    // A status_request_v2 extension with two items (ocsp_multi and ocsp)
-    // using OCSPStatusRequests with 5 ResponderIds and 1 Extension each.
-    private static final byte[] CSRLV2_TWO_NON_DEF_ITEMS = {
-            2,   90,    2,    1,   42,    0,  -13,    0,
-           59,  -95,   57,   48,   55,   49,   16,   48,
-           14,    6,    3,   85,    4,   10,   19,    7,
-           83,  111,  109,  101,   73,  110,   99,   49,
-           16,   48,   14,    6,    3,   85,    4,   11,
-           19,    7,   83,  111,  109,  101,   80,   75,
-           73,   49,   17,   48,   15,    6,    3,   85,
-            4,    3,   19,    8,   83,  111,  109,  101,
-           79,   67,   83,   80,    0,   68,  -95,   66,
-           48,   64,   49,   13,   48,   11,    6,    3,
-           85,    4,   10,   19,    4,   79,  104,   77,
-          121,   49,   14,   48,   12,    6,    3,   85,
-            4,   11,   19,    5,   66,  101,   97,  114,
-          115,   49,   15,   48,   13,    6,    3,   85,
-            4,   11,   19,    6,   84,  105,  103,  101,
-          114,  115,   49,   14,   48,   12,    6,    3,
-           85,    4,    3,   19,    5,   76,  105,  111,
-          110,  115,    0,   58,  -95,   56,   48,   54,
-           49,   16,   48,   14,    6,    3,   85,    4,
-           10,   19,    7,   67,  111,  109,  112,   97,
-          110,  121,   49,   13,   48,   11,    6,    3,
-           85,    4,   11,   19,    4,   87,  101,  115,
-          116,   49,   19,   48,   17,    6,    3,   85,
-            4,    3,   19,   10,   82,  101,  115,  112,
-          111,  110,  100,  101,  114,   49,    0,   24,
-          -94,   22,    4,   20,  -67,  -36,  114,  121,
-           92,  -79,  116,   -1,  102, -107,    7,  -21,
-           18, -113,   64,   76,   96,   -7,  -66,  -63,
-            0,   24,  -94,   22,    4,   20,  -51,  -69,
-          107,  -82,  -39,  -87,   45,   25,   41,   28,
-          -76,  -68,  -11, -110,  -94,  -97,   62,   47,
-           58, -125,    0,   51,   48,   49,   48,   47,
-            6,    9,   43,    6,    1,    5,    5,    7,
-           48,    1,    2,    4,   34,    4,   32,  -26,
-          -81, -120,  -61, -127,  -79,    0,  -39,  -54,
-           49,    3,  -51,  -57,  -85,   19, -126,   94,
-           -2,   21,   26,   98,    6,  105,  -35,  -37,
-          -29,  -73,  101,   53,   44,   15,  -19,    1,
-            1,   42,    0,  -13,    0,   59,  -95,   57,
-           48,   55,   49,   16,   48,   14,    6,    3,
-           85,    4,   10,   19,    7,   83,  111,  109,
-          101,   73,  110,   99,   49,   16,   48,   14,
-            6,    3,   85,    4,   11,   19,    7,   83,
-          111,  109,  101,   80,   75,   73,   49,   17,
-           48,   15,    6,    3,   85,    4,    3,   19,
-            8,   83,  111,  109,  101,   79,   67,   83,
-           80,    0,   68,  -95,   66,   48,   64,   49,
-           13,   48,   11,    6,    3,   85,    4,   10,
-           19,    4,   79,  104,   77,  121,   49,   14,
-           48,   12,    6,    3,   85,    4,   11,   19,
-            5,   66,  101,   97,  114,  115,   49,   15,
-           48,   13,    6,    3,   85,    4,   11,   19,
-            6,   84,  105,  103,  101,  114,  115,   49,
-           14,   48,   12,    6,    3,   85,    4,    3,
-           19,    5,   76,  105,  111,  110,  115,    0,
-           58,  -95,   56,   48,   54,   49,   16,   48,
-           14,    6,    3,   85,    4,   10,   19,    7,
-           67,  111,  109,  112,   97,  110,  121,   49,
-           13,   48,   11,    6,    3,   85,    4,   11,
-           19,    4,   87,  101,  115,  116,   49,   19,
-           48,   17,    6,    3,   85,    4,    3,   19,
-           10,   82,  101,  115,  112,  111,  110,  100,
-          101,  114,   49,    0,   24,  -94,   22,    4,
-           20,  -67,  -36,  114,  121,   92,  -79,  116,
-           -1,  102, -107,    7,  -21,   18, -113,   64,
-           76,   96,   -7,  -66,  -63,    0,   24,  -94,
-           22,    4,   20,  -51,  -69,  107,  -82,  -39,
-          -87,   45,   25,   41,   28,  -76,  -68,  -11,
-         -110,  -94,  -97,   62,   47,   58, -125,    0,
-           51,   48,   49,   48,   47,    6,    9,   43,
-            6,    1,    5,    5,    7,   48,    1,    2,
-            4,   34,    4,   32,  -26,  -81, -120,  -61,
-         -127,  -79,    0,  -39,  -54,   49,    3,  -51,
-          -57,  -85,   19, -126,   94,   -2,   21,   26,
-           98,    6,  105,  -35,  -37,  -29,  -73,  101,
-           53,   44,   15,  -19
-    };
-
-    public static void main(String[] args) throws Exception {
-        Map<String, TestCase> testList =
-                new LinkedHashMap<String, TestCase>() {{
-            put("CTOR (default)", testCtorDefault);
-            put("CTOR (List<CertStatusReqItemV2)", testCtorItemList);
-            put("CTOR (HandshakeInStream, getRequestList",
-                    testCtorInStream);
-        }};
-
-        TestUtils.runTests(testList);
-    }
-
-    public static final TestCase testCtorDefault = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                CertStatusReqListV2Extension csrlV2 =
-                        new CertStatusReqListV2Extension();
-                HandshakeOutStream hsout = new HandshakeOutStream(null);
-                csrlV2.send(hsout);
-                TestUtils.valueCheck(wrapExtData(new byte[0]),
-                        hsout.toByteArray());
-
-                // The length should be 4 (2 bytes for the type, 2 for the
-                // encoding of zero-length
-                if (csrlV2.length() != 4) {
-                    throw new RuntimeException("Incorrect length from " +
-                            "default object.  Expected 4, got " +
-                            csrlV2.length());
-                }
-
-                // Since there's no data, there are no status_type or request
-                // data fields defined.  An empty, unmodifiable list should be
-                // returned when obtained from the extension.
-                List<CertStatusReqItemV2> itemList = csrlV2.getRequestItems();
-                if (!itemList.isEmpty()) {
-                    throw new RuntimeException("Default CSRLV2 returned " +
-                            "non-empty request list");
-                } else {
-                    try {
-                        itemList.add(new CertStatusReqItemV2(
-                                StatusRequestType.OCSP_MULTI,
-                                new OCSPStatusRequest()));
-                        throw new RuntimeException("Returned itemList is " +
-                                "modifiable!");
-                    } catch (UnsupportedOperationException uoe) { }
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    public static final TestCase testCtorItemList = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            OCSPStatusRequest osr = new OCSPStatusRequest();
-            List<CertStatusReqItemV2> noItems = Collections.emptyList();
-            List<CertStatusReqItemV2> defList =
-                    new ArrayList<CertStatusReqItemV2>() {{
-                add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, osr));
-                add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr));
-            }};
-            List<CertStatusReqItemV2> unknownTypesList =
-                    new ArrayList<CertStatusReqItemV2>() {{
-                add(new CertStatusReqItemV2(StatusRequestType.get(8),
-                        new UnknownStatusRequest(new byte[0])));
-                add(new CertStatusReqItemV2(StatusRequestType.get(12),
-                        new UnknownStatusRequest(new byte[5])));
-            }};
-
-            try {
-                HandshakeOutStream hsout = new HandshakeOutStream(null);
-                StatusRequest basicStatReq = new OCSPStatusRequest();
-
-                // Create an extension using a default-style OCSPStatusRequest
-                // (no responder IDs, no extensions).
-                CertStatusReqListV2Extension csrlv2 =
-                        new CertStatusReqListV2Extension(defList);
-                csrlv2.send(hsout);
-                TestUtils.valueCheck(wrapExtData(CSRLV2_DEF),
-                        hsout.toByteArray());
-                hsout.reset();
-
-                // Create the extension using a StatusRequestType not already
-                // instantiated as a static StatusRequestType
-                // (e.g. OCSP/OCSP_MULTI)
-                csrlv2 = new CertStatusReqListV2Extension(unknownTypesList);
-                List<CertStatusReqItemV2> itemList = csrlv2.getRequestItems();
-                if (itemList.size() != unknownTypesList.size()) {
-                    throw new RuntimeException("Custom CSRLV2 returned " +
-                            "an incorrect number of items: expected " +
-                            unknownTypesList.size() + ", got " +
-                            itemList.size());
-                } else {
-                    // Verify that the list is unmodifiable
-                    try {
-                        itemList.add(new CertStatusReqItemV2(
-                                StatusRequestType.OCSP_MULTI,
-                                new OCSPStatusRequest()));
-                        throw new RuntimeException("Returned itemList is " +
-                                "modifiable!");
-                    } catch (UnsupportedOperationException uoe) { }
-                }
-
-                // Pass a null value for the item list.  This should throw
-                // an exception
-                try {
-                    CertStatusReqListV2Extension csrlv2Null =
-                            new CertStatusReqListV2Extension(null);
-                    throw new RuntimeException("Constructor accepted a " +
-                            "null request list");
-                } catch (NullPointerException npe) { }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the constructor that builds the ob ject using data from
-    // a HandshakeInStream
-    // This also tests the length, getReqType and getRequest methods
-    public static final TestCase testCtorInStream = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            OCSPStatusRequest osr;
-            CertStatusReqListV2Extension csrlv2;
-
-            try {
-                // To simulate the extension coming in a ServerHello, the
-                // type and length would already be read by HelloExtensions
-                // and there is no extension data
-                HandshakeInStream hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(new byte[0]));
-                csrlv2 = new CertStatusReqListV2Extension(hsis,
-                        hsis.available());
-
-                // Verify length/request list
-                if (csrlv2.length() != 4) {
-                     throw new RuntimeException("Invalid length: received " +
-                            csrlv2.length() + ", expected 4");
-                } else {
-                    List<CertStatusReqItemV2> itemList =
-                            csrlv2.getRequestItems();
-                    if (!itemList.isEmpty()) {
-                        throw new RuntimeException("Default CSRLV2 returned " +
-                                "non-empty request list");
-                    } else {
-                        try {
-                            itemList.add(new CertStatusReqItemV2(
-                                    StatusRequestType.OCSP_MULTI,
-                                    new OCSPStatusRequest()));
-                            throw new RuntimeException("Returned itemList is " +
-                                    "modifiable!");
-                        } catch (UnsupportedOperationException uoe) { }
-                    }
-                }
-
-                // Try the an extension with our basic client-generated
-                // status_request_v2 (2 items, ocsp_multi and ocsp, each with
-                // a default OCSPStatusRequest
-                hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_DEF));
-                csrlv2 = new CertStatusReqListV2Extension(hsis,
-                        hsis.available());
-                if (csrlv2.length() != (CSRLV2_DEF.length + 4)) {
-                    throw new RuntimeException("Invalid length: received " +
-                            csrlv2.length() + ", expected " +
-                            CSRLV2_DEF.length + 4);
-                } else {
-                    List<CertStatusReqItemV2> itemList =
-                            csrlv2.getRequestItems();
-                    if (itemList.size() != 2) {
-                        throw new RuntimeException("Unexpected number of " +
-                                "items request list, expected 2, got " +
-                                itemList.size());
-                    } else {
-                        try {
-                            itemList.add(new CertStatusReqItemV2(
-                                    StatusRequestType.OCSP_MULTI,
-                                    new OCSPStatusRequest()));
-                            throw new RuntimeException("Returned itemList is " +
-                                    "modifiable!");
-                        } catch (UnsupportedOperationException uoe) { }
-                    }
-                }
-
-                // Try incoming data with an illegal zero-length
-                // certificate_status_req_list
-                try {
-                    hsis = new HandshakeInStream();
-                    hsis.incomingRecord(
-                            ByteBuffer.wrap(CSRLV2_INVALID_ZEROLEN));
-                    csrlv2 = new CertStatusReqListV2Extension(hsis,
-                            hsis.available());
-                    throw new RuntimeException("Unxpected successful " +
-                            "object construction");
-                } catch (SSLException ssle) { }
-
-                // Try extensions where the certificate_status_req_list length
-                // is either too long or too short
-                try {
-                    hsis = new HandshakeInStream();
-                    hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_LEN_TOO_LONG));
-                    csrlv2 = new CertStatusReqListV2Extension(hsis,
-                            hsis.available());
-                    throw new RuntimeException("Unxpected successful " +
-                            "object construction");
-                } catch (SSLException ssle) { }
-
-                try {
-                    hsis = new HandshakeInStream();
-                    hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_LEN_TOO_SHORT));
-                    csrlv2 = new CertStatusReqListV2Extension(hsis,
-                            hsis.available());
-                    throw new RuntimeException("Unxpected successful " +
-                            "object construction");
-                } catch (SSLException ssle) { }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Take CSRE extension data and add extension type and length decorations
-    private static byte[] wrapExtData(byte[] extData) {
-        int bufferLen = extData.length + 4;
-        ByteBuffer bb = ByteBuffer.allocate(bufferLen);
-
-        bb.putShort((short)ExtensionType.EXT_STATUS_REQUEST_V2.id);
-        bb.putShort((short)extData.length);
-        if (extData.length != 0) {
-            bb.put(extData);
-        }
-        return bb.array();
-    }
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/OCSPStatusRequestTests.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,332 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.
- *
- * 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.security.cert.*;
-import java.util.*;
-import java.nio.ByteBuffer;
-import javax.security.auth.x500.X500Principal;
-import sun.security.provider.certpath.ResponderId;
-import sun.security.provider.certpath.OCSPNonceExtension;
-
-/*
- * Checks that the hash value for a certificate's issuer name is generated
- * correctly. Requires any certificate that is not self-signed.
- *
- * NOTE: this test uses Sun private classes which are subject to change.
- */
-public class OCSPStatusRequestTests {
-
-    private static final boolean debug = false;
-
-    // The default (no Responder IDs or Extensions)
-    private static final byte[] DEF_OCSPREQ_BYTES = { 0, 0, 0, 0 };
-
-    // OCSP Extension with one Responder ID (byName: CN=OCSP Signer) and
-    // a nonce extension (32 bytes).
-    private static final byte[] OCSPREQ_1RID_1EXT = {
-            0,   28,    0,   26,  -95,   24,   48,   22,
-           49,   20,   48,   18,    6,    3,   85,    4,
-            3,   19,   11,   79,   67,   83,   80,   32,
-           83,  105,  103,  110,  101,  114,    0,   51,
-           48,   49,   48,   47,    6,    9,   43,    6,
-            1,    5,    5,    7,   48,    1,    2,    4,
-           34,    4,   32,  -34,  -83,  -66,  -17,  -34,
-          -83,  -66,  -17,  -34,  -83,  -66,  -17,  -34,
-          -83,  -66,  -17,  -34,  -83,  -66,  -17,  -34,
-          -83,  -66,  -17,  -34,  -83,  -66,  -17,  -34,
-          -83,  -66,  -17
-    };
-
-    public static void main(String[] args) throws Exception {
-        Map<String, TestCase> testList =
-                new LinkedHashMap<String, TestCase>() {{
-            put("CTOR (default)", testCtorDefault);
-            put("CTOR (Responder Id and Extension)", testCtorRidsExts);
-            put("CTOR (HandshakeInStream)", testCtorInStream);
-            put("CTOR (byte array)", testCtorByteArray);
-            put("Length tests", testLength);
-            put("Equals tests", testEquals);
-        }};
-
-        TestUtils.runTests(testList);
-    }
-
-    // Test the default constructor and its encoding
-    public static final TestCase testCtorDefault = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                // Create a OCSPStatusRequest with a single ResponderId
-                // and Extension
-                OCSPStatusRequest osrDefault = new OCSPStatusRequest();
-                HandshakeOutStream hsout = new HandshakeOutStream(null);
-                osrDefault.send(hsout);
-                System.out.println("Encoded Result:");
-                TestUtils.dumpBytes(hsout.toByteArray());
-
-                TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the constructor form that allows the user to specify zero
-    // or more ResponderId objects and/or Extensions
-    public static final TestCase testCtorRidsExts = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                List<ResponderId> ridList = new LinkedList<ResponderId>() {{
-                    add(new ResponderId(new X500Principal("CN=OCSP Signer")));
-                }};
-                List<Extension> extList = new LinkedList<Extension>() {{
-                    add(new OCSPNonceExtension(32));
-                }};
-
-                // Default-style OCSPStatusRequest using both empty Lists and
-                // null inputs
-                OCSPStatusRequest osrDef1 =
-                        new OCSPStatusRequest(new LinkedList<ResponderId>(),
-                                null);
-                OCSPStatusRequest osrDef2 =
-                        new OCSPStatusRequest(null,
-                                new LinkedList<Extension>());
-                HandshakeOutStream hsout = new HandshakeOutStream(null);
-                osrDef1.send(hsout);
-                System.out.println("Encoded Result:");
-                TestUtils.dumpBytes(hsout.toByteArray());
-                TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
-
-                hsout.reset();
-                osrDef2.send(hsout);
-                System.out.println("Encoded Result:");
-                TestUtils.dumpBytes(hsout.toByteArray());
-                TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
-
-                hsout.reset();
-                OCSPStatusRequest osrWithItems =
-                        new OCSPStatusRequest(ridList, extList);
-                osrWithItems.send(hsout);
-                System.out.println("Encoded Result:");
-                byte[] encodedData = hsout.toByteArray();
-                TestUtils.dumpBytes(encodedData);
-                // Check everything except the last 32 bytes (nonce data)
-                TestUtils.valueCheck(OCSPREQ_1RID_1EXT, encodedData, 0, 0,
-                        encodedData.length - 32);
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the constructor that builds the ob ject using data from
-    // a HandshakeInStream
-    public static final TestCase testCtorInStream = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                ResponderId checkRid =
-                        new ResponderId(new X500Principal("CN=OCSP Signer"));
-                Extension checkExt = new OCSPNonceExtension(32);
-
-                HandshakeInStream hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
-                OCSPStatusRequest osr = new OCSPStatusRequest(hsis);
-
-                List<ResponderId> ridList = osr.getResponderIds();
-                List<Extension> extList = osr.getExtensions();
-
-                if (ridList.size() != 1 || !ridList.contains(checkRid)) {
-                    throw new RuntimeException("Responder list mismatch");
-                } else if (extList.size() !=  1 ||
-                        !extList.get(0).getId().equals(checkExt.getId())) {
-                    throw new RuntimeException("Extension list mismatch");
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the constructor form that takes the data from a byte array
-    public static final TestCase testCtorByteArray = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                ResponderId checkRid =
-                        new ResponderId(new X500Principal("CN=OCSP Signer"));
-                Extension checkExt = new OCSPNonceExtension(32);
-
-                OCSPStatusRequest osr =
-                        new OCSPStatusRequest(OCSPREQ_1RID_1EXT);
-
-                List<ResponderId> ridList = osr.getResponderIds();
-                List<Extension> extList = osr.getExtensions();
-
-                if (ridList.size() != 1 || !ridList.contains(checkRid)) {
-                    throw new RuntimeException("Responder list mismatch");
-                } else if (extList.size() !=  1 ||
-                        !extList.get(0).getId().equals(checkExt.getId())) {
-                    throw new RuntimeException("Extension list mismatch");
-                }
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the length functions for both default and non-default
-    // OCSPStatusRequest objects
-    public static final TestCase testLength = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                HandshakeInStream hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
-                OCSPStatusRequest osr = new OCSPStatusRequest(hsis);
-                OCSPStatusRequest osrDefault = new OCSPStatusRequest();
-
-                if (osrDefault.length() != DEF_OCSPREQ_BYTES.length) {
-                    throw new RuntimeException("Invalid length for default: " +
-                            "Expected" + DEF_OCSPREQ_BYTES.length +
-                            ", received " + osrDefault.length());
-                } else if (osr.length() != OCSPREQ_1RID_1EXT.length) {
-                    throw new RuntimeException("Invalid length for default: " +
-                            "Expected" + OCSPREQ_1RID_1EXT.length +
-                            ", received " + osr.length());
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test the equals method with default and non-default objects
-    public static final TestCase testEquals = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            try {
-                // Make two different lists with the same ResponderId values
-                // and also make a extension list
-                List<ResponderId> ridList1 = new LinkedList<ResponderId>() {{
-                    add(new ResponderId(new X500Principal("CN=OCSP Signer")));
-                }};
-                List<ResponderId> ridList2 = new LinkedList<ResponderId>() {{
-                    add(new ResponderId(new X500Principal("CN=OCSP Signer")));
-                }};
-                List<Extension> extList = new LinkedList<Extension>() {{
-                    add(new OCSPNonceExtension(32));
-                }};
-
-                // We expect two default OCSP objects to be equal
-                OCSPStatusRequest osrDefault = new OCSPStatusRequest();
-                if (!osrDefault.equals(new OCSPStatusRequest())) {
-                    throw new RuntimeException("Default OCSPStatusRequest" +
-                            " equality test failed");
-                }
-
-                // null test (expect false return)
-                if (osrDefault.equals(null)) {
-                    throw new RuntimeException("OCSPStatusRequest matched" +
-                            " unexpectedly");
-                }
-
-                // Self-reference test
-                OCSPStatusRequest osrSelfRef = osrDefault;
-                if (!osrDefault.equals(osrSelfRef)) {
-                    throw new RuntimeException("Default OCSPStatusRequest" +
-                            " equality test failed");
-                }
-
-                // Two OCSPStatusRequests with matching ResponderIds should
-                // be considered equal
-                OCSPStatusRequest osrByList1 =
-                        new OCSPStatusRequest(ridList1, null);
-                OCSPStatusRequest osrByList2 = new OCSPStatusRequest(ridList2,
-                        Collections.emptyList());
-                if (!osrByList1.equals(osrByList2)) {
-                    throw new RuntimeException("Single Responder ID " +
-                            "OCSPStatusRequest equality test failed");
-                }
-
-                // We expect OCSPStatusRequests with different nonces to be
-                // considered unequal.
-                HandshakeInStream hsis = new HandshakeInStream();
-                hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
-                OCSPStatusRequest osrStream = new OCSPStatusRequest(hsis);
-                OCSPStatusRequest osrRidExt = new OCSPStatusRequest(ridList1,
-                        extList);
-                if (osrStream.equals(osrRidExt)) {
-                    throw new RuntimeException("OCSPStatusRequest matched" +
-                            " unexpectedly");
-                }
-
-                pass = Boolean.TRUE;
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusReqSelection.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,899 +0,0 @@
-/*
- * Copyright (c) 2016, 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.
- *
- * 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.
- */
-
-// SunJSSE does not support dynamic system properties, no way to re-use
-// system properties in samevm/agentvm mode.
-
-// See ../../../../RunStatReqSelect.java for the jtreg header
-
-package sun.security.ssl;
-
-import javax.net.ssl.*;
-import javax.net.ssl.SSLEngineResult.*;
-import javax.security.auth.x500.X500Principal;
-import java.io.*;
-import java.math.BigInteger;
-import java.security.*;
-import java.nio.*;
-import java.security.cert.X509Certificate;
-import java.security.cert.Extension;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-
-import sun.security.provider.certpath.OCSPNonceExtension;
-import sun.security.provider.certpath.ResponderId;
-import sun.security.testlibrary.SimpleOCSPServer;
-import sun.security.testlibrary.CertificateBuilder;
-
-public class StatusReqSelection {
-
-    /*
-     * Enables logging of the SSLEngine operations.
-     */
-    private static final boolean logging = true;
-
-    /*
-     * Enables the JSSE system debugging system property:
-     *
-     *     -Djavax.net.debug=all
-     *
-     * This gives a lot of low-level information about operations underway,
-     * including specific handshake messages, and might be best examined
-     * after gaining some familiarity with this application.
-     */
-    private static final boolean debug = false;
-
-    // The following items are used to set up the keystores.
-    private static final String passwd = "passphrase";
-    private static final String ROOT_ALIAS = "root";
-    private static final String INT_ALIAS = "intermediate";
-    private static final String SSL_ALIAS = "ssl";
-
-    // PKI and server components we will need for this test
-    private static KeyManagerFactory kmf;
-    private static TrustManagerFactory tmf;
-    private static KeyStore rootKeystore;       // Root CA Keystore
-    private static KeyStore intKeystore;        // Intermediate CA Keystore
-    private static KeyStore serverKeystore;     // SSL Server Keystore
-    private static KeyStore trustStore;         // SSL Client trust store
-    private static SimpleOCSPServer rootOcsp;   // Root CA OCSP Responder
-    private static int rootOcspPort;            // Port for root OCSP
-    private static SimpleOCSPServer intOcsp;    // Intermediate CA OCSP server
-    private static int intOcspPort;             // Port for intermediate OCSP
-    private static SSLContext ctxStaple;        // SSLContext for all tests
-
-    // Some useful objects we will need for test purposes
-    private static final SecureRandom RNG = new SecureRandom();
-
-    // We'll be using these objects repeatedly to make hello messages
-    private static final ProtocolVersion VER_1_0 = ProtocolVersion.TLS10;
-    private static final ProtocolVersion VER_1_2 = ProtocolVersion.TLS12;
-    private static final CipherSuiteList SUITES = new CipherSuiteList(
-            CipherSuite.valueOf("TLS_RSA_WITH_AES_128_GCM_SHA256"));
-    private static final SessionId SID = new SessionId(new byte[0]);
-    private static final HelloExtension RNIEXT =
-            new RenegotiationInfoExtension(new byte[0], new byte[0]);
-    private static final List<SignatureAndHashAlgorithm> algList =
-            new ArrayList<SignatureAndHashAlgorithm>() {{
-               add(SignatureAndHashAlgorithm.valueOf(4, 1, 0));
-            }};         // List with only SHA256withRSA
-    private static final SignatureAlgorithmsExtension SIGALGEXT =
-            new SignatureAlgorithmsExtension(algList);
-
-    /*
-     * Main entry point for this test.
-     */
-    public static void main(String args[]) throws Exception {
-        int testsPassed = 0;
-
-        if (debug) {
-            System.setProperty("javax.net.debug", "ssl");
-        }
-
-        // All tests will have stapling enabled on the server side
-        System.setProperty("jdk.tls.server.enableStatusRequestExtension",
-                "true");
-
-        // Create a single SSLContext that we can use for all tests
-        ctxStaple = SSLContext.getInstance("TLS");
-
-        // Create the PKI we will use for the test and start the OCSP servers
-        createPKI();
-
-        // Set up the KeyManagerFactory and TrustManagerFactory
-        kmf = KeyManagerFactory.getInstance("PKIX");
-        kmf.init(serverKeystore, passwd.toCharArray());
-        tmf = TrustManagerFactory.getInstance("PKIX");
-        tmf.init(trustStore);
-
-        List<TestCase> testList = new ArrayList<TestCase>() {{
-            add(new TestCase("ClientHello: No stapling extensions",
-                    makeHelloNoStaplingExts(), false, false));
-            add(new TestCase("ClientHello: Default status_request only",
-                    makeDefaultStatReqOnly(), true, false));
-            add(new TestCase("ClientHello: Default status_request_v2 only",
-                    makeDefaultStatReqV2Only(), false, true));
-            add(new TestCase("ClientHello: Both status_request exts, default",
-                    makeDefaultStatReqBoth(), false, true));
-            add(new TestCase(
-                    "ClientHello: Hello with status_request and responder IDs",
-                    makeStatReqWithRid(), false, false));
-            add(new TestCase(
-                    "ClientHello: Hello with status_request using no " +
-                    "responder IDs but provides the OCSP nonce extension",
-                    makeStatReqNoRidNonce(), true, false));
-            add(new TestCase("ClientHello with default status_request and " +
-                    "status_request_v2 with ResponderIds",
-                    makeStatReqDefV2WithRid(), true, false));
-            add(new TestCase("ClientHello with default status_request and " +
-                    "status_request_v2 (OCSP_MULTI with ResponderId, " +
-                    "OCSP as a default request)",
-                    makeStatReqDefV2MultiWithRidSingleDef(), false, true));
-            add(new TestCase("ClientHello with status_request and " +
-                    "status_request_v2 and all OCSPStatusRequests use " +
-                    "Responder IDs",
-                    makeStatReqAllWithRid(), false, false));
-            add(new TestCase("ClientHello with default status_request and " +
-                    "status_request_v2 that has a default OCSP item and " +
-                    "multiple OCSP_MULTI items, only one is default",
-                    makeHelloMultiV2andSingle(), false, true));
-        }};
-
-        // Run the client and server property tests
-        for (TestCase test : testList) {
-            try {
-                log("*** Test: " + test.testName);
-                if (runTest(test)) {
-                    log("PASS: status_request: " + test.statReqEnabled +
-                            ", status_request_v2: " + test.statReqV2Enabled);
-                    testsPassed++;
-                }
-            } catch (Exception e) {
-                // If we get an exception, we'll count it as a failure
-                log("Test failure due to exception: " + e);
-            }
-            log("");
-        }
-
-        // Summary
-        if (testsPassed != testList.size()) {
-            throw new RuntimeException(testList.size() - testsPassed +
-                    " tests failed out of " + testList.size() + " total.");
-        } else {
-            log("Total tests: " + testList.size() + ", all passed");
-        }
-    }
-
-    private static boolean runTest(TestCase test) throws Exception {
-        SSLEngineResult serverResult;
-
-        // Create a Server SSLEngine to receive our customized ClientHello
-        ctxStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
-        SSLEngine engine = ctxStaple.createSSLEngine();
-        engine.setUseClientMode(false);
-        engine.setNeedClientAuth(false);
-
-        SSLSession session = engine.getSession();
-        ByteBuffer serverOut = ByteBuffer.wrap("I'm a Server".getBytes());
-        ByteBuffer serverIn =
-                ByteBuffer.allocate(session.getApplicationBufferSize() + 50);
-        ByteBuffer sTOc =
-                ByteBuffer.allocateDirect(session.getPacketBufferSize());
-
-        // Send the ClientHello ByteBuffer in the test case
-        if (debug) {
-            System.out.println("Sending Client Hello:\n" +
-                    dumpHexBytes(test.data));
-        }
-
-        // Consume the client hello
-        serverResult = engine.unwrap(test.data, serverIn);
-        if (debug) {
-            log("server unwrap: ", serverResult);
-        }
-        if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
-            throw new SSLException("Server unwrap got status: " +
-                    serverResult.getStatus());
-        } else if (serverResult.getHandshakeStatus() !=
-                SSLEngineResult.HandshakeStatus.NEED_TASK) {
-             throw new SSLException("Server unwrap expected NEED_TASK, got: " +
-                    serverResult.getHandshakeStatus());
-        }
-        runDelegatedTasks(serverResult, engine);
-        if (engine.getHandshakeStatus() !=
-                SSLEngineResult.HandshakeStatus.NEED_WRAP) {
-            throw new SSLException("Expected NEED_WRAP, got: " +
-                    engine.getHandshakeStatus());
-        }
-
-        // Generate a TLS record with the ServerHello
-        serverResult = engine.wrap(serverOut, sTOc);
-        if (debug) {
-            log("client wrap: ", serverResult);
-        }
-        if (serverResult.getStatus() != SSLEngineResult.Status.OK) {
-            throw new SSLException("Client wrap got status: " +
-                    serverResult.getStatus());
-        }
-        sTOc.flip();
-
-        if (debug) {
-            log("Server Response:\n" + dumpHexBytes(sTOc));
-        }
-
-        return checkServerHello(sTOc, test.statReqEnabled,
-                test.statReqV2Enabled);
-    }
-
-    /**
-     * Make a TLSv1.2 ClientHello with only RNI and no stapling extensions
-     */
-    private static ByteBuffer makeHelloNoStaplingExts() throws IOException {
-        // Craft the ClientHello byte buffer
-        HelloExtensions exts = new HelloExtensions();
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a TLSv1.2 ClientHello with the RNI and Status Request extensions
-     */
-    private static ByteBuffer makeDefaultStatReqOnly() throws IOException {
-        // Craft the ClientHello byte buffer
-        HelloExtensions exts = new HelloExtensions();
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, null)));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a TLSv1.2 ClientHello with the RNI and Status Request V2 extension
-     */
-    private static ByteBuffer makeDefaultStatReqV2Only() throws IOException {
-        // Craft the ClientHello byte buffer
-        HelloExtensions exts = new HelloExtensions();
-        OCSPStatusRequest osr = new OCSPStatusRequest();
-        List<CertStatusReqItemV2> itemList = new ArrayList<>(2);
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                osr));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr));
-
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        exts.add(new CertStatusReqListV2Extension(itemList));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-    /**
-     * Make a TLSv1.2 ClientHello with Status Request and Status Request V2
-     * extensions.
-     */
-    private static ByteBuffer makeDefaultStatReqBoth() throws IOException {
-        // Craft the ClientHello byte buffer
-        HelloExtensions exts = new HelloExtensions();
-        OCSPStatusRequest osr = new OCSPStatusRequest();
-        List<CertStatusReqItemV2> itemList = new ArrayList<>(2);
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                osr));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr));
-
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, null)));
-        exts.add(new CertStatusReqListV2Extension(itemList));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a ClientHello using a status_request that has a single
-     * responder ID in it.
-     */
-    private static ByteBuffer makeStatReqWithRid() throws IOException {
-        HelloExtensions exts = new HelloExtensions();
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        List<ResponderId> rids = new ArrayList<ResponderId>() {{
-            add(new ResponderId(new X500Principal("CN=Foo")));
-        }};
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(rids, null)));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a ClientHello using a status_request that has no
-     * responder IDs but does provide the nonce extension.
-     */
-    private static ByteBuffer makeStatReqNoRidNonce() throws IOException {
-        HelloExtensions exts = new HelloExtensions();
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        List<Extension> ocspExts = new ArrayList<Extension>() {{
-            add(new OCSPNonceExtension(16));
-        }};
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, ocspExts)));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a ClientHello using a default status_request and a
-     * status_request_v2 that has a single responder ID in it.
-     */
-    private static ByteBuffer makeStatReqDefV2WithRid() throws IOException {
-        HelloExtensions exts = new HelloExtensions();
-        List<ResponderId> rids = new ArrayList<ResponderId>() {{
-            add(new ResponderId(new X500Principal("CN=Foo")));
-        }};
-        List<CertStatusReqItemV2> itemList = new ArrayList<>(2);
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                new OCSPStatusRequest(rids, null)));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP,
-                new OCSPStatusRequest(rids, null)));
-
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, null)));
-        exts.add(new CertStatusReqListV2Extension(itemList));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a ClientHello using a default status_request and a
-     * status_request_v2 that has a single responder ID in it for the
-     * OCSP_MULTI request item and a default OCSP request item.
-     */
-    private static ByteBuffer makeStatReqDefV2MultiWithRidSingleDef()
-            throws IOException {
-        HelloExtensions exts = new HelloExtensions();
-        List<ResponderId> rids = new ArrayList<ResponderId>() {{
-            add(new ResponderId(new X500Principal("CN=Foo")));
-        }};
-        List<CertStatusReqItemV2> itemList = new ArrayList<>(2);
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                new OCSPStatusRequest(rids, null)));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, null)));
-
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, null)));
-        exts.add(new CertStatusReqListV2Extension(itemList));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a ClientHello using status_request and status_request_v2 where
-     * all underlying OCSPStatusRequests use responder IDs.
-     */
-    private static ByteBuffer makeStatReqAllWithRid() throws IOException {
-        HelloExtensions exts = new HelloExtensions();
-        List<ResponderId> rids = new ArrayList<ResponderId>() {{
-            add(new ResponderId(new X500Principal("CN=Foo")));
-        }};
-        List<CertStatusReqItemV2> itemList = new ArrayList<>(2);
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                new OCSPStatusRequest(rids, null)));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP,
-                new OCSPStatusRequest(rids, null)));
-
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(rids, null)));
-        exts.add(new CertStatusReqListV2Extension(itemList));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Make a TLSv1.2 ClientHello multiple CertStatusReqItemV2s of different
-     * types.  One of the middle items should be acceptable while the others
-     * have responder IDs.  The status_request (v1) should also be acceptable
-     * but should be overridden in favor of the status_request_v2.
-     */
-    private static ByteBuffer makeHelloMultiV2andSingle() throws IOException {
-        // Craft the ClientHello byte buffer
-        HelloExtensions exts = new HelloExtensions();
-        List<ResponderId> fooRid = Collections.singletonList(
-                new ResponderId(new X500Principal("CN=Foo")));
-        List<ResponderId> barRid = Collections.singletonList(
-                new ResponderId(new X500Principal("CN=Bar")));
-        List<CertStatusReqItemV2> itemList = new ArrayList<>();
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, null)));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                new OCSPStatusRequest(fooRid, null)));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                new OCSPStatusRequest(null, null)));
-        itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
-                new OCSPStatusRequest(barRid, null)));
-
-        exts.add(RNIEXT);
-        exts.add(SIGALGEXT);
-        exts.add(new CertStatusReqExtension(StatusRequestType.OCSP,
-                new OCSPStatusRequest(null, null)));
-        exts.add(new CertStatusReqListV2Extension(itemList));
-        return createTlsRecord(Record.ct_handshake, VER_1_2,
-                createClientHelloMsg(VER_1_2, SID, SUITES, exts));
-    }
-
-    /**
-     * Wrap a TLS content message into a TLS record header
-     *
-     * @param contentType a byte containing the content type value
-     * @param pv the protocol version for this record
-     * @param data a byte buffer containing the message data
-     * @return
-     */
-    private static ByteBuffer createTlsRecord(byte contentType,
-            ProtocolVersion pv, ByteBuffer data) {
-        int msgLen = (data != null) ? data.limit() : 0;
-
-        // Allocate enough space to hold the TLS record header + the message
-        ByteBuffer recordBuf = ByteBuffer.allocate(msgLen + 5);
-        recordBuf.put(contentType);
-        recordBuf.putShort((short)pv.v);
-        recordBuf.putShort((short)msgLen);
-        if (msgLen > 0) {
-            recordBuf.put(data);
-        }
-
-        recordBuf.flip();
-        return recordBuf;
-    }
-
-    /**
-     * Craft and encode a ClientHello message as a byte array.
-     *
-     * @param pv the protocol version asserted in the hello message.
-     * @param sessId the session ID for this hello message.
-     * @param suites a list consisting of one or more cipher suite objects
-     * @param extensions a list of HelloExtension objects
-     *
-     * @return a byte array containing the encoded ClientHello message.
-     */
-    private static ByteBuffer createClientHelloMsg(ProtocolVersion pv,
-            SessionId sessId, CipherSuiteList suites,
-            HelloExtensions extensions) throws IOException {
-        ByteBuffer msgBuf;
-
-        HandshakeOutStream hsos =
-                new HandshakeOutStream(new SSLEngineOutputRecord());
-
-        // Construct the client hello object from the first 3 parameters
-        HandshakeMessage.ClientHello cHello =
-                new HandshakeMessage.ClientHello(RNG, pv, sessId, suites,
-                        false);
-
-        // Use the HelloExtensions provided by the caller
-        if (extensions != null) {
-            cHello.extensions = extensions;
-        }
-
-        cHello.send(hsos);
-        msgBuf = ByteBuffer.allocate(hsos.size() + 4);
-
-        // Combine the handshake type with the length
-        msgBuf.putInt((HandshakeMessage.ht_client_hello << 24) |
-                (hsos.size() & 0x00FFFFFF));
-        msgBuf.put(hsos.toByteArray());
-        msgBuf.flip();
-        return msgBuf;
-    }
-
-    /*
-     * If the result indicates that we have outstanding tasks to do,
-     * go ahead and run them in this thread.
-     */
-    private static void runDelegatedTasks(SSLEngineResult result,
-            SSLEngine engine) throws Exception {
-
-        if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
-            Runnable runnable;
-            while ((runnable = engine.getDelegatedTask()) != null) {
-                if (debug) {
-                    log("\trunning delegated task...");
-                }
-                runnable.run();
-            }
-            HandshakeStatus hsStatus = engine.getHandshakeStatus();
-            if (hsStatus == HandshakeStatus.NEED_TASK) {
-                throw new Exception(
-                    "handshake shouldn't need additional tasks");
-            }
-            if (debug) {
-                log("\tnew HandshakeStatus: " + hsStatus);
-            }
-        }
-    }
-
-    private static void log(String str, SSLEngineResult result) {
-        if (!logging) {
-            return;
-        }
-        HandshakeStatus hsStatus = result.getHandshakeStatus();
-        log(str +
-            result.getStatus() + "/" + hsStatus + ", " +
-            result.bytesConsumed() + "/" + result.bytesProduced() +
-            " bytes");
-        if (hsStatus == HandshakeStatus.FINISHED) {
-            log("\t...ready for application data");
-        }
-    }
-
-    private static void log(String str) {
-        if (logging) {
-            System.out.println(str);
-        }
-    }
-
-    /**
-     * Dump a ByteBuffer as a hexdump to stdout.  The dumping routine will
-     * start at the current position of the buffer and run to its limit.
-     * After completing the dump, the position will be returned to its
-     * starting point.
-     *
-     * @param data the ByteBuffer to dump to stdout.
-     *
-     * @return the hexdump of the byte array.
-     */
-    private static String dumpHexBytes(ByteBuffer data) {
-        StringBuilder sb = new StringBuilder();
-        if (data != null) {
-            int i = 0;
-            data.mark();
-            while (data.hasRemaining()) {
-                if (i % 16 == 0 && i != 0) {
-                    sb.append("\n");
-                }
-                sb.append(String.format("%02X ", data.get()));
-                i++;
-            }
-            data.reset();
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * Tests the ServerHello for the presence (or not) of the status_request
-     * or status_request_v2 hello extension.  It is assumed that the provided
-     * ByteBuffer has its position set at the first byte of the TLS record
-     * containing the ServerHello and contains the entire hello message.  Upon
-     * successful completion of this method the ByteBuffer will have its
-     * position reset to the initial offset in the buffer.  If an exception is
-     * thrown the position at the time of the exception will be preserved.
-     *
-     * @param statReqPresent true if the status_request hello extension should
-     * be present.
-     * @param statReqV2Present true if the status_request_v2 hello extension
-     * should be present.
-     *
-     * @return true if the ServerHello's extension set matches the presence
-     *      booleans for status_request and status_request_v2.  False if
-     *      not, or if the TLS record or message is of the wrong type.
-     */
-    private static boolean checkServerHello(ByteBuffer data,
-            boolean statReqPresent, boolean statReqV2Present) {
-        boolean hasV1 = false;
-        boolean hasV2 = false;
-        Objects.requireNonNull(data);
-        int startPos = data.position();
-        data.mark();
-
-        // Process the TLS record header
-        int type = Byte.toUnsignedInt(data.get());
-        int ver_major = Byte.toUnsignedInt(data.get());
-        int ver_minor = Byte.toUnsignedInt(data.get());
-        int recLen = Short.toUnsignedInt(data.getShort());
-
-        // Simple sanity checks
-        if (type != 22) {
-            log("Not a handshake: Type = " + type);
-            return false;
-        } else if (recLen > data.remaining()) {
-            log("Incomplete record in buffer: Record length = " + recLen +
-                    ", Remaining = " + data.remaining());
-            return false;
-        }
-
-        // Grab the handshake message header.
-        int msgHdr = data.getInt();
-        int msgType = (msgHdr >> 24) & 0x000000FF;
-        int msgLen = msgHdr & 0x00FFFFFF;
-
-        // More simple sanity checks
-        if (msgType != 2) {
-            log("Not a ServerHello: Type = " + msgType);
-            return false;
-        }
-
-        // Skip over the protocol version and server random
-        data.position(data.position() + 34);
-
-        // Jump past the session ID
-        int sessLen = Byte.toUnsignedInt(data.get());
-        if (sessLen != 0) {
-            data.position(data.position() + sessLen);
-        }
-
-        // Skip the cipher suite and compression method
-        data.position(data.position() + 3);
-
-        // Go through the extensions and look for the request extension
-        // expected by the caller.
-        int extsLen = Short.toUnsignedInt(data.getShort());
-        while (data.position() < recLen + startPos + 5) {
-            int extType = Short.toUnsignedInt(data.getShort());
-            int extLen = Short.toUnsignedInt(data.getShort());
-            hasV1 |= (extType == ExtensionType.EXT_STATUS_REQUEST.id);
-            hasV2 |= (extType == ExtensionType.EXT_STATUS_REQUEST_V2.id);
-            data.position(data.position() + extLen);
-        }
-
-        if (hasV1 != statReqPresent) {
-            log("The status_request extension is " +
-                    "inconsistent with the expected result: expected = " +
-                    statReqPresent + ", actual = " + hasV1);
-        }
-        if (hasV2 != statReqV2Present) {
-            log("The status_request_v2 extension is " +
-                    "inconsistent with the expected result: expected = " +
-                    statReqV2Present + ", actual = " + hasV2);
-        }
-
-        // Reset the position to the initial spot at the start of this method.
-        data.reset();
-
-        return ((hasV1 == statReqPresent) && (hasV2 == statReqV2Present));
-    }
-
-    /**
-     * Creates the PKI components necessary for this test, including
-     * Root CA, Intermediate CA and SSL server certificates, the keystores
-     * for each entity, a client trust store, and starts the OCSP responders.
-     */
-    private static void createPKI() throws Exception {
-        CertificateBuilder cbld = new CertificateBuilder();
-        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
-        keyGen.initialize(2048);
-        KeyStore.Builder keyStoreBuilder =
-                KeyStore.Builder.newInstance("PKCS12", null,
-                        new KeyStore.PasswordProtection(passwd.toCharArray()));
-
-        // Generate Root, IntCA, EE keys
-        KeyPair rootCaKP = keyGen.genKeyPair();
-        log("Generated Root CA KeyPair");
-        KeyPair intCaKP = keyGen.genKeyPair();
-        log("Generated Intermediate CA KeyPair");
-        KeyPair sslKP = keyGen.genKeyPair();
-        log("Generated SSL Cert KeyPair");
-
-        // Set up the Root CA Cert
-        cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
-        cbld.setPublicKey(rootCaKP.getPublic());
-        cbld.setSerialNumber(new BigInteger("1"));
-        // Make a 3 year validity starting from 60 days ago
-        long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
-        long end = start + TimeUnit.DAYS.toMillis(1085);
-        cbld.setValidity(new Date(start), new Date(end));
-        addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
-        addCommonCAExts(cbld);
-        // Make our Root CA Cert!
-        X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
-                "SHA256withRSA");
-        log("Root CA Created:\n" + certInfo(rootCert));
-
-        // Now build a keystore and add the keys and cert
-        rootKeystore = keyStoreBuilder.getKeyStore();
-        java.security.cert.Certificate[] rootChain = {rootCert};
-        rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
-                passwd.toCharArray(), rootChain);
-
-        // Now fire up the OCSP responder
-        rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
-        rootOcsp.enableLog(debug);
-        rootOcsp.setNextUpdateInterval(3600);
-        rootOcsp.start();
-
-        // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) {
-            Thread.sleep(50);
-        }
-        if (!rootOcsp.isServerReady()) {
-            throw new RuntimeException("Server not ready yet");
-        }
-
-        rootOcspPort = rootOcsp.getPort();
-        String rootRespURI = "http://localhost:" + rootOcspPort;
-        log("Root OCSP Responder URI is " + rootRespURI);
-
-        // Now that we have the root keystore and OCSP responder we can
-        // create our intermediate CA.
-        cbld.reset();
-        cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
-        cbld.setPublicKey(intCaKP.getPublic());
-        cbld.setSerialNumber(new BigInteger("100"));
-        // Make a 2 year validity starting from 30 days ago
-        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
-        end = start + TimeUnit.DAYS.toMillis(730);
-        cbld.setValidity(new Date(start), new Date(end));
-        addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
-        addCommonCAExts(cbld);
-        cbld.addAIAExt(Collections.singletonList(rootRespURI));
-        // Make our Intermediate CA Cert!
-        X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
-                "SHA256withRSA");
-        log("Intermediate CA Created:\n" + certInfo(intCaCert));
-
-        // Provide intermediate CA cert revocation info to the Root CA
-        // OCSP responder.
-        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
-            new HashMap<>();
-        revInfo.put(intCaCert.getSerialNumber(),
-                new SimpleOCSPServer.CertStatusInfo(
-                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
-        rootOcsp.updateStatusDb(revInfo);
-
-        // Now build a keystore and add the keys, chain and root cert as a TA
-        intKeystore = keyStoreBuilder.getKeyStore();
-        java.security.cert.Certificate[] intChain = {intCaCert, rootCert};
-        intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
-                passwd.toCharArray(), intChain);
-        intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
-
-        // Now fire up the Intermediate CA OCSP responder
-        intOcsp = new SimpleOCSPServer(intKeystore, passwd,
-                INT_ALIAS, null);
-        intOcsp.enableLog(debug);
-        intOcsp.setNextUpdateInterval(3600);
-        intOcsp.start();
-
-        // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) {
-            Thread.sleep(50);
-        }
-        if (!intOcsp.isServerReady()) {
-            throw new RuntimeException("Server not ready yet");
-        }
-
-        intOcspPort = intOcsp.getPort();
-        String intCaRespURI = "http://localhost:" + intOcspPort;
-        log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
-
-        // Last but not least, let's make our SSLCert and add it to its own
-        // Keystore
-        cbld.reset();
-        cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
-        cbld.setPublicKey(sslKP.getPublic());
-        cbld.setSerialNumber(new BigInteger("4096"));
-        // Make a 1 year validity starting from 7 days ago
-        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
-        end = start + TimeUnit.DAYS.toMillis(365);
-        cbld.setValidity(new Date(start), new Date(end));
-
-        // Add extensions
-        addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
-        boolean[] kuBits = {true, false, true, false, false, false,
-            false, false, false};
-        cbld.addKeyUsageExt(kuBits);
-        List<String> ekuOids = new ArrayList<>();
-        ekuOids.add("1.3.6.1.5.5.7.3.1");
-        ekuOids.add("1.3.6.1.5.5.7.3.2");
-        cbld.addExtendedKeyUsageExt(ekuOids);
-        cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
-        cbld.addAIAExt(Collections.singletonList(intCaRespURI));
-        // Make our SSL Server Cert!
-        X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
-                "SHA256withRSA");
-        log("SSL Certificate Created:\n" + certInfo(sslCert));
-
-        // Provide SSL server cert revocation info to the Intermeidate CA
-        // OCSP responder.
-        revInfo = new HashMap<>();
-        revInfo.put(sslCert.getSerialNumber(),
-                new SimpleOCSPServer.CertStatusInfo(
-                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
-        intOcsp.updateStatusDb(revInfo);
-
-        // Now build a keystore and add the keys, chain and root cert as a TA
-        serverKeystore = keyStoreBuilder.getKeyStore();
-        java.security.cert.Certificate[] sslChain = {sslCert, intCaCert, rootCert};
-        serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
-                passwd.toCharArray(), sslChain);
-        serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
-
-        // And finally a Trust Store for the client
-        trustStore = keyStoreBuilder.getKeyStore();
-        trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
-    }
-
-    private static void addCommonExts(CertificateBuilder cbld,
-            PublicKey subjKey, PublicKey authKey) throws IOException {
-        cbld.addSubjectKeyIdExt(subjKey);
-        cbld.addAuthorityKeyIdExt(authKey);
-    }
-
-    private static void addCommonCAExts(CertificateBuilder cbld)
-            throws IOException {
-        cbld.addBasicConstraintsExt(true, true, -1);
-        // Set key usage bits for digitalSignature, keyCertSign and cRLSign
-        boolean[] kuBitSettings = {true, false, false, false, false, true,
-            true, false, false};
-        cbld.addKeyUsageExt(kuBitSettings);
-    }
-
-    /**
-     * Helper routine that dumps only a few cert fields rather than
-     * the whole toString() output.
-     *
-     * @param cert an X509Certificate to be displayed
-     *
-     * @return the String output of the issuer, subject and
-     * serial number
-     */
-    private static String certInfo(X509Certificate cert) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
-                append("\n");
-        sb.append("Subject: ").append(cert.getSubjectX500Principal()).
-                append("\n");
-        sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
-        return sb.toString();
-    }
-
-    private static class TestCase {
-        public final String testName;
-        public final ByteBuffer data;
-        public final boolean statReqEnabled;
-        public final boolean statReqV2Enabled;
-
-        TestCase(String name, ByteBuffer buffer, boolean srEn, boolean srv2En) {
-            testName = (name != null) ? name : "";
-            data = Objects.requireNonNull(buffer,
-                    "TestCase requires a non-null ByteBuffer");
-            statReqEnabled = srEn;
-            statReqV2Enabled = srv2En;
-        }
-    }
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusResponseManagerTests.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,460 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.
- *
- * 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.IOException;
-import java.math.BigInteger;
-import java.security.cert.*;
-import java.util.*;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.PublicKey;
-import java.util.concurrent.TimeUnit;
-
-import sun.security.testlibrary.SimpleOCSPServer;
-import sun.security.testlibrary.CertificateBuilder;
-
-/*
- * Checks that the hash value for a certificate's issuer name is generated
- * correctly. Requires any certificate that is not self-signed.
- *
- * NOTE: this test uses Sun private classes which are subject to change.
- */
-public class StatusResponseManagerTests {
-
-    private static final boolean debug = true;
-    private static final boolean ocspDebug = false;
-
-    // PKI components we will need for this test
-    static String passwd = "passphrase";
-    static String ROOT_ALIAS = "root";
-    static String INT_ALIAS = "intermediate";
-    static String SSL_ALIAS = "ssl";
-    static KeyStore rootKeystore;           // Root CA Keystore
-    static KeyStore intKeystore;            // Intermediate CA Keystore
-    static KeyStore serverKeystore;         // SSL Server Keystore
-    static KeyStore trustStore;             // SSL Client trust store
-    static X509Certificate rootCert;
-    static X509Certificate intCert;
-    static X509Certificate sslCert;
-    static SimpleOCSPServer rootOcsp;       // Root CA OCSP Responder
-    static int rootOcspPort;                // Port number for root OCSP
-    static SimpleOCSPServer intOcsp;        // Intermediate CA OCSP Responder
-    static int intOcspPort;                 // Port number for intermed. OCSP
-
-    static X509Certificate[] chain;
-
-    public static void main(String[] args) throws Exception {
-        Map<String, TestCase> testList =
-                new LinkedHashMap<String, TestCase>() {{
-            put("Basic OCSP fetch test", testOcspFetch);
-            put("Clear StatusResponseManager cache", testClearSRM);
-            put("Basic OCSP_MULTI fetch test", testOcspMultiFetch);
-            put("Test Cache Expiration", testCacheExpiry);
-        }};
-
-        // Create the CAs and OCSP responders
-        createPKI();
-
-        // Grab the certificates and make a chain we can reuse for tests
-        sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS);
-        intCert = (X509Certificate)intKeystore.getCertificate(INT_ALIAS);
-        rootCert = (X509Certificate)rootKeystore.getCertificate(ROOT_ALIAS);
-        chain = new X509Certificate[3];
-        chain[0] = sslCert;
-        chain[1] = intCert;
-        chain[2] = rootCert;
-
-        TestUtils.runTests(testList);
-
-        intOcsp.stop();
-        rootOcsp.stop();
-    }
-
-    // Test a simple RFC 6066 server-side fetch
-    public static final TestCase testOcspFetch = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            StatusResponseManager srm = new StatusResponseManager();
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            StatusRequest oReq = new OCSPStatusRequest();
-
-            try {
-                // Get OCSP responses for non-root certs in the chain
-                Map<X509Certificate, byte[]> responseMap = srm.get(
-                        StatusRequestType.OCSP, oReq, chain, 5000,
-                        TimeUnit.MILLISECONDS);
-
-                // There should be one entry in the returned map and
-                // one entry in the cache when the operation is complete.
-                if (responseMap.size() != 1) {
-                    message = "Incorrect number of responses: expected 1, got "
-                            + responseMap.size();
-                } else if (!responseMap.containsKey(sslCert)) {
-                    message = "Response map key is incorrect, expected " +
-                            sslCert.getSubjectX500Principal().toString();
-                } else if (srm.size() != 1) {
-                    message = "Incorrect number of cache entries: " +
-                            "expected 1, got " + srm.size();
-                } else {
-                    pass = Boolean.TRUE;
-                }
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test clearing the StatusResponseManager cache.
-    public static final TestCase testClearSRM = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            StatusResponseManager srm = new StatusResponseManager();
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            StatusRequest oReq = new OCSPStatusRequest();
-
-            try {
-                // Get OCSP responses for non-root certs in the chain
-                srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
-                        TimeUnit.MILLISECONDS);
-
-                // There should be two entries in the returned map and
-                // two entries in the cache when the operation is complete.
-                if (srm.size() != 2) {
-                    message = "Incorrect number of responses: expected 2, got "
-                            + srm.size();
-                } else {
-                    // Next, clear the SRM, then check the size again
-                    srm.clear();
-                    if (srm.size() != 0) {
-                        message = "Incorrect number of responses: expected 0," +
-                                " got " + srm.size();
-                    } else {
-                        pass = Boolean.TRUE;
-                    }
-                }
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test a simple RFC 6961 server-side fetch
-    public static final TestCase testOcspMultiFetch = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            StatusResponseManager srm = new StatusResponseManager();
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            StatusRequest oReq = new OCSPStatusRequest();
-
-            try {
-                // Get OCSP responses for non-root certs in the chain
-                Map<X509Certificate, byte[]> responseMap = srm.get(
-                        StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
-                        TimeUnit.MILLISECONDS);
-
-                // There should be two entries in the returned map and
-                // two entries in the cache when the operation is complete.
-                if (responseMap.size() != 2) {
-                    message = "Incorrect number of responses: expected 2, got "
-                            + responseMap.size();
-                } else if (!responseMap.containsKey(sslCert) ||
-                        !responseMap.containsKey(intCert)) {
-                    message = "Response map keys are incorrect, expected " +
-                            sslCert.getSubjectX500Principal().toString() +
-                            " and " +
-                            intCert.getSubjectX500Principal().toString();
-                } else if (srm.size() != 2) {
-                    message = "Incorrect number of cache entries: " +
-                            "expected 2, got " + srm.size();
-                } else {
-                    pass = Boolean.TRUE;
-                }
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    // Test cache expiration
-    public static final TestCase testCacheExpiry = new TestCase() {
-        @Override
-        public Map.Entry<Boolean, String> runTest() {
-            // For this test, we will set the cache expiry to 5 seconds
-            System.setProperty("jdk.tls.stapling.cacheLifetime", "5");
-            StatusResponseManager srm = new StatusResponseManager();
-            Boolean pass = Boolean.FALSE;
-            String message = null;
-            StatusRequest oReq = new OCSPStatusRequest();
-
-            try {
-                // Get OCSP responses for non-root certs in the chain
-                srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000,
-                        TimeUnit.MILLISECONDS);
-
-                // There should be two entries in the returned map and
-                // two entries in the cache when the operation is complete.
-                if (srm.size() != 2) {
-                    message = "Incorrect number of responses: expected 2, got "
-                            + srm.size();
-                } else {
-                    // Next, wait for more than 5 seconds so the responses
-                    // in the SRM will expire.
-                    Thread.sleep(7000);
-                    if (srm.size() != 0) {
-                        message = "Incorrect number of responses: expected 0," +
-                                " got " + srm.size();
-                    } else {
-                        pass = Boolean.TRUE;
-                    }
-                }
-            } catch (Exception e) {
-                e.printStackTrace(System.out);
-                message = e.getClass().getName();
-            }
-
-            // Set the cache lifetime back to the default
-            System.setProperty("jdk.tls.stapling.cacheLifetime", "");
-            return new AbstractMap.SimpleEntry<>(pass, message);
-        }
-    };
-
-    /**
-     * Creates the PKI components necessary for this test, including
-     * Root CA, Intermediate CA and SSL server certificates, the keystores
-     * for each entity, a client trust store, and starts the OCSP responders.
-     */
-    private static void createPKI() throws Exception {
-        CertificateBuilder cbld = new CertificateBuilder();
-        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
-        keyGen.initialize(2048);
-        KeyStore.Builder keyStoreBuilder =
-                KeyStore.Builder.newInstance("PKCS12", null,
-                        new KeyStore.PasswordProtection(passwd.toCharArray()));
-
-        // Generate Root, IntCA, EE keys
-        KeyPair rootCaKP = keyGen.genKeyPair();
-        log("Generated Root CA KeyPair");
-        KeyPair intCaKP = keyGen.genKeyPair();
-        log("Generated Intermediate CA KeyPair");
-        KeyPair sslKP = keyGen.genKeyPair();
-        log("Generated SSL Cert KeyPair");
-
-        // Set up the Root CA Cert
-        cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany");
-        cbld.setPublicKey(rootCaKP.getPublic());
-        cbld.setSerialNumber(new BigInteger("1"));
-        // Make a 3 year validity starting from 60 days ago
-        long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
-        long end = start + TimeUnit.DAYS.toMillis(1085);
-        cbld.setValidity(new Date(start), new Date(end));
-        addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic());
-        addCommonCAExts(cbld);
-        // Make our Root CA Cert!
-        X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(),
-                "SHA256withRSA");
-        log("Root CA Created:\n" + certInfo(rootCert));
-
-        // Now build a keystore and add the keys and cert
-        rootKeystore = keyStoreBuilder.getKeyStore();
-        Certificate[] rootChain = {rootCert};
-        rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(),
-                passwd.toCharArray(), rootChain);
-
-        // Now fire up the OCSP responder
-        rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null);
-        rootOcsp.enableLog(ocspDebug);
-        rootOcsp.setNextUpdateInterval(3600);
-        rootOcsp.start();
-
-        // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) {
-            Thread.sleep(50);
-        }
-        if (!rootOcsp.isServerReady()) {
-            throw new RuntimeException("Server not ready yet");
-        }
-
-        rootOcspPort = rootOcsp.getPort();
-        String rootRespURI = "http://localhost:" + rootOcspPort;
-        log("Root OCSP Responder URI is " + rootRespURI);
-
-        // Now that we have the root keystore and OCSP responder we can
-        // create our intermediate CA.
-        cbld.reset();
-        cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany");
-        cbld.setPublicKey(intCaKP.getPublic());
-        cbld.setSerialNumber(new BigInteger("100"));
-        // Make a 2 year validity starting from 30 days ago
-        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30);
-        end = start + TimeUnit.DAYS.toMillis(730);
-        cbld.setValidity(new Date(start), new Date(end));
-        addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic());
-        addCommonCAExts(cbld);
-        cbld.addAIAExt(Collections.singletonList(rootRespURI));
-        // Make our Intermediate CA Cert!
-        X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(),
-                "SHA256withRSA");
-        log("Intermediate CA Created:\n" + certInfo(intCaCert));
-
-        // Provide intermediate CA cert revocation info to the Root CA
-        // OCSP responder.
-        Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo =
-            new HashMap<>();
-        revInfo.put(intCaCert.getSerialNumber(),
-                new SimpleOCSPServer.CertStatusInfo(
-                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
-        rootOcsp.updateStatusDb(revInfo);
-
-        // Now build a keystore and add the keys, chain and root cert as a TA
-        intKeystore = keyStoreBuilder.getKeyStore();
-        Certificate[] intChain = {intCaCert, rootCert};
-        intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(),
-                passwd.toCharArray(), intChain);
-        intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
-
-        // Now fire up the Intermediate CA OCSP responder
-        intOcsp = new SimpleOCSPServer(intKeystore, passwd,
-                INT_ALIAS, null);
-        intOcsp.enableLog(ocspDebug);
-        intOcsp.setNextUpdateInterval(3600);
-        intOcsp.start();
-
-        // Wait 5 seconds for server ready
-        for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) {
-            Thread.sleep(50);
-        }
-        if (!intOcsp.isServerReady()) {
-            throw new RuntimeException("Server not ready yet");
-        }
-
-        intOcspPort = intOcsp.getPort();
-        String intCaRespURI = "http://localhost:" + intOcspPort;
-        log("Intermediate CA OCSP Responder URI is " + intCaRespURI);
-
-        // Last but not least, let's make our SSLCert and add it to its own
-        // Keystore
-        cbld.reset();
-        cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany");
-        cbld.setPublicKey(sslKP.getPublic());
-        cbld.setSerialNumber(new BigInteger("4096"));
-        // Make a 1 year validity starting from 7 days ago
-        start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7);
-        end = start + TimeUnit.DAYS.toMillis(365);
-        cbld.setValidity(new Date(start), new Date(end));
-
-        // Add extensions
-        addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic());
-        boolean[] kuBits = {true, false, true, false, false, false,
-            false, false, false};
-        cbld.addKeyUsageExt(kuBits);
-        List<String> ekuOids = new ArrayList<>();
-        ekuOids.add("1.3.6.1.5.5.7.3.1");
-        ekuOids.add("1.3.6.1.5.5.7.3.2");
-        cbld.addExtendedKeyUsageExt(ekuOids);
-        cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost"));
-        cbld.addAIAExt(Collections.singletonList(intCaRespURI));
-        // Make our SSL Server Cert!
-        X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(),
-                "SHA256withRSA");
-        log("SSL Certificate Created:\n" + certInfo(sslCert));
-
-        // Provide SSL server cert revocation info to the Intermeidate CA
-        // OCSP responder.
-        revInfo = new HashMap<>();
-        revInfo.put(sslCert.getSerialNumber(),
-                new SimpleOCSPServer.CertStatusInfo(
-                        SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD));
-        intOcsp.updateStatusDb(revInfo);
-
-        // Now build a keystore and add the keys, chain and root cert as a TA
-        serverKeystore = keyStoreBuilder.getKeyStore();
-        Certificate[] sslChain = {sslCert, intCaCert, rootCert};
-        serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(),
-                passwd.toCharArray(), sslChain);
-        serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert);
-
-        // And finally a Trust Store for the client
-        trustStore = keyStoreBuilder.getKeyStore();
-        trustStore.setCertificateEntry(ROOT_ALIAS, rootCert);
-    }
-
-    private static void addCommonExts(CertificateBuilder cbld,
-            PublicKey subjKey, PublicKey authKey) throws IOException {
-        cbld.addSubjectKeyIdExt(subjKey);
-        cbld.addAuthorityKeyIdExt(authKey);
-    }
-
-    private static void addCommonCAExts(CertificateBuilder cbld)
-            throws IOException {
-        cbld.addBasicConstraintsExt(true, true, -1);
-        // Set key usage bits for digitalSignature, keyCertSign and cRLSign
-        boolean[] kuBitSettings = {true, false, false, false, false, true,
-            true, false, false};
-        cbld.addKeyUsageExt(kuBitSettings);
-    }
-
-    /**
-     * Helper routine that dumps only a few cert fields rather than
-     * the whole toString() output.
-     *
-     * @param cert An X509Certificate to be displayed
-     *
-     * @return The {@link String} output of the issuer, subject and
-     * serial number
-     */
-    private static String certInfo(X509Certificate cert) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Issuer: ").append(cert.getIssuerX500Principal()).
-                append("\n");
-        sb.append("Subject: ").append(cert.getSubjectX500Principal()).
-                append("\n");
-        sb.append("Serial: ").append(cert.getSerialNumber()).append("\n");
-        return sb.toString();
-    }
-
-    /**
-     * Log a message on stdout
-     *
-     * @param message The message to log
-     */
-    private static void log(String message) {
-        if (debug) {
-            System.out.println(message);
-        }
-    }
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestCase.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- * 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.util.Map;
-
-public interface TestCase {
-    Map.Entry<Boolean, String> runTest();
-}
--- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestUtils.java	Mon Jun 25 21:22:16 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- * 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.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Map;
-
-public class TestUtils {
-
-    // private constructor to prevent instantiation for this utility class
-    private TestUtils() {
-        throw new AssertionError();
-    }
-
-    public static void runTests(Map<String, TestCase> testList) {
-        int testNo = 0;
-        int numberFailed = 0;
-        Map.Entry<Boolean, String> result;
-
-        System.out.println("============ Tests ============");
-        for (String testName : testList.keySet()) {
-            System.out.println("Test " + ++testNo + ": " + testName);
-            result = testList.get(testName).runTest();
-            System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL"));
-            System.out.println(" " +
-                    (result.getValue() != null ? result.getValue() : ""));
-            System.out.println("-------------------------------------------");
-            if (!result.getKey()) {
-                numberFailed++;
-            }
-        }
-
-        System.out.println("End Results: " + (testList.size() - numberFailed) +
-                " Passed" + ", " + numberFailed + " Failed.");
-        if (numberFailed > 0) {
-            throw new RuntimeException(
-                    "One or more tests failed, see test output for details");
-        }
-    }
-
-    public static void dumpBytes(byte[] data) {
-        dumpBytes(ByteBuffer.wrap(data));
-    }
-
-    public static void dumpBytes(ByteBuffer data) {
-        int i = 0;
-
-        data.mark();
-        while (data.hasRemaining()) {
-            if (i % 16 == 0 && i != 0) {
-                System.out.print("\n");
-            }
-            System.out.print(String.format("%02X ", data.get()));
-            i++;
-        }
-        System.out.print("\n");
-        data.reset();
-    }
-
-    public static void valueCheck(byte[] array1, byte[] array2) {
-        if (!Arrays.equals(array1, array2)) {
-            throw new RuntimeException("Array mismatch");
-        }
-    }
-
-    // Compares a range of bytes at specific offsets in each array
-    public static void valueCheck(byte[] array1, byte[] array2, int skip1,
-            int skip2, int length) {
-        ByteBuffer buf1 = ByteBuffer.wrap(array1);
-        ByteBuffer buf2 = ByteBuffer.wrap(array2);
-
-        // Skip past however many bytes are requested in both buffers
-        buf1.position(skip1);
-        buf2.position(skip2);
-
-        // Reset the limits on each buffer to account for the length
-        buf1.limit(buf1.position() + length);
-        buf2.limit(buf2.position() + length);
-
-        if (!buf1.equals(buf2)) {
-            throw new RuntimeException("Array range mismatch");
-        }
-    }
-
-    // Concatenate 1 or more arrays
-    public static byte[] gatherBuffers(byte[]... arrays) {
-        int totalLength = 0;
-        for (byte[] ar : arrays) {
-            totalLength += ar != null ? ar.length : 0;
-        }
-
-        byte[] resultBuf = new byte[totalLength];
-        int offset = 0;
-        for (byte[] ar : arrays) {
-            if (ar != null) {
-                System.arraycopy(ar, 0, resultBuf, offset, ar.length);
-                offset += ar.length;
-            }
-        }
-        return resultBuf;
-    }
-}
--- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -357,6 +357,7 @@
 
         SSLSocket sslSocket =
             (SSLSocket)sslsf.createSocket("localhost", serverPort);
+        sslSocket.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
         try {
             InputStream sslIS = sslSocket.getInputStream();
             OutputStream sslOS = sslSocket.getOutputStream();
--- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/CertRequestOverflow.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/CertRequestOverflow.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -96,6 +96,9 @@
         SSLServerSocket sslServerSocket =
             (SSLServerSocket) sslssf.createServerSocket(serverPort);
         serverPort = sslServerSocket.getLocalPort();
+        if (debug) {
+            System.out.println("Server port is " + serverPort);
+        }
 
         // enable endpoint identification
         // ignore, we may test the feature when known how to parse client
@@ -153,6 +156,10 @@
         SSLSocketFactory sslsf = getContext(false).getSocketFactory();
         SSLSocket sslSocket = (SSLSocket)
             sslsf.createSocket("localhost", serverPort);
+        if (debug) {
+            System.out.println("Connected to: " +
+                    sslSocket.getRemoteSocketAddress());
+        }
 
         // enable endpoint identification
         SSLParameters params = sslSocket.getSSLParameters();
@@ -214,7 +221,7 @@
             tms = new TrustManager[] {clientTM};
         }
 
-        SSLContext ctx = SSLContext.getInstance("TLS");
+        SSLContext ctx = SSLContext.getInstance("TLSv1.2");
         ctx.init(kmf.getKeyManagers(), tms, null);
 
         return ctx;
@@ -243,17 +250,19 @@
             return serverChecked;
         }
 
-
+        @Override
         public void checkClientTrusted(X509Certificate chain[], String authType)
                 throws CertificateException {
             tm.checkClientTrusted(chain, authType);
         }
 
+        @Override
         public void checkServerTrusted(X509Certificate chain[], String authType)
                 throws CertificateException {
             tm.checkServerTrusted(chain, authType);
         }
 
+        @Override
         public X509Certificate[] getAcceptedIssuers() {
             // (hack code) increase the size of the returned array to make a
             // overflow CertificateRequest.
@@ -268,24 +277,28 @@
             return issuersList.toArray(issuers);
         }
 
+        @Override
         public void checkClientTrusted(X509Certificate[] chain, String authType,
                 Socket socket) throws CertificateException {
             clientChecked = true;
             tm.checkClientTrusted(chain, authType);
         }
 
+        @Override
         public void checkServerTrusted(X509Certificate[] chain, String authType,
                 Socket socket) throws CertificateException {
             serverChecked = true;
             tm.checkServerTrusted(chain, authType);
         }
 
+        @Override
         public void checkClientTrusted(X509Certificate[] chain, String authType,
             SSLEngine engine) throws CertificateException {
             clientChecked = true;
             tm.checkClientTrusted(chain, authType);
         }
 
+        @Override
         public void checkServerTrusted(X509Certificate[] chain, String authType,
             SSLEngine engine) throws CertificateException {
             serverChecked = true;
@@ -357,6 +370,7 @@
     void startServer(boolean newThread) throws Exception {
         if (newThread) {
             serverThread = new Thread() {
+                @Override
                 public void run() {
                     try {
                         doServerSide();
@@ -381,6 +395,7 @@
     void startClient(boolean newThread) throws Exception {
         if (newThread) {
             clientThread = new Thread() {
+                @Override
                 public void run() {
                     try {
                         doClientSide();
--- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/SelfIssuedCert.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/SelfIssuedCert.java	Mon Jun 25 13:41:39 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -209,6 +209,7 @@
 
         SSLSocket sslSocket =
             (SSLSocket)sslsf.createSocket("localhost", serverPort);
+        sslSocket.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
 
         InputStream sslIS = sslSocket.getInputStream();
         OutputStream sslOS = sslSocket.getOutputStream();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/internal/TEST.properties	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,3 @@
+modules = \
+    java.base/sun.security.ssl
+bootclasspath.dirs=.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/internal/TestRun.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8145255
+ * @run main/othervm java.base/sun.security.ssl.TestHkdf
+ * @summary HKDF for Sun JSSE
+ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ssl/internal/java.base/sun/security/ssl/TestHkdf.java	Mon Jun 25 13:41:39 2018 -0700
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * Actual test code for the package private HKDF implementation
+ */
+
+package sun.security.ssl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.security.NoSuchAlgorithmException;
+import java.security.InvalidKeyException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+public class TestHkdf {
+    public static class TestData {
+        public TestData(String name, String algStr, String ikmStr,
+                String saltStr, String infoStr, int oLen, String expPrkStr,
+                String expOkmStr) {
+            testName = Objects.requireNonNull(name);
+            algName = Objects.requireNonNull(algStr);
+            IKM = hex2bin(Objects.requireNonNull(ikmStr));
+            if ((outLen = oLen) <= 0) {
+                throw new IllegalArgumentException(
+                        "Output length must be greater than 0");
+            }
+            expectedPRK = hex2bin(Objects.requireNonNull(expPrkStr));
+            expectedOKM = hex2bin(Objects.requireNonNull(expOkmStr));
+
+            // Non-mandatory fields - may be null
+            salt = (saltStr != null) ? hex2bin(saltStr) : null;
+            info = (infoStr != null) ? hex2bin(infoStr) : null;
+        }
+
+        public final String testName;
+        public final String algName;
+        public final byte[] IKM;
+        public final byte[] salt;
+        public final byte[] info;
+        public final int outLen;
+        public final byte[] expectedPRK;
+        public final byte[] expectedOKM;
+    }
+
+    public static final List<TestData> testList = new LinkedList<TestData>() {{
+        add(new TestData("RFC 5689 Test Case 1", "SHA-256",
+            "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+            "000102030405060708090a0b0c",
+            "f0f1f2f3f4f5f6f7f8f9",
+            42,
+            "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5",
+            "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf" +
+            "34007208d5b887185865"));
+        add(new TestData("RFC 5689 Test Case 2", "SHA-256",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" +
+            "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" +
+            "404142434445464748494a4b4c4d4e4f",
+            "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" +
+            "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" +
+            "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+            "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" +
+            "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" +
+            "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+            82,
+            "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244",
+            "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c" +
+            "59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71" +
+            "cc30c58179ec3e87c14c01d5c1f3434f1d87"));
+        add(new TestData("RFC 5689 Test Case 3", "SHA-256",
+            "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+            null, null, 42,
+            "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04",
+            "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d" +
+            "9d201395faa4b61a96c8"));
+        add(new TestData("RFC 5689 Test Case 4", "SHA-1",
+            "0b0b0b0b0b0b0b0b0b0b0b",
+            "000102030405060708090a0b0c",
+            "f0f1f2f3f4f5f6f7f8f9",
+            42,
+            "9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243",
+            "085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2" +
+            "c22e422478d305f3f896"));
+        add(new TestData("RFC 5689 Test Case 5", "SHA-1",
+            "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" +
+            "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" +
+            "404142434445464748494a4b4c4d4e4f",
+            "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" +
+            "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" +
+            "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+            "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" +
+            "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" +
+            "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+            82,
+            "8adae09a2a307059478d309b26c4115a224cfaf6",
+            "0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe" +
+            "8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e" +
+            "927336d0441f4c4300e2cff0d0900b52d3b4"));
+        add(new TestData("RFC 5689 Test Case 6", "SHA-1",
+            "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+            null, null, 42,
+            "da8c8a73c7fa77288ec6f5e7c297786aa0d32d01",
+            "0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0" +
+            "ea00033de03984d34918"));
+        add(new TestData("RFC 5689 Test Case 7", "SHA-1",
+            "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
+            null, null, 42,
+            "2adccada18779e7c2077ad2eb19d3f3e731385dd",
+            "2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5" +
+            "673a081d70cce7acfc48"));
+    }};
+
+    public static void main(String args[]) throws Exception {
+        int testsPassed = 0;
+
+        int testNo = 0;
+        for (TestData test : testList) {
+            System.out.println("*** Test " + ++testNo + ": " +
+                    test.testName);
+            if (runVector(test)) {
+                testsPassed++;
+            }
+        }
+
+        System.out.println("Total tests: " + testList.size() +
+                ", Passed: " + testsPassed + ", Failed: " +
+                (testList.size() - testsPassed));
+        if (testsPassed != testList.size()) {
+            throw new RuntimeException("One or more tests failed.  " +
+                    "Check output for details");
+        }
+    }
+
+    private static boolean runVector(TestData testData)
+            throws NoSuchAlgorithmException, InvalidKeyException {
+        String kdfName, prfName;
+        HKDF kdfHkdf;
+        boolean result = true;
+        SecretKey actualPRK;
+        SecretKey actualOKM;
+        byte[] deriveData;
+
+        // Get an instance of the HKDF derivation engine
+        kdfHkdf = new HKDF(testData.algName);
+
+        // Set up the input keying material and the salt as a secret
+        SecretKey ikmKey = new SecretKeySpec(testData.IKM, "HKDF-IKM");
+        SecretKey saltKey = (testData.salt != null) ?
+                new SecretKeySpec(testData.salt, "HKDF-Salt") : null;
+
+        // *** HKDF-Extract-only testing
+        System.out.println("* HKDF-Extract-Only:");
+        actualPRK = kdfHkdf.extract(saltKey, ikmKey, "HKDF-PRK");
+        result &= compareKeyAndData(actualPRK, testData.expectedPRK);
+
+        // *** HKDF Expand-Only testing
+        // For these tests, we'll use the actualPRK as the input key
+        System.out.println("* HKDF-Expand-Only:");
+        actualOKM = kdfHkdf.expand(actualPRK, testData.info, testData.outLen,
+                "HKDF-OKM");
+        result &= compareKeyAndData(actualOKM, testData.expectedOKM);
+
+        // *** HKDF Extract-then-Expand testing
+        // System.out.println("* HKDF-Extract-then-Expand:");
+        // actualOKM = kdfHkdf.extractExpand(ikmKey, saltKey, testData.info,
+        //         testData.outLen, "HKDF-OKM2");
+        // result &= compareKeyAndData(actualOKM, testData.expectedOKM);
+
+        return result;
+    }
+
+    /**
+     * Compare actual key output from HKDF against an expected output value.
+     *
+     * @param outKey the KDF output in key form
+     * @param expectedOut the expected value
+     *
+     * @return true if the underlying data for outKey, outData and
+     * expectedOut are the same.
+     */
+    private static boolean compareKeyAndData(SecretKey outKey,
+            byte[] expectedOut) {
+        boolean result = false;
+
+        if (Arrays.equals(outKey.getEncoded(), expectedOut)) {
+            System.out.println("\t* Key output: Pass");
+            result = true;
+        } else {
+            System.out.println("\t* Key output: FAIL");
+            System.out.println("Expected:\n" +
+                    dumpHexBytes(expectedOut, 16, "\n", " "));
+            System.out.println("Actual:\n" +
+                    dumpHexBytes(outKey.getEncoded(), 16, "\n", " "));
+            System.out.println();
+        }
+
+        return result;
+    }
+
+    /**
+     * Dump the hex bytes of a buffer into string form.
+     *
+     * @param data The array of bytes to dump to stdout.
+     * @param itemsPerLine The number of bytes to display per line
+     *      if the {@code lineDelim} character is blank then all bytes
+     *      will be printed on a single line.
+     * @param lineDelim The delimiter between lines
+     * @param itemDelim The delimiter between bytes
+     *
+     * @return The hexdump of the byte array
+     */
+    private static String dumpHexBytes(byte[] data, int itemsPerLine,
+            String lineDelim, String itemDelim) {
+        StringBuilder sb = new StringBuilder();
+        if (data != null) {
+            for (int i = 0; i < data.length; i++) {
+                if (i % itemsPerLine == 0 && i != 0) {
+                    sb.append(lineDelim);
+                }
+                sb.append(String.format("%02X", data[i])).append(itemDelim);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private static byte[] hex2bin(String hex) {
+        int i;
+        int len = hex.length();
+        byte[] data = new byte [len / 2];
+        for (i = 0; i < len; i += 2) {
+            data[i / 2] = (byte)((Character.digit(hex.charAt(i), 16) << 4) +
+                    Character.digit(hex.charAt(i + 1), 16));
+        }
+        return data;
+    }
+}
--- a/test/jdk/sun/security/tools/keytool/PrintSSL.java	Mon Jun 25 21:22:16 2018 +0300
+++ b/test/jdk/sun/security/tools/keytool/PrintSSL.java	Mon Jun 25 13:41:39 2018 -0700
@@ -53,7 +53,7 @@
         // make sure that "-printcert" works with weak algorithms
         OutputAnalyzer out = SecurityTools.keytool("-genkeypair "
                 + "-keystore keystore -storepass passphrase "
-                + "-keypass passphrase -keyalg rsa -keysize 512 "
+                + "-keypass passphrase -keyalg rsa -keysize 1024 "
                 + "-sigalg MD5withRSA -alias rsa_alias -dname CN=Server");
         System.out.println(out.getOutput());
         out.shouldHaveExitValue(0);
@@ -92,7 +92,7 @@
             System.setProperty("javax.net.ssl.keyStorePassword", "passphrase");
             System.setProperty("javax.net.ssl.keyStore", "keystore");
             SSLServerSocketFactory sslssf =
-                    (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+                (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
             try (ServerSocket server = sslssf.createServerSocket(0)) {
                 this.serverPort = server.getLocalPort();
                 System.out.printf("%nServer started on: %s%n", getServerPort());
@@ -110,5 +110,5 @@
         }
 
     }
+}
 
-}