|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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.InvalidAlgorithmParameterException; |
|
import java.security.InvalidKeyException; |
|
import java.security.Key; |
|
import java.security.KeyFactory; |
|
import java.security.NoSuchAlgorithmException; |
|
import java.security.PrivateKey; |
|
import java.security.PublicKey; |
|
import java.security.Signature; |
|
import java.security.SignatureException; |
|
import java.text.MessageFormat; |
|
import java.util.EnumSet; |
|
import java.util.Locale; |
|
import java.util.Map; |
|
import javax.crypto.interfaces.DHPublicKey; |
|
import javax.crypto.spec.DHParameterSpec; |
|
import javax.crypto.spec.DHPublicKeySpec; |
|
import sun.security.ssl.DHKeyExchange.DHECredentials; |
|
import sun.security.ssl.DHKeyExchange.DHEPossession; |
|
import sun.security.ssl.SSLHandshake.HandshakeMessage; |
|
import sun.security.ssl.X509Authentication.X509Credentials; |
|
import sun.security.ssl.X509Authentication.X509Possession; |
|
import sun.security.util.HexDumpEncoder; |
|
import sun.security.util.KeyUtil; |
|
|
|
|
|
|
|
*/ |
|
final class DHServerKeyExchange { |
|
static final SSLConsumer dhHandshakeConsumer = |
|
new DHServerKeyExchangeConsumer(); |
|
static final HandshakeProducer dhHandshakeProducer = |
|
new DHServerKeyExchangeProducer(); |
|
|
|
|
|
|
|
*/ |
|
private static final |
|
class DHServerKeyExchangeMessage extends HandshakeMessage { |
|
// public key encapsulated in this message |
|
private final byte[] p; |
|
private final byte[] g; |
|
private final byte[] y; |
|
|
|
|
|
private final boolean useExplicitSigAlgorithm; |
|
private final SignatureScheme signatureScheme; |
|
|
|
|
|
private final byte[] paramsSignature; |
|
|
|
DHServerKeyExchangeMessage( |
|
HandshakeContext handshakeContext) throws IOException { |
|
super(handshakeContext); |
|
|
|
|
|
ServerHandshakeContext shc = |
|
(ServerHandshakeContext)handshakeContext; |
|
|
|
DHEPossession dhePossession = null; |
|
X509Possession x509Possession = null; |
|
for (SSLPossession possession : shc.handshakePossessions) { |
|
if (possession instanceof DHEPossession) { |
|
dhePossession = (DHEPossession)possession; |
|
if (x509Possession != null) { |
|
break; |
|
} |
|
} else if (possession instanceof X509Possession) { |
|
x509Possession = (X509Possession)possession; |
|
if (dhePossession != null) { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (dhePossession == null) { |
|
|
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, |
|
"No DHE credentials negotiated for server key exchange"); |
|
} |
|
DHPublicKey publicKey = dhePossession.publicKey; |
|
DHParameterSpec params = publicKey.getParams(); |
|
this.p = Utilities.toByteArray(params.getP()); |
|
this.g = Utilities.toByteArray(params.getG()); |
|
this.y = Utilities.toByteArray(publicKey.getY()); |
|
|
|
if (x509Possession == null) { |
|
|
|
paramsSignature = null; |
|
signatureScheme = null; |
|
useExplicitSigAlgorithm = false; |
|
} else { |
|
useExplicitSigAlgorithm = |
|
shc.negotiatedProtocol.useTLS12PlusSpec(); |
|
Signature signer; |
|
if (useExplicitSigAlgorithm) { |
|
Map.Entry<SignatureScheme, Signature> schemeAndSigner = |
|
SignatureScheme.getSignerOfPreferableAlgorithm( |
|
shc.algorithmConstraints, |
|
shc.peerRequestedSignatureSchemes, |
|
x509Possession, |
|
shc.negotiatedProtocol); |
|
if (schemeAndSigner == null) { |
|
// Unlikely, the credentials generator should have |
|
|
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"No supported signature algorithm for " + |
|
x509Possession.popPrivateKey.getAlgorithm() + |
|
" key"); |
|
} else { |
|
signatureScheme = schemeAndSigner.getKey(); |
|
signer = schemeAndSigner.getValue(); |
|
} |
|
} else { |
|
signatureScheme = null; |
|
try { |
|
signer = getSignature( |
|
x509Possession.popPrivateKey.getAlgorithm(), |
|
x509Possession.popPrivateKey); |
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) { |
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Unsupported signature algorithm: " + |
|
x509Possession.popPrivateKey.getAlgorithm(), e); |
|
} |
|
} |
|
|
|
byte[] signature; |
|
try { |
|
updateSignature(signer, shc.clientHelloRandom.randomBytes, |
|
shc.serverHelloRandom.randomBytes); |
|
signature = signer.sign(); |
|
} catch (SignatureException ex) { |
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Failed to sign dhe parameters: " + |
|
x509Possession.popPrivateKey.getAlgorithm(), ex); |
|
} |
|
paramsSignature = signature; |
|
} |
|
} |
|
|
|
DHServerKeyExchangeMessage(HandshakeContext handshakeContext, |
|
ByteBuffer m) throws IOException { |
|
super(handshakeContext); |
|
|
|
|
|
ClientHandshakeContext chc = |
|
(ClientHandshakeContext)handshakeContext; |
|
|
|
this.p = Record.getBytes16(m); |
|
this.g = Record.getBytes16(m); |
|
this.y = Record.getBytes16(m); |
|
|
|
try { |
|
KeyUtil.validate(new DHPublicKeySpec( |
|
new BigInteger(1, y), |
|
new BigInteger(1, p), |
|
new BigInteger(1, p))); |
|
} catch (InvalidKeyException ike) { |
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Invalid DH ServerKeyExchange: invalid parameters", ike); |
|
} |
|
|
|
X509Credentials x509Credentials = null; |
|
for (SSLCredentials cd : chc.handshakeCredentials) { |
|
if (cd instanceof X509Credentials) { |
|
x509Credentials = (X509Credentials)cd; |
|
break; |
|
} |
|
} |
|
|
|
if (x509Credentials == null) { |
|
|
|
if (m.hasRemaining()) { |
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Invalid DH ServerKeyExchange: unknown extra data"); |
|
} |
|
|
|
this.signatureScheme = null; |
|
this.paramsSignature = null; |
|
this.useExplicitSigAlgorithm = false; |
|
|
|
return; |
|
} |
|
|
|
this.useExplicitSigAlgorithm = |
|
chc.negotiatedProtocol.useTLS12PlusSpec(); |
|
if (useExplicitSigAlgorithm) { |
|
int ssid = Record.getInt16(m); |
|
signatureScheme = SignatureScheme.valueOf(ssid); |
|
if (signatureScheme == null) { |
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Invalid signature algorithm (" + ssid + |
|
") used in DH ServerKeyExchange handshake message"); |
|
} |
|
|
|
if (!chc.localSupportedSignAlgs.contains(signatureScheme)) { |
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Unsupported signature algorithm (" + |
|
signatureScheme.name + |
|
") used in DH ServerKeyExchange handshake message"); |
|
} |
|
} else { |
|
this.signatureScheme = null; |
|
} |
|
|
|
|
|
this.paramsSignature = Record.getBytes16(m); |
|
Signature signer; |
|
if (useExplicitSigAlgorithm) { |
|
try { |
|
signer = signatureScheme.getVerifier( |
|
x509Credentials.popPublicKey); |
|
} catch (NoSuchAlgorithmException | InvalidKeyException | |
|
InvalidAlgorithmParameterException nsae) { |
|
throw chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Unsupported signature algorithm: " + |
|
signatureScheme.name, nsae); |
|
} |
|
} else { |
|
try { |
|
signer = getSignature( |
|
x509Credentials.popPublicKey.getAlgorithm(), |
|
x509Credentials.popPublicKey); |
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) { |
|
throw chc.conContext.fatal(Alert.INTERNAL_ERROR, |
|
"Unsupported signature algorithm: " + |
|
x509Credentials.popPublicKey.getAlgorithm(), e); |
|
} |
|
} |
|
|
|
try { |
|
updateSignature(signer, |
|
chc.clientHelloRandom.randomBytes, |
|
chc.serverHelloRandom.randomBytes); |
|
|
|
if (!signer.verify(paramsSignature)) { |
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Invalid signature on DH ServerKeyExchange message"); |
|
} |
|
} catch (SignatureException ex) { |
|
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"Cannot verify DH ServerKeyExchange signature", ex); |
|
} |
|
} |
|
|
|
@Override |
|
public SSLHandshake handshakeType() { |
|
return SSLHandshake.SERVER_KEY_EXCHANGE; |
|
} |
|
|
|
@Override |
|
public int messageLength() { |
|
int sigLen = 0; |
|
if (paramsSignature != null) { |
|
sigLen = 2 + paramsSignature.length; |
|
if (useExplicitSigAlgorithm) { |
|
sigLen += SignatureScheme.sizeInRecord(); |
|
} |
|
} |
|
|
|
return 6 + p.length + g.length + y.length + sigLen; |
|
// 6: overhead for p, g, y values |
|
} |
|
|
|
@Override |
|
public void send(HandshakeOutStream hos) throws IOException { |
|
hos.putBytes16(p); |
|
hos.putBytes16(g); |
|
hos.putBytes16(y); |
|
|
|
if (paramsSignature != null) { |
|
if (useExplicitSigAlgorithm) { |
|
hos.putInt16(signatureScheme.id); |
|
} |
|
|
|
hos.putBytes16(paramsSignature); |
|
} |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
if (paramsSignature == null) { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"DH ServerKeyExchange\": '{'\n" + |
|
" \"parameters\": '{'\n" + |
|
" \"dh_p\": '{'\n" + |
|
"{0}\n" + |
|
" '}',\n" + |
|
" \"dh_g\": '{'\n" + |
|
"{1}\n" + |
|
" '}',\n" + |
|
" \"dh_Ys\": '{'\n" + |
|
"{2}\n" + |
|
" '}',\n" + |
|
" '}'\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
|
|
HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
Object[] messageFields = { |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(p), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(g), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(y), " "), |
|
}; |
|
|
|
return messageFormat.format(messageFields); |
|
} |
|
|
|
if (useExplicitSigAlgorithm) { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"DH ServerKeyExchange\": '{'\n" + |
|
" \"parameters\": '{'\n" + |
|
" \"dh_p\": '{'\n" + |
|
"{0}\n" + |
|
" '}',\n" + |
|
" \"dh_g\": '{'\n" + |
|
"{1}\n" + |
|
" '}',\n" + |
|
" \"dh_Ys\": '{'\n" + |
|
"{2}\n" + |
|
" '}',\n" + |
|
" '}',\n" + |
|
" \"digital signature\": '{'\n" + |
|
" \"signature algorithm\": \"{3}\"\n" + |
|
" \"signature\": '{'\n" + |
|
"{4}\n" + |
|
" '}',\n" + |
|
" '}'\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
|
|
HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
Object[] messageFields = { |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(p), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(g), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(y), " "), |
|
signatureScheme.name, |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(paramsSignature), " ") |
|
}; |
|
|
|
return messageFormat.format(messageFields); |
|
} else { |
|
MessageFormat messageFormat = new MessageFormat( |
|
"\"DH ServerKeyExchange\": '{'\n" + |
|
" \"parameters\": '{'\n" + |
|
" \"dh_p\": '{'\n" + |
|
"{0}\n" + |
|
" '}',\n" + |
|
" \"dh_g\": '{'\n" + |
|
"{1}\n" + |
|
" '}',\n" + |
|
" \"dh_Ys\": '{'\n" + |
|
"{2}\n" + |
|
" '}',\n" + |
|
" '}',\n" + |
|
" \"signature\": '{'\n" + |
|
"{3}\n" + |
|
" '}'\n" + |
|
"'}'", |
|
Locale.ENGLISH); |
|
|
|
HexDumpEncoder hexEncoder = new HexDumpEncoder(); |
|
Object[] messageFields = { |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(p), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(g), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(y), " "), |
|
Utilities.indent( |
|
hexEncoder.encodeBuffer(paramsSignature), " ") |
|
}; |
|
|
|
return messageFormat.format(messageFields); |
|
} |
|
} |
|
|
|
private static Signature getSignature(String keyAlgorithm, |
|
Key key) throws NoSuchAlgorithmException, InvalidKeyException { |
|
Signature signer; |
|
switch (keyAlgorithm) { |
|
case "DSA": |
|
signer = Signature.getInstance(JsseJce.SIGNATURE_DSA); |
|
break; |
|
case "RSA": |
|
signer = RSASignature.getInstance(); |
|
break; |
|
default: |
|
throw new NoSuchAlgorithmException( |
|
"neither an RSA or a DSA key : " + keyAlgorithm); |
|
} |
|
|
|
if (signer != null) { |
|
if (key instanceof PublicKey) { |
|
signer.initVerify((PublicKey)(key)); |
|
} else { |
|
signer.initSign((PrivateKey)key); |
|
} |
|
} |
|
|
|
return signer; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void updateSignature(Signature sig, byte[] clntNonce, |
|
byte[] svrNonce) throws SignatureException { |
|
int tmp; |
|
|
|
sig.update(clntNonce); |
|
sig.update(svrNonce); |
|
|
|
sig.update((byte)(p.length >> 8)); |
|
sig.update((byte)(p.length & 0x0ff)); |
|
sig.update(p); |
|
|
|
sig.update((byte)(g.length >> 8)); |
|
sig.update((byte)(g.length & 0x0ff)); |
|
sig.update(g); |
|
|
|
sig.update((byte)(y.length >> 8)); |
|
sig.update((byte)(y.length & 0x0ff)); |
|
sig.update(y); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
static final class DHServerKeyExchangeProducer |
|
implements HandshakeProducer { |
|
|
|
private DHServerKeyExchangeProducer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public byte[] produce(ConnectionContext context, |
|
HandshakeMessage message) throws IOException { |
|
|
|
ServerHandshakeContext shc = (ServerHandshakeContext)context; |
|
DHServerKeyExchangeMessage skem = |
|
new DHServerKeyExchangeMessage(shc); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Produced DH ServerKeyExchange handshake message", skem); |
|
} |
|
|
|
|
|
skem.write(shc.handshakeOutput); |
|
shc.handshakeOutput.flush(); |
|
|
|
|
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
static final class DHServerKeyExchangeConsumer implements SSLConsumer { |
|
|
|
private DHServerKeyExchangeConsumer() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public void consume(ConnectionContext context, |
|
ByteBuffer message) throws IOException { |
|
|
|
ClientHandshakeContext chc = (ClientHandshakeContext)context; |
|
|
|
DHServerKeyExchangeMessage skem = |
|
new DHServerKeyExchangeMessage(chc, message); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine( |
|
"Consuming DH ServerKeyExchange handshake message", skem); |
|
} |
|
|
|
// |
|
// validate |
|
// |
|
|
|
DHPublicKey publicKey; |
|
try { |
|
KeyFactory kf = KeyFactory.getInstance("DiffieHellman"); |
|
DHPublicKeySpec spec = new DHPublicKeySpec( |
|
new BigInteger(1, skem.y), |
|
new BigInteger(1, skem.p), |
|
new BigInteger(1, skem.g)); |
|
publicKey = (DHPublicKey)kf.generatePublic(spec); |
|
} catch (GeneralSecurityException gse) { |
|
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, |
|
"Could not generate DHPublicKey", gse); |
|
} |
|
|
|
if (!chc.algorithmConstraints.permits( |
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) { |
|
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, |
|
"DH ServerKeyExchange does not comply to " + |
|
"algorithm constraints"); |
|
} |
|
|
|
// |
|
// update |
|
|
|
NamedGroup namedGroup = NamedGroup.valueOf(publicKey.getParams()); |
|
chc.handshakeCredentials.add( |
|
new DHECredentials(publicKey, namedGroup)); |
|
|
|
// |
|
// produce |
|
// |
|
// Need no new handshake message producers here. |
|
} |
|
} |
|
} |
|
|