|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.io.IOException; |
|
import java.lang.reflect.InvocationTargetException; |
|
import java.nio.ByteBuffer; |
|
import java.security.AccessControlContext; |
|
import java.security.AccessController; |
|
import java.security.Principal; |
|
import java.security.PrivilegedAction; |
|
import java.text.MessageFormat; |
|
import java.util.Locale; |
|
import javax.crypto.SecretKey; |
|
import javax.net.ssl.SNIHostName; |
|
import javax.net.ssl.StandardConstants; |
|
|
|
import sun.misc.HexDumpEncoder; |
|
import sun.security.ssl.KrbKeyExchange.KrbPremasterSecret; |
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
|
|
|
|
|
|
*/ |
|
final class KrbClientKeyExchange { |
|
static final SSLConsumer krbHandshakeConsumer = |
|
new KrbClientKeyExchangeConsumer(); |
|
static final HandshakeProducer krbHandshakeProducer = |
|
new KrbClientKeyExchangeProducer(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class KrbClientKeyExchangeMessage extends HandshakeMessage { |
|
|
|
private static final String KRB5_CLASS_NAME = |
|
"sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl"; |
|
|
|
private static final Class<?> krb5Class = AccessController.doPrivileged( |
|
new PrivilegedAction<Class<?>>() { |
|
@Override |
|
public Class<?> run() { |
|
try { |
|
return Class.forName(KRB5_CLASS_NAME, true, null); |
|
} catch (ClassNotFoundException cnf) { |
|
return null; |
|
} |
|
} |
|
}); |
|
|
|
private static KrbClientKeyExchangeHelper newKrb5Instance() { |
|
if (krb5Class != null) { |
|
try { |
|
return (KrbClientKeyExchangeHelper)krb5Class. |
|
getDeclaredConstructor().newInstance(); |
|
} catch (InstantiationException | IllegalAccessException | |
|
NoSuchMethodException | InvocationTargetException e) { |
|
throw new AssertionError(e); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
private final KrbClientKeyExchangeHelper krb5Helper; |
|
|
|
private KrbClientKeyExchangeMessage(HandshakeContext context) { |
|
super(context); |
|
if ((krb5Helper = newKrb5Instance()) == null) |
|
throw new IllegalStateException("Kerberos is unavailable"); |
|
} |
|
|
|
KrbClientKeyExchangeMessage(HandshakeContext context, |
|
byte[] preMaster, String serverName, |
|
AccessControlContext acc) throws IOException { |
|
this(context); |
|
krb5Helper.init(preMaster, serverName, acc); |
|
} |
|
|
|
KrbClientKeyExchangeMessage(HandshakeContext context, |
|
ByteBuffer message, Object serverKeys, |
|
AccessControlContext acc) throws IOException { |
|
this(context); |
|
byte[] encodedTicket = Record.getBytes16(message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine("encoded Kerberos service ticket", |
|
encodedTicket); |
|
} |
|
|
|
Record.getBytes16(message); |
|
byte[] encryptedPreMasterSecret = Record.getBytes16(message); |
|
if (encryptedPreMasterSecret != null && SSLLogger.isOn && |
|
SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine("encrypted Kerberos pre-master secret", |
|
encryptedPreMasterSecret); |
|
} |
|
krb5Helper.init(encodedTicket, encryptedPreMasterSecret, |
|
serverKeys, acc); |
|
} |
|
|
|
@Override |
|
SSLHandshake handshakeType() { |
|
return SSLHandshake.CLIENT_KEY_EXCHANGE; |
|
} |
|
|
|
@Override |
|
int messageLength() { |
|
return (6 + krb5Helper.getEncodedTicket().length + |
|
krb5Helper.getEncryptedPreMasterSecret().length); |
|
} |
|
|
|
@Override |
|
void send(HandshakeOutStream hos) throws IOException { |
|
hos.putBytes16(krb5Helper.getEncodedTicket()); |
|
hos.putBytes16(null); |
|
hos.putBytes16(krb5Helper.getEncryptedPreMasterSecret()); |
|
} |
|
|
|
byte[] getPlainPreMasterSecret() { |
|
return krb5Helper.getPlainPreMasterSecret(); |
|
} |
|
|
|
Principal getPeerPrincipal() { |
|
return krb5Helper.getPeerPrincipal(); |
|
} |
|
|
|
Principal getLocalPrincipal() { |
|
return krb5Helper.getLocalPrincipal(); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"KRB5 ClientKeyExchange\": '{'\n" + |
|
" \"ticket\": '{'\n" + |
|
"{0}\n" + |
|
" '}'\n" + |
|
" \"pre-master\": '{'\n" + |
|
" \"plain\": '{'\n" + |
|
"{1}\n" + |
|
" '}'\n" + |
|
" \"encrypted\": '{'\n" + |
|
"{2}\n" + |
|
" '}'\n" + |
|
" '}'\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
|
|
HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
Object[] messageFields = { |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer( |
|
krb5Helper.getEncodedTicket()), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer( |
|
krb5Helper.getPlainPreMasterSecret()), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer( |
|
krb5Helper.getEncryptedPreMasterSecret()), " "), |
|
}; |
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class KrbClientKeyExchangeProducer implements HandshakeProducer { |
|
|
|
private KrbClientKeyExchangeProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
KrbClientKeyExchangeMessage kerberosMsg = null; |
|
String hostName = null; |
|
if (chc.negotiatedServerName != null) { |
|
if (chc.negotiatedServerName.getType() == |
|
StandardConstants.SNI_HOST_NAME) { |
|
SNIHostName sniHostName = null; |
|
if (chc.negotiatedServerName instanceof SNIHostName) { |
|
sniHostName = (SNIHostName) chc.negotiatedServerName; |
|
} else { |
|
try { |
|
sniHostName = new SNIHostName( |
|
chc.negotiatedServerName.getEncoded()); |
|
} catch (IllegalArgumentException iae) { |
|
// unlikely to happen, just in case ... |
|
} |
|
} |
|
if (sniHostName != null) |
|
hostName = sniHostName.getAsciiName(); |
|
} |
|
} else { |
|
hostName = chc.handshakeSession.getPeerHost(); |
|
} |
|
try { |
|
KrbPremasterSecret premasterSecret = |
|
KrbPremasterSecret.createPremasterSecret( |
|
chc.negotiatedProtocol, |
|
chc.sslContext.getSecureRandom()); |
|
kerberosMsg = new KrbClientKeyExchangeMessage(chc, |
|
premasterSecret.preMaster, hostName, |
|
chc.conContext.acc); |
|
chc.handshakePossessions.add(premasterSecret); |
|
} catch (IOException e) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Error generating KRB premaster secret." + |
|
" Hostname: " + hostName + " - Negotiated" + |
|
" server name: " + chc.negotiatedServerName); |
|
} |
|
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Cannot generate KRB premaster secret", e); |
|
} |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Produced KRB5 ClientKeyExchange handshake message", |
|
kerberosMsg); |
|
} |
|
|
|
|
|
chc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal()); |
|
chc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal()); |
|
|
|
|
|
kerberosMsg.write(chc.handshakeOutput); |
|
chc.handshakeOutput.flush(); |
|
|
|
|
|
SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
chc.negotiatedCipherSuite.keyExchange, |
|
chc.negotiatedProtocol); |
|
if (ke == null) { |
|
|
|
throw chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Not supported key exchange type"); |
|
} else { |
|
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); |
|
SecretKey masterSecret = |
|
masterKD.deriveKey("MasterSecret", null); |
|
|
|
chc.handshakeSession.setMasterSecret(masterSecret); |
|
|
|
SSLTrafficKeyDerivation kd = |
|
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); |
|
if (kd == null) { |
|
|
|
throw chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Not supported key derivation: " + |
|
chc.negotiatedProtocol); |
|
} else { |
|
chc.handshakeKeyDerivation = |
|
kd.createKeyDerivation(chc, masterSecret); |
|
} |
|
} |
|
|
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class KrbClientKeyExchangeConsumer implements SSLConsumer { |
|
|
|
private KrbClientKeyExchangeConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
ByteBuffer message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
Object serviceCreds = null; |
|
for (SSLPossession possession : shc.handshakePossessions) { |
|
if (possession instanceof KrbKeyExchange.KrbServiceCreds) { |
|
serviceCreds = ((KrbKeyExchange.KrbServiceCreds) possession) |
|
.serviceCreds; |
|
break; |
|
} |
|
} |
|
if (serviceCreds == null) { |
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"No Kerberos service credentials for KRB Client Key Exchange"); |
|
} |
|
|
|
KrbClientKeyExchangeMessage kerberosMsg = |
|
new KrbClientKeyExchangeMessage(shc, |
|
message, serviceCreds, shc.conContext.acc); |
|
KrbPremasterSecret premasterSecret = KrbPremasterSecret.decode( |
|
shc.negotiatedProtocol, |
|
ProtocolVersion.valueOf(shc.clientHelloVersion), |
|
kerberosMsg.getPlainPreMasterSecret(), |
|
shc.sslContext.getSecureRandom()); |
|
shc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal()); |
|
shc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal()); |
|
shc.handshakeCredentials.add(premasterSecret); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Consuming KRB5 ClientKeyExchange handshake message", |
|
kerberosMsg); |
|
} |
|
|
|
|
|
SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
shc.negotiatedCipherSuite.keyExchange, |
|
shc.negotiatedProtocol); |
|
if (ke == null) { |
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Not supported key exchange type"); |
|
} else { |
|
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); |
|
SecretKey masterSecret = |
|
masterKD.deriveKey("MasterSecret", null); |
|
|
|
|
|
shc.handshakeSession.setMasterSecret(masterSecret); |
|
SSLTrafficKeyDerivation kd = |
|
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); |
|
if (kd == null) { |
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Not supported key derivation: " + |
|
shc.negotiatedProtocol); |
|
} else { |
|
shc.handshakeKeyDerivation = |
|
kd.createKeyDerivation(shc, masterSecret); |
|
} |
|
} |
|
} |
|
} |
|
} |