Mercurial > hg > openjdk > jdk8u > jdk
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>
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; + } +}