8038089: TLS optional support for Kerberos cipher suites needs to be re-examine
Reviewed-by: xuelei
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Tue Jun 02 04:01:04 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Tue Jun 02 22:26:36 2015 +0800
@@ -44,8 +44,6 @@
import javax.net.ssl.*;
-import javax.security.auth.Subject;
-
import sun.security.ssl.HandshakeMessage.*;
import static sun.security.ssl.CipherSuite.KeyExchange.*;
@@ -234,7 +232,8 @@
case HandshakeMessage.ht_certificate:
if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
- || keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
+ || ClientKeyExchangeService.find(keyExchange.name) != null) {
+ // No external key exchange provider needs a cert now.
fatalSE(Alerts.alert_unexpected_message,
"unexpected server cert chain");
// NOTREACHED
@@ -333,13 +332,9 @@
throw new SSLProtocolException(
"Protocol violation: server sent a server key exchange"
+ " message for key exchange " + keyExchange);
- case K_KRB5:
- case K_KRB5_EXPORT:
- throw new SSLProtocolException(
- "unexpected receipt of server key exchange algorithm");
default:
throw new SSLProtocolException(
- "unsupported key exchange algorithm = "
+ "unsupported or unexpected key exchange algorithm = "
+ keyExchange);
}
break;
@@ -350,10 +345,11 @@
throw new SSLHandshakeException(
"Client authentication requested for "+
"anonymous cipher suite.");
- } else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
+ } else if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+ // No external key exchange provider needs a cert now.
throw new SSLHandshakeException(
"Client certificate requested for "+
- "kerberos cipher suite.");
+ "external cipher suite: " + keyExchange);
}
certRequest = new CertificateRequest(input, protocolVersion);
if (debug != null && Debug.isOn("handshake")) {
@@ -626,45 +622,17 @@
}
// validate subject identity
- if (sessionSuite.keyExchange == K_KRB5 ||
- sessionSuite.keyExchange == K_KRB5_EXPORT) {
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(sessionSuite.keyExchange.name);
+ if (p != null) {
Principal localPrincipal = session.getLocalPrincipal();
- Subject subject = null;
- try {
- subject = AccessController.doPrivileged(
- new PrivilegedExceptionAction<Subject>() {
- @Override
- public Subject run() throws Exception {
- return Krb5Helper.getClientSubject(getAccSE());
- }});
- } catch (PrivilegedActionException e) {
- subject = null;
- if (debug != null && Debug.isOn("session")) {
- System.out.println("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 (debug != null && Debug.isOn("session"))
- System.out.println("Subject identity is same");
- }
+ if (p.isRelated(true, getAccSE(), localPrincipal)) {
+ if (debug != null && Debug.isOn("session"))
+ System.out.println("Subject identity is same");
} else {
- if (debug != null && Debug.isOn("session"))
- System.out.println("Kerberos credentials are not" +
- " present in the current Subject; check if " +
- " javax.security.auth.useSubjectAsCreds" +
- " system property has been set to false");
- throw new SSLProtocolException
- ("Server resumed session with no subject");
+ throw new SSLProtocolException("Server resumed" +
+ " session with wrong subject identity or no subject");
}
}
@@ -1012,8 +980,14 @@
ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
+ default:
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p == null) {
+ // somethings very wrong
+ throw new RuntimeException
+ ("Unsupported key exchange: " + keyExchange);
+ }
String sniHostname = null;
for (SNIServerName serverName : requestedServerNames) {
if (serverName instanceof SNIHostName) {
@@ -1022,13 +996,13 @@
}
}
- KerberosClientKeyExchange kerberosMsg = null;
+ ClientKeyExchange exMsg = null;
if (sniHostname != null) {
// use first requested SNI hostname
try {
- kerberosMsg = new KerberosClientKeyExchange(
- sniHostname, getAccSE(), protocolVersion,
- sslContext.getSecureRandom());
+ exMsg = p.createClientExchange(
+ sniHostname, getAccSE(), protocolVersion,
+ sslContext.getSecureRandom());
} catch(IOException e) {
if (serverNamesAccepted) {
// server accepted requested SNI hostname,
@@ -1044,26 +1018,22 @@
}
}
- if (kerberosMsg == null) {
+ if (exMsg == null) {
String hostname = getHostSE();
if (hostname == null) {
throw new IOException("Hostname is required" +
- " to use Kerberos cipher suites");
+ " to use " + keyExchange + " key exchange");
}
- kerberosMsg = new KerberosClientKeyExchange(
- hostname, getAccSE(), protocolVersion,
- sslContext.getSecureRandom());
+ exMsg = p.createClientExchange(
+ hostname, getAccSE(), protocolVersion,
+ sslContext.getSecureRandom());
}
// Record the principals involved in exchange
- session.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
- session.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
- m2 = kerberosMsg;
+ session.setPeerPrincipal(exMsg.getPeerPrincipal());
+ session.setLocalPrincipal(exMsg.getLocalPrincipal());
+ m2 = exMsg;
break;
- default:
- // somethings very wrong
- throw new RuntimeException
- ("Unsupported key exchange: " + keyExchange);
}
if (debug != null && Debug.isOn("handshake")) {
m2.print(System.out);
@@ -1094,13 +1064,6 @@
case K_RSA_EXPORT:
preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
- byte[] secretBytes =
- ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret();
- preMasterSecret = new SecretKeySpec(secretBytes,
- "TlsPremasterSecret");
- break;
case K_DHE_RSA:
case K_DHE_DSS:
case K_DH_ANON:
@@ -1116,8 +1079,13 @@
preMasterSecret = ecdh.getAgreedSecret(serverKey);
break;
default:
- throw new IOException("Internal error: unknown key exchange "
- + keyExchange);
+ if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+ preMasterSecret =
+ ((ClientKeyExchange) m2).clientKeyExchange();
+ } else {
+ throw new IOException("Internal error: unknown key exchange "
+ + keyExchange);
+ }
}
calculateKeys(preMasterSecret, null);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java Tue Jun 02 22:26:36 2015 +0800
@@ -0,0 +1,60 @@
+/*
+ * 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 javax.crypto.SecretKey;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.Principal;
+
+/**
+ * Models a non-certificate based ClientKeyExchange
+ */
+public abstract class ClientKeyExchange extends HandshakeMessage {
+
+ public ClientKeyExchange() {
+ }
+
+ @Override
+ int messageType() {
+ return ht_client_key_exchange;
+ }
+
+ @Override
+ abstract public int messageLength();
+
+ @Override
+ abstract public void send(HandshakeOutStream s) throws IOException;
+
+ @Override
+ abstract public void print(PrintStream s) throws IOException;
+
+ abstract public SecretKey clientKeyExchange();
+
+ abstract public Principal getPeerPrincipal();
+
+ abstract public Principal getLocalPrincipal();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java Tue Jun 02 22:26:36 2015 +0800
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import sun.security.action.GetPropertyAction;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.util.*;
+
+/**
+ * Models a service that provides support for a particular client key exchange
+ * mode. Currently used to implement Kerberos-related cipher suites.
+ *
+ * @since 1.9
+ */
+public interface ClientKeyExchangeService {
+
+ static class Loader {
+ private static final Map<String,ClientKeyExchangeService>
+ providers = new HashMap<>();
+
+ static {
+ final String key = "java.home";
+ String path = AccessController.doPrivileged(
+ new GetPropertyAction(key), null,
+ new PropertyPermission(key, "read"));
+ ServiceLoader<ClientKeyExchangeService> sc =
+ AccessController.doPrivileged(
+ (PrivilegedAction<ServiceLoader<ClientKeyExchangeService>>)
+ () -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class),
+ null,
+ new FilePermission(new File(path, "-").toString(), "read"));
+ Iterator<ClientKeyExchangeService> iter = sc.iterator();
+ while (iter.hasNext()) {
+ ClientKeyExchangeService cs = iter.next();
+ for (String ex: cs.supported()) {
+ providers.put(ex, cs);
+ }
+ }
+ }
+
+ }
+
+ public static ClientKeyExchangeService find(String ex) {
+ return Loader.providers.get(ex);
+ }
+
+
+ /**
+ * Returns the supported key exchange modes by this provider.
+ * @return the supported key exchange modes
+ */
+ String[] supported();
+
+ /**
+ * Returns a generalized credential object on the server side. The server
+ * side can use the info to determine if a cipher suite can be enabled.
+ * @param acc the AccessControlContext of the SSL session
+ * @return the credential object
+ */
+ Object getServiceCreds(AccessControlContext acc);
+
+ /**
+ * Returns the host name for a service principal. The info can be used in
+ * SNI or host name verifier.
+ * @param principal the principal of a service
+ * @return the string formed host name
+ */
+ String getServiceHostName(Principal principal);
+
+ /**
+ * Returns whether the specified principal is related to the current
+ * SSLSession. The info can be used to verify a SSL resume.
+ * @param isClient if true called from client side, otherwise from server
+ * @param acc the AccessControlContext of the SSL session
+ * @param p the specified principal
+ * @return true if related
+ */
+ boolean isRelated(boolean isClient, AccessControlContext acc, Principal p);
+
+ /**
+ * Creates the ClientKeyExchange object on the client side.
+ * @param serverName the intented peer name
+ * @param acc the AccessControlContext of the SSL session
+ * @param protocolVersion the TLS protocol version
+ * @param rand the SecureRandom that will used to generate the premaster
+ * @return the new Exchanger object
+ * @throws IOException if there is an error
+ */
+ ClientKeyExchange createClientExchange(String serverName, AccessControlContext acc,
+ ProtocolVersion protocolVersion, SecureRandom rand) throws IOException;
+
+ /**
+ * Create the ClientKeyExchange on the server side.
+ * @param protocolVersion the protocol version
+ * @param clientVersion the input protocol version
+ * @param rand a SecureRandom object used to generate premaster
+ * (if the server has to create one)
+ * @param encodedTicket the ticket from client
+ * @param encrypted the encrypted premaster secret from client
+ * @param acc the AccessControlContext of the SSL session
+ * @param ServiceCreds the service side credentials object as retrived from
+ * {@link #getServiceCreds}
+ * @return the new Exchanger object
+ * @throws IOException if there is an error
+ */
+ ClientKeyExchange createServerExchange(
+ ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
+ SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
+ AccessControlContext acc, Object ServiceCreds) throws IOException;
+}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Tue Jun 02 04:01:04 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Tue Jun 02 22:26:36 2015 +0800
@@ -64,24 +64,9 @@
// 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;
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find("KRB5");
+ kerberosAvailable = (p != null);
}
static {
--- a/jdk/src/java.base/share/classes/sun/security/ssl/KerberosClientKeyExchange.java Tue Jun 02 04:01:04 2015 +0000
+++ /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/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Helper.java Tue Jun 02 04:01:04 2015 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2009, 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.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.Permission;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginException;
-
-/**
- * A helper class for Kerberos APIs.
- */
-public final class Krb5Helper {
-
- private Krb5Helper() { }
-
- // loads Krb5Proxy implementation class if available
- private static final String IMPL_CLASS =
- "sun.security.ssl.krb5.Krb5ProxyImpl";
-
- private static final Krb5Proxy proxy =
- AccessController.doPrivileged(new PrivilegedAction<Krb5Proxy>() {
- @Override
- public Krb5Proxy run() {
- try {
- Class<?> c = Class.forName(IMPL_CLASS, true, null);
- return (Krb5Proxy)c.newInstance();
- } catch (ClassNotFoundException cnf) {
- return null;
- } catch (InstantiationException e) {
- throw new AssertionError(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }});
-
- /**
- * 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");
- }
-
- /**
- * Returns the Subject associated with client-side of the SSL socket.
- */
- public static Subject getClientSubject(AccessControlContext acc)
- throws LoginException {
- ensureAvailable();
- return proxy.getClientSubject(acc);
- }
-
- /**
- * Returns the Subject associated with server-side of the SSL socket.
- */
- public static Subject getServerSubject(AccessControlContext acc)
- throws LoginException {
- ensureAvailable();
- return proxy.getServerSubject(acc);
- }
-
- /**
- * Returns the KerberosKeys for the default server-side principal.
- */
- public static Object getServiceCreds(AccessControlContext acc)
- throws LoginException {
- ensureAvailable();
- return proxy.getServiceCreds(acc);
- }
-
- /**
- * Returns the server-side principal name associated with the KerberosKey.
- */
- public static String getServerPrincipalName(Object serviceCreds) {
- ensureAvailable();
- return proxy.getServerPrincipalName(serviceCreds);
- }
-
- /**
- * Returns the hostname embedded in the principal name.
- */
- public static String getPrincipalHostName(Principal principal) {
- ensureAvailable();
- return proxy.getPrincipalHostName(principal);
- }
-
- /**
- * Returns a ServicePermission for the principal name and action.
- */
- public static Permission getServicePermission(String principalName,
- String action) {
- ensureAvailable();
- return proxy.getServicePermission(principalName, action);
- }
-
- /**
- * Determines if the Subject might contain creds for princ.
- */
- public static boolean isRelated(Subject subject, Principal princ) {
- ensureAvailable();
- return proxy.isRelated(subject, princ);
- }
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Proxy.java Tue Jun 02 04:01:04 2015 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2009, 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.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;
-
-/**
- * An interface to a subset of the Kerberos APIs to avoid a static dependency
- * on the types defined by these APIs.
- */
-public interface Krb5Proxy {
-
- /**
- * Returns the Subject associated with the client-side of the SSL socket.
- */
- Subject getClientSubject(AccessControlContext acc) throws LoginException;
-
- /**
- * Returns the Subject associated with the server-side of the SSL socket.
- */
- Subject getServerSubject(AccessControlContext acc) throws LoginException;
-
-
- /**
- * Returns the Kerberos ServiceCreds for the default server-side principal.
- */
- Object getServiceCreds(AccessControlContext acc) throws LoginException;
-
- /**
- * Returns the server-side principal name associated with the KerberosKey.
- */
- String getServerPrincipalName(Object serviceCreds);
-
- /**
- * Returns the hostname embedded in the principal name.
- */
- String getPrincipalHostName(Principal principal);
-
- /**
- * Returns a ServicePermission for the principal name and action.
- */
- Permission getServicePermission(String principalName, String action);
-
- /**
- * Determines if the Subject might contain creds for princ.
- */
- boolean isRelated(Subject subject, Principal princ);
-}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java Tue Jun 02 04:01:04 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java Tue Jun 02 22:26:36 2015 +0800
@@ -352,18 +352,13 @@
components.add("ECDH_ANON");
}
break;
- case K_KRB5:
- if (!forCertPathOnly) {
- components.add("KRB5");
+ default:
+ if (ClientKeyExchangeService.find(keyExchange.name) != null) {
+ if (!forCertPathOnly) {
+ components.add(keyExchange.name);
+ }
}
- break;
- case K_KRB5_EXPORT:
- if (!forCertPathOnly) {
- components.add("KRB5_EXPORT");
- }
- break;
- default:
- // ignore
+ // otherwise ignore
}
return components;
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Tue Jun 02 04:01:04 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Tue Jun 02 22:26:36 2015 +0800
@@ -425,10 +425,9 @@
// change record of peer identity even by accident, much
// less do it intentionally.
//
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
throw new SSLPeerUnverifiedException("no certificates expected"
- + " for Kerberos cipher suites");
+ + " for " + cipherSuite.keyExchange + " cipher suites");
}
if (peerCerts == null) {
throw new SSLPeerUnverifiedException("peer not authenticated");
@@ -481,10 +480,9 @@
// change record of peer identity even by accident, much
// less do it intentionally.
//
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
throw new SSLPeerUnverifiedException("no certificates expected"
- + " for Kerberos cipher suites");
+ + " for " + cipherSuite.keyExchange + " cipher suites");
}
if (peerCerts == null) {
throw new SSLPeerUnverifiedException("peer not authenticated");
@@ -522,10 +520,9 @@
* change record of peer identity even by accident, much
* less do it intentionally.
*/
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
throw new SSLPeerUnverifiedException("no certificates expected"
- + " for Kerberos cipher suites");
+ + " for " + cipherSuite.keyExchange + " cipher suites");
}
if (peerCerts != null) {
return peerCerts.clone();
@@ -540,7 +537,7 @@
*
* @return the peer's principal. Returns an X500Principal of the
* end-entity certificate for X509-based cipher suites, and
- * Principal for Kerberos cipher suites.
+ * Principal for Kerberos cipher suites, etc.
*
* @throws SSLPeerUnverifiedException if the peer's identity has not
* been verified
@@ -549,12 +546,10 @@
public Principal getPeerPrincipal()
throws SSLPeerUnverifiedException
{
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
if (peerPrincipal == null) {
throw new SSLPeerUnverifiedException("peer not authenticated");
} else {
- // Eliminate dependency on KerberosPrincipal
return peerPrincipal;
}
}
@@ -569,15 +564,13 @@
*
* @return the principal sent to the peer. Returns an X500Principal
* of the end-entity certificate for X509-based cipher suites, and
- * Principal for Kerberos cipher suites. If no principal was
+ * Principal for Kerberos cipher suites, etc. If no principal was
* sent, then null is returned.
*/
@Override
public Principal getLocalPrincipal() {
- if ((cipherSuite.keyExchange == K_KRB5) ||
- (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
- // Eliminate dependency on KerberosPrincipal
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
return (localPrincipal == null ? null : localPrincipal);
}
return (localCerts == null ? null :
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Tue Jun 02 04:01:04 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Tue Jun 02 22:26:36 2015 +0800
@@ -38,8 +38,6 @@
import javax.net.ssl.*;
-import javax.security.auth.Subject;
-
import sun.security.util.KeyUtil;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.HandshakeMessage.*;
@@ -237,18 +235,6 @@
handshakeState.update(pms, resumingSession);
preMasterSecret = this.clientKeyExchange(pms);
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
- KerberosClientKeyExchange kke =
- new KerberosClientKeyExchange(protocolVersion,
- clientRequestedVersion,
- sslContext.getSecureRandom(),
- input,
- this.getAccSE(),
- serviceCreds);
- handshakeState.update(kke, resumingSession);
- preMasterSecret = this.clientKeyExchange(kke);
- break;
case K_DHE_RSA:
case K_DHE_DSS:
case K_DH_ANON:
@@ -273,8 +259,24 @@
preMasterSecret = this.clientKeyExchange(ecdhcke);
break;
default:
- throw new SSLProtocolException
- ("Unrecognized key exchange: " + keyExchange);
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p == null) {
+ throw new SSLProtocolException
+ ("Unrecognized key exchange: " + keyExchange);
+ }
+ byte[] encodedTicket = input.getBytes16();
+ input.getBytes16();
+ byte[] secret = input.getBytes16();
+ ClientKeyExchange cke = p.createServerExchange(protocolVersion,
+ clientRequestedVersion,
+ sslContext.getSecureRandom(),
+ encodedTicket,
+ secret,
+ this.getAccSE(), serviceCreds);
+ handshakeState.update(cke, resumingSession);
+ preMasterSecret = this.clientKeyExchange(cke);
+ break;
}
//
@@ -624,47 +626,21 @@
// validate subject identity
if (resumingSession) {
CipherSuite suite = previous.getSuite();
- if (suite.keyExchange == K_KRB5 ||
- suite.keyExchange == K_KRB5_EXPORT) {
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(suite.keyExchange.name);
+ if (p != null) {
Principal localPrincipal = previous.getLocalPrincipal();
- Subject subject = null;
- try {
- subject = AccessController.doPrivileged(
- new PrivilegedExceptionAction<Subject>() {
- @Override
- public Subject run() throws Exception {
- return
- Krb5Helper.getServerSubject(getAccSE());
- }});
- } catch (PrivilegedActionException e) {
- subject = null;
- if (debug != null && Debug.isOn("session")) {
- System.out.println("Attempt to obtain" +
- " subject failed!");
- }
- }
-
- if (subject != null) {
- // Eliminate dependency on KerberosPrincipal
- if (Krb5Helper.isRelated(subject, localPrincipal)) {
- if (debug != null && Debug.isOn("session"))
- System.out.println("Subject can" +
- " provide creds for princ");
- } else {
- resumingSession = false;
- if (debug != null && Debug.isOn("session"))
- System.out.println("Subject cannot" +
- " provide creds for princ");
- }
+ if (p.isRelated(
+ false, getAccSE(), localPrincipal)) {
+ if (debug != null && Debug.isOn("session"))
+ System.out.println("Subject can" +
+ " provide creds for princ");
} else {
resumingSession = false;
if (debug != null && Debug.isOn("session"))
- System.out.println("Kerberos credentials are" +
- " not present in the current Subject;" +
- " check if " +
- " javax.security.auth.useSubjectAsCreds" +
- " system property has been set to false");
+ System.out.println("Subject cannot" +
+ " provide creds for princ");
}
}
}
@@ -871,9 +847,8 @@
* defined in the protocol spec are explicitly stated to require
* using RSA certificates.
*/
- if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
- // Server certificates are omitted for Kerberos ciphers
-
+ if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
+ // No external key exchange provider needs a cert now.
} else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
if (certs == null) {
throw new RuntimeException("no certificates");
@@ -915,9 +890,7 @@
ServerKeyExchange m3;
switch (keyExchange) {
case K_RSA:
- case K_KRB5:
- case K_KRB5_EXPORT:
- // no server key exchange for RSA or KRB5 ciphersuites
+ // no server key exchange for RSA ciphersuites
m3 = null;
break;
case K_RSA_EXPORT:
@@ -980,6 +953,13 @@
m3 = null;
break;
default:
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p != null) {
+ // No external key exchange provider needs a cert now.
+ m3 = null;
+ break;
+ }
throw new RuntimeException("internal error: " + keyExchange);
}
if (m3 != null) {
@@ -1000,10 +980,10 @@
// Needed only if server requires client to authenticate self.
// Illegal for anonymous flavors, so we need to check that.
//
- // CertificateRequest is omitted for Kerberos ciphers
+ // No external key exchange provider needs a cert now.
if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
- keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) {
+ ClientKeyExchangeService.find(keyExchange.name) == null) {
CertificateRequest m4;
X509Certificate caCerts[];
@@ -1313,13 +1293,6 @@
}
setupStaticECDHKeys();
break;
- case K_KRB5:
- case K_KRB5_EXPORT:
- // need Kerberos Key
- if (!setupKerberosKeys()) {
- return false;
- }
- break;
case K_DH_ANON:
// no certs needed for anonymous
setupEphemeralDHKeys(suite.exportable, null);
@@ -1331,8 +1304,26 @@
}
break;
default:
- // internal error, unknown key exchange
- throw new RuntimeException("Unrecognized cipherSuite: " + suite);
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find(keyExchange.name);
+ if (p == null) {
+ // internal error, unknown key exchange
+ throw new RuntimeException("Unrecognized cipherSuite: " + suite);
+ }
+ // need service creds
+ if (serviceCreds == null) {
+ AccessControlContext acc = getAccSE();
+ serviceCreds = p.getServiceCreds(acc);
+ if (serviceCreds != null) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Using serviceCreds");
+ }
+ }
+ if (serviceCreds == null) {
+ return false;
+ }
+ }
+ break;
}
setCipherSuite(suite);
@@ -1522,73 +1513,10 @@
return true;
}
- /**
- * Retrieve the Kerberos key for the specified server principal
- * from the JAAS configuration file.
- *
- * @return true if successful, false if not available or invalid
+ /*
+ * Returns premaster secret for external key exchange services.
*/
- private boolean setupKerberosKeys() {
- if (serviceCreds != null) {
- return true;
- }
- try {
- final AccessControlContext acc = getAccSE();
- 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 (debug != null && Debug.isOn("handshake")) {
- System.out.println("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) {
- 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");
- }
- return false;
- }
- }
- }
- return serviceCreds != null;
- } catch (PrivilegedActionException e) {
- // Likely exception here is LoginExceptin
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Attempt to obtain Kerberos key failed: "
- + e.toString());
- }
- return false;
- }
- }
-
- /*
- * For Kerberos ciphers, the premaster secret is encrypted using
- * the session key. See RFC 2712.
- */
- private SecretKey clientKeyExchange(KerberosClientKeyExchange mesg)
+ private SecretKey clientKeyExchange(ClientKeyExchange mesg)
throws IOException {
if (debug != null && Debug.isOn("handshake")) {
@@ -1599,8 +1527,7 @@
session.setPeerPrincipal(mesg.getPeerPrincipal());
session.setLocalPrincipal(mesg.getLocalPrincipal());
- byte[] b = mesg.getUnencryptedPreMasterSecret();
- return new SecretKeySpec(b, "TlsPremasterSecret");
+ return mesg.clientKeyExchange();
}
/*
@@ -1705,7 +1632,7 @@
*/
if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
// get X500Principal of the end-entity certificate for X509-based
- // ciphersuites, or Kerberos principal for Kerberos ciphersuites
+ // ciphersuites, or Kerberos principal for Kerberos ciphersuites, etc
session.getPeerPrincipal();
}
--- a/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java Tue Jun 02 04:01:04 2015 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java Tue Jun 02 22:26:36 2015 +0800
@@ -35,7 +35,7 @@
import javax.security.auth.x500.X500Principal;
-import sun.security.ssl.Krb5Helper;
+import sun.security.ssl.ClientKeyExchangeService;
import sun.security.x509.X500Name;
import sun.net.util.IPAddressUtil;
@@ -108,7 +108,12 @@
* Return the Server name from Kerberos principal.
*/
public static String getServerName(Principal principal) {
- return Krb5Helper.getPrincipalHostName(principal);
+ ClientKeyExchangeService p =
+ ClientKeyExchangeService.find("KRB5");
+ if (p == null) {
+ throw new AssertionError("Kerberos should have been available");
+ }
+ return p.getServiceHostName(principal);
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/META-INF/services/sun.security.ssl.ClientKeyExchangeService Tue Jun 02 22:26:36 2015 +0800
@@ -0,0 +1,2 @@
+sun.security.krb5.internal.ssl.Krb5KeyExchangeService
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java Tue Jun 02 22:26:36 2015 +0800
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.krb5.internal.ssl;
+
+import java.io.*;
+import java.security.*;
+import java.util.Arrays;
+
+import javax.net.ssl.*;
+
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.EncryptedData;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.internal.crypto.KeyUsage;
+
+import sun.security.ssl.Debug;
+import sun.security.ssl.HandshakeInStream;
+import sun.security.ssl.HandshakeMessage;
+import sun.security.ssl.ProtocolVersion;
+
+/**
+ * This is the Kerberos premaster secret in the Kerberos client key
+ * exchange message (CLIENT --> SERVER); it holds the
+ * Kerberos-encrypted pre-master secret. The secret is encrypted using the
+ * Kerberos session key. The padding and size of the resulting message
+ * depends on the session key type, but the pre-master secret is
+ * always exactly 48 bytes.
+ *
+ */
+final class KerberosPreMasterSecret {
+
+ private ProtocolVersion protocolVersion; // preMaster [0,1]
+ private byte preMaster[]; // 48 bytes
+ private byte encrypted[];
+
+ /**
+ * Constructor used by client to generate premaster secret.
+ *
+ * Client randomly creates a pre-master secret and encrypts it
+ * using the Kerberos session key; only the server can decrypt
+ * it, using the session key available in the service ticket.
+ *
+ * @param protocolVersion used to set preMaster[0,1]
+ * @param generator random number generator for generating premaster secret
+ * @param sessionKey Kerberos session key for encrypting premaster secret
+ */
+ KerberosPreMasterSecret(ProtocolVersion protocolVersion,
+ SecureRandom generator, EncryptionKey sessionKey) throws IOException {
+
+ if (sessionKey.getEType() ==
+ EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+ throw new IOException(
+ "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+ "are not supported for TLS Kerberos cipher suites");
+ }
+
+ this.protocolVersion = protocolVersion;
+ preMaster = generatePreMaster(generator, protocolVersion);
+
+ // Encrypt premaster secret
+ try {
+ EncryptedData eData = new EncryptedData(sessionKey, preMaster,
+ KeyUsage.KU_UNKNOWN);
+ encrypted = eData.getBytes(); // not ASN.1 encoded.
+
+ } catch (KrbException e) {
+ throw (SSLKeyException)new SSLKeyException
+ ("Kerberos premaster secret error").initCause(e);
+ }
+ }
+
+ /*
+ * Constructor used by server to decrypt encrypted premaster secret.
+ * The protocol version in preMaster[0,1] must match either currentVersion
+ * or clientVersion, otherwise, the premaster secret is set to
+ * a random one to foil possible attack.
+ *
+ * @param currentVersion version of protocol being used
+ * @param clientVersion version requested by client
+ * @param generator random number generator used to generate
+ * bogus premaster secret if premaster secret verification fails
+ * @param input input stream from which to read the encrypted
+ * premaster secret
+ * @param sessionKey Kerberos session key to be used for decryption
+ */
+ KerberosPreMasterSecret(ProtocolVersion currentVersion,
+ ProtocolVersion clientVersion,
+ SecureRandom generator, byte[] encrypted,
+ EncryptionKey sessionKey) throws IOException {
+
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ if (encrypted != null) {
+ Debug.println(System.out,
+ "encrypted premaster secret", encrypted);
+ }
+ }
+
+ if (sessionKey.getEType() ==
+ EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
+ throw new IOException(
+ "session keys with des3-cbc-hmac-sha1-kd encryption type " +
+ "are not supported for TLS Kerberos cipher suites");
+ }
+
+ // Decrypt premaster secret
+ try {
+ EncryptedData data = new EncryptedData(sessionKey.getEType(),
+ null /* optional kvno */, encrypted);
+
+ byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ if (encrypted != null) {
+ Debug.println(System.out,
+ "decrypted premaster secret", temp);
+ }
+ }
+
+ // Remove padding bytes after decryption. Only DES and DES3 have
+ // paddings and we don't support DES3 in TLS (see above)
+
+ if (temp.length == 52 &&
+ data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) {
+ // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00.
+ if (paddingByteIs(temp, 52, (byte)4) ||
+ paddingByteIs(temp, 52, (byte)0)) {
+ temp = Arrays.copyOf(temp, 48);
+ }
+ } else if (temp.length == 56 &&
+ data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) {
+ // For des-cbc-md5, 8 paddings with 0x08, or no padding
+ if (paddingByteIs(temp, 56, (byte)8)) {
+ temp = Arrays.copyOf(temp, 48);
+ }
+ }
+
+ preMaster = temp;
+
+ protocolVersion = ProtocolVersion.valueOf(preMaster[0],
+ preMaster[1]);
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ System.out.println("Kerberos PreMasterSecret version: "
+ + protocolVersion);
+ }
+ } catch (Exception e) {
+ // catch exception & process below
+ preMaster = null;
+ protocolVersion = currentVersion;
+ }
+
+ // check if the premaster secret version is ok
+ // the specification says that it must be the maximum version supported
+ // by the client from its ClientHello message. However, many
+ // old implementations send the negotiated version, so accept both
+ // for SSL v3.0 and TLS v1.0.
+ // NOTE that we may be comparing two unsupported version numbers in
+ // the second case, which is why we cannot use object references
+ // equality in this special case
+ boolean versionMismatch = (protocolVersion.v != clientVersion.v);
+
+ /*
+ * we never checked the client_version in server side
+ * for TLS v1.0 and SSL v3.0. For compatibility, we
+ * maintain this behavior.
+ */
+ if (versionMismatch && (clientVersion.v <= 0x0301)) {
+ versionMismatch = (protocolVersion.v != currentVersion.v);
+ }
+
+ /*
+ * Bogus decrypted ClientKeyExchange? If so, conjure a
+ * a random preMaster secret that will fail later during
+ * Finished message processing. This is a countermeasure against
+ * the "interactive RSA PKCS#1 encryption envelop attack" reported
+ * in June 1998. Preserving the executation path will
+ * mitigate timing attacks and force consistent error handling
+ * that will prevent an attacking client from differentiating
+ * different kinds of decrypted ClientKeyExchange bogosities.
+ */
+ if ((preMaster == null) || (preMaster.length != 48)
+ || versionMismatch) {
+ if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
+ System.out.println("Kerberos PreMasterSecret error, "
+ + "generating random secret");
+ if (preMaster != null) {
+ Debug.println(System.out, "Invalid secret", preMaster);
+ }
+ }
+
+ /*
+ * Randomize the preMaster secret with the
+ * ClientHello.client_version, as will produce invalid master
+ * secret to prevent the attacks.
+ */
+ preMaster = generatePreMaster(generator, clientVersion);
+ protocolVersion = clientVersion;
+ }
+ }
+
+ /**
+ * Checks if all paddings of data are b
+ * @param data the block with padding
+ * @param len length of data, >= 48
+ * @param b expected padding byte
+ */
+ private static boolean paddingByteIs(byte[] data, int len, byte b) {
+ for (int i=48; i<len; i++) {
+ if (data[i] != b) return false;
+ }
+ return true;
+ }
+
+ /*
+ * Used by server to generate premaster secret in case of
+ * problem decoding ticket.
+ *
+ * @param protocolVersion used for preMaster[0,1]
+ * @param generator random number generator to use for generating secret.
+ */
+ KerberosPreMasterSecret(ProtocolVersion protocolVersion,
+ SecureRandom generator) {
+
+ this.protocolVersion = protocolVersion;
+ preMaster = generatePreMaster(generator, protocolVersion);
+ }
+
+ private static byte[] generatePreMaster(SecureRandom rand,
+ ProtocolVersion ver) {
+
+ byte[] pm = new byte[48];
+ rand.nextBytes(pm);
+ pm[0] = ver.major;
+ pm[1] = ver.minor;
+
+ return pm;
+ }
+
+ // Clone not needed; internal use only
+ byte[] getUnencrypted() {
+ return preMaster;
+ }
+
+ // Clone not needed; internal use only
+ byte[] getEncrypted() {
+ return encrypted;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java Tue Jun 02 22:26:36 2015 +0800
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.krb5.internal.ssl;
+
+import sun.security.ssl.ClientKeyExchange;
+import sun.security.ssl.Debug;
+import sun.security.ssl.ClientKeyExchangeService;
+import sun.security.ssl.HandshakeOutStream;
+
+import sun.security.jgss.GSSCaller;
+import sun.security.jgss.krb5.Krb5Util;
+import sun.security.jgss.krb5.ServiceCreds;
+import sun.security.krb5.EncryptedData;
+import sun.security.krb5.EncryptionKey;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.PrincipalName;
+import sun.security.krb5.internal.EncTicketPart;
+import sun.security.krb5.internal.Ticket;
+import sun.security.krb5.internal.crypto.KeyUsage;
+import sun.security.ssl.ProtocolVersion;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.kerberos.KeyTab;
+import javax.security.auth.kerberos.ServicePermission;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.InetAddress;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureRandom;
+import java.util.Set;
+
+/**
+ * The provider for TLS_KRB_ cipher suites.
+ *
+ * @since 1.9
+ */
+public class Krb5KeyExchangeService implements ClientKeyExchangeService {
+
+ public static final Debug debug = Debug.getInstance("ssl");
+
+ @Override
+ public String[] supported() {
+ return new String[] { "KRB5", "KRB5_EXPORT" };
+ }
+
+ @Override
+ public Object getServiceCreds(AccessControlContext acc) {
+ try {
+ ServiceCreds serviceCreds = AccessController.doPrivileged(
+ (PrivilegedExceptionAction<ServiceCreds>)
+ () -> Krb5Util.getServiceCreds(
+ GSSCaller.CALLER_SSL_SERVER, null, acc));
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Using Kerberos creds");
+ }
+ String serverPrincipal = serviceCreds.getName();
+ if (serverPrincipal != null) {
+ // When service is bound, we check ASAP. Otherwise,
+ // will check after client request is received
+ // in in Kerberos ClientKeyExchange
+ SecurityManager sm = System.getSecurityManager();
+ try {
+ if (sm != null) {
+ // Eliminate dependency on ServicePermission
+ sm.checkPermission(new ServicePermission(
+ serverPrincipal, "accept"), acc);
+ }
+ } catch (SecurityException se) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Permission to access Kerberos"
+ + " secret key denied");
+ }
+ return null;
+ }
+ }
+ return serviceCreds;
+ } catch (PrivilegedActionException e) {
+ // Likely exception here is LoginException
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Attempt to obtain Kerberos key failed: "
+ + e.toString());
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public String getServiceHostName(Principal principal) {
+ if (principal == null) {
+ return null;
+ }
+ String hostName = null;
+ try {
+ PrincipalName princName =
+ new PrincipalName(principal.getName(),
+ PrincipalName.KRB_NT_SRV_HST);
+ String[] nameParts = princName.getNameStrings();
+ if (nameParts.length >= 2) {
+ hostName = nameParts[1];
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ return hostName;
+ }
+
+
+ @Override
+ public boolean isRelated(boolean isClient,
+ AccessControlContext acc, Principal p) {
+
+ if (p == null) return false;
+ try {
+ Subject subject = AccessController.doPrivileged(
+ (PrivilegedExceptionAction<Subject>)
+ () -> Krb5Util.getSubject(
+ isClient ? GSSCaller.CALLER_SSL_CLIENT
+ : GSSCaller.CALLER_SSL_SERVER,
+ acc));
+ if (subject == null) {
+ if (debug != null && Debug.isOn("session")) {
+ System.out.println("Kerberos credentials are" +
+ " not present in the current Subject;" +
+ " check if " +
+ " javax.security.auth.useSubjectAsCreds" +
+ " system property has been set to false");
+ }
+ return false;
+ }
+ Set<Principal> principals =
+ subject.getPrincipals(Principal.class);
+ if (principals.contains(p)) {
+ // bound to this principal
+ return true;
+ } else {
+ if (isClient) {
+ return false;
+ } else {
+ for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) {
+ if (!pc.isBound()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ } catch (PrivilegedActionException pae) {
+ if (debug != null && Debug.isOn("session")) {
+ System.out.println("Attempt to obtain" +
+ " subject failed! " + pae);
+ }
+ return false;
+ }
+
+ }
+
+ public ClientKeyExchange createClientExchange(
+ String serverName, AccessControlContext acc,
+ ProtocolVersion protocolVerson, SecureRandom rand) throws IOException {
+ return new ExchangerImpl(serverName, acc, protocolVerson, rand);
+ }
+
+ public ClientKeyExchange createServerExchange(
+ ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
+ SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
+ AccessControlContext acc, Object serviceCreds) throws IOException {
+ return new ExchangerImpl(protocolVersion, clientVersion, rand,
+ encodedTicket, encrypted, acc, serviceCreds);
+ }
+
+ static class ExchangerImpl extends ClientKeyExchange {
+
+ final private KerberosPreMasterSecret preMaster;
+ final private byte[] encodedTicket;
+ final private KerberosPrincipal peerPrincipal;
+ final private KerberosPrincipal localPrincipal;
+
+ @Override
+ public int messageLength() {
+ return encodedTicket.length + preMaster.getEncrypted().length + 6;
+ }
+
+ @Override
+ public void send(HandshakeOutStream s) throws IOException {
+ s.putBytes16(encodedTicket);
+ s.putBytes16(null);
+ s.putBytes16(preMaster.getEncrypted());
+ }
+
+ @Override
+ public void print(PrintStream s) throws IOException {
+ s.println("*** ClientKeyExchange, Kerberos");
+
+ if (debug != null && Debug.isOn("verbose")) {
+ Debug.println(s, "Kerberos service ticket", encodedTicket);
+ Debug.println(s, "Random Secret", preMaster.getUnencrypted());
+ Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted());
+ }
+ }
+
+ ExchangerImpl(String serverName, AccessControlContext acc,
+ ProtocolVersion protocolVersion, SecureRandom rand) throws IOException {
+
+ // Get service ticket
+ KerberosTicket ticket = getServiceTicket(serverName, acc);
+ encodedTicket = ticket.getEncoded();
+
+ // Record the Kerberos principals
+ peerPrincipal = ticket.getServer();
+ localPrincipal = ticket.getClient();
+
+ // Optional authenticator, encrypted using session key,
+ // currently ignored
+
+ // Generate premaster secret and encrypt it using session key
+ EncryptionKey sessionKey = new EncryptionKey(
+ ticket.getSessionKeyType(),
+ ticket.getSessionKey().getEncoded());
+
+ preMaster = new KerberosPreMasterSecret(protocolVersion,
+ rand, sessionKey);
+ }
+
+ ExchangerImpl(
+ ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand,
+ byte[] encodedTicket, byte[] encrypted,
+ AccessControlContext acc, Object serviceCreds) throws IOException {
+
+ // Read ticket
+ this.encodedTicket = encodedTicket;
+
+ if (debug != null && Debug.isOn("verbose")) {
+ Debug.println(System.out,
+ "encoded Kerberos service ticket", encodedTicket);
+ }
+
+ EncryptionKey sessionKey = null;
+ KerberosPrincipal tmpPeer = null;
+ KerberosPrincipal tmpLocal = null;
+
+ try {
+ Ticket t = new Ticket(encodedTicket);
+
+ EncryptedData encPart = t.encPart;
+ PrincipalName ticketSname = t.sname;
+
+ final ServiceCreds creds = (ServiceCreds)serviceCreds;
+ final KerberosPrincipal princ =
+ new KerberosPrincipal(ticketSname.toString());
+
+ // For bound service, permission already checked at setup
+ if (creds.getName() == null) {
+ SecurityManager sm = System.getSecurityManager();
+ try {
+ if (sm != null) {
+ // Eliminate dependency on ServicePermission
+ sm.checkPermission(new ServicePermission(
+ ticketSname.toString(), "accept"), acc);
+ }
+ } catch (SecurityException se) {
+ serviceCreds = null;
+ // Do not destroy keys. Will affect Subject
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Permission to access Kerberos"
+ + " secret key denied");
+ se.printStackTrace(System.out);
+ }
+ throw new IOException("Kerberos service not allowedy");
+ }
+ }
+ KerberosKey[] serverKeys = AccessController.doPrivileged(
+ new PrivilegedAction<KerberosKey[]>() {
+ @Override
+ public KerberosKey[] run() {
+ return creds.getKKeys(princ);
+ }
+ });
+ if (serverKeys.length == 0) {
+ throw new IOException("Found no key for " + princ +
+ (creds.getName() == null ? "" :
+ (", this keytab is for " + creds.getName() + " only")));
+ }
+
+ /*
+ * permission to access and use the secret key of the Kerberized
+ * "host" service is done in ServerHandshaker.getKerberosKeys()
+ * to ensure server has the permission to use the secret key
+ * before promising the client
+ */
+
+ // See if we have the right key to decrypt the ticket to get
+ // the session key.
+ int encPartKeyType = encPart.getEType();
+ Integer encPartKeyVersion = encPart.getKeyVersionNumber();
+ KerberosKey dkey = null;
+ try {
+ dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
+ } catch (KrbException ke) { // a kvno mismatch
+ throw new IOException(
+ "Cannot find key matching version number", ke);
+ }
+ if (dkey == null) {
+ // %%% Should print string repr of etype
+ throw new IOException("Cannot find key of appropriate type" +
+ " to decrypt ticket - need etype " + encPartKeyType);
+ }
+
+ EncryptionKey secretKey = new EncryptionKey(
+ encPartKeyType,
+ dkey.getEncoded());
+
+ // Decrypt encPart using server's secret key
+ byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
+
+ // Reset data stream after decryption, remove redundant bytes
+ byte[] temp = encPart.reset(bytes);
+ EncTicketPart encTicketPart = new EncTicketPart(temp);
+
+ // Record the Kerberos Principals
+ tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName());
+ tmpLocal = new KerberosPrincipal(ticketSname.getName());
+
+ sessionKey = encTicketPart.key;
+
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("server principal: " + ticketSname);
+ System.out.println("cname: " + encTicketPart.cname.toString());
+ }
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("KerberosWrapper error getting session key,"
+ + " generating random secret (" + e.getMessage() + ")");
+ }
+ sessionKey = null;
+ }
+
+ //input.getBytes16(); // XXX Read and ignore authenticator
+
+ if (sessionKey != null) {
+ preMaster = new KerberosPreMasterSecret(protocolVersion,
+ clientVersion, rand, encrypted, sessionKey);
+ } else {
+ // Generate bogus premaster secret
+ preMaster = new KerberosPreMasterSecret(clientVersion, rand);
+ }
+
+ peerPrincipal = tmpPeer;
+ localPrincipal = tmpLocal;
+ }
+
+ // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
+ private static KerberosTicket getServiceTicket(String serverName,
+ final AccessControlContext acc) throws IOException {
+
+ if ("localhost".equals(serverName) ||
+ "localhost.localdomain".equals(serverName)) {
+
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Get the local hostname");
+ }
+ String localHost = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<String>() {
+ public String run() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (java.net.UnknownHostException e) {
+ if (debug != null && Debug.isOn("handshake")) {
+ System.out.println("Warning,"
+ + " cannot get the local hostname: "
+ + e.getMessage());
+ }
+ return null;
+ }
+ }
+ });
+ if (localHost != null) {
+ serverName = localHost;
+ }
+ }
+
+ // Resolve serverName (possibly in IP addr form) to Kerberos principal
+ // name for service with hostname
+ String serviceName = "host/" + serverName;
+ PrincipalName principal;
+ try {
+ principal = new PrincipalName(serviceName,
+ PrincipalName.KRB_NT_SRV_HST);
+ } catch (SecurityException se) {
+ throw se;
+ } catch (Exception e) {
+ IOException ioe = new IOException("Invalid service principal" +
+ " name: " + serviceName);
+ ioe.initCause(e);
+ throw ioe;
+ }
+ String realm = principal.getRealmAsString();
+
+ final String serverPrincipal = principal.toString();
+ final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
+ final String clientPrincipal = null; // use default
+
+
+ // check permission to obtain a service ticket to initiate a
+ // context with the "host" service
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ServicePermission(serverPrincipal,
+ "initiate"), acc);
+ }
+
+ try {
+ KerberosTicket ticket = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<KerberosTicket>() {
+ public KerberosTicket run() throws Exception {
+ return Krb5Util.getTicketFromSubjectAndTgs(
+ GSSCaller.CALLER_SSL_CLIENT,
+ clientPrincipal, serverPrincipal,
+ tgsPrincipal, acc);
+ }});
+
+ if (ticket == null) {
+ throw new IOException("Failed to find any kerberos service" +
+ " ticket for " + serverPrincipal);
+ }
+ return ticket;
+ } catch (PrivilegedActionException e) {
+ IOException ioe = new IOException(
+ "Attempt to obtain kerberos service ticket for " +
+ serverPrincipal + " failed!");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+
+ @Override
+ public SecretKey clientKeyExchange() {
+ byte[] secretBytes = preMaster.getUnencrypted();
+ return new SecretKeySpec(secretBytes, "TlsPremasterSecret");
+ }
+
+ @Override
+ public Principal getPeerPrincipal() {
+ return peerPrincipal;
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ return localPrincipal;
+ }
+
+ /**
+ * Determines if a kvno matches another kvno. Used in the method
+ * findKey(etype, version, keys). Always returns true if either input
+ * is null or zero, in case any side does not have kvno info available.
+ *
+ * Note: zero is included because N/A is not a legal value for kvno
+ * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
+ * that the kvno is N/A might be lost when converting between
+ * EncryptionKey and KerberosKey.
+ */
+ private static boolean versionMatches(Integer v1, int v2) {
+ if (v1 == null || v1 == 0 || v2 == 0) {
+ return true;
+ }
+ return v1.equals(v2);
+ }
+
+ private static KerberosKey findKey(int etype, Integer version,
+ KerberosKey[] keys) throws KrbException {
+ int ktype;
+ boolean etypeFound = false;
+
+ // When no matched kvno is found, returns tke key of the same
+ // etype with the highest kvno
+ int kvno_found = 0;
+ KerberosKey key_found = null;
+
+ for (int i = 0; i < keys.length; i++) {
+ ktype = keys[i].getKeyType();
+ if (etype == ktype) {
+ int kv = keys[i].getVersionNumber();
+ etypeFound = true;
+ if (versionMatches(version, kv)) {
+ return keys[i];
+ } else if (kv > kvno_found) {
+ key_found = keys[i];
+ kvno_found = kv;
+ }
+ }
+ }
+ // Key not found.
+ // %%% kludge to allow DES keys to be used for diff etypes
+ if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
+ etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
+ for (int i = 0; i < keys.length; i++) {
+ ktype = keys[i].getKeyType();
+ if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
+ ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
+ int kv = keys[i].getVersionNumber();
+ etypeFound = true;
+ if (versionMatches(version, kv)) {
+ return new KerberosKey(keys[i].getPrincipal(),
+ keys[i].getEncoded(),
+ etype,
+ kv);
+ } else if (kv > kvno_found) {
+ key_found = new KerberosKey(keys[i].getPrincipal(),
+ keys[i].getEncoded(),
+ etype,
+ kv);
+ kvno_found = kv;
+ }
+ }
+ }
+ }
+ if (etypeFound) {
+ return key_found;
+ }
+ return null;
+ }
+ }
+}
--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java Tue Jun 02 04:01:04 2015 +0000
+++ /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/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java Tue Jun 02 04:01:04 2015 +0000
+++ /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/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java Tue Jun 02 04:01:04 2015 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2009, 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.security.AccessControlContext;
-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;
-
-import sun.security.jgss.GSSCaller;
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.PrincipalName;
-import sun.security.ssl.Krb5Proxy;
-
-/**
- * An implementation of Krb5Proxy that simply delegates to the appropriate
- * Kerberos APIs.
- */
-public class Krb5ProxyImpl implements Krb5Proxy {
-
- public Krb5ProxyImpl() { }
-
- @Override
- public Subject getClientSubject(AccessControlContext acc)
- throws LoginException {
- return Krb5Util.getSubject(GSSCaller.CALLER_SSL_CLIENT, acc);
- }
-
- @Override
- public Subject getServerSubject(AccessControlContext acc)
- throws LoginException {
- return Krb5Util.getSubject(GSSCaller.CALLER_SSL_SERVER, acc);
- }
-
- @Override
- public Object getServiceCreds(AccessControlContext acc)
- throws LoginException {
- ServiceCreds serviceCreds =
- Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc);
- return serviceCreds;
- }
-
- @Override
- public String getServerPrincipalName(Object serviceCreds) {
- return ((ServiceCreds)serviceCreds).getName();
- }
-
- @Override
- public String getPrincipalHostName(Principal principal) {
- if (principal == null) {
- return null;
- }
- String hostName = null;
- try {
- PrincipalName princName =
- new PrincipalName(principal.getName(),
- PrincipalName.KRB_NT_SRV_HST);
- String[] nameParts = princName.getNameStrings();
- if (nameParts.length >= 2) {
- hostName = nameParts[1];
- }
- } catch (Exception e) {
- // ignore
- }
- return hostName;
- }
-
-
- @Override
- public Permission getServicePermission(String principalName,
- String action) {
- return new ServicePermission(principalName, action);
- }
-
- @Override
- public boolean isRelated(Subject subject, Principal princ) {
- if (princ == null) return false;
- Set<Principal> principals =
- subject.getPrincipals(Principal.class);
- if (principals.contains(princ)) {
- // bound to this principal
- return true;
- }
- for (KeyTab pc: subject.getPrivateCredentials(KeyTab.class)) {
- if (!pc.isBound()) {
- return true;
- }
- }
- return false;
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/SSLwithPerms.java Tue Jun 02 22:26:36 2015 +0800
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8038089
+ * @summary TLS optional support for Kerberos cipher suites needs to be re-examined
+ * @library ../../../../java/security/testlibrary/
+ * @run main/othervm SSLwithPerms
+ */
+import java.io.*;
+import javax.net.ssl.*;
+import javax.security.auth.AuthPermission;
+import javax.security.auth.kerberos.ServicePermission;
+import java.net.SocketPermission;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.Principal;
+import java.security.Security;
+import java.security.SecurityPermission;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.PropertyPermission;
+
+import sun.security.jgss.GSSUtil;
+
+public class SSLwithPerms {
+
+ static String KRB5_CONF = "krb5.conf";
+ static String JAAS_CONF = "jaas.conf";
+ static String REALM = "REALM";
+ static String KTAB = "ktab";
+ static String HOST = "host." + REALM.toLowerCase(Locale.US);
+ static String SERVER = "host/" + HOST;
+ static String USER = "user";
+ static char[] PASS = "password".toCharArray();
+
+ public static void main(String[] args) throws Exception {
+
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+ if (args.length == 0) {
+ KDC kdc = KDC.create(REALM, HOST, 0, true);
+
+ kdc.addPrincipal(USER, PASS);
+ kdc.addPrincipalRandKey("krbtgt/" + REALM);
+ kdc.addPrincipalRandKey(SERVER);
+ KDC.saveConfig(KRB5_CONF, kdc);
+ kdc.writeKtab(KTAB);
+
+ File f = new File(JAAS_CONF);
+ FileOutputStream fos = new FileOutputStream(f);
+ fos.write((
+ "ssl {\n" +
+ " com.sun.security.auth.module.Krb5LoginModule required\n" +
+ " principal=\"" + SERVER + "\"\n" +
+ " useKeyTab=true\n" +
+ " keyTab=" + KTAB + "\n" +
+ " isInitiator=false\n" +
+ " storeKey=true;\n};\n"
+ ).getBytes());
+ fos.close();
+
+ Proc pc = Proc.create("SSLwithPerms")
+ .args("client")
+ .inheritIO()
+ .prop("java.security.manager", "")
+ .prop("java.security.krb5.conf", KRB5_CONF)
+ .prop("sun.net.spi.nameservice.provider.1", "ns,mock")
+ .prop("javax.net.ssl", "handshake")
+ .prop("sun.security.krb5.debug", "true")
+ .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
+ .perm(new PropertyPermission("sun.security.krb5.principal", "read"))
+ .perm(new FilePermission("port", "read"))
+ .perm(new FilePermission(KTAB, "read"))
+ .perm(new RuntimePermission("accessClassInPackage.sun.net.spi.nameservice"))
+ .perm(new AuthPermission("modifyPrincipals"))
+ .perm(new AuthPermission("modifyPrivateCredentials"))
+ .perm(new AuthPermission("doAs"))
+ .perm(new SocketPermission("127.0.0.1", "connect"))
+ .perm(new ServicePermission("host/host.realm@REALM", "initiate"))
+ .start();
+
+ Proc ps = Proc.create("SSLwithPerms")
+ .args("server")
+ .inheritIO()
+ .prop("java.security.manager", "")
+ .prop("java.security.krb5.conf", KRB5_CONF)
+ .prop("java.security.auth.login.config", JAAS_CONF)
+ .prop("javax.net.ssl", "handshake")
+ .prop("sun.security.krb5.debug", "true")
+ .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms"))
+ .perm(new AuthPermission("createLoginContext.ssl"))
+ .perm(new AuthPermission("doAs"))
+ .perm(new FilePermission("port", "write"))
+ .perm(new SocketPermission("127.0.0.1", "accept"))
+ .perm(new ServicePermission("host/host.realm@REALM", "accept"))
+ .start();
+
+ if (pc.waitFor() != 0) {
+ throw new Exception();
+ }
+ if (ps.waitFor() != 0) {
+ throw new Exception();
+ }
+ } else if (args[0].equals("client")) {
+ Context c;
+ c = Context.fromUserPass(USER, PASS, false);
+ c.doAs(new JsseClientAction(), null);
+ } else if (args[0].equals("server")) {
+ final Context s = Context.fromJAAS("ssl");
+ s.doAs(new JsseServerAction(), null);
+ }
+ }
+
+ private static class JsseClientAction implements Action {
+ public byte[] run(Context s, byte[] input) throws Exception {
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory) SSLSocketFactory.getDefault();
+ while (!Files.exists(Paths.get("port"))) {
+ Thread.sleep(100);
+ }
+ int port = ByteBuffer.allocate(4)
+ .put(Files.readAllBytes(Paths.get("port"))).getInt(0);
+ System.out.println("Connecting " + SERVER + ":" + port);
+ SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(HOST, port);
+
+ // Enable only a KRB5 cipher suite.
+ String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
+ sslSocket.setEnabledCipherSuites(enabledSuites);
+
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(Collections.singletonList(new SNIHostName(HOST)));
+ sslSocket.setSSLParameters(params);
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ sslSocket.getInputStream()));
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
+ sslSocket.getOutputStream()));
+
+ String outStr = "Hello There!\n";
+ out.write(outStr);
+ out.flush();
+ System.out.print("Sending " + outStr);
+
+ String inStr = in.readLine();
+ System.out.println("Received " + inStr);
+
+ String cipherSuiteChosen = sslSocket.getSession().getCipherSuite();
+ System.out.println("Cipher suite in use: " + cipherSuiteChosen);
+ Principal self = sslSocket.getSession().getLocalPrincipal();
+ System.out.println("I am: " + self.toString());
+ Principal peer = sslSocket.getSession().getPeerPrincipal();
+ System.out.println("Server is: " + peer.toString());
+
+ sslSocket.close();
+ return null;
+ }
+ }
+
+ private static class JsseServerAction implements Action {
+ public byte[] run(Context s, byte[] input) throws Exception {
+ SSLServerSocketFactory sslssf =
+ (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ SSLServerSocket sslServerSocket =
+ (SSLServerSocket) sslssf.createServerSocket(0); // any port
+ int port = sslServerSocket.getLocalPort();
+ System.out.println("Listening on " + port);
+
+ String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"};
+ sslServerSocket.setEnabledCipherSuites(enabledSuites);
+
+ Files.write(Paths.get("port"), ByteBuffer.allocate(4).putInt(port).array());
+ System.out.println("Waiting for incoming connection...");
+
+ SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
+
+ System.out.println("Got connection from client "
+ + sslSocket.getInetAddress());
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ sslSocket.getInputStream()));
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
+ sslSocket.getOutputStream()));
+
+ String inStr = in.readLine();
+ System.out.println("Received " + inStr);
+
+ String outStr = inStr + " " + new Date().toString() + "\n";
+ out.write(outStr);
+ System.out.println("Sending " + outStr);
+ out.flush();
+
+ String cipherSuiteChosen =
+ sslSocket.getSession().getCipherSuite();
+ System.out.println("Cipher suite in use: " + cipherSuiteChosen);
+ Principal self = sslSocket.getSession().getLocalPrincipal();
+ System.out.println("I am: " + self.toString());
+ Principal peer = sslSocket.getSession().getPeerPrincipal();
+ System.out.println("Client is: " + peer.toString());
+
+ sslSocket.close();
+ return null;
+ }
+ }
+}