changeset 50768:68fa3d4026ea

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>
author xuelei
date Mon, 25 Jun 2018 13:41:39 -0700
parents 356eaea05bf0
children 1bf8f9840705
files 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
diffstat 272 files changed, 44884 insertions(+), 33068 deletions(-) [+]
line wrap: on
line diff
--- 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);
+            }
+        }
+