|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.io.IOException; |
|
import java.math.BigInteger; |
|
import java.nio.ByteBuffer; |
|
import java.security.CryptoPrimitive; |
|
import java.security.GeneralSecurityException; |
|
import java.security.KeyFactory; |
|
import java.text.MessageFormat; |
|
import java.util.EnumSet; |
|
import java.util.Locale; |
|
import javax.crypto.SecretKey; |
|
import javax.crypto.interfaces.DHPublicKey; |
|
import javax.crypto.spec.DHParameterSpec; |
|
import javax.crypto.spec.DHPublicKeySpec; |
|
import javax.net.ssl.SSLHandshakeException; |
|
import sun.security.ssl.DHKeyExchange.DHECredentials; |
|
import sun.security.ssl.DHKeyExchange.DHEPossession; |
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
import sun.security.ssl.SupportedGroupsExtension.NamedGroup; |
|
import sun.misc.HexDumpEncoder; |
|
|
|
|
|
|
|
*/ |
|
final class DHClientKeyExchange { |
|
static final DHClientKeyExchangeConsumer dhHandshakeConsumer = |
|
new DHClientKeyExchangeConsumer(); |
|
static final DHClientKeyExchangeProducer dhHandshakeProducer = |
|
new DHClientKeyExchangeProducer(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class DHClientKeyExchangeMessage extends HandshakeMessage { |
|
private byte[] y; |
|
|
|
DHClientKeyExchangeMessage( |
|
HandshakeContext handshakeContext) throws IOException { |
|
super(handshakeContext); |
|
|
|
ClientHandshakeContext chc = |
|
(ClientHandshakeContext)handshakeContext; |
|
|
|
DHEPossession dhePossession = null; |
|
for (SSLPossession possession : chc.handshakePossessions) { |
|
if (possession instanceof DHEPossession) { |
|
dhePossession = (DHEPossession)possession; |
|
break; |
|
} |
|
} |
|
|
|
if (dhePossession == null) { |
|
|
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"No DHE credentials negotiated for client key exchange"); |
|
} |
|
|
|
DHPublicKey publicKey = dhePossession.publicKey; |
|
DHParameterSpec params = publicKey.getParams(); |
|
this.y = Utilities.toByteArray(publicKey.getY()); |
|
} |
|
|
|
DHClientKeyExchangeMessage(HandshakeContext handshakeContext, |
|
ByteBuffer m) throws IOException { |
|
super(handshakeContext); |
|
|
|
ServerHandshakeContext shc = |
|
(ServerHandshakeContext)handshakeContext; |
|
|
|
if (m.remaining() < 3) { |
|
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Invalid DH ClientKeyExchange message: insufficient data"); |
|
} |
|
|
|
this.y = Record.getBytes16(m); |
|
|
|
if (m.hasRemaining()) { |
|
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Invalid DH ClientKeyExchange message: unknown extra data"); |
|
} |
|
} |
|
|
|
@Override |
|
public SSLHandshake handshakeType() { |
|
return SSLHandshake.CLIENT_KEY_EXCHANGE; |
|
} |
|
|
|
@Override |
|
public int messageLength() { |
|
return y.length + 2; |
|
} |
|
|
|
@Override |
|
public void send(HandshakeOutStream hos) throws IOException { |
|
hos.putBytes16(y); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"DH ClientKeyExchange\": '{'\n" + |
|
" \"parameters\": '{'\n" + |
|
" \"dh_Yc\": '{'\n" + |
|
"{0}\n" + |
|
" '}',\n" + |
|
" '}'\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
|
|
HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
Object[] messageFields = { |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(y), " "), |
|
}; |
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class DHClientKeyExchangeProducer implements HandshakeProducer { |
|
|
|
private DHClientKeyExchangeProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
DHECredentials dheCredentials = null; |
|
for (SSLCredentials cd : chc.handshakeCredentials) { |
|
if (cd instanceof DHECredentials) { |
|
dheCredentials = (DHECredentials)cd; |
|
break; |
|
} |
|
} |
|
|
|
if (dheCredentials == null) { |
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"No DHE credentials negotiated for client key exchange"); |
|
} |
|
|
|
|
|
DHEPossession dhePossession = new DHEPossession( |
|
dheCredentials, chc.sslContext.getSecureRandom()); |
|
chc.handshakePossessions.add(dhePossession); |
|
DHClientKeyExchangeMessage ckem = |
|
new DHClientKeyExchangeMessage(chc); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Produced DH ClientKeyExchange handshake message", ckem); |
|
} |
|
|
|
|
|
ckem.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 DHClientKeyExchangeConsumer implements SSLConsumer { |
|
|
|
private DHClientKeyExchangeConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
ByteBuffer message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
DHEPossession dhePossession = null; |
|
for (SSLPossession possession : shc.handshakePossessions) { |
|
if (possession instanceof DHEPossession) { |
|
dhePossession = (DHEPossession)possession; |
|
break; |
|
} |
|
} |
|
|
|
if (dhePossession == null) { |
|
|
|
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"No expected DHE possessions for client key exchange"); |
|
} |
|
|
|
SSLKeyExchange ke = SSLKeyExchange.valueOf( |
|
shc.negotiatedCipherSuite.keyExchange, |
|
shc.negotiatedProtocol); |
|
if (ke == null) { |
|
|
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Not supported key exchange type"); |
|
} |
|
|
|
DHClientKeyExchangeMessage ckem = |
|
new DHClientKeyExchangeMessage(shc, message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Consuming DH ClientKeyExchange handshake message", ckem); |
|
} |
|
|
|
|
|
try { |
|
DHParameterSpec params = dhePossession.publicKey.getParams(); |
|
DHPublicKeySpec spec = new DHPublicKeySpec( |
|
new BigInteger(1, ckem.y), |
|
params.getP(), params.getG()); |
|
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman"); |
|
DHPublicKey peerPublicKey = |
|
(DHPublicKey)kf.generatePublic(spec); |
|
|
|
|
|
if (!shc.algorithmConstraints.permits( |
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
peerPublicKey)) { |
|
throw new SSLHandshakeException( |
|
"DHPublicKey does not comply to algorithm constraints"); |
|
} |
|
|
|
NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
shc.handshakeCredentials.add( |
|
new DHECredentials(peerPublicKey, namedGroup)); |
|
} catch (GeneralSecurityException | java.io.IOException e) { |
|
throw (SSLHandshakeException)(new SSLHandshakeException( |
|
"Could not generate DHPublicKey").initCause(e)); |
|
} |
|
|
|
|
|
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); |
|
} |
|
} |
|
} |
|
} |