提交 0311eae5 编写于 作者: V vinnie

6894643: Separate out dependency on Kerberos

Reviewed-by: alanb, xuelei
上级 aa7e1399
...@@ -39,6 +39,7 @@ AUTO_FILES_JAVA_DIRS = \ ...@@ -39,6 +39,7 @@ AUTO_FILES_JAVA_DIRS = \
sun/security/provider \ sun/security/provider \
sun/security/rsa \ sun/security/rsa \
sun/security/ssl \ sun/security/ssl \
sun/security/ssl/krb5 \
sun/security/timestamp \ sun/security/timestamp \
sun/security/validator \ sun/security/validator \
sun/security/x509 \ sun/security/x509 \
......
...@@ -40,7 +40,6 @@ import java.util.List; ...@@ -40,7 +40,6 @@ import java.util.List;
import java.security.Principal; import java.security.Principal;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
...@@ -413,14 +412,15 @@ final public class StartTlsResponseImpl extends StartTlsResponse { ...@@ -413,14 +412,15 @@ final public class StartTlsResponseImpl extends StartTlsResponse {
try { try {
HostnameChecker checker = HostnameChecker.getInstance( HostnameChecker checker = HostnameChecker.getInstance(
HostnameChecker.TYPE_LDAP); HostnameChecker.TYPE_LDAP);
Principal principal = getPeerPrincipal(session); // Use ciphersuite to determine whether Kerberos is active.
if (principal instanceof KerberosPrincipal) { if (session.getCipherSuite().startsWith("TLS_KRB5")) {
if (!checker.match(hostname, (KerberosPrincipal) principal)) { Principal principal = getPeerPrincipal(session);
if (!checker.match(hostname, principal)) {
throw new SSLPeerUnverifiedException( throw new SSLPeerUnverifiedException(
"hostname of the kerberos principal:" + principal + "hostname of the kerberos principal:" + principal +
" does not match the hostname:" + hostname); " does not match the hostname:" + hostname);
} }
} else { } else { // X.509
// get the subject's certificate // get the subject's certificate
certs = session.getPeerCertificates(); certs = session.getPeerCertificates();
......
...@@ -36,7 +36,6 @@ import java.security.Principal; ...@@ -36,7 +36,6 @@ import java.security.Principal;
import java.security.cert.*; import java.security.cert.*;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import javax.security.auth.kerberos.KerberosPrincipal;
import sun.security.util.HostnameChecker; import sun.security.util.HostnameChecker;
import sun.security.util.DerValue; import sun.security.util.DerValue;
...@@ -109,20 +108,18 @@ class VerifierWrapper implements javax.net.ssl.HostnameVerifier { ...@@ -109,20 +108,18 @@ class VerifierWrapper implements javax.net.ssl.HostnameVerifier {
/* /*
* In com.sun.net.ssl.HostnameVerifier the method is defined * In com.sun.net.ssl.HostnameVerifier the method is defined
* as verify(String urlHostname, String certHostname). * as verify(String urlHostname, String certHostname).
* This means we need to extract the hostname from the certificate * This means we need to extract the hostname from the X.509 certificate
* in this wrapper * or from the Kerberos principal name, in this wrapper.
*/ */
public boolean verify(String hostname, javax.net.ssl.SSLSession session) { public boolean verify(String hostname, javax.net.ssl.SSLSession session) {
try { try {
String serverName; String serverName;
Principal principal = getPeerPrincipal(session); // Use ciphersuite to determine whether Kerberos is active.
// X.500 principal or Kerberos principal. if (session.getCipherSuite().startsWith("TLS_KRB5")) {
// (Use ciphersuite check to determine whether Kerberos is present.)
if (session.getCipherSuite().startsWith("TLS_KRB5") &&
principal instanceof KerberosPrincipal) {
serverName = serverName =
HostnameChecker.getServerName((KerberosPrincipal)principal); HostnameChecker.getServerName(getPeerPrincipal(session));
} else {
} else { // X.509
Certificate[] serverChain = session.getPeerCertificates(); Certificate[] serverChain = session.getPeerCertificates();
if ((serverChain == null) || (serverChain.length == 0)) { if ((serverChain == null) || (serverChain.length == 0)) {
return false; return false;
......
...@@ -54,7 +54,6 @@ import java.util.Iterator; ...@@ -54,7 +54,6 @@ import java.util.Iterator;
import java.security.AccessController; import java.security.AccessController;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.net.ssl.*; import javax.net.ssl.*;
import sun.security.x509.X500Name; import sun.security.x509.X500Name;
...@@ -466,16 +465,14 @@ final class HttpsClient extends HttpClient ...@@ -466,16 +465,14 @@ final class HttpsClient extends HttpClient
HostnameChecker checker = HostnameChecker.getInstance( HostnameChecker checker = HostnameChecker.getInstance(
HostnameChecker.TYPE_TLS); HostnameChecker.TYPE_TLS);
Principal principal = getPeerPrincipal(); // Use ciphersuite to determine whether Kerberos is present.
// X.500 principal or Kerberos principal. if (cipher.startsWith("TLS_KRB5")) {
// (Use ciphersuite check to determine whether Kerberos is present.) if (!checker.match(host, getPeerPrincipal())) {
if (cipher.startsWith("TLS_KRB5") &&
principal instanceof KerberosPrincipal) {
if (!checker.match(host, (KerberosPrincipal)principal)) {
throw new SSLPeerUnverifiedException("Hostname checker" + throw new SSLPeerUnverifiedException("Hostname checker" +
" failed for Kerberos"); " failed for Kerberos");
} }
} else { } else { // X.509
// get the subject's certificate // get the subject's certificate
peerCerts = session.getPeerCertificates(); peerCerts = session.getPeerCertificates();
......
...@@ -74,7 +74,7 @@ final class CipherSuite implements Comparable { ...@@ -74,7 +74,7 @@ final class CipherSuite implements Comparable {
// Flag indicating if CipherSuite availability can change dynamically. // Flag indicating if CipherSuite availability can change dynamically.
// This is the case when we rely on a JCE cipher implementation that // This is the case when we rely on a JCE cipher implementation that
// may not be available in the installed JCE providers. // may not be available in the installed JCE providers.
// It is true because we might not have an ECC or Kerberos implementation. // It is true because we might not have an ECC implementation.
final static boolean DYNAMIC_AVAILABILITY = true; final static boolean DYNAMIC_AVAILABILITY = true;
private final static boolean ALLOW_ECC = Debug.getBooleanProperty private final static boolean ALLOW_ECC = Debug.getBooleanProperty
......
...@@ -44,9 +44,6 @@ import javax.crypto.spec.SecretKeySpec; ...@@ -44,9 +44,6 @@ import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*; import javax.net.ssl.*;
import javax.security.auth.Subject; import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.jgss.GSSCaller;
import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
...@@ -362,9 +359,7 @@ final class ClientHandshaker extends Handshaker { ...@@ -362,9 +359,7 @@ final class ClientHandshaker extends Handshaker {
subject = AccessController.doPrivileged( subject = AccessController.doPrivileged(
new PrivilegedExceptionAction<Subject>() { new PrivilegedExceptionAction<Subject>() {
public Subject run() throws Exception { public Subject run() throws Exception {
return Krb5Util.getSubject( return Krb5Helper.getClientSubject(getAccSE());
GSSCaller.CALLER_SSL_CLIENT,
getAccSE());
}}); }});
} catch (PrivilegedActionException e) { } catch (PrivilegedActionException e) {
subject = null; subject = null;
...@@ -375,8 +370,9 @@ final class ClientHandshaker extends Handshaker { ...@@ -375,8 +370,9 @@ final class ClientHandshaker extends Handshaker {
} }
if (subject != null) { if (subject != null) {
Set<KerberosPrincipal> principals = // Eliminate dependency on KerberosPrincipal
subject.getPrincipals(KerberosPrincipal.class); Set<Principal> principals =
subject.getPrincipals(Principal.class);
if (!principals.contains(localPrincipal)) { if (!principals.contains(localPrincipal)) {
throw new SSLProtocolException("Server resumed" + throw new SSLProtocolException("Server resumed" +
" session with wrong subject identity"); " session with wrong subject identity");
...@@ -754,7 +750,7 @@ final class ClientHandshaker extends Handshaker { ...@@ -754,7 +750,7 @@ final class ClientHandshaker extends Handshaker {
case K_KRB5: case K_KRB5:
case K_KRB5_EXPORT: case K_KRB5_EXPORT:
byte[] secretBytes = byte[] secretBytes =
((KerberosClientKeyExchange)m2).getPreMasterSecret().getUnencrypted(); ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret();
preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret"); preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret");
break; break;
case K_DHE_RSA: case K_DHE_RSA:
......
/* /*
* Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -160,7 +160,7 @@ public class Debug { ...@@ -160,7 +160,7 @@ public class Debug {
System.err.println(prefix + ": "+message); System.err.println(prefix + ": "+message);
} }
static void println(PrintStream s, String name, byte[] data) { public static void println(PrintStream s, String name, byte[] data) {
s.print(name + ": { "); s.print(name + ": { ");
if (data == null) { if (data == null) {
s.print("null"); s.print("null");
......
/* /*
* Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -52,7 +52,7 @@ import javax.net.ssl.SSLException; ...@@ -52,7 +52,7 @@ import javax.net.ssl.SSLException;
* *
* @author David Brownell * @author David Brownell
*/ */
class HandshakeInStream extends InputStream { public class HandshakeInStream extends InputStream {
InputRecord r; InputRecord r;
...@@ -196,7 +196,7 @@ class HandshakeInStream extends InputStream { ...@@ -196,7 +196,7 @@ class HandshakeInStream extends InputStream {
return b; return b;
} }
byte[] getBytes16() throws IOException { public byte[] getBytes16() throws IOException {
int len = getInt16(); int len = getInt16();
byte b[] = new byte[len]; byte b[] = new byte[len];
......
/* /*
* Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
* have any questions. * have any questions.
*/ */
package sun.security.ssl; package sun.security.ssl;
import java.io.*; import java.io.*;
...@@ -73,7 +72,7 @@ import sun.security.ssl.CipherSuite.*; ...@@ -73,7 +72,7 @@ import sun.security.ssl.CipherSuite.*;
* *
* @author David Brownell * @author David Brownell
*/ */
abstract class HandshakeMessage { public abstract class HandshakeMessage {
HandshakeMessage() { } HandshakeMessage() { }
...@@ -92,7 +91,7 @@ abstract class HandshakeMessage { ...@@ -92,7 +91,7 @@ abstract class HandshakeMessage {
static final byte ht_finished = 20; static final byte ht_finished = 20;
/* Class and subclass dynamic debugging support */ /* Class and subclass dynamic debugging support */
static final Debug debug = Debug.getInstance("ssl"); public static final Debug debug = Debug.getInstance("ssl");
/** /**
* Utility method to convert a BigInteger to a byte array in unsigned * Utility method to convert a BigInteger to a byte array in unsigned
...@@ -468,7 +467,6 @@ class CertificateMsg extends HandshakeMessage ...@@ -468,7 +467,6 @@ class CertificateMsg extends HandshakeMessage
} }
} }
/* /*
* ServerKeyExchange ... SERVER --> CLIENT * ServerKeyExchange ... SERVER --> CLIENT
* *
......
/* /*
* Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -41,7 +41,7 @@ import java.security.MessageDigest; ...@@ -41,7 +41,7 @@ import java.security.MessageDigest;
* *
* @author David Brownell * @author David Brownell
*/ */
class HandshakeOutStream extends OutputStream { public class HandshakeOutStream extends OutputStream {
private SSLSocketImpl socket; private SSLSocketImpl socket;
private SSLEngineImpl engine; private SSLEngineImpl engine;
...@@ -196,7 +196,7 @@ class HandshakeOutStream extends OutputStream { ...@@ -196,7 +196,7 @@ class HandshakeOutStream extends OutputStream {
write(b, 0, b.length); write(b, 0, b.length);
} }
void putBytes16(byte b[]) throws IOException { public void putBytes16(byte b[]) throws IOException {
if (b == null) { if (b == null) {
putInt16(0); putInt16(0);
return; return;
......
...@@ -29,199 +29,67 @@ import java.io.IOException; ...@@ -29,199 +29,67 @@ import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.security.AccessController; import java.security.AccessController;
import java.security.AccessControlContext; import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction; import java.security.Principal;
import java.security.PrivilegedActionException; import java.security.PrivilegedAction;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.net.InetAddress; import javax.crypto.SecretKey;
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.Realm;
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;
/** /**
* This is Kerberos option in the client key exchange message * A helper class that calls the KerberosClientKeyExchange implementation.
* (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
*
*/ */
final class KerberosClientKeyExchange extends HandshakeMessage { public class KerberosClientKeyExchange extends HandshakeMessage {
private KerberosPreMasterSecret preMaster;
private byte[] encodedTicket;
private KerberosPrincipal peerPrincipal;
private KerberosPrincipal localPrincipal;
/**
* 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
*/
KerberosClientKeyExchange(String serverName, boolean isLoopback,
AccessControlContext acc, ProtocolVersion protocolVersion,
SecureRandom rand) throws IOException {
// Get service ticket
KerberosTicket ticket = getServiceTicket(serverName, isLoopback, 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);
}
/** private static final String IMPL_CLASS =
* Creates an instance of KerberosClientKeyExchange from its ASN.1 encoding. "sun.security.ssl.krb5.KerberosClientKeyExchangeImpl";
* 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 serverKey server's master secret key
*/
KerberosClientKeyExchange(ProtocolVersion protocolVersion,
ProtocolVersion clientVersion,
SecureRandom rand, HandshakeInStream input, KerberosKey[] serverKeys)
throws IOException {
// Read ticket private static final Class<?> implClass = AccessController.doPrivileged(
encodedTicket = input.getBytes16(); new PrivilegedAction<Class<?>>() {
public Class<?> run() {
if (debug != null && Debug.isOn("verbose")) { try {
Debug.println(System.out, return Class.forName(IMPL_CLASS, true, null);
"encoded Kerberos service ticket", encodedTicket); } catch (ClassNotFoundException cnf) {
} return null;
}
EncryptionKey sessionKey = null; }
try {
Ticket t = new Ticket(encodedTicket);
EncryptedData encPart = t.encPart;
PrincipalName ticketSname = t.sname;
Realm ticketRealm = t.realm;
String serverPrincipal = serverKeys[0].getPrincipal().getName();
/*
* 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
*/
// Check that ticket Sname matches serverPrincipal
String ticketPrinc = ticketSname.toString().concat("@" +
ticketRealm.toString());
if (!ticketPrinc.equals(serverPrincipal)) {
if (debug != null && Debug.isOn("handshake"))
System.out.println("Service principal in Ticket does not"
+ " match associated principal in KerberosKey");
throw new IOException("Server principal is " +
serverPrincipal + " but ticket is for " +
ticketPrinc);
} }
);
// See if we have the right key to decrypt the ticket to get private final KerberosClientKeyExchange impl = createImpl();
// the session key.
int encPartKeyType = encPart.getEType(); private KerberosClientKeyExchange createImpl() {
KerberosKey dkey = findKey(encPartKeyType, serverKeys); if (getClass() == KerberosClientKeyExchange.class) {
if (dkey == null) { try {
// %%% Should print string repr of etype return (KerberosClientKeyExchange)implClass.newInstance();
throw new IOException( } catch (InstantiationException e) {
"Cannot find key of appropriate type to decrypt ticket - need etype " + throw new AssertionError(e);
encPartKeyType); } catch (IllegalAccessException e) {
throw new AssertionError(e);
} }
}
return null;
}
EncryptionKey secretKey = new EncryptionKey( public KerberosClientKeyExchange() {
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, true);
EncTicketPart encTicketPart = new EncTicketPart(temp);
// Record the Kerberos Principals
peerPrincipal =
new KerberosPrincipal(encTicketPart.cname.getName());
localPrincipal = new KerberosPrincipal(ticketSname.getName());
sessionKey = encTicketPart.key; public KerberosClientKeyExchange(String serverName, boolean isLoopback,
AccessControlContext acc, ProtocolVersion protocolVersion,
SecureRandom rand) throws IOException {
if (debug != null && Debug.isOn("handshake")) { if (impl != null) {
System.out.println("server principal: " + serverPrincipal); init(serverName, isLoopback, acc, protocolVersion, rand);
System.out.println("realm: " + encTicketPart.crealm.toString()); } else {
System.out.println("cname: " + encTicketPart.cname.toString()); throw new IllegalStateException("Kerberos is unavailable");
}
} 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 public KerberosClientKeyExchange(ProtocolVersion protocolVersion,
ProtocolVersion clientVersion, SecureRandom rand,
HandshakeInStream input, SecretKey[] serverKeys) throws IOException {
if (sessionKey != null) { if (impl != null) {
preMaster = new KerberosPreMasterSecret(protocolVersion, init(protocolVersion, clientVersion, rand, input, serverKeys);
clientVersion, rand, input, sessionKey);
} else { } else {
// Generate bogus premaster secret throw new IllegalStateException("Kerberos is unavailable");
preMaster = new KerberosPreMasterSecret(protocolVersion, rand);
} }
} }
...@@ -229,138 +97,46 @@ final class KerberosClientKeyExchange extends HandshakeMessage { ...@@ -229,138 +97,46 @@ final class KerberosClientKeyExchange extends HandshakeMessage {
return ht_client_key_exchange; return ht_client_key_exchange;
} }
int messageLength() { public int messageLength() {
return (6 + encodedTicket.length + preMaster.getEncrypted().length); return impl.messageLength();
} }
void send(HandshakeOutStream s) throws IOException { public void send(HandshakeOutStream s) throws IOException {
s.putBytes16(encodedTicket); impl.send(s);
s.putBytes16(null); // XXX no authenticator
s.putBytes16(preMaster.getEncrypted());
} }
void print(PrintStream s) throws IOException { @Override
s.println("*** ClientKeyExchange, Kerberos"); public void print(PrintStream p) throws IOException {
impl.print(p);
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 public void init(String serverName, boolean isLoopback,
private static KerberosTicket getServiceTicket(String srvName, AccessControlContext acc, ProtocolVersion protocolVersion,
boolean isLoopback, final AccessControlContext acc) throws IOException { SecureRandom rand) throws IOException {
// get the local hostname if srvName is loopback address
String serverName = srvName;
if (isLoopback) {
String localHost = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<String>() {
public String run() {
String hostname;
try {
hostname = InetAddress.getLocalHost().getHostName();
} catch (java.net.UnknownHostException e) {
hostname = "localhost";
}
return hostname;
}
});
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 if (impl != null) {
// context with the "host" service impl.init(serverName, isLoopback, acc, protocolVersion, rand);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new ServicePermission(serverPrincipal,
"initiate"), acc);
} }
}
try { public void init(ProtocolVersion protocolVersion,
KerberosTicket ticket = AccessController.doPrivileged( ProtocolVersion clientVersion, SecureRandom rand,
new PrivilegedExceptionAction<KerberosTicket>() { HandshakeInStream input, SecretKey[] serverKeys) throws IOException {
public KerberosTicket run() throws Exception {
return Krb5Util.getTicketFromSubjectAndTgs(
GSSCaller.CALLER_SSL_CLIENT,
clientPrincipal, serverPrincipal,
tgsPrincipal, acc);
}});
if (ticket == null) { if (impl != null) {
throw new IOException("Failed to find any kerberos service" + impl.init(protocolVersion, clientVersion, rand, input, serverKeys);
" 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;
} }
} }
KerberosPreMasterSecret getPreMasterSecret() { public byte[] getUnencryptedPreMasterSecret() {
return preMaster; return impl.getUnencryptedPreMasterSecret();
} }
KerberosPrincipal getPeerPrincipal() { public Principal getPeerPrincipal(){
return peerPrincipal; return impl.getPeerPrincipal();
} }
KerberosPrincipal getLocalPrincipal() { public Principal getLocalPrincipal(){
return localPrincipal; return impl.getLocalPrincipal();
}
private static KerberosKey findKey(int etype, KerberosKey[] keys) {
int ktype;
for (int i = 0; i < keys.length; i++) {
ktype = keys[i].getKeyType();
if (etype == ktype) {
return keys[i];
}
}
// 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) {
return new KerberosKey(keys[i].getPrincipal(),
keys[i].getEncoded(),
etype,
keys[i].getVersionNumber());
}
}
}
return null;
} }
} }
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.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>() {
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 SecretKey[] getServerKeys(AccessControlContext acc)
throws LoginException {
ensureAvailable();
return proxy.getServerKeys(acc);
}
/**
* Returns the server-side principal name associated with the KerberosKey.
*/
public static String getServerPrincipalName(SecretKey kerberosKey) {
ensureAvailable();
return proxy.getServerPrincipalName(kerberosKey);
}
/**
* 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);
}
}
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.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 KerberosKeys for the default server-side principal.
*/
SecretKey[] getServerKeys(AccessControlContext acc) throws LoginException;
/**
* Returns the server-side principal name associated with the KerberosKey.
*/
String getServerPrincipalName(SecretKey kerberosKey);
/**
* 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);
}
/* /*
* Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2002-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -45,7 +45,7 @@ package sun.security.ssl; ...@@ -45,7 +45,7 @@ package sun.security.ssl;
* @author Andreas Sterbenz * @author Andreas Sterbenz
* @since 1.4.1 * @since 1.4.1
*/ */
final class ProtocolVersion { public final class ProtocolVersion {
// dummy protocol version value for invalid SSLSession // dummy protocol version value for invalid SSLSession
final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE"); final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
...@@ -80,10 +80,10 @@ final class ProtocolVersion { ...@@ -80,10 +80,10 @@ final class ProtocolVersion {
// version in 16 bit MSB format as it appears in records and // version in 16 bit MSB format as it appears in records and
// messages, i.e. 0x0301 for TLS 1.0 // messages, i.e. 0x0301 for TLS 1.0
final int v; public final int v;
// major and minor version // major and minor version
final byte major, minor; public final byte major, minor;
// name used in JSSE (e.g. TLSv1 for TLS 1.0) // name used in JSSE (e.g. TLSv1 for TLS 1.0)
final String name; final String name;
...@@ -117,7 +117,7 @@ final class ProtocolVersion { ...@@ -117,7 +117,7 @@ final class ProtocolVersion {
* Return a ProtocolVersion with the specified major and minor version * Return a ProtocolVersion with the specified major and minor version
* numbers. Never throws exceptions. * numbers. Never throws exceptions.
*/ */
static ProtocolVersion valueOf(int major, int minor) { public static ProtocolVersion valueOf(int major, int minor) {
major &= 0xff; major &= 0xff;
minor &= 0xff; minor &= 0xff;
int v = (major << 8) | minor; int v = (major << 8) | minor;
......
...@@ -48,7 +48,6 @@ import javax.net.ssl.SSLPeerUnverifiedException; ...@@ -48,7 +48,6 @@ import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLPermission; import javax.net.ssl.SSLPermission;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import static sun.security.ssl.CipherSuite.*; import static sun.security.ssl.CipherSuite.*;
...@@ -469,8 +468,8 @@ final class SSLSessionImpl implements SSLSession { ...@@ -469,8 +468,8 @@ final class SSLSessionImpl implements SSLSession {
* defining the session. * defining the session.
* *
* @return the peer's principal. Returns an X500Principal of the * @return the peer's principal. Returns an X500Principal of the
* end-entity certiticate for X509-based cipher suites, and * end-entity certificate for X509-based cipher suites, and
* KerberosPrincipal for Kerberos cipher suites. * Principal for Kerberos cipher suites.
* *
* @throws SSLPeerUnverifiedException if the peer's identity has not * @throws SSLPeerUnverifiedException if the peer's identity has not
* been verified * been verified
...@@ -483,7 +482,8 @@ final class SSLSessionImpl implements SSLSession { ...@@ -483,7 +482,8 @@ final class SSLSessionImpl implements SSLSession {
if (peerPrincipal == null) { if (peerPrincipal == null) {
throw new SSLPeerUnverifiedException("peer not authenticated"); throw new SSLPeerUnverifiedException("peer not authenticated");
} else { } else {
return (KerberosPrincipal)peerPrincipal; // Eliminate dependency on KerberosPrincipal
return peerPrincipal;
} }
} }
if (peerCerts == null) { if (peerCerts == null) {
...@@ -497,15 +497,15 @@ final class SSLSessionImpl implements SSLSession { ...@@ -497,15 +497,15 @@ final class SSLSessionImpl implements SSLSession {
* *
* @return the principal sent to the peer. Returns an X500Principal * @return the principal sent to the peer. Returns an X500Principal
* of the end-entity certificate for X509-based cipher suites, and * of the end-entity certificate for X509-based cipher suites, and
* KerberosPrincipal for Kerberos cipher suites. If no principal was * Principal for Kerberos cipher suites. If no principal was
* sent, then null is returned. * sent, then null is returned.
*/ */
public Principal getLocalPrincipal() { public Principal getLocalPrincipal() {
if ((cipherSuite.keyExchange == K_KRB5) || if ((cipherSuite.keyExchange == K_KRB5) ||
(cipherSuite.keyExchange == K_KRB5_EXPORT)) { (cipherSuite.keyExchange == K_KRB5_EXPORT)) {
return (localPrincipal == null ? null : // Eliminate dependency on KerberosPrincipal
(KerberosPrincipal)localPrincipal); return (localPrincipal == null ? null : localPrincipal);
} }
return (localCerts == null ? null : return (localCerts == null ? null :
localCerts[0].getSubjectX500Principal()); localCerts[0].getSubjectX500Principal());
......
...@@ -39,11 +39,6 @@ import javax.crypto.spec.SecretKeySpec; ...@@ -39,11 +39,6 @@ import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*; import javax.net.ssl.*;
import javax.security.auth.Subject; import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.ServicePermission;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.jgss.GSSCaller;
import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager;
...@@ -69,7 +64,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -69,7 +64,7 @@ final class ServerHandshaker extends Handshaker {
private X509Certificate[] certs; private X509Certificate[] certs;
private PrivateKey privateKey; private PrivateKey privateKey;
private KerberosKey[] kerberosKeys; private SecretKey[] kerberosKeys;
// flag to check for clientCertificateVerify message // flag to check for clientCertificateVerify message
private boolean needClientVerify = false; private boolean needClientVerify = false;
...@@ -366,9 +361,8 @@ final class ServerHandshaker extends Handshaker { ...@@ -366,9 +361,8 @@ final class ServerHandshaker extends Handshaker {
subject = AccessController.doPrivileged( subject = AccessController.doPrivileged(
new PrivilegedExceptionAction<Subject>() { new PrivilegedExceptionAction<Subject>() {
public Subject run() throws Exception { public Subject run() throws Exception {
return Krb5Util.getSubject( return
GSSCaller.CALLER_SSL_SERVER, Krb5Helper.getServerSubject(getAccSE());
getAccSE());
}}); }});
} catch (PrivilegedActionException e) { } catch (PrivilegedActionException e) {
subject = null; subject = null;
...@@ -379,8 +373,9 @@ final class ServerHandshaker extends Handshaker { ...@@ -379,8 +373,9 @@ final class ServerHandshaker extends Handshaker {
} }
if (subject != null) { if (subject != null) {
Set<KerberosPrincipal> principals = // Eliminate dependency on KerberosPrincipal
subject.getPrincipals(KerberosPrincipal.class); Set<Principal> principals =
subject.getPrincipals(Principal.class);
if (!principals.contains(localPrincipal)) { if (!principals.contains(localPrincipal)) {
resumingSession = false; resumingSession = false;
if (debug != null && Debug.isOn("session")) { if (debug != null && Debug.isOn("session")) {
...@@ -914,11 +909,11 @@ final class ServerHandshaker extends Handshaker { ...@@ -914,11 +909,11 @@ final class ServerHandshaker extends Handshaker {
try { try {
final AccessControlContext acc = getAccSE(); final AccessControlContext acc = getAccSE();
kerberosKeys = AccessController.doPrivileged( kerberosKeys = AccessController.doPrivileged(
new PrivilegedExceptionAction<KerberosKey[]>() { // Eliminate dependency on KerberosKey
public KerberosKey[] run() throws Exception { new PrivilegedExceptionAction<SecretKey[]>() {
public SecretKey[] run() throws Exception {
// get kerberos key for the default principal // get kerberos key for the default principal
return Krb5Util.getKeys( return Krb5Helper.getServerKeys(acc);
GSSCaller.CALLER_SSL_SERVER, null, acc);
}}); }});
// check permission to access and use the secret key of the // check permission to access and use the secret key of the
...@@ -931,12 +926,13 @@ final class ServerHandshaker extends Handshaker { ...@@ -931,12 +926,13 @@ final class ServerHandshaker extends Handshaker {
} }
String serverPrincipal = String serverPrincipal =
kerberosKeys[0].getPrincipal().getName(); Krb5Helper.getServerPrincipalName(kerberosKeys[0]);
SecurityManager sm = System.getSecurityManager(); SecurityManager sm = System.getSecurityManager();
try { try {
if (sm != null) { if (sm != null) {
sm.checkPermission(new ServicePermission(serverPrincipal, // Eliminate dependency on ServicePermission
"accept"), acc); sm.checkPermission(Krb5Helper.getServicePermission(
serverPrincipal, "accept"), acc);
} }
} catch (SecurityException se) { } catch (SecurityException se) {
kerberosKeys = null; kerberosKeys = null;
...@@ -973,7 +969,7 @@ final class ServerHandshaker extends Handshaker { ...@@ -973,7 +969,7 @@ final class ServerHandshaker extends Handshaker {
session.setPeerPrincipal(mesg.getPeerPrincipal()); session.setPeerPrincipal(mesg.getPeerPrincipal());
session.setLocalPrincipal(mesg.getLocalPrincipal()); session.setLocalPrincipal(mesg.getLocalPrincipal());
byte[] b = mesg.getPreMasterSecret().getUnencrypted(); byte[] b = mesg.getUnencryptedPreMasterSecret();
return new SecretKeySpec(b, "TlsPremasterSecret"); return new SecretKeySpec(b, "TlsPremasterSecret");
} }
......
/*
* Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.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 javax.crypto.SecretKey;
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.Realm;
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.ssl.Debug;
import sun.security.ssl.HandshakeInStream;
import sun.security.ssl.HandshakeOutStream;
import sun.security.ssl.KerberosClientKeyExchange;
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, boolean isLoopback,
AccessControlContext acc, ProtocolVersion protocolVersion,
SecureRandom rand) throws IOException {
// Get service ticket
KerberosTicket ticket = getServiceTicket(serverName, isLoopback, 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 serverKey server's master secret key
*/
@Override
public void init(ProtocolVersion protocolVersion,
ProtocolVersion clientVersion,
SecureRandom rand, HandshakeInStream input, SecretKey[] secretKeys)
throws IOException {
KerberosKey[] serverKeys = (KerberosKey[])secretKeys;
// 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;
Realm ticketRealm = t.realm;
String serverPrincipal = serverKeys[0].getPrincipal().getName();
/*
* 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
*/
// Check that ticket Sname matches serverPrincipal
String ticketPrinc = ticketSname.toString().concat("@" +
ticketRealm.toString());
if (!ticketPrinc.equals(serverPrincipal)) {
if (debug != null && Debug.isOn("handshake"))
System.out.println("Service principal in Ticket does not"
+ " match associated principal in KerberosKey");
throw new IOException("Server principal is " +
serverPrincipal + " but ticket is for " +
ticketPrinc);
}
// See if we have the right key to decrypt the ticket to get
// the session key.
int encPartKeyType = encPart.getEType();
KerberosKey dkey = findKey(encPartKeyType, serverKeys);
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, true);
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: " + serverPrincipal);
System.out.println("realm: " + encTicketPart.crealm.toString());
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(protocolVersion, 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 srvName,
boolean isLoopback, final AccessControlContext acc) throws IOException {
// get the local hostname if srvName is loopback address
String serverName = srvName;
if (isLoopback) {
String localHost = java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<String>() {
public String run() {
String hostname;
try {
hostname = InetAddress.getLocalHost().getHostName();
} catch (java.net.UnknownHostException e) {
hostname = "localhost";
}
return hostname;
}
});
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;
}
private static KerberosKey findKey(int etype, KerberosKey[] keys) {
int ktype;
for (int i = 0; i < keys.length; i++) {
ktype = keys[i].getKeyType();
if (etype == ktype) {
return keys[i];
}
}
// 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) {
return new KerberosKey(keys[i].getPrincipal(),
keys[i].getEncoded(),
etype,
keys[i].getVersionNumber());
}
}
}
return null;
}
}
/* /*
* Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
* have any questions. * have any questions.
*/ */
package sun.security.ssl; package sun.security.ssl.krb5;
import java.io.*; import java.io.*;
import java.security.*; import java.security.*;
...@@ -36,6 +36,11 @@ import sun.security.krb5.EncryptedData; ...@@ -36,6 +36,11 @@ import sun.security.krb5.EncryptedData;
import sun.security.krb5.KrbException; import sun.security.krb5.KrbException;
import sun.security.krb5.internal.crypto.KeyUsage; 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 * This is the Kerberos premaster secret in the Kerberos client key
* exchange message (CLIENT --> SERVER); it holds the * exchange message (CLIENT --> SERVER); it holds the
...@@ -211,12 +216,12 @@ final class KerberosPreMasterSecret { ...@@ -211,12 +216,12 @@ final class KerberosPreMasterSecret {
return pm; return pm;
} }
// Clone not needed; package internal use only // Clone not needed; internal use only
byte[] getUnencrypted() { byte[] getUnencrypted() {
return preMaster; return preMaster;
} }
// Clone not needed; package internal use only // Clone not needed; internal use only
byte[] getEncrypted() { byte[] getEncrypted() {
return encrypted; return encrypted;
} }
......
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.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 javax.crypto.SecretKey;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosKey;
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.krb5.PrincipalName;
import sun.security.ssl.Krb5Proxy;
/**
* An implementatin 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 SecretKey[] getServerKeys(AccessControlContext acc)
throws LoginException {
return Krb5Util.getKeys(GSSCaller.CALLER_SSL_SERVER, null, acc);
}
@Override
public String getServerPrincipalName(SecretKey kerberosKey) {
return ((KerberosKey)kerberosKey).getPrincipal().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);
}
}
...@@ -32,10 +32,9 @@ import java.security.Principal; ...@@ -32,10 +32,9 @@ import java.security.Principal;
import java.security.cert.*; import java.security.cert.*;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import javax.security.auth.kerberos.KerberosPrincipal;
import sun.security.ssl.Krb5Helper;
import sun.security.x509.X500Name; import sun.security.x509.X500Name;
import sun.security.krb5.PrincipalName;
import sun.net.util.IPAddressUtil; import sun.net.util.IPAddressUtil;
...@@ -98,8 +97,7 @@ public class HostnameChecker { ...@@ -98,8 +97,7 @@ public class HostnameChecker {
/** /**
* Perform the check for Kerberos. * Perform the check for Kerberos.
*/ */
public static boolean match(String expectedName, public static boolean match(String expectedName, Principal principal) {
KerberosPrincipal principal) {
String hostName = getServerName(principal); String hostName = getServerName(principal);
return (expectedName.equalsIgnoreCase(hostName)); return (expectedName.equalsIgnoreCase(hostName));
} }
...@@ -107,23 +105,8 @@ public class HostnameChecker { ...@@ -107,23 +105,8 @@ public class HostnameChecker {
/** /**
* Return the Server name from Kerberos principal. * Return the Server name from Kerberos principal.
*/ */
public static String getServerName(KerberosPrincipal principal) { public static String getServerName(Principal principal) {
if (principal == null) { return Krb5Helper.getPrincipalHostName(principal);
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;
} }
/** /**
......
...@@ -30,4 +30,3 @@ public interface Action { ...@@ -30,4 +30,3 @@ public interface Action {
*/ */
byte[] run(Context s, byte[] input) throws Exception; byte[] run(Context s, byte[] input) throws Exception;
} }
...@@ -302,4 +302,3 @@ public class HttpNegotiateServer { ...@@ -302,4 +302,3 @@ public class HttpNegotiateServer {
} }
} }
} }
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
* @test
* @bug 6894643
* @summary Test JSSE Kerberos ciphersuite
*/
import java.io.*;
import java.net.InetAddress;
import javax.net.ssl.*;
import java.security.Principal;
import java.util.Date;
import sun.security.jgss.GSSUtil;
public class SSL {
private static final String KRB5_CIPHER = "TLS_KRB5_WITH_3DES_EDE_CBC_SHA";
private static final int PORT = 4569;
private static final int LOOP_LIMIT = 1;
private static final char[] PASS = "secret".toCharArray();
private static int loopCount = 0;
private static String SERVER;
public static void main(String[] args) throws Exception {
KDC kdc = KDC.create(OneKDC.REALM);
// Run this after KDC, so our own DNS service can be started
try {
SERVER = InetAddress.getLocalHost().getHostName();
} catch (java.net.UnknownHostException e) {
SERVER = "localhost";
}
kdc.addPrincipal(OneKDC.USER, OneKDC.PASS);
kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM);
kdc.addPrincipal("host/" + SERVER, PASS);
KDC.saveConfig(OneKDC.KRB5_CONF, kdc);
System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF);
final Context c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
final Context s = Context.fromUserPass("host/" + SERVER, PASS, true);
c.startAsClient("host/" + SERVER, GSSUtil.GSS_KRB5_MECH_OID);
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
new Thread(new Runnable() {
public void run() {
try {
s.doAs(new JsseServerAction(), null);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
// Warm the server
Thread.sleep(2000);
c.doAs(new JsseClientAction(), null);
}
// Following codes copied from
// http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/lab/part2.html#JSSE
private static class JsseClientAction implements Action {
public byte[] run(Context s, byte[] input) throws Exception {
SSLSocketFactory sslsf =
(SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(SERVER, PORT);
// Enable only a KRB5 cipher suite.
String enabledSuites[] = {KRB5_CIPHER};
sslSocket.setEnabledCipherSuites(enabledSuites);
// Should check for exception if enabledSuites is not supported
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(PORT);
// Enable only a KRB5 cipher suite.
String enabledSuites[] = {KRB5_CIPHER};
sslServerSocket.setEnabledCipherSuites(enabledSuites);
// Should check for exception if enabledSuites is not supported
while (loopCount++ < LOOP_LIMIT) {
System.out.println("Waiting for incoming connection...");
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
System.out.println("Got connection from client "
+ sslSocket.getInetAddress());
BufferedReader in = new BufferedReader(new InputStreamReader(
sslSocket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
sslSocket.getOutputStream()));
String inStr = in.readLine();
System.out.println("Received " + inStr);
String outStr = inStr + " " + new Date().toString() + "\n";
out.write(outStr);
System.out.println("Sending " + outStr);
out.flush();
String cipherSuiteChosen =
sslSocket.getSession().getCipherSuite();
System.out.println("Cipher suite in use: " + cipherSuiteChosen);
Principal self = sslSocket.getSession().getLocalPrincipal();
System.out.println("I am: " + self.toString());
Principal peer = sslSocket.getSession().getPeerPrincipal();
System.out.println("Client is: " + peer.toString());
sslSocket.close();
}
return null;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册