changeset 14141:80cdee1da353

8245474: Add TLS_KRB5 cipher suites support according to RFC-2712 Reviewed-by: aph Contributed-by: Alexey Bakhtin <alexey@azul.com>, Martin Balao <mbalao@redhat.com>
author mbalao
date Tue, 25 Aug 2020 15:48:30 -0300
parents 896bbc5499ff
children ad16e2ef963c
files make/data/checkdeps/refs.allowed src/share/classes/sun/security/ssl/CipherSuite.java src/share/classes/sun/security/ssl/ClientHello.java src/share/classes/sun/security/ssl/JsseJce.java src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java src/share/classes/sun/security/ssl/Krb5Helper.java src/share/classes/sun/security/ssl/Krb5Proxy.java src/share/classes/sun/security/ssl/KrbClientKeyExchange.java src/share/classes/sun/security/ssl/KrbClientKeyExchangeHelper.java src/share/classes/sun/security/ssl/KrbKeyExchange.java src/share/classes/sun/security/ssl/SSLKeyExchange.java src/share/classes/sun/security/ssl/SSLSessionImpl.java src/share/classes/sun/security/ssl/ServerHello.java src/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java src/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java src/share/classes/sun/security/ssl/krb5/KrbClientKeyExchangeHelperImpl.java
diffstat 17 files changed, 1455 insertions(+), 939 deletions(-) [+]
line wrap: on
line diff
--- a/make/data/checkdeps/refs.allowed	Tue Aug 25 08:30:00 2020 -0700
+++ b/make/data/checkdeps/refs.allowed	Tue Aug 25 15:48:30 2020 -0300
@@ -10,23 +10,21 @@
 # then this means that there are references to Kerberos types that do not
 # exist. These references are harmless.
 #
-javax.security.auth.kerberos.KerberosKey=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
-javax.security.auth.kerberos.KerberosPrincipal=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
-javax.security.auth.kerberos.KerberosTicket=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
+javax.security.auth.kerberos.KerberosKey=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+javax.security.auth.kerberos.KerberosPrincipal=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+javax.security.auth.kerberos.KerberosTicket=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
 javax.security.auth.kerberos.KeyTab=sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
-javax.security.auth.kerberos.ServicePermission=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
-sun.security.jgss.GSSCaller=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
-sun.security.jgss.krb5.Krb5Util=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
-sun.security.jgss.krb5.ServiceCreds=sun.security.ssl.krb5.Krb5ProxyImpl,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.EncryptedData= sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.EncryptionKey=sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.internal.crypto.KeyUsage=sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.internal.EncTicketPart=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.internal.Krb5=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.internal.Ticket=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.KrbException=sun.security.ssl.krb5.KerberosPreMasterSecret,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.PrincipalName=sun.security.ssl.krb5.Krb5ProxyImpl,sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
-sun.security.krb5.Realm=sun.security.ssl.krb5.KerberosClientKeyExchangeImpl,compact1,compact2
+javax.security.auth.kerberos.ServicePermission=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
+sun.security.jgss.GSSCaller=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
+sun.security.jgss.krb5.Krb5Util=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
+sun.security.jgss.krb5.ServiceCreds=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
+sun.security.krb5.EncryptedData=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+sun.security.krb5.EncryptionKey=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+sun.security.krb5.internal.crypto.KeyUsage=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+sun.security.krb5.internal.EncTicketPart=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+sun.security.krb5.internal.Ticket=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+sun.security.krb5.KrbException=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,compact1,compact2
+sun.security.krb5.PrincipalName=sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl,sun.security.ssl.krb5.Krb5ProxyImpl,compact1,compact2
 
 # Residual references to java.beans.
 # The RemoveMethods tool does not yet purge the constant pool.
--- a/src/share/classes/sun/security/ssl/CipherSuite.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/CipherSuite.java	Tue Aug 25 15:48:30 2020 -0300
@@ -478,6 +478,49 @@
             ProtocolVersion.PROTOCOLS_TO_12,
             K_RSA, B_NULL, M_MD5, H_SHA256),
 
+
+    // Supported Kerberos ciphersuites from RFC2712
+    TLS_KRB5_WITH_3DES_EDE_CBC_SHA(
+            0x001f, false, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_KRB5, B_3DES, M_SHA, H_SHA256),
+    TLS_KRB5_WITH_3DES_EDE_CBC_MD5(
+            0x0023, false, "TLS_KRB5_WITH_3DES_EDE_CBC_MD5", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_KRB5, B_3DES, M_MD5, H_SHA256),
+    TLS_KRB5_WITH_RC4_128_SHA(
+            0x0020, false, "TLS_KRB5_WITH_RC4_128_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_KRB5, B_RC4_128, M_SHA, H_SHA256),
+    TLS_KRB5_WITH_RC4_128_MD5(
+            0x0024, false, "TLS_KRB5_WITH_RC4_128_MD5", "",
+            ProtocolVersion.PROTOCOLS_TO_12,
+            K_KRB5, B_RC4_128, M_MD5, H_SHA256),
+    TLS_KRB5_WITH_DES_CBC_SHA(
+            0x001e, false, "TLS_KRB5_WITH_DES_CBC_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_11,
+            K_KRB5, B_DES, M_SHA, H_SHA256),
+    TLS_KRB5_WITH_DES_CBC_MD5(
+            0x0022, false, "TLS_KRB5_WITH_DES_CBC_MD5", "",
+            ProtocolVersion.PROTOCOLS_TO_11,
+            K_KRB5, B_DES, M_MD5, H_SHA256),
+    TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA(
+            0x0026, false, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_KRB5_EXPORT, B_DES_40, M_SHA, H_SHA256),
+    TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5(
+            0x0029, false, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", "",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_KRB5_EXPORT, B_DES_40, M_MD5, H_SHA256),
+    TLS_KRB5_EXPORT_WITH_RC4_40_SHA(
+            0x0028, false, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_KRB5_EXPORT, B_RC4_40, M_SHA, H_SHA256),
+    TLS_KRB5_EXPORT_WITH_RC4_40_MD5(
+            0x002B, false, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", "",
+            ProtocolVersion.PROTOCOLS_TO_10,
+            K_KRB5_EXPORT, B_RC4_40, M_MD5, H_SHA256),
+
     // Definition of the CipherSuites that are not supported but the names
     // are known.
     TLS_CHACHA20_POLY1305_SHA256(                    // TLS 1.3
@@ -516,20 +559,10 @@
     CS_FEFF("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",           0xfeff),
 
     // Unsupported Kerberos cipher suites from RFC 2712
-    CS_001E("TLS_KRB5_WITH_DES_CBC_SHA",                    0x001E),
-    CS_001F("TLS_KRB5_WITH_3DES_EDE_CBC_SHA",               0x001F),
-    CS_0020("TLS_KRB5_WITH_RC4_128_SHA",                    0x0020),
     CS_0021("TLS_KRB5_WITH_IDEA_CBC_SHA",                   0x0021),
-    CS_0022("TLS_KRB5_WITH_DES_CBC_MD5",                    0x0022),
-    CS_0023("TLS_KRB5_WITH_3DES_EDE_CBC_MD5",               0x0023),
-    CS_0024("TLS_KRB5_WITH_RC4_128_MD5",                    0x0024),
     CS_0025("TLS_KRB5_WITH_IDEA_CBC_MD5",                   0x0025),
-    CS_0026("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",          0x0026),
     CS_0027("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",          0x0027),
-    CS_0028("TLS_KRB5_EXPORT_WITH_RC4_40_SHA",              0x0028),
-    CS_0029("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",          0x0029),
     CS_002A("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",          0x002a),
-    CS_002B("TLS_KRB5_EXPORT_WITH_RC4_40_MD5",              0x002B),
 
     // Unsupported cipher suites from RFC 4162
     CS_0096("TLS_RSA_WITH_SEED_CBC_SHA",                    0x0096),
@@ -1035,6 +1068,10 @@
         K_ECDHE_RSA     ("ECDHE_RSA",      true,  false,  NAMED_GROUP_ECDHE),
         K_ECDH_ANON     ("ECDH_anon",      true,  true,   NAMED_GROUP_ECDHE),
 
+        // Kerberos cipher suites
+        K_KRB5          ("KRB5",           true,  false,  NAMED_GROUP_NONE),
+        K_KRB5_EXPORT   ("KRB5_EXPORT",    true,  false,  NAMED_GROUP_NONE),
+
         // renegotiation protection request signaling cipher suite
         K_SCSV          ("SCSV",           true,  true,   NAMED_GROUP_NONE);
 
@@ -1065,6 +1102,8 @@
 
             if (groupType == NAMED_GROUP_ECDHE) {
                 return (allowed && JsseJce.isEcAvailable());
+            } else if (name.startsWith("KRB")) {
+                return (allowed && JsseJce.isKerberosAvailable());
             } else {
                 return allowed;
             }
--- a/src/share/classes/sun/security/ssl/ClientHello.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/ClientHello.java	Tue Aug 25 15:48:30 2020 -0300
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,6 +28,10 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.security.SecureRandom;
 import java.security.cert.X509Certificate;
 import java.text.MessageFormat;
@@ -40,6 +45,10 @@
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLProtocolException;
+import javax.security.auth.Subject;
+
+import static sun.security.ssl.CipherSuite.KeyExchange.K_KRB5;
+import static sun.security.ssl.CipherSuite.KeyExchange.K_KRB5_EXPORT;
 import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
 import sun.security.ssl.SSLHandshake.HandshakeMessage;
 import sun.security.ssl.SupportedVersionsExtension.CHSupportedVersionsSpec;
@@ -887,6 +896,58 @@
                     }
                 }
 
+                // Validate KRB5 cipher suites
+                if (resumingSession) {
+                    CipherSuite suite = previous.getSuite();
+                    if (suite.keyExchange == K_KRB5 ||
+                            suite.keyExchange == K_KRB5_EXPORT) {
+                        Principal localPrincipal = previous.getLocalPrincipal();
+
+                        Subject subject = null;
+                        try {
+                            subject = AccessController.doPrivileged(
+                                    new PrivilegedExceptionAction<Subject>() {
+                                        @Override
+                                        public Subject run() throws Exception {
+                                            return Krb5Helper.getServerSubject(
+                                                    shc.conContext.acc);
+                                        }});
+                        } catch (PrivilegedActionException e) {
+                            subject = null;
+                            if (SSLLogger.isOn &&
+                                    SSLLogger.isOn("ssl,handshake,verbose")) {
+                                SSLLogger.finest("Attempt to obtain" +
+                                        " subject failed!");
+                            }
+                        }
+
+                        if (subject != null) {
+                            // Eliminate dependency on KerberosPrincipal
+                            if (Krb5Helper.isRelated(subject, localPrincipal)) {
+                                if (SSLLogger.isOn &&
+                                        SSLLogger.isOn("ssl,handshake,verbose"))
+                                    SSLLogger.finest("Subject can" +
+                                            " provide creds for princ");
+                            } else {
+                                resumingSession = false;
+                                if (SSLLogger.isOn &&
+                                        SSLLogger.isOn("ssl,handshake,verbose"))
+                                    SSLLogger.finest("Subject cannot" +
+                                            " provide creds for princ");
+                            }
+                        } else {
+                            resumingSession = false;
+                            if (SSLLogger.isOn &&
+                                    SSLLogger.isOn("ssl,handshake,verbose"))
+                                SSLLogger.finest("Kerberos credentials are" +
+                                        " not present in the current Subject;" +
+                                        " check if " +
+                                        " javax.security.auth.useSubjectCredsOnly" +
+                                        " system property has been set to false");
+                        }
+                    }
+                }
+
                 // ensure that the endpoint identification algorithm matches the
                 // one in the session
                 String identityAlg = shc.sslConfig.identificationProtocol;
--- a/src/share/classes/sun/security/ssl/JsseJce.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/JsseJce.java	Tue Aug 25 15:48:30 2020 -0300
@@ -49,6 +49,30 @@
 
     private static final ProviderList fipsProviderList;
 
+    // Flag indicating whether Kerberos crypto is available.
+    // If true, then all the Kerberos-based crypto we need is available.
+    private final static boolean kerberosAvailable;
+    static {
+        boolean temp;
+        try {
+            AccessController.doPrivileged(
+                new PrivilegedExceptionAction<Void>() {
+                    @Override
+                    public Void run() throws Exception {
+                        // Test for Kerberos using the bootstrap class loader
+                        Class.forName("sun.security.krb5.PrincipalName", true,
+                                null);
+                        return null;
+                    }
+                });
+            temp = true;
+
+        } catch (Exception e) {
+            temp = false;
+        }
+        kerberosAvailable = temp;
+    }
+
     static {
         // force FIPS flag initialization
         // Because isFIPS() is synchronized and cryptoProvider is not modified
@@ -175,6 +199,10 @@
         return EcAvailability.isAvailable;
     }
 
+    static boolean isKerberosAvailable() {
+        return kerberosAvailable;
+    }
+
     /**
      * Return an JCE cipher implementation for the specified algorithm.
      */
--- a/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java	Tue Aug 25 08:30:00 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.SecureRandom;
-import javax.crypto.SecretKey;
-
-/**
- * A helper class that calls the KerberosClientKeyExchange implementation.
- */
-public class KerberosClientKeyExchange extends HandshakeMessage {
-
-    private static final String IMPL_CLASS =
-        "sun.security.ssl.krb5.KerberosClientKeyExchangeImpl";
-
-    private static final Class<?> implClass = AccessController.doPrivileged(
-            new PrivilegedAction<Class<?>>() {
-                @Override
-                public Class<?> run() {
-                    try {
-                        return Class.forName(IMPL_CLASS, true, null);
-                    } catch (ClassNotFoundException cnf) {
-                        return null;
-                    }
-                }
-            }
-        );
-    private final KerberosClientKeyExchange impl = createImpl();
-
-    private KerberosClientKeyExchange createImpl() {
-        if (implClass != null &&
-                getClass() == KerberosClientKeyExchange.class) {
-            try {
-                return (KerberosClientKeyExchange)implClass.newInstance();
-            } catch (InstantiationException e) {
-                throw new AssertionError(e);
-            } catch (IllegalAccessException e) {
-                throw new AssertionError(e);
-            }
-        }
-        return null;
-    }
-
-    // This constructor will be called when constructing an instance of its
-    // subclass -- KerberosClientKeyExchangeImpl.  Please won't check the
-    // value of impl variable in this constructor.
-    protected KerberosClientKeyExchange() {
-        // please won't check the value of impl variable
-    }
-
-    public KerberosClientKeyExchange(String serverName,
-        AccessControlContext acc, ProtocolVersion protocolVersion,
-        SecureRandom rand) throws IOException {
-
-        if (impl != null) {
-            init(serverName, acc, protocolVersion, rand);
-        } else {
-            throw new IllegalStateException("Kerberos is unavailable");
-        }
-    }
-
-    public KerberosClientKeyExchange(ProtocolVersion protocolVersion,
-            ProtocolVersion clientVersion, SecureRandom rand,
-            HandshakeInStream input, AccessControlContext acc,
-            Object serverKeys) throws IOException {
-
-        if (impl != null) {
-            init(protocolVersion, clientVersion, rand, input, acc, serverKeys);
-        } else {
-            throw new IllegalStateException("Kerberos is unavailable");
-        }
-    }
-
-    @Override
-    int messageType() {
-        return ht_client_key_exchange;
-    }
-
-    @Override
-    public int messageLength() {
-        return impl.messageLength();
-    }
-
-    @Override
-    public void send(HandshakeOutStream s) throws IOException {
-        impl.send(s);
-    }
-
-    @Override
-    public void print(PrintStream p) throws IOException {
-        impl.print(p);
-    }
-
-    public void init(String serverName,
-        AccessControlContext acc, ProtocolVersion protocolVersion,
-        SecureRandom rand) throws IOException {
-
-        if (impl != null) {
-            impl.init(serverName, acc, protocolVersion, rand);
-        }
-    }
-
-    public void init(ProtocolVersion protocolVersion,
-            ProtocolVersion clientVersion, SecureRandom rand,
-            HandshakeInStream input, AccessControlContext acc,
-            Object ServiceCreds) throws IOException {
-
-        if (impl != null) {
-            impl.init(protocolVersion, clientVersion,
-                                    rand, input, acc, ServiceCreds);
-        }
-    }
-
-    public byte[] getUnencryptedPreMasterSecret() {
-        return impl.getUnencryptedPreMasterSecret();
-    }
-
-    public Principal getPeerPrincipal(){
-        return impl.getPeerPrincipal();
-    }
-
-    public Principal getLocalPrincipal(){
-        return impl.getLocalPrincipal();
-    }
-}
--- a/src/share/classes/sun/security/ssl/Krb5Helper.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/Krb5Helper.java	Tue Aug 25 15:48:30 2020 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -61,16 +61,9 @@
                 }
             }});
 
-    /**
-     * Returns true if Kerberos is available.
-     */
-    public static boolean isAvailable() {
-        return proxy != null;
-    }
-
     private static void ensureAvailable() {
         if (proxy == null)
-            throw new AssertionError("Kerberos should have been available");
+            throw new AssertionError("Kerberos should be available");
     }
 
     /**
--- a/src/share/classes/sun/security/ssl/Krb5Proxy.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/Krb5Proxy.java	Tue Aug 25 15:48:30 2020 -0300
@@ -28,7 +28,6 @@
 import java.security.AccessControlContext;
 import java.security.Permission;
 import java.security.Principal;
-import javax.crypto.SecretKey;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginException;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/KrbClientKeyExchange.java	Tue Aug 25 15:48:30 2020 -0300
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
+ * Copyright (c) 2020, Red Hat, Inc.
+ * 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.lang.reflect.InvocationTargetException;
+import java.nio.ByteBuffer;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.StandardConstants;
+
+import sun.misc.HexDumpEncoder;
+import sun.security.ssl.KrbKeyExchange.KrbPremasterSecret;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "ClientKeyExchange" handshake message.
+ */
+final class KrbClientKeyExchange {
+    static final SSLConsumer krbHandshakeConsumer =
+            new KrbClientKeyExchangeConsumer();
+    static final HandshakeProducer krbHandshakeProducer =
+            new KrbClientKeyExchangeProducer();
+
+    /**
+     * The KRB5 ClientKeyExchange handshake message (CLIENT -> SERVER).
+     * It holds the Kerberos ticket and the encrypted pre-master secret
+     * encrypted with the session key sealed in the ticket.
+     *
+     * From RFC 2712:
+     *
+     *  struct
+     *  {
+     *    opaque Ticket;
+     *    opaque authenticator;            // optional, ignored
+     *    opaque EncryptedPreMasterSecret; // encrypted with the session key
+     *                                     // sealed in the ticket
+     *  } KerberosWrapper;
+     *
+     */
+    private static final
+            class KrbClientKeyExchangeMessage extends HandshakeMessage {
+
+        private static final String KRB5_CLASS_NAME =
+                "sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl";
+
+        private static final Class<?> krb5Class = AccessController.doPrivileged(
+                new PrivilegedAction<Class<?>>() {
+                    @Override
+                    public Class<?> run() {
+                        try {
+                            return Class.forName(KRB5_CLASS_NAME, true, null);
+                        } catch (ClassNotFoundException cnf) {
+                            return null;
+                        }
+                    }
+                });
+
+        private static KrbClientKeyExchangeHelper newKrb5Instance() {
+            if (krb5Class != null) {
+                try {
+                    return (KrbClientKeyExchangeHelper)krb5Class.
+                            getDeclaredConstructor().newInstance();
+                } catch (InstantiationException | IllegalAccessException |
+                        NoSuchMethodException | InvocationTargetException e) {
+                    throw new AssertionError(e);
+                }
+            }
+            return null;
+        }
+
+        private final KrbClientKeyExchangeHelper krb5Helper;
+
+        private KrbClientKeyExchangeMessage(HandshakeContext context) {
+            super(context);
+            if ((krb5Helper = newKrb5Instance()) == null)
+                throw new IllegalStateException("Kerberos is unavailable");
+        }
+
+        KrbClientKeyExchangeMessage(HandshakeContext context,
+                byte[] preMaster, String serverName,
+                AccessControlContext acc) throws IOException {
+            this(context);
+            krb5Helper.init(preMaster, serverName, acc);
+        }
+
+        KrbClientKeyExchangeMessage(HandshakeContext context,
+                ByteBuffer message, Object serverKeys,
+                AccessControlContext acc) throws IOException {
+            this(context);
+            byte[] encodedTicket = Record.getBytes16(message);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("encoded Kerberos service ticket",
+                        encodedTicket);
+            }
+            // Read and ignore the authenticator
+            Record.getBytes16(message);
+            byte[] encryptedPreMasterSecret = Record.getBytes16(message);
+            if (encryptedPreMasterSecret != null && SSLLogger.isOn &&
+                    SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("encrypted Kerberos pre-master secret",
+                        encryptedPreMasterSecret);
+            }
+            krb5Helper.init(encodedTicket, encryptedPreMasterSecret,
+                    serverKeys, acc);
+        }
+
+        @Override
+        SSLHandshake handshakeType() {
+            return SSLHandshake.CLIENT_KEY_EXCHANGE;
+        }
+
+        @Override
+        int messageLength() {
+            return (6 + krb5Helper.getEncodedTicket().length +
+                    krb5Helper.getEncryptedPreMasterSecret().length);
+        }
+
+        @Override
+        void send(HandshakeOutStream hos) throws IOException {
+            hos.putBytes16(krb5Helper.getEncodedTicket());
+            hos.putBytes16(null); // XXX no authenticator
+            hos.putBytes16(krb5Helper.getEncryptedPreMasterSecret());
+        }
+
+        byte[] getPlainPreMasterSecret() {
+            return krb5Helper.getPlainPreMasterSecret();
+        }
+
+        Principal getPeerPrincipal() {
+            return krb5Helper.getPeerPrincipal();
+        }
+
+        Principal getLocalPrincipal() {
+            return krb5Helper.getLocalPrincipal();
+        }
+
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                    "\"KRB5 ClientKeyExchange\": '{'\n" +
+                    "  \"ticket\": '{'\n" +
+                    "{0}\n" +
+                    "  '}'\n" +
+                    "  \"pre-master\": '{'\n" +
+                    "    \"plain\": '{'\n" +
+                    "{1}\n" +
+                    "    '}'\n" +
+                    "    \"encrypted\": '{'\n" +
+                    "{2}\n" +
+                    "    '}'\n" +
+                    "  '}'\n" +
+                    "'}'",
+                    Locale.ENGLISH);
+
+            HexDumpEncoder hexEncoder = new HexDumpEncoder();
+            Object[] messageFields = {
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(
+                                krb5Helper.getEncodedTicket()), "  "),
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(
+                                krb5Helper.getPlainPreMasterSecret()), "      "),
+                Utilities.indent(
+                        hexEncoder.encodeBuffer(
+                                krb5Helper.getEncryptedPreMasterSecret()), "      "),
+            };
+            return messageFormat.format(messageFields);
+        }
+    }
+
+    /**
+     * The KRB5 "ClientKeyExchange" handshake message producer.
+     */
+    private static final
+            class KrbClientKeyExchangeProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private KrbClientKeyExchangeProducer() {
+            // blank
+        }
+
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // This happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            KrbClientKeyExchangeMessage kerberosMsg = null;
+            String hostName = null;
+            if (chc.negotiatedServerName != null) {
+                if (chc.negotiatedServerName.getType() ==
+                        StandardConstants.SNI_HOST_NAME) {
+                    SNIHostName sniHostName = null;
+                    if (chc.negotiatedServerName instanceof SNIHostName) {
+                        sniHostName = (SNIHostName) chc.negotiatedServerName;
+                    } else {
+                        try {
+                            sniHostName = new SNIHostName(
+                                    chc.negotiatedServerName.getEncoded());
+                        } catch (IllegalArgumentException iae) {
+                            // unlikely to happen, just in case ...
+                        }
+                    }
+                    if (sniHostName != null)
+                        hostName = sniHostName.getAsciiName();
+                }
+            } else {
+                hostName = chc.handshakeSession.getPeerHost();
+            }
+            try {
+                KrbPremasterSecret premasterSecret =
+                        KrbPremasterSecret.createPremasterSecret(
+                                chc.negotiatedProtocol,
+                                chc.sslContext.getSecureRandom());
+                kerberosMsg = new KrbClientKeyExchangeMessage(chc,
+                        premasterSecret.preMaster, hostName,
+                        chc.conContext.acc);
+                chc.handshakePossessions.add(premasterSecret);
+            } catch (IOException e) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                            "Error generating KRB premaster secret." +
+                            " Hostname: " + hostName + " - Negotiated" +
+                            " server name: " + chc.negotiatedServerName);
+                }
+                throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                        "Cannot generate KRB premaster secret", e);
+            }
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Produced KRB5 ClientKeyExchange handshake message",
+                    kerberosMsg);
+            }
+
+            // Record the principals involved in the exchange
+            chc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
+            chc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
+
+            // Output the handshake message.
+            kerberosMsg.write(chc.handshakeOutput);
+            chc.handshakeOutput.flush();
+
+            // update the states
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    chc.negotiatedCipherSuite.keyExchange,
+                    chc.negotiatedProtocol);
+            if (ke == null) {
+                // unlikely
+                throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            } else {
+                SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
+                SecretKey masterSecret =
+                        masterKD.deriveKey("MasterSecret", null);
+
+                chc.handshakeSession.setMasterSecret(masterSecret);
+
+                SSLTrafficKeyDerivation kd =
+                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
+                if (kd == null) {
+                    // unlikely
+                    throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                                    chc.negotiatedProtocol);
+                } else {
+                    chc.handshakeKeyDerivation =
+                            kd.createKeyDerivation(chc, masterSecret);
+                }
+            }
+
+            // The handshake message has been delivered.
+            return null;
+        }
+    }
+
+    /**
+     * The KRB5 "ClientKeyExchange" handshake message consumer.
+     */
+    private static final
+            class KrbClientKeyExchangeConsumer implements SSLConsumer {
+        // Prevent instantiation of this class.
+        private KrbClientKeyExchangeConsumer() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                ByteBuffer message) throws IOException {
+            // The consuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            Object serviceCreds = null;
+            for (SSLPossession possession : shc.handshakePossessions) {
+                if (possession instanceof KrbKeyExchange.KrbServiceCreds) {
+                    serviceCreds = ((KrbKeyExchange.KrbServiceCreds) possession)
+                            .serviceCreds;
+                    break;
+                }
+            }
+            if (serviceCreds == null) {  // unlikely
+                throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
+                    "No Kerberos service credentials for KRB Client Key Exchange");
+            }
+
+            KrbClientKeyExchangeMessage kerberosMsg =
+                    new KrbClientKeyExchangeMessage(shc,
+                            message, serviceCreds, shc.conContext.acc);
+            KrbPremasterSecret premasterSecret = KrbPremasterSecret.decode(
+                    shc.negotiatedProtocol,
+                    ProtocolVersion.valueOf(shc.clientHelloVersion),
+                    kerberosMsg.getPlainPreMasterSecret(),
+                    shc.sslContext.getSecureRandom());
+            shc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
+            shc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
+            shc.handshakeCredentials.add(premasterSecret);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine(
+                    "Consuming KRB5 ClientKeyExchange handshake message",
+                    kerberosMsg);
+            }
+
+            // update the states
+            SSLKeyExchange ke = SSLKeyExchange.valueOf(
+                    shc.negotiatedCipherSuite.keyExchange,
+                    shc.negotiatedProtocol);
+            if (ke == null) {   // unlikely
+                throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                        "Not supported key exchange type");
+            } else {
+                SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
+                SecretKey masterSecret =
+                        masterKD.deriveKey("MasterSecret", null);
+
+                // update the states
+                shc.handshakeSession.setMasterSecret(masterSecret);
+                SSLTrafficKeyDerivation kd =
+                        SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
+                if (kd == null) {       // unlikely
+                    throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
+                            "Not supported key derivation: " +
+                                    shc.negotiatedProtocol);
+                } else {
+                    shc.handshakeKeyDerivation =
+                            kd.createKeyDerivation(shc, masterSecret);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/KrbClientKeyExchangeHelper.java	Tue Aug 25 15:48:30 2020 -0300
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.Principal;
+
+/**
+ * Helper interface for KrbClientKeyExchange SSL classes to access
+ * the Kerberos implementation without static-linking. This enables
+ * Java SE Embedded 8 compilation using 'compact1' profile -where
+ * SSL classes are available but Kerberos are not-.
+ */
+public interface KrbClientKeyExchangeHelper {
+
+    void init(byte[] preMaster, String serverName,
+            AccessControlContext acc) throws IOException;
+
+    void init(byte[] encodedTicket, byte[] preMasterEnc,
+            Object serviceCreds, AccessControlContext acc)
+                    throws IOException;
+
+    byte[] getEncodedTicket();
+
+    byte[] getEncryptedPreMasterSecret();
+
+    byte[] getPlainPreMasterSecret();
+
+    Principal getPeerPrincipal();
+
+    Principal getLocalPrincipal();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/KrbKeyExchange.java	Tue Aug 25 15:48:30 2020 -0300
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
+ * Copyright (c) 2020, Red Hat, Inc.
+ * 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.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLHandshakeException;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+final class KrbKeyExchange {
+    static final SSLPossessionGenerator poGenerator =
+            new KrbPossessionGenerator();
+    static final SSLKeyAgreementGenerator kaGenerator =
+            new KrbKAGenerator();
+
+    static final
+            class KrbPossessionGenerator implements SSLPossessionGenerator {
+        /**
+         * Kerberos service credentials. Having this possession in
+         * the server side will enable the use of a KRB cipher suite in
+         * T12ServerHelloProducer::chooseCipherSuite.
+         */
+        @Override
+        public SSLPossession createPossession(HandshakeContext handshakeContext) {
+            Object serviceCreds = null;
+            try {
+                final AccessControlContext acc = handshakeContext.conContext.acc;
+                serviceCreds = AccessController.doPrivileged(
+                        // Eliminate dependency on KerberosKey
+                        new PrivilegedExceptionAction<Object>() {
+                            @Override
+                            public Object run() throws Exception {
+                                // get kerberos key for the default principal
+                                return Krb5Helper.getServiceCreds(acc);
+                            }});
+
+                // check permission to access and use the secret key of the
+                // Kerberized "host" service
+                if (serviceCreds != null) {
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine("Using Kerberos creds");
+                    }
+                    String serverPrincipal =
+                            Krb5Helper.getServerPrincipalName(serviceCreds);
+                    if (serverPrincipal != null) {
+                        // When service is bound, we check ASAP. Otherwise,
+                        // will check after client request is received
+                        // in Kerberos ClientKeyExchange
+                        SecurityManager sm = System.getSecurityManager();
+                        try {
+                            if (sm != null) {
+                                // Eliminate dependency on ServicePermission
+                                sm.checkPermission(Krb5Helper.getServicePermission(
+                                        serverPrincipal, "accept"), acc);
+                            }
+                        } catch (SecurityException se) {
+                            // Do not destroy keys. Will affect Subject
+                            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                                SSLLogger.fine("Permission to access Kerberos"
+                                        + " secret key denied");
+                            }
+                            return null;
+                        }
+                    }
+                    return new KrbServiceCreds(serviceCreds);
+                }
+            } catch (PrivilegedActionException e) {
+                // Likely exception here is LoginException
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Attempt to obtain Kerberos key failed: "
+                            + e.toString());
+                }
+            }
+            return null;
+        }
+    }
+
+    static final
+        class KrbServiceCreds implements SSLPossession {
+        final Object serviceCreds;
+
+        KrbServiceCreds(Object serviceCreds) {
+            this.serviceCreds = serviceCreds;
+        }
+    }
+
+    static final
+        class KrbPremasterSecret implements SSLPossession, SSLCredentials {
+        final byte[] preMaster; // 48 bytes
+
+        KrbPremasterSecret(byte[] premasterSecret) {
+            preMaster = premasterSecret;
+        }
+
+        static KrbPremasterSecret createPremasterSecret(
+                ProtocolVersion protocolVersion, SecureRandom rand) {
+            byte[] pm = new byte[48];
+            rand.nextBytes(pm);
+            pm[0] = protocolVersion.major;
+            pm[1] = protocolVersion.minor;
+            return new KrbPremasterSecret(pm);
+        }
+
+        static KrbPremasterSecret decode(ProtocolVersion protocolVersion,
+                ProtocolVersion clientVersion, byte[] preMaster,
+                SecureRandom rand) {
+            KrbPremasterSecret preMasterSecret = null;
+            boolean versionMismatch = true;
+            ProtocolVersion preMasterProtocolVersion = null;
+            if (preMaster != null && preMaster.length == 48) {
+                preMasterProtocolVersion =
+                        ProtocolVersion.valueOf(preMaster[0], preMaster[1]);
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                     SSLLogger.fine("Kerberos pre-master secret protocol" +
+                             " version: " + preMasterProtocolVersion);
+                }
+
+                // Check if the pre-master secret protocol version is valid.
+                // The specification states that it must be the maximum version
+                // supported by the client from its ClientHello message. However,
+                // many old implementations send the negotiated version, so accept
+                // both for SSL v3.0 and TLS v1.0.
+                versionMismatch =
+                        (preMasterProtocolVersion.compare(clientVersion) != 0);
+                if (versionMismatch &&
+                        (clientVersion.compare(ProtocolVersion.TLS10) <= 0)) {
+                    versionMismatch =
+                            (preMasterProtocolVersion.compare(protocolVersion) != 0);
+                }
+            }
+
+            if (versionMismatch) {
+                /*
+                 * Bogus decrypted ClientKeyExchange? If so, conjure a
+                 * a random pre-master secret that will fail later during
+                 * Finished message processing. This is a countermeasure against
+                 * the "interactive RSA PKCS#1 encryption envelop attack" reported
+                 * in June 1998. Preserving the execution path will
+                 * mitigate timing attacks and force consistent error handling.
+                 */
+                preMasterSecret = createPremasterSecret(clientVersion, rand);
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine("Kerberos pre-master secret error," +
+                            " generating random secret for safe failure.");
+                }
+            } else {
+                preMasterSecret = new KrbPremasterSecret(preMaster);
+            }
+            return preMasterSecret;
+        }
+    }
+
+    private static final class KrbKAGenerator implements SSLKeyAgreementGenerator {
+        // Prevent instantiation of this class.
+        private KrbKAGenerator() {
+            // blank
+        }
+
+        @Override
+        public SSLKeyDerivation createKeyDerivation(
+                HandshakeContext context) throws IOException {
+
+            KrbPremasterSecret premaster = null;
+            if (context instanceof ClientHandshakeContext) {
+                for (SSLPossession possession : context.handshakePossessions) {
+                    if (possession instanceof KrbPremasterSecret) {
+                        premaster = (KrbPremasterSecret)possession;
+                        break;
+                    }
+                }
+            } else {
+                for (SSLCredentials credential : context.handshakeCredentials) {
+                    if (credential instanceof KrbPremasterSecret) {
+                        premaster = (KrbPremasterSecret)credential;
+                        break;
+                    }
+                }
+            }
+
+            if (premaster == null) {
+                throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No sufficient KRB key agreement parameters negotiated");
+            }
+
+            return new KRBKAKeyDerivation(context, premaster.preMaster);
+
+        }
+
+        private static final
+            class KRBKAKeyDerivation implements SSLKeyDerivation {
+            private final HandshakeContext context;
+            private final byte[] secretBytes;
+
+            KRBKAKeyDerivation(HandshakeContext context,
+                               byte[] secret) {
+                this.context = context;
+                this.secretBytes = secret;
+            }
+
+            @Override
+            public SecretKey deriveKey(String algorithm,
+                                       AlgorithmParameterSpec params) throws IOException {
+                try {
+                    SecretKey preMasterSecret = new SecretKeySpec(secretBytes,
+                            "TlsPremasterSecret");
+
+                    SSLMasterKeyDerivation mskd =
+                            SSLMasterKeyDerivation.valueOf(
+                                    context.negotiatedProtocol);
+                    if (mskd == null) {
+                        // unlikely
+                        throw new SSLHandshakeException(
+                                "No expected master key derivation for protocol: " +
+                                        context.negotiatedProtocol.name);
+                    }
+                    SSLKeyDerivation kd = mskd.createKeyDerivation(
+                            context, preMasterSecret);
+                    return kd.deriveKey("MasterSecret", params);
+                } catch (Exception gse) {
+                    throw (SSLHandshakeException) new SSLHandshakeException(
+                            "Could not generate secret").initCause(gse);
+                }
+            }
+        }
+    }
+}
--- a/src/share/classes/sun/security/ssl/SSLKeyExchange.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/SSLKeyExchange.java	Tue Aug 25 15:48:30 2020 -0300
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Azul Systems, Inc. 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
@@ -234,6 +235,10 @@
                 }
             case K_ECDH_ANON:
                 return SSLKeyExECDHANON.KE;
+            case K_KRB5:
+                return SSLKeyExKRB5.KE;
+            case K_KRB5_EXPORT:
+                return SSLKeyExKRB5EXPORT.KE;
         }
 
         return null;
@@ -325,6 +330,16 @@
                 null, T12KeyAgreement.ECDHE);
     }
 
+    private static class SSLKeyExKRB5 {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                null, T12KeyAgreement.KRB5);
+    }
+
+    private static class SSLKeyExKRB5EXPORT {
+        private static SSLKeyExchange KE = new SSLKeyExchange(
+                null, T12KeyAgreement.KRB5_EXPORT);
+    }
+
     private enum T12KeyAgreement implements SSLKeyAgreement {
         RSA             ("rsa",         null,
                                         RSAKeyExchange.kaGenerator),
@@ -337,7 +352,11 @@
         ECDH            ("ecdh",        null,
                                         ECDHKeyExchange.ecdhKAGenerator),
         ECDHE           ("ecdhe",       ECDHKeyExchange.poGenerator,
-                                        ECDHKeyExchange.ecdheKAGenerator);
+                                        ECDHKeyExchange.ecdheKAGenerator),
+        KRB5            ("krb5",        KrbKeyExchange.poGenerator,
+                                        KrbKeyExchange.kaGenerator),
+        KRB5_EXPORT     ("krb5_export", KrbKeyExchange.poGenerator,
+                                        KrbKeyExchange.kaGenerator);
 
         final String name;
         final SSLPossessionGenerator possessionGenerator;
@@ -427,6 +446,15 @@
                                 ECDHClientKeyExchange.ecdheHandshakeProducer
                             )
                         });
+                    case KRB5:
+                    case KRB5_EXPORT:
+                        return (Map.Entry<Byte,
+                                HandshakeProducer>[])(new Map.Entry[] {
+                                new SimpleImmutableEntry<>(
+                                        SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                        KrbClientKeyExchange.krbHandshakeProducer
+                                )
+                        });
                 }
             } else {
                 switch (this) {
@@ -540,6 +568,15 @@
                                 ECDHClientKeyExchange.ecdheHandshakeConsumer
                             )
                         });
+                    case KRB5:
+                    case KRB5_EXPORT:
+                        return (Map.Entry<Byte,
+                                SSLConsumer>[])(new Map.Entry[] {
+                                new SimpleImmutableEntry<>(
+                                        SSLHandshake.CLIENT_KEY_EXCHANGE.id,
+                                        KrbClientKeyExchange.krbHandshakeConsumer
+                                )
+                        });
                 }
             }
 
--- a/src/share/classes/sun/security/ssl/SSLSessionImpl.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/SSLSessionImpl.java	Tue Aug 25 15:48:30 2020 -0300
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Azul Systems, Inc. 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
@@ -24,6 +25,9 @@
  */
 package sun.security.ssl;
 
+import static sun.security.ssl.CipherSuite.KeyExchange.K_KRB5;
+import static sun.security.ssl.CipherSuite.KeyExchange.K_KRB5_EXPORT;
+
 import java.math.BigInteger;
 import java.net.InetAddress;
 import java.security.Principal;
@@ -74,6 +78,7 @@
     private final ProtocolVersion       protocolVersion;
     private final SessionId             sessionId;
     private X509Certificate[]   peerCerts;
+    private Principal           peerPrincipal;
     private CipherSuite         cipherSuite;
     private SecretKey           masterSecret;
     final boolean               useExtendedMasterSecret;
@@ -89,6 +94,7 @@
     private SSLSessionContextImpl       context;
     private boolean             invalidated;
     private X509Certificate[]   localCerts;
+    private Principal           localPrincipal;
     private PrivateKey          localPrivateKey;
     private final Collection<SignatureScheme>     localSupportedSignAlgs;
     private String[]            peerSupportedSignAlgs;      // for certificate
@@ -231,6 +237,8 @@
         this.identificationProtocol = baseSession.getIdentificationProtocol();
         this.localCerts = baseSession.localCerts;
         this.peerCerts = baseSession.peerCerts;
+        this.localPrincipal = baseSession.localPrincipal;
+        this.peerPrincipal = baseSession.peerPrincipal;
         this.statusResponses = baseSession.statusResponses;
         this.resumptionMasterSecret = baseSession.resumptionMasterSecret;
         this.context = baseSession.context;
@@ -322,10 +330,20 @@
         }
     }
 
+    void setPeerPrincipal(Principal peer) {
+        if (peerPrincipal == null) {
+            peerPrincipal = peer;
+        }
+    }
+
     void setLocalCertificates(X509Certificate[] local) {
         localCerts = local;
     }
 
+    void setLocalPrincipal(Principal local) {
+        localPrincipal = local;
+    }
+
     void setLocalPrivateKey(PrivateKey privateKey) {
         localPrivateKey = privateKey;
     }
@@ -538,8 +556,8 @@
      * Return the cert chain presented by the peer in the
      * java.security.cert format.
      * Note: This method can be used only when using certificate-based
-     * cipher suites; using it with non-certificate-based cipher suites
-     * will throw an SSLPeerUnverifiedException.
+     * cipher suites; using it with non-certificate-based cipher suites,
+     * such as Kerberos, will throw an SSLPeerUnverifiedException.
      *
      * @return array of peer X.509 certs, with the peer's own cert
      *  first in the chain, and with the "root" CA last.
@@ -552,6 +570,11 @@
         // change record of peer identity even by accident, much
         // less do it intentionally.
         //
+        if ((cipherSuite.keyExchange == K_KRB5) ||
+            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+            throw new SSLPeerUnverifiedException("no certificates expected"
+                        + " for Kerberos cipher suites");
+        }
         if (peerCerts == null) {
             throw new SSLPeerUnverifiedException("peer not authenticated");
         }
@@ -584,8 +607,8 @@
      * Return the cert chain presented by the peer in the
      * javax.security.cert format.
      * Note: This method can be used only when using certificate-based
-     * cipher suites; using it with non-certificate-based cipher suites
-     * will throw an SSLPeerUnverifiedException.
+     * cipher suites; using it with non-certificate-based cipher suites,
+     * such as Kerberos, will throw an SSLPeerUnverifiedException.
      *
      * @return array of peer X.509 certs, with the peer's own cert
      *  first in the chain, and with the "root" CA last.
@@ -603,6 +626,11 @@
         // change record of peer identity even by accident, much
         // less do it intentionally.
         //
+        if ((cipherSuite.keyExchange == K_KRB5) ||
+            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+            throw new SSLPeerUnverifiedException("no certificates expected"
+                        + " for Kerberos cipher suites");
+        }
         if (peerCerts == null) {
             throw new SSLPeerUnverifiedException("peer not authenticated");
         }
@@ -626,8 +654,8 @@
     /**
      * Return the cert chain presented by the peer.
      * Note: This method can be used only when using certificate-based
-     * cipher suites; using it with non-certificate-based cipher suites
-     * will throw an SSLPeerUnverifiedException.
+     * cipher suites; using it with non-certificate-based cipher suites,
+     * such as Kerberos, will throw an SSLPeerUnverifiedException.
      *
      * @return array of peer X.509 certs, with the peer's own cert
      *  first in the chain, and with the "root" CA last.
@@ -639,6 +667,11 @@
          * change record of peer identity even by accident, much
          * less do it intentionally.
          */
+        if ((cipherSuite.keyExchange == K_KRB5) ||
+            (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+            throw new SSLPeerUnverifiedException("no certificates expected"
+                        + " for Kerberos cipher suites");
+        }
         if (peerCerts != null) {
             return peerCerts.clone();
         } else {
@@ -674,7 +707,8 @@
      * defining the session.
      *
      * @return the peer's principal. Returns an X500Principal of the
-     * end-entity certificate for X509-based cipher suites.
+     * end-entity certificate for X509-based cipher suites, and
+     * Principal for Kerberos cipher suites.
      *
      * @throws SSLPeerUnverifiedException if the peer's identity has not
      *          been verified
@@ -684,6 +718,8 @@
                 throws SSLPeerUnverifiedException
     {
         if (peerCerts == null) {
+            if (peerPrincipal != null)
+                return peerPrincipal;
             throw new SSLPeerUnverifiedException("peer not authenticated");
         }
         return peerCerts[0].getSubjectX500Principal();
@@ -693,13 +729,17 @@
      * Returns the principal that was sent to the peer during handshaking.
      *
      * @return the principal sent to the peer. Returns an X500Principal
-     * of the end-entity certificate for X509-based cipher suites.
-     * If no principal was sent, then null is returned.
+     * of the end-entity certificate for X509-based cipher suites, and
+     * Principal for Kerberos cipher suites. If no principal was
+     * sent, then null is returned.
      */
     @Override
     public Principal getLocalPrincipal() {
-        return ((localCerts == null || localCerts.length == 0) ? null :
-                localCerts[0].getSubjectX500Principal());
+        if (localCerts != null && localCerts.length != 0)
+            return localCerts[0].getSubjectX500Principal();
+        else if (localPrincipal != null)
+            return localPrincipal;
+        return null;
     }
 
     /*
--- a/src/share/classes/sun/security/ssl/ServerHello.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/ServerHello.java	Tue Aug 25 15:48:30 2020 -0300
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,19 +28,26 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.security.AccessController;
 import java.security.AlgorithmConstraints;
 import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.text.MessageFormat;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLProtocolException;
+import javax.security.auth.Subject;
+
 import sun.security.ssl.CipherSuite.KeyExchange;
 import sun.security.ssl.ClientHello.ClientHelloMessage;
 import sun.security.ssl.SSLCipher.SSLReadCipher;
@@ -1033,6 +1041,52 @@
                             "Server resumed with wrong protocol version");
                     }
 
+                    // validate subject identity
+                    if (sessionSuite.keyExchange == KeyExchange.K_KRB5 ||
+                            sessionSuite.keyExchange == KeyExchange.K_KRB5_EXPORT) {
+                        Principal localPrincipal = chc.resumingSession.getLocalPrincipal();
+
+                        Subject subject = null;
+                        try {
+                            subject = AccessController.doPrivileged(
+                                    new PrivilegedExceptionAction<Subject>() {
+                                        @Override
+                                        public Subject run() throws Exception {
+                                            return Krb5Helper.getClientSubject(
+                                                    chc.conContext.acc);
+                                        }});
+                        } catch (PrivilegedActionException e) {
+                            subject = null;
+                            if (SSLLogger.isOn &&
+                                    SSLLogger.isOn("ssl,handshake,verbose")) {
+                                SSLLogger.fine("Attempt to obtain" +
+                                        " subject failed!");
+                            }
+                        }
+
+                        if (subject != null) {
+                            // Eliminate dependency on KerberosPrincipal
+                            Set<Principal> principals =
+                                    subject.getPrincipals(Principal.class);
+                            if (!principals.contains(localPrincipal)) {
+                                throw new SSLProtocolException("Server resumed" +
+                                        " session with wrong subject identity");
+                            } else {
+                                if (SSLLogger.isOn &&
+                                        SSLLogger.isOn("ssl,handshake,verbose"))
+                                    SSLLogger.fine("Subject identity is same");
+                            }
+                        } else {
+                            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose"))
+                                SSLLogger.fine("Kerberos credentials are not" +
+                                        " present in the current Subject; check if" +
+                                        " javax.security.auth.useSubjectCredsOnly" +
+                                        " system property has been set to false");
+                            throw new SSLProtocolException
+                                    ("Server resumed session with no subject");
+                        }
+                    }
+
                     // looks fine;  resume it.
                     chc.isResumption = true;
                     chc.resumingSession.setAsSessionResumption(true);
--- a/src/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java	Tue Aug 25 08:30:00 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,463 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  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.krb5;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
-import java.security.SecureRandom;
-import java.net.InetAddress;
-import java.security.PrivilegedAction;
-
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.ServicePermission;
-import sun.security.jgss.GSSCaller;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.PrincipalName;
-import sun.security.krb5.internal.Ticket;
-import sun.security.krb5.internal.EncTicketPart;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.Krb5;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeOutStream;
-import sun.security.ssl.Krb5Helper;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is Kerberos option in the client key exchange message
- * (CLIENT -> SERVER). It holds the Kerberos ticket and the encrypted
- * premaster secret encrypted with the session key sealed in the ticket.
- * From RFC 2712:
- *  struct
- *  {
- *    opaque Ticket;
- *    opaque authenticator;            // optional
- *    opaque EncryptedPreMasterSecret; // encrypted with the session key
- *                                     // which is sealed in the ticket
- *  } KerberosWrapper;
- *
- *
- * Ticket and authenticator are encrypted as per RFC 1510 (in ASN.1)
- * Encrypted pre-master secret has the same structure as it does for RSA
- * except for Kerberos, the encryption key is the session key instead of
- * the RSA public key.
- *
- * XXX authenticator currently ignored
- *
- */
-public final class KerberosClientKeyExchangeImpl
-    extends sun.security.ssl.KerberosClientKeyExchange {
-
-    private KerberosPreMasterSecret preMaster;
-    private byte[] encodedTicket;
-    private KerberosPrincipal peerPrincipal;
-    private KerberosPrincipal localPrincipal;
-
-    public KerberosClientKeyExchangeImpl() {
-    }
-
-    /**
-     * Creates an instance of KerberosClientKeyExchange consisting of the
-     * Kerberos service ticket, authenticator and encrypted premaster secret.
-     * Called by client handshaker.
-     *
-     * @param serverName name of server with which to do handshake;
-     *             this is used to get the Kerberos service ticket
-     * @param protocolVersion Maximum version supported by client (i.e,
-     *          version it requested in client hello)
-     * @param rand random number generator to use for generating pre-master
-     *          secret
-     */
-    @Override
-    public void init(String serverName,
-        AccessControlContext acc, ProtocolVersion protocolVersion,
-        SecureRandom rand) throws IOException {
-
-         // Get service ticket
-         KerberosTicket ticket = getServiceTicket(serverName, acc);
-         encodedTicket = ticket.getEncoded();
-
-         // Record the Kerberos principals
-         peerPrincipal = ticket.getServer();
-         localPrincipal = ticket.getClient();
-
-         // Optional authenticator, encrypted using session key,
-         // currently ignored
-
-         // Generate premaster secret and encrypt it using session key
-         EncryptionKey sessionKey = new EncryptionKey(
-                                        ticket.getSessionKeyType(),
-                                        ticket.getSessionKey().getEncoded());
-
-         preMaster = new KerberosPreMasterSecret(protocolVersion,
-             rand, sessionKey);
-    }
-
-    /**
-     * Creates an instance of KerberosClientKeyExchange from its ASN.1 encoding.
-     * Used by ServerHandshaker to verify and obtain premaster secret.
-     *
-     * @param protocolVersion current protocol version
-     * @param clientVersion version requested by client in its ClientHello;
-     *          used by premaster secret version check
-     * @param rand random number generator used for generating random
-     *          premaster secret if ticket and/or premaster verification fails
-     * @param input inputstream from which to get ASN.1-encoded KerberosWrapper
-     * @param acc the AccessControlContext of the handshaker
-     * @param serviceCreds server's creds
-     */
-    @Override
-    public void init(ProtocolVersion protocolVersion,
-        ProtocolVersion clientVersion,
-        SecureRandom rand, HandshakeInStream input, AccessControlContext acc, Object serviceCreds)
-        throws IOException {
-
-        // Read ticket
-        encodedTicket = input.getBytes16();
-
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(System.out,
-                "encoded Kerberos service ticket", encodedTicket);
-        }
-
-        EncryptionKey sessionKey = null;
-
-        try {
-            Ticket t = new Ticket(encodedTicket);
-
-            EncryptedData encPart = t.encPart;
-            PrincipalName ticketSname = t.sname;
-
-            final ServiceCreds creds = (ServiceCreds)serviceCreds;
-            final KerberosPrincipal princ =
-                    new KerberosPrincipal(ticketSname.toString());
-
-            // For bound service, permission already checked at setup
-            if (creds.getName() == null) {
-                SecurityManager sm = System.getSecurityManager();
-                try {
-                    if (sm != null) {
-                        // Eliminate dependency on ServicePermission
-                        sm.checkPermission(Krb5Helper.getServicePermission(
-                                ticketSname.toString(), "accept"), acc);
-                    }
-                } catch (SecurityException se) {
-                    serviceCreds = null;
-                    // Do not destroy keys. Will affect Subject
-                    if (debug != null && Debug.isOn("handshake")) {
-                        System.out.println("Permission to access Kerberos"
-                                + " secret key denied");
-                    }
-                    throw new IOException("Kerberos service not allowedy");
-                }
-            }
-            KerberosKey[] serverKeys = AccessController.doPrivileged(
-                    new PrivilegedAction<KerberosKey[]>() {
-                        @Override
-                        public KerberosKey[] run() {
-                            return creds.getKKeys(princ);
-                        }
-                    });
-            if (serverKeys.length == 0) {
-                throw new IOException("Found no key for " + princ +
-                        (creds.getName() == null ? "" :
-                        (", this keytab is for " + creds.getName() + " only")));
-            }
-
-            /*
-             * permission to access and use the secret key of the Kerberized
-             * "host" service is done in ServerHandshaker.getKerberosKeys()
-             * to ensure server has the permission to use the secret key
-             * before promising the client
-             */
-
-            // See if we have the right key to decrypt the ticket to get
-            // the session key.
-            int encPartKeyType = encPart.getEType();
-            Integer encPartKeyVersion = encPart.getKeyVersionNumber();
-            KerberosKey dkey = null;
-            try {
-                dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
-            } catch (KrbException ke) { // a kvno mismatch
-                throw new IOException(
-                        "Cannot find key matching version number", ke);
-            }
-            if (dkey == null) {
-                // %%% Should print string repr of etype
-                throw new IOException("Cannot find key of appropriate type" +
-                        " to decrypt ticket - need etype " + encPartKeyType);
-            }
-
-            EncryptionKey secretKey = new EncryptionKey(
-                encPartKeyType,
-                dkey.getEncoded());
-
-            // Decrypt encPart using server's secret key
-            byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
-
-            // Reset data stream after decryption, remove redundant bytes
-            byte[] temp = encPart.reset(bytes);
-            EncTicketPart encTicketPart = new EncTicketPart(temp);
-
-            // Record the Kerberos Principals
-            peerPrincipal =
-                new KerberosPrincipal(encTicketPart.cname.getName());
-            localPrincipal = new KerberosPrincipal(ticketSname.getName());
-
-            sessionKey = encTicketPart.key;
-
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("server principal: " + ticketSname);
-                System.out.println("cname: " + encTicketPart.cname.toString());
-            }
-        } catch (IOException e) {
-            throw e;
-        } catch (Exception e) {
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("KerberosWrapper error getting session key,"
-                        + " generating random secret (" + e.getMessage() + ")");
-            }
-            sessionKey = null;
-        }
-
-        input.getBytes16();   // XXX Read and ignore authenticator
-
-        if (sessionKey != null) {
-            preMaster = new KerberosPreMasterSecret(protocolVersion,
-                clientVersion, rand, input, sessionKey);
-        } else {
-            // Generate bogus premaster secret
-            preMaster = new KerberosPreMasterSecret(clientVersion, rand);
-        }
-    }
-
-    @Override
-    public int messageLength() {
-        return (6 + encodedTicket.length + preMaster.getEncrypted().length);
-    }
-
-    @Override
-    public void send(HandshakeOutStream s) throws IOException {
-        s.putBytes16(encodedTicket);
-        s.putBytes16(null); // XXX no authenticator
-        s.putBytes16(preMaster.getEncrypted());
-    }
-
-    @Override
-    public void print(PrintStream s) throws IOException {
-        s.println("*** ClientKeyExchange, Kerberos");
-
-        if (debug != null && Debug.isOn("verbose")) {
-            Debug.println(s, "Kerberos service ticket", encodedTicket);
-            Debug.println(s, "Random Secret", preMaster.getUnencrypted());
-            Debug.println(s, "Encrypted random Secret",
-                preMaster.getEncrypted());
-        }
-    }
-
-    // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
-    private static KerberosTicket getServiceTicket(String serverName,
-        final AccessControlContext acc) throws IOException {
-
-        if ("localhost".equals(serverName) ||
-                "localhost.localdomain".equals(serverName)) {
-
-            if (debug != null && Debug.isOn("handshake")) {
-                System.out.println("Get the local hostname");
-            }
-            String localHost = java.security.AccessController.doPrivileged(
-                new java.security.PrivilegedAction<String>() {
-                public String run() {
-                    try {
-                        return InetAddress.getLocalHost().getHostName();
-                    } catch (java.net.UnknownHostException e) {
-                        if (debug != null && Debug.isOn("handshake")) {
-                            System.out.println("Warning,"
-                                + " cannot get the local hostname: "
-                                + e.getMessage());
-                        }
-                        return null;
-                    }
-                }
-            });
-            if (localHost != null) {
-                serverName = localHost;
-            }
-        }
-
-        // Resolve serverName (possibly in IP addr form) to Kerberos principal
-        // name for service with hostname
-        String serviceName = "host/" + serverName;
-        PrincipalName principal;
-        try {
-            principal = new PrincipalName(serviceName,
-                                PrincipalName.KRB_NT_SRV_HST);
-        } catch (SecurityException se) {
-            throw se;
-        } catch (Exception e) {
-            IOException ioe = new IOException("Invalid service principal" +
-                                " name: " + serviceName);
-            ioe.initCause(e);
-            throw ioe;
-        }
-        String realm = principal.getRealmAsString();
-
-        final String serverPrincipal = principal.toString();
-        final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
-        final String clientPrincipal = null;  // use default
-
-
-        // check permission to obtain a service ticket to initiate a
-        // context with the "host" service
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-           sm.checkPermission(new ServicePermission(serverPrincipal,
-                                "initiate"), acc);
-        }
-
-        try {
-            KerberosTicket ticket = AccessController.doPrivileged(
-                new PrivilegedExceptionAction<KerberosTicket>() {
-                public KerberosTicket run() throws Exception {
-                    return Krb5Util.getTicketFromSubjectAndTgs(
-                        GSSCaller.CALLER_SSL_CLIENT,
-                        clientPrincipal, serverPrincipal,
-                        tgsPrincipal, acc);
-                        }});
-
-            if (ticket == null) {
-                throw new IOException("Failed to find any kerberos service" +
-                        " ticket for " + serverPrincipal);
-            }
-            return ticket;
-        } catch (PrivilegedActionException e) {
-            IOException ioe = new IOException(
-                "Attempt to obtain kerberos service ticket for " +
-                        serverPrincipal + " failed!");
-            ioe.initCause(e);
-            throw ioe;
-        }
-    }
-
-    @Override
-    public byte[] getUnencryptedPreMasterSecret() {
-        return preMaster.getUnencrypted();
-    }
-
-    @Override
-    public KerberosPrincipal getPeerPrincipal() {
-        return peerPrincipal;
-    }
-
-    @Override
-    public KerberosPrincipal getLocalPrincipal() {
-        return localPrincipal;
-    }
-
-    /**
-     * Determines if a kvno matches another kvno. Used in the method
-     * findKey(etype, version, keys). Always returns true if either input
-     * is null or zero, in case any side does not have kvno info available.
-     *
-     * Note: zero is included because N/A is not a legal value for kvno
-     * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
-     * that the kvno is N/A might be lost when converting between
-     * EncryptionKey and KerberosKey.
-     */
-    private static boolean versionMatches(Integer v1, int v2) {
-        if (v1 == null || v1 == 0 || v2 == 0) {
-            return true;
-        }
-        return v1.equals(v2);
-    }
-
-    private static KerberosKey findKey(int etype, Integer version,
-            KerberosKey[] keys) throws KrbException {
-        int ktype;
-        boolean etypeFound = false;
-
-        // When no matched kvno is found, returns tke key of the same
-        // etype with the highest kvno
-        int kvno_found = 0;
-        KerberosKey key_found = null;
-
-        for (int i = 0; i < keys.length; i++) {
-            ktype = keys[i].getKeyType();
-            if (etype == ktype) {
-                int kv = keys[i].getVersionNumber();
-                etypeFound = true;
-                if (versionMatches(version, kv)) {
-                    return keys[i];
-                } else if (kv > kvno_found) {
-                    key_found = keys[i];
-                    kvno_found = kv;
-                }
-            }
-        }
-        // Key not found.
-        // %%% kludge to allow DES keys to be used for diff etypes
-        if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
-            etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
-            for (int i = 0; i < keys.length; i++) {
-                ktype = keys[i].getKeyType();
-                if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
-                        ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
-                    int kv = keys[i].getVersionNumber();
-                    etypeFound = true;
-                    if (versionMatches(version, kv)) {
-                        return new KerberosKey(keys[i].getPrincipal(),
-                            keys[i].getEncoded(),
-                            etype,
-                            kv);
-                    } else if (kv > kvno_found) {
-                        key_found = new KerberosKey(keys[i].getPrincipal(),
-                                keys[i].getEncoded(),
-                                etype,
-                                kv);
-                        kvno_found = kv;
-                    }
-                }
-            }
-        }
-        if (etypeFound) {
-            return key_found;
-        }
-        return null;
-    }
-}
--- a/src/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java	Tue Aug 25 08:30:00 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.ssl.krb5;
-
-import java.io.*;
-import java.security.*;
-import java.util.Arrays;
-
-import javax.net.ssl.*;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeMessage;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is the Kerberos premaster secret in the Kerberos client key
- * exchange message (CLIENT --> SERVER); it holds the
- * Kerberos-encrypted pre-master secret. The secret is encrypted using the
- * Kerberos session key.  The padding and size of the resulting message
- * depends on the session key type, but the pre-master secret is
- * always exactly 48 bytes.
- *
- */
-final class KerberosPreMasterSecret {
-
-    private ProtocolVersion protocolVersion; // preMaster [0,1]
-    private byte preMaster[];           // 48 bytes
-    private byte encrypted[];
-
-    /**
-     * Constructor used by client to generate premaster secret.
-     *
-     * Client randomly creates a pre-master secret and encrypts it
-     * using the Kerberos session key; only the server can decrypt
-     * it, using the session key available in the service ticket.
-     *
-     * @param protocolVersion used to set preMaster[0,1]
-     * @param generator random number generator for generating premaster secret
-     * @param sessionKey Kerberos session key for encrypting premaster secret
-     */
-    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
-        SecureRandom generator, EncryptionKey sessionKey) throws IOException {
-
-        if (sessionKey.getEType() ==
-            EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
-            throw new IOException(
-               "session keys with des3-cbc-hmac-sha1-kd encryption type " +
-               "are not supported for TLS Kerberos cipher suites");
-        }
-
-        this.protocolVersion = protocolVersion;
-        preMaster = generatePreMaster(generator, protocolVersion);
-
-        // Encrypt premaster secret
-        try {
-            EncryptedData eData = new EncryptedData(sessionKey, preMaster,
-                KeyUsage.KU_UNKNOWN);
-            encrypted = eData.getBytes();  // not ASN.1 encoded.
-
-        } catch (KrbException e) {
-            throw (SSLKeyException)new SSLKeyException
-                ("Kerberos premaster secret error").initCause(e);
-        }
-    }
-
-    /*
-     * Constructor used by server to decrypt encrypted premaster secret.
-     * The protocol version in preMaster[0,1] must match either currentVersion
-     * or clientVersion, otherwise, the premaster secret is set to
-     * a random one to foil possible attack.
-     *
-     * @param currentVersion version of protocol being used
-     * @param clientVersion version requested by client
-     * @param generator random number generator used to generate
-     *        bogus premaster secret if premaster secret verification fails
-     * @param input input stream from which to read the encrypted
-     *        premaster secret
-     * @param sessionKey Kerberos session key to be used for decryption
-     */
-    KerberosPreMasterSecret(ProtocolVersion currentVersion,
-        ProtocolVersion clientVersion,
-        SecureRandom generator, HandshakeInStream input,
-        EncryptionKey sessionKey) throws IOException {
-
-         // Extract encrypted premaster secret from message
-         encrypted = input.getBytes16();
-
-         if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-            if (encrypted != null) {
-                Debug.println(System.out,
-                     "encrypted premaster secret", encrypted);
-            }
-         }
-
-        if (sessionKey.getEType() ==
-            EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
-            throw new IOException(
-               "session keys with des3-cbc-hmac-sha1-kd encryption type " +
-               "are not supported for TLS Kerberos cipher suites");
-        }
-
-        // Decrypt premaster secret
-        try {
-            EncryptedData data = new EncryptedData(sessionKey.getEType(),
-                        null /* optional kvno */, encrypted);
-
-            byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                 if (encrypted != null) {
-                     Debug.println(System.out,
-                         "decrypted premaster secret", temp);
-                 }
-            }
-
-            // Remove padding bytes after decryption. Only DES and DES3 have
-            // paddings and we don't support DES3 in TLS (see above)
-
-            if (temp.length == 52 &&
-                    data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
-                // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
-                if (paddingByteIs(temp, 52, (byte)4) ||
-                        paddingByteIs(temp, 52, (byte)0)) {
-                    temp = Arrays.copyOf(temp, 48);
-                }
-            } else if (temp.length == 56 &&
-                    data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
-                // For des-cbc-md5, 8 paddings with 0x08, or no padding
-                if (paddingByteIs(temp, 56, (byte)8)) {
-                    temp = Arrays.copyOf(temp, 48);
-                }
-            }
-
-            preMaster = temp;
-
-            protocolVersion = ProtocolVersion.valueOf(preMaster[0],
-                 preMaster[1]);
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                 System.out.println("Kerberos PreMasterSecret version: "
-                        + protocolVersion);
-            }
-        } catch (Exception e) {
-            // catch exception & process below
-            preMaster = null;
-            protocolVersion = currentVersion;
-        }
-
-        // check if the premaster secret version is ok
-        // the specification says that it must be the maximum version supported
-        // by the client from its ClientHello message. However, many
-        // old implementations send the negotiated version, so accept both
-        // for SSL v3.0 and TLS v1.0.
-        // NOTE that we may be comparing two unsupported version numbers in
-        // the second case, which is why we cannot use object references
-        // equality in this special case
-        boolean versionMismatch = (protocolVersion.v != clientVersion.v);
-
-        /*
-         * we never checked the client_version in server side
-         * for TLS v1.0 and SSL v3.0. For compatibility, we
-         * maintain this behavior.
-         */
-        if (versionMismatch && (clientVersion.v <= 0x0301)) {
-            versionMismatch = (protocolVersion.v != currentVersion.v);
-        }
-
-        /*
-         * Bogus decrypted ClientKeyExchange? If so, conjure a
-         * a random preMaster secret that will fail later during
-         * Finished message processing. This is a countermeasure against
-         * the "interactive RSA PKCS#1 encryption envelop attack" reported
-         * in June 1998. Preserving the executation path will
-         * mitigate timing attacks and force consistent error handling
-         * that will prevent an attacking client from differentiating
-         * different kinds of decrypted ClientKeyExchange bogosities.
-         */
-         if ((preMaster == null) || (preMaster.length != 48)
-                || versionMismatch) {
-            if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
-                System.out.println("Kerberos PreMasterSecret error, "
-                                   + "generating random secret");
-                if (preMaster != null) {
-                    Debug.println(System.out, "Invalid secret", preMaster);
-                }
-            }
-
-            /*
-             * Randomize the preMaster secret with the
-             * ClientHello.client_version, as will produce invalid master
-             * secret to prevent the attacks.
-             */
-            preMaster = generatePreMaster(generator, clientVersion);
-            protocolVersion = clientVersion;
-        }
-    }
-
-    /**
-     * Checks if all paddings of data are b
-     * @param data the block with padding
-     * @param len length of data, >= 48
-     * @param b expected padding byte
-     */
-    private static boolean paddingByteIs(byte[] data, int len, byte b) {
-        for (int i=48; i<len; i++) {
-            if (data[i] != b) return false;
-        }
-        return true;
-    }
-
-    /*
-     * Used by server to generate premaster secret in case of
-     * problem decoding ticket.
-     *
-     * @param protocolVersion used for preMaster[0,1]
-     * @param generator random number generator to use for generating secret.
-     */
-    KerberosPreMasterSecret(ProtocolVersion protocolVersion,
-        SecureRandom generator) {
-
-        this.protocolVersion = protocolVersion;
-        preMaster = generatePreMaster(generator, protocolVersion);
-    }
-
-    private static byte[] generatePreMaster(SecureRandom rand,
-        ProtocolVersion ver) {
-
-        byte[] pm = new byte[48];
-        rand.nextBytes(pm);
-        pm[0] = ver.major;
-        pm[1] = ver.minor;
-
-        return pm;
-    }
-
-    // Clone not needed; internal use only
-    byte[] getUnencrypted() {
-        return preMaster;
-    }
-
-    // Clone not needed; internal use only
-    byte[] getEncrypted() {
-        return encrypted;
-    }
-}
--- a/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java	Tue Aug 25 08:30:00 2020 -0700
+++ b/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java	Tue Aug 25 15:48:30 2020 -0300
@@ -29,9 +29,7 @@
 import java.security.Permission;
 import java.security.Principal;
 import java.util.Set;
-import javax.crypto.SecretKey;
 import javax.security.auth.Subject;
-import javax.security.auth.kerberos.KerberosKey;
 import javax.security.auth.kerberos.KeyTab;
 import javax.security.auth.kerberos.ServicePermission;
 import javax.security.auth.login.LoginException;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ssl/krb5/KrbClientKeyExchangeHelperImpl.java	Tue Aug 25 15:48:30 2020 -0300
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2003, 2020, 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.krb5;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.AccessController;
+import java.security.AccessControlContext;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+import java.util.Arrays;
+import java.security.PrivilegedAction;
+
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.net.ssl.SSLKeyException;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.ServicePermission;
+import sun.security.jgss.GSSCaller;
+
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.EncryptedData;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.internal.Ticket;
+import sun.security.krb5.internal.EncTicketPart;
+import sun.security.krb5.internal.crypto.KeyUsage;
+
+import sun.security.jgss.krb5.Krb5Util;
+import sun.security.jgss.krb5.ServiceCreds;
+import sun.security.krb5.KrbException;
+
+import sun.security.ssl.Krb5Helper;
+import sun.security.ssl.SSLLogger;
+
+public final class KrbClientKeyExchangeHelperImpl
+        implements sun.security.ssl.KrbClientKeyExchangeHelper {
+
+    private byte[] preMaster;
+    private byte[] preMasterEnc;
+    private byte[] encodedTicket;
+    private KerberosPrincipal peerPrincipal;
+    private KerberosPrincipal localPrincipal;
+
+    /**
+     * Initialises an instance of KrbClientKeyExchangeHelperImpl.
+     * Used by the TLS client to create a Kerberos Client Key Exchange
+     * message.
+     *
+     * @param preMaster plain pre-master secret
+     * @param serverName name of the TLS server to perform the handshake;
+     *                   used by the TLS client to get a Kerberos service
+     *                   ticket which contains the session key to encrypt
+     *                   the pre-master secret
+     * @param acc the TLS client security context for the handshake
+     */
+    @Override
+    public void init(byte[] preMaster, String serverName,
+            AccessControlContext acc) throws IOException {
+        this.preMaster = preMaster;
+
+        // Get service ticket
+        KerberosTicket ticket = getServiceTicket(serverName, acc);
+        encodedTicket = ticket.getEncoded();
+
+        // Record the Kerberos principals
+        peerPrincipal = ticket.getServer();
+        localPrincipal = ticket.getClient();
+
+        // Encrypt the pre-master secret with the Kerberos session key
+        EncryptionKey sessionKey = new EncryptionKey(
+                ticket.getSessionKeyType(),
+                ticket.getSessionKey().getEncoded());
+        encryptPremasterSecret(sessionKey);
+    }
+
+    /**
+     * Initialises an instance of KrbClientKeyExchangeHelperImpl.
+     * Used by the TLS server to process the content of a Kerberos Client Key
+     * Exchange message (received from a TLS client).
+     *
+     * @param encodedTicket the encoded Kerberos ticket (TGS) received from the
+     *                      TLS client. This ticket seals the Kerberos session
+     *                      key.
+     * @param preMasterEnc the pre-master secret encrypted with the Kerberos
+     *                     session key
+     * @param serviceCreds the TLS server Kerberos credentials used to process
+     *                     the received Kerberos ticket.
+     * @param acc the TLS server security context for the handshake
+     */
+    @Override
+    public void init(byte[] encodedTicket, byte[] preMasterEnc,
+            Object serviceCreds, AccessControlContext acc) throws IOException {
+        this.encodedTicket = encodedTicket;
+        this.preMasterEnc = preMasterEnc;
+        EncryptionKey sessionKey = null;
+
+        try {
+            Ticket t = new Ticket(encodedTicket);
+
+            EncryptedData encPart = t.encPart;
+            PrincipalName ticketSname = t.sname;
+
+            final ServiceCreds creds = (ServiceCreds) serviceCreds;
+            final KerberosPrincipal princ =
+                    new KerberosPrincipal(ticketSname.toString());
+
+            // For bound service, permission already checked at setup
+            if (creds.getName() == null) {
+                SecurityManager sm = System.getSecurityManager();
+                try {
+                    if (sm != null) {
+                        // Eliminate dependency on ServicePermission
+                        sm.checkPermission(Krb5Helper.getServicePermission(
+                                ticketSname.toString(), "accept"), acc);
+                    }
+                } catch (SecurityException se) {
+                    // Do not destroy keys. Will affect Subject
+                    if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                        SSLLogger.fine("Permission to access Kerberos"
+                                + " secret key denied");
+                    }
+                    throw new IOException("Kerberos service not allowed");
+                }
+            }
+            KerberosKey[] serverKeys = AccessController.doPrivileged(
+                    new PrivilegedAction<KerberosKey[]>() {
+                        @Override
+                        public KerberosKey[] run() {
+                            return creds.getKKeys(princ);
+                        }
+                    });
+            if (serverKeys.length == 0) {
+                throw new IOException("Found no key for " + princ +
+                        (creds.getName() == null ? "" :
+                        (", this keytab is for " + creds.getName() + " only")));
+            }
+
+            // See if we have the right key to decrypt the ticket to get
+            // the session key.
+            int encPartKeyType = encPart.getEType();
+            Integer encPartKeyVersion = encPart.getKeyVersionNumber();
+            KerberosKey dkey = null;
+            try {
+                dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
+            } catch (KrbException ke) { // a kvno mismatch
+                throw new IOException(
+                        "Cannot find key matching version number", ke);
+            }
+            if (dkey == null) {
+                // %%% Should print string repr of etype
+                throw new IOException("Cannot find key of appropriate type" +
+                        " to decrypt ticket - need etype " + encPartKeyType);
+            }
+
+            EncryptionKey secretKey = new EncryptionKey(
+                encPartKeyType,
+                dkey.getEncoded());
+
+            // Decrypt encPart using server's secret key
+            byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
+
+            // Reset data stream after decryption, remove redundant bytes
+            byte[] temp = encPart.reset(bytes);
+            EncTicketPart encTicketPart = new EncTicketPart(temp);
+
+            // Record the Kerberos Principals
+            peerPrincipal =
+                    new KerberosPrincipal(encTicketPart.cname.getName());
+            localPrincipal = new KerberosPrincipal(ticketSname.getName());
+
+            sessionKey = encTicketPart.key;
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("server principal: " + ticketSname);
+                SSLLogger.fine("cname: " + encTicketPart.cname.toString());
+            }
+        } catch (Exception e) {
+            sessionKey = null;
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Error getting the Kerberos session key" +
+                        " to decrypt the pre-master secret");
+            }
+        }
+        if (sessionKey != null)
+            decryptPremasterSecret(sessionKey);
+    }
+
+    @Override
+    public byte[] getEncodedTicket() {
+        return encodedTicket;
+    }
+
+    @Override
+    public byte[] getEncryptedPreMasterSecret() {
+        return preMasterEnc;
+    }
+
+    @Override
+    public byte[] getPlainPreMasterSecret() {
+        return preMaster;
+    }
+
+    @Override
+    public KerberosPrincipal getPeerPrincipal() {
+        return peerPrincipal;
+    }
+
+    @Override
+    public KerberosPrincipal getLocalPrincipal() {
+        return localPrincipal;
+    }
+
+    private void encryptPremasterSecret(EncryptionKey sessionKey)
+            throws IOException {
+        if (sessionKey.getEType() ==
+                EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+            throw new IOException(
+                    "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+                    "are not supported for TLS Kerberos cipher suites");
+        }
+        try {
+            EncryptedData eData = new EncryptedData(sessionKey, preMaster,
+                    KeyUsage.KU_UNKNOWN);
+            preMasterEnc = eData.getBytes(); // not ASN.1 encoded.
+        } catch (KrbException e) {
+            throw (IOException) new SSLKeyException("Kerberos pre-master" +
+                    " secret error").initCause(e);
+        }
+    }
+
+    private void decryptPremasterSecret(EncryptionKey sessionKey)
+            throws IOException {
+        if (sessionKey.getEType() ==
+                EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+            throw new IOException(
+                    "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+                    "are not supported for TLS Kerberos cipher suites");
+        }
+        try {
+            EncryptedData data = new EncryptedData(sessionKey.getEType(),
+                        null /* optional kvno */, preMasterEnc);
+            byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                 if (preMasterEnc != null) {
+                     SSLLogger.fine("decrypted premaster secret", temp);
+                 }
+            }
+
+            // Remove padding bytes after decryption. Only DES and DES3 have
+            // paddings and we don't support DES3 in TLS (see above)
+            if (temp.length == 52 &&
+                    data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
+                // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
+                if (paddingByteIs(temp, 52, (byte)4) ||
+                        paddingByteIs(temp, 52, (byte)0)) {
+                    temp = Arrays.copyOf(temp, 48);
+                }
+            } else if (temp.length == 56 &&
+                    data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
+                // For des-cbc-md5, 8 paddings with 0x08, or no padding
+                if (paddingByteIs(temp, 56, (byte)8)) {
+                    temp = Arrays.copyOf(temp, 48);
+                }
+            }
+
+            preMaster = temp;
+        } catch (Exception e) {
+            // Decrypting the pre-master secret was not possible.
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Error decrypting the pre-master secret");
+            }
+        }
+    }
+
+    /**
+     * Checks if all paddings of data are b
+     * @param data the block with padding
+     * @param len length of data, >= 48
+     * @param b expected padding byte
+     */
+    private static boolean paddingByteIs(byte[] data, int len, byte b) {
+        for (int i=48; i<len; i++) {
+            if (data[i] != b) return false;
+        }
+        return true;
+    }
+
+    // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
+    private static KerberosTicket getServiceTicket(String serverName,
+        final AccessControlContext acc) throws IOException {
+
+        if ("localhost".equals(serverName) ||
+                "localhost.localdomain".equals(serverName)) {
+
+            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                SSLLogger.fine("Get the local hostname");
+            }
+            String localHost = java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction<String>() {
+                public String run() {
+                    try {
+                        return InetAddress.getLocalHost().getHostName();
+                    } catch (UnknownHostException e) {
+                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                            SSLLogger.fine("Warning,"
+                                + " cannot get the local hostname: "
+                                + e.getMessage());
+                        }
+                        return null;
+                    }
+                }
+            });
+            if (localHost != null) {
+                serverName = localHost;
+            }
+        }
+
+        // Resolve serverName (possibly in IP addr form) to Kerberos principal
+        // name for service with hostname
+        String serviceName = "host/" + serverName;
+        PrincipalName principal;
+        try {
+            principal = new PrincipalName(serviceName,
+                                PrincipalName.KRB_NT_SRV_HST);
+        } catch (SecurityException se) {
+            throw se;
+        } catch (Exception e) {
+            IOException ioe = new IOException("Invalid service principal" +
+                                " name: " + serviceName);
+            ioe.initCause(e);
+            throw ioe;
+        }
+        String realm = principal.getRealmAsString();
+
+        final String serverPrincipal = principal.toString();
+        final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
+        final String clientPrincipal = null;  // use default
+
+
+        // check permission to obtain a service ticket to initiate a
+        // context with the "host" service
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+           sm.checkPermission(new ServicePermission(serverPrincipal,
+                                "initiate"), acc);
+        }
+
+        try {
+            KerberosTicket ticket = AccessController.doPrivileged(
+                new PrivilegedExceptionAction<KerberosTicket>() {
+                public KerberosTicket run() throws Exception {
+                    return Krb5Util.getTicketFromSubjectAndTgs(
+                        GSSCaller.CALLER_SSL_CLIENT,
+                        clientPrincipal, serverPrincipal,
+                        tgsPrincipal, acc);
+                        }});
+
+            if (ticket == null) {
+                throw new IOException("Failed to find any kerberos service" +
+                        " ticket for " + serverPrincipal);
+            }
+            return ticket;
+        } catch (PrivilegedActionException e) {
+            IOException ioe = new IOException(
+                "Attempt to obtain kerberos service ticket for " +
+                        serverPrincipal + " failed!");
+            ioe.initCause(e);
+            throw ioe;
+        }
+    }
+
+    /**
+     * Determines if a kvno matches another kvno. Used in the method
+     * findKey(etype, version, keys). Always returns true if either input
+     * is null or zero, in case any side does not have kvno info available.
+     *
+     * Note: zero is included because N/A is not a legal value for kvno
+     * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
+     * that the kvno is N/A might be lost when converting between
+     * EncryptionKey and KerberosKey.
+     */
+    private static boolean versionMatches(Integer v1, int v2) {
+        if (v1 == null || v1 == 0 || v2 == 0) {
+            return true;
+        }
+        return v1.equals(v2);
+    }
+
+    private static KerberosKey findKey(int etype, Integer version,
+            KerberosKey[] keys) throws KrbException {
+        int ktype;
+        boolean etypeFound = false;
+
+        // When no matched kvno is found, returns tke key of the same
+        // etype with the highest kvno
+        int kvno_found = 0;
+        KerberosKey key_found = null;
+
+        for (int i = 0; i < keys.length; i++) {
+            ktype = keys[i].getKeyType();
+            if (etype == ktype) {
+                int kv = keys[i].getVersionNumber();
+                etypeFound = true;
+                if (versionMatches(version, kv)) {
+                    return keys[i];
+                } else if (kv > kvno_found) {
+                    key_found = keys[i];
+                    kvno_found = kv;
+                }
+            }
+        }
+        // Key not found.
+        // %%% kludge to allow DES keys to be used for diff etypes
+        if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
+            etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
+            for (int i = 0; i < keys.length; i++) {
+                ktype = keys[i].getKeyType();
+                if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
+                        ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
+                    int kv = keys[i].getVersionNumber();
+                    etypeFound = true;
+                    if (versionMatches(version, kv)) {
+                        return new KerberosKey(keys[i].getPrincipal(),
+                            keys[i].getEncoded(),
+                            etype,
+                            kv);
+                    } else if (kv > kvno_found) {
+                        key_found = new KerberosKey(keys[i].getPrincipal(),
+                                keys[i].getEncoded(),
+                                etype,
+                                kv);
+                        kvno_found = kv;
+                    }
+                }
+            }
+        }
+        if (etypeFound) {
+            return key_found;
+        }
+        return null;
+    }
+}