|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.io.IOException; |
|
import java.nio.ByteBuffer; |
|
import java.security.AlgorithmConstraints; |
|
import java.security.CryptoPrimitive; |
|
import java.security.GeneralSecurityException; |
|
import java.security.KeyFactory; |
|
import java.security.PublicKey; |
|
import java.security.interfaces.ECPublicKey; |
|
import java.security.spec.ECParameterSpec; |
|
import java.security.spec.ECPoint; |
|
import java.security.spec.ECPublicKeySpec; |
|
import java.text.MessageFormat; |
|
import java.util.EnumSet; |
|
import java.util.Locale; |
|
import javax.crypto.SecretKey; |
|
import javax.net.ssl.SSLHandshakeException; |
|
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials; |
|
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; |
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
import sun.security.ssl.SupportedGroupsExtension.NamedGroup; |
|
import sun.security.ssl.X509Authentication.X509Credentials; |
|
import sun.security.ssl.X509Authentication.X509Possession; |
|
import sun.misc.HexDumpEncoder; |
|
|
|
|
|
|
|
*/ |
|
final class ECDHClientKeyExchange { |
|
static final SSLConsumer ecdhHandshakeConsumer = |
|
new ECDHClientKeyExchangeConsumer(); |
|
static final HandshakeProducer ecdhHandshakeProducer = |
|
new ECDHClientKeyExchangeProducer(); |
|
|
|
static final SSLConsumer ecdheHandshakeConsumer = |
|
new ECDHEClientKeyExchangeConsumer(); |
|
static final HandshakeProducer ecdheHandshakeProducer = |
|
new ECDHEClientKeyExchangeProducer(); |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class ECDHClientKeyExchangeMessage extends HandshakeMessage { |
|
private final byte[] encodedPoint; |
|
|
|
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, |
|
ECPublicKey publicKey) { |
|
super(handshakeContext); |
|
|
|
ECPoint point = publicKey.getW(); |
|
ECParameterSpec params = publicKey.getParams(); |
|
encodedPoint = JsseJce.encodePoint(point, params.getCurve()); |
|
} |
|
|
|
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, |
|
ByteBuffer m) throws IOException { |
|
super(handshakeContext); |
|
if (m.remaining() != 0) { |
|
this.encodedPoint = Record.getBytes8(m); |
|
} else { |
|
this.encodedPoint = new byte[0]; |
|
} |
|
} |
|
|
|
|
|
static void checkConstraints(AlgorithmConstraints constraints, |
|
ECPublicKey publicKey, |
|
byte[] encodedPoint) throws SSLHandshakeException { |
|
|
|
try { |
|
ECParameterSpec params = publicKey.getParams(); |
|
ECPoint point = |
|
JsseJce.decodePoint(encodedPoint, params.getCurve()); |
|
ECPublicKeySpec spec = new ECPublicKeySpec(point, params); |
|
|
|
KeyFactory kf = JsseJce.getKeyFactory("EC"); |
|
ECPublicKey peerPublicKey = |
|
(ECPublicKey)kf.generatePublic(spec); |
|
|
|
|
|
if (!constraints.permits( |
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
peerPublicKey)) { |
|
throw new SSLHandshakeException( |
|
"ECPublicKey does not comply to algorithm constraints"); |
|
} |
|
} catch (GeneralSecurityException | java.io.IOException e) { |
|
throw (SSLHandshakeException) new SSLHandshakeException( |
|
"Could not generate ECPublicKey").initCause(e); |
|
} |
|
} |
|
|
|
@Override |
|
public SSLHandshake handshakeType() { |
|
return SSLHandshake.CLIENT_KEY_EXCHANGE; |
|
} |
|
|
|
@Override |
|
public int messageLength() { |
|
if (encodedPoint == null || encodedPoint.length == 0) { |
|
return 0; |
|
} else { |
|
return 1 + encodedPoint.length; |
|
} |
|
} |
|
|
|
@Override |
|
public void send(HandshakeOutStream hos) throws IOException { |
|
if (encodedPoint != null && encodedPoint.length != 0) { |
|
hos.putBytes8(encodedPoint); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"ECDH ClientKeyExchange\": '{'\n" + |
|
" \"ecdh public\": '{'\n" + |
|
"{0}\n" + |
|
" '}',\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
if (encodedPoint == null || encodedPoint.length == 0) { |
|
Object[] messageFields = { |
|
" <implicit>" |
|
}; |
|
return messageFormat.format(messageFields); |
|
} else { |
|
HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
Object[] messageFields = { |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(encodedPoint), " "), |
|
}; |
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class ECDHClientKeyExchangeProducer implements HandshakeProducer { |
|
|
|
private ECDHClientKeyExchangeProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
X509Credentials x509Credentials = null; |
|
for (SSLCredentials credential : chc.handshakeCredentials) { |
|
if (credential instanceof X509Credentials) { |
|
x509Credentials = (X509Credentials)credential; |
|
break; |
|
} |
|
} |
|
|
|
if (x509Credentials == null) { |
|
throw chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"No server certificate for ECDH client key exchange"); |
|
} |
|
|
|
PublicKey publicKey = x509Credentials.popPublicKey; |
|
if (!publicKey.getAlgorithm().equals("EC")) { |
|
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Not EC server certificate for ECDH client key exchange"); |
|
} |
|
|
|
ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); |
|
NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
if (namedGroup == null) { |
|
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Unsupported EC server cert for ECDH client key exchange"); |
|
} |
|
|
|
ECDHEPossession ecdhePossession = new ECDHEPossession( |
|
namedGroup, chc.sslContext.getSecureRandom()); |
|
chc.handshakePossessions.add(ecdhePossession); |
|
ECDHClientKeyExchangeMessage cke = |
|
new ECDHClientKeyExchangeMessage( |
|
chc, ecdhePossession.publicKey); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Produced ECDH ClientKeyExchange handshake message", cke); |
|
} |
|
|
|
|
|
cke.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 ECDHClientKeyExchangeConsumer implements SSLConsumer { |
|
|
|
private ECDHClientKeyExchangeConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
ByteBuffer message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
X509Possession x509Possession = null; |
|
for (SSLPossession possession : shc.handshakePossessions) { |
|
if (possession instanceof X509Possession) { |
|
x509Possession = (X509Possession)possession; |
|
break; |
|
} |
|
} |
|
|
|
if (x509Possession == null) { |
|
|
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"No expected EC server cert for ECDH client key exchange"); |
|
} |
|
|
|
ECParameterSpec params = x509Possession.getECParameterSpec(); |
|
if (params == null) { |
|
|
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Not EC server cert for ECDH client key exchange"); |
|
} |
|
|
|
NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
if (namedGroup == null) { |
|
|
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Unsupported EC server cert for ECDH 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"); |
|
} |
|
|
|
|
|
ECDHClientKeyExchangeMessage cke = |
|
new ECDHClientKeyExchangeMessage(shc, message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Consuming ECDH ClientKeyExchange handshake message", cke); |
|
} |
|
|
|
|
|
try { |
|
ECPoint point = |
|
JsseJce.decodePoint(cke.encodedPoint, params.getCurve()); |
|
ECPublicKeySpec spec = new ECPublicKeySpec(point, params); |
|
|
|
KeyFactory kf = JsseJce.getKeyFactory("EC"); |
|
ECPublicKey peerPublicKey = |
|
(ECPublicKey)kf.generatePublic(spec); |
|
|
|
|
|
if (!shc.algorithmConstraints.permits( |
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
peerPublicKey)) { |
|
throw new SSLHandshakeException( |
|
"ECPublicKey does not comply to algorithm constraints"); |
|
} |
|
|
|
shc.handshakeCredentials.add(new ECDHECredentials( |
|
peerPublicKey, namedGroup)); |
|
} catch (GeneralSecurityException | java.io.IOException e) { |
|
throw (SSLHandshakeException)(new SSLHandshakeException( |
|
"Could not generate ECPublicKey").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); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class ECDHEClientKeyExchangeProducer implements HandshakeProducer { |
|
|
|
private ECDHEClientKeyExchangeProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
ECDHECredentials ecdheCredentials = null; |
|
for (SSLCredentials cd : chc.handshakeCredentials) { |
|
if (cd instanceof ECDHECredentials) { |
|
ecdheCredentials = (ECDHECredentials)cd; |
|
break; |
|
} |
|
} |
|
|
|
if (ecdheCredentials == null) { |
|
throw chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"No ECDHE credentials negotiated for client key exchange"); |
|
} |
|
|
|
ECDHEPossession ecdhePossession = new ECDHEPossession( |
|
ecdheCredentials, chc.sslContext.getSecureRandom()); |
|
chc.handshakePossessions.add(ecdhePossession); |
|
ECDHClientKeyExchangeMessage cke = |
|
new ECDHClientKeyExchangeMessage( |
|
chc, ecdhePossession.publicKey); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Produced ECDHE ClientKeyExchange handshake message", cke); |
|
} |
|
|
|
|
|
cke.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 ECDHEClientKeyExchangeConsumer implements SSLConsumer { |
|
|
|
private ECDHEClientKeyExchangeConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
ByteBuffer message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
|
|
ECDHEPossession ecdhePossession = null; |
|
for (SSLPossession possession : shc.handshakePossessions) { |
|
if (possession instanceof ECDHEPossession) { |
|
ecdhePossession = (ECDHEPossession)possession; |
|
break; |
|
} |
|
} |
|
if (ecdhePossession == null) { |
|
|
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"No expected ECDHE possessions for client key exchange"); |
|
} |
|
|
|
ECParameterSpec params = ecdhePossession.publicKey.getParams(); |
|
NamedGroup namedGroup = NamedGroup.valueOf(params); |
|
if (namedGroup == null) { |
|
|
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"Unsupported EC server cert for ECDHE 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"); |
|
} |
|
|
|
|
|
ECDHClientKeyExchangeMessage cke = |
|
new ECDHClientKeyExchangeMessage(shc, message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Consuming ECDHE ClientKeyExchange handshake message", cke); |
|
} |
|
|
|
|
|
try { |
|
ECPoint point = |
|
JsseJce.decodePoint(cke.encodedPoint, params.getCurve()); |
|
ECPublicKeySpec spec = new ECPublicKeySpec(point, params); |
|
|
|
KeyFactory kf = JsseJce.getKeyFactory("EC"); |
|
ECPublicKey peerPublicKey = |
|
(ECPublicKey)kf.generatePublic(spec); |
|
|
|
|
|
if (!shc.algorithmConstraints.permits( |
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), |
|
peerPublicKey)) { |
|
throw new SSLHandshakeException( |
|
"ECPublicKey does not comply to algorithm constraints"); |
|
} |
|
|
|
shc.handshakeCredentials.add(new ECDHECredentials( |
|
peerPublicKey, namedGroup)); |
|
} catch (GeneralSecurityException | java.io.IOException e) { |
|
throw (SSLHandshakeException)(new SSLHandshakeException( |
|
"Could not generate ECPublicKey").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); |
|
} |
|
} |
|
} |
|
} |