mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8171279: Support X25519 and X448 in TLS
Reviewed-by: xuelei, mullan
This commit is contained in:
parent
3353016bbc
commit
118fd65d44
27 changed files with 1789 additions and 1001 deletions
|
@ -36,7 +36,6 @@ import java.security.PrivateKey;
|
|||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
|
@ -44,25 +43,30 @@ import java.security.spec.ECPublicKeySpec;
|
|||
import java.util.EnumSet;
|
||||
import javax.crypto.KeyAgreement;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
||||
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.security.ssl.XDHKeyExchange.XDHECredentials;
|
||||
import sun.security.ssl.XDHKeyExchange.XDHEPossession;
|
||||
import sun.security.util.ECUtil;
|
||||
|
||||
final class ECDHKeyExchange {
|
||||
static final SSLPossessionGenerator poGenerator =
|
||||
new ECDHEPossessionGenerator();
|
||||
static final SSLKeyAgreementGenerator ecdheKAGenerator =
|
||||
new ECDHEKAGenerator();
|
||||
static final SSLKeyAgreementGenerator ecdhKAGenerator =
|
||||
new ECDHKAGenerator();
|
||||
|
||||
static final class ECDHECredentials implements SSLCredentials {
|
||||
// TLSv1.3
|
||||
static final SSLKeyAgreementGenerator ecdheKAGenerator =
|
||||
new ECDHEKAGenerator();
|
||||
|
||||
// TLSv1-1.2, the KA gets more difficult with EC/XEC keys
|
||||
static final SSLKeyAgreementGenerator ecdheXdhKAGenerator =
|
||||
new ECDHEXDHKAGenerator();
|
||||
|
||||
static final class ECDHECredentials implements NamedGroupCredentials {
|
||||
final ECPublicKey popPublicKey;
|
||||
final NamedGroup namedGroup;
|
||||
|
||||
|
@ -71,6 +75,16 @@ final class ECDHKeyExchange {
|
|||
this.namedGroup = namedGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getPublicKey() {
|
||||
return popPublicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedGroup getNamedGroup() {
|
||||
return namedGroup;
|
||||
}
|
||||
|
||||
static ECDHECredentials valueOf(NamedGroup namedGroup,
|
||||
byte[] encodedPoint) throws IOException, GeneralSecurityException {
|
||||
|
||||
|
@ -98,7 +112,7 @@ final class ECDHKeyExchange {
|
|||
}
|
||||
}
|
||||
|
||||
static final class ECDHEPossession implements SSLPossession {
|
||||
static final class ECDHEPossession implements NamedGroupPossession {
|
||||
final PrivateKey privateKey;
|
||||
final ECPublicKey publicKey;
|
||||
final NamedGroup namedGroup;
|
||||
|
@ -199,6 +213,21 @@ final class ECDHKeyExchange {
|
|||
"Could not generate ECPublicKey").initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedGroup getNamedGroup() {
|
||||
return namedGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
|
@ -210,24 +239,31 @@ final class ECDHKeyExchange {
|
|||
|
||||
@Override
|
||||
public SSLPossession createPossession(HandshakeContext context) {
|
||||
NamedGroup preferableNamedGroup = null;
|
||||
|
||||
NamedGroup preferableNamedGroup;
|
||||
|
||||
// Find most preferred EC or XEC groups
|
||||
if ((context.clientRequestedNamedGroups != null) &&
|
||||
(!context.clientRequestedNamedGroups.isEmpty())) {
|
||||
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
||||
context.negotiatedProtocol,
|
||||
context.algorithmConstraints,
|
||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||
new NamedGroupType[] {
|
||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||
NamedGroupType.NAMED_GROUP_XDH },
|
||||
context.clientRequestedNamedGroups);
|
||||
} else {
|
||||
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
||||
context.negotiatedProtocol,
|
||||
context.algorithmConstraints,
|
||||
NamedGroupType.NAMED_GROUP_ECDHE);
|
||||
new NamedGroupType[] {
|
||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||
NamedGroupType.NAMED_GROUP_XDH });
|
||||
}
|
||||
|
||||
if (preferableNamedGroup != null) {
|
||||
return new ECDHEPossession(preferableNamedGroup,
|
||||
context.sslContext.getSecureRandom());
|
||||
return preferableNamedGroup.createPossession(
|
||||
context.sslContext.getSecureRandom());
|
||||
}
|
||||
|
||||
// no match found, cannot use this cipher suite.
|
||||
|
@ -298,7 +334,7 @@ final class ECDHKeyExchange {
|
|||
"No sufficient ECDHE key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new ECDHEKAKeyDerivation(shc,
|
||||
return new KAKeyDerivation("ECDH", shc,
|
||||
x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);
|
||||
}
|
||||
|
||||
|
@ -347,7 +383,7 @@ final class ECDHKeyExchange {
|
|||
"No sufficient ECDH key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new ECDHEKAKeyDerivation(chc,
|
||||
return new KAKeyDerivation("ECDH", chc,
|
||||
ecdhePossession.privateKey, x509Credentials.popPublicKey);
|
||||
}
|
||||
}
|
||||
|
@ -391,94 +427,73 @@ final class ECDHKeyExchange {
|
|||
"No sufficient ECDHE key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new ECDHEKAKeyDerivation(context,
|
||||
return new KAKeyDerivation("ECDH", context,
|
||||
ecdhePossession.privateKey, ecdheCredentials.popPublicKey);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A Generator for TLSv1-1.2 to create a ECDHE or a XDH KeyDerivation
|
||||
* object depending on the negotiated group.
|
||||
*/
|
||||
private static final
|
||||
class ECDHEKAKeyDerivation implements SSLKeyDerivation {
|
||||
private final HandshakeContext context;
|
||||
private final PrivateKey localPrivateKey;
|
||||
private final PublicKey peerPublicKey;
|
||||
|
||||
ECDHEKAKeyDerivation(HandshakeContext context,
|
||||
PrivateKey localPrivateKey,
|
||||
PublicKey peerPublicKey) {
|
||||
this.context = context;
|
||||
this.localPrivateKey = localPrivateKey;
|
||||
this.peerPublicKey = peerPublicKey;
|
||||
class ECDHEXDHKAGenerator implements SSLKeyAgreementGenerator {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHEXDHKAGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
return t12DeriveKey(algorithm, params);
|
||||
} else {
|
||||
return t13DeriveKey(algorithm, params);
|
||||
}
|
||||
}
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException {
|
||||
|
||||
private SecretKey t12DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
try {
|
||||
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey preMasterSecret =
|
||||
ka.generateSecret("TlsPremasterSecret");
|
||||
NamedGroupPossession namedGroupPossession = null;
|
||||
NamedGroupCredentials namedGroupCredentials = null;
|
||||
NamedGroup namedGroup = null;
|
||||
|
||||
SSLMasterKeyDerivation mskd =
|
||||
SSLMasterKeyDerivation.valueOf(
|
||||
context.negotiatedProtocol);
|
||||
if (mskd == null) {
|
||||
// unlikely
|
||||
throw new SSLHandshakeException(
|
||||
"No expected master key derivation for protocol: " +
|
||||
context.negotiatedProtocol.name);
|
||||
// Find a possession/credential combo using the same named group
|
||||
search:
|
||||
for (SSLPossession poss : context.handshakePossessions) {
|
||||
for (SSLCredentials cred : context.handshakeCredentials) {
|
||||
if (((poss instanceof ECDHEPossession) &&
|
||||
(cred instanceof ECDHECredentials)) ||
|
||||
(((poss instanceof XDHEPossession) &&
|
||||
(cred instanceof XDHECredentials)))) {
|
||||
NamedGroupPossession p = (NamedGroupPossession)poss;
|
||||
NamedGroupCredentials c = (NamedGroupCredentials)cred;
|
||||
if (p.getNamedGroup() != c.getNamedGroup()) {
|
||||
continue;
|
||||
} else {
|
||||
namedGroup = p.getNamedGroup();
|
||||
}
|
||||
namedGroupPossession = p;
|
||||
namedGroupCredentials = c;
|
||||
break search;
|
||||
}
|
||||
}
|
||||
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||
context, preMasterSecret);
|
||||
return kd.deriveKey("MasterSecret", params);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
|
||||
private SecretKey t13DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
try {
|
||||
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey sharedSecret =
|
||||
ka.generateSecret("TlsPremasterSecret");
|
||||
|
||||
HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
|
||||
SSLKeyDerivation kd = context.handshakeKeyDerivation;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
if (kd == null) { // No PSK is in use.
|
||||
// If PSK is not in use Early Secret will still be
|
||||
// HKDF-Extract(0, 0).
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKeySpec ikm =
|
||||
new SecretKeySpec(zeros, "TlsPreSharedSecret");
|
||||
SecretKey earlySecret =
|
||||
hkdf.extract(zeros, ikm, "TlsEarlySecret");
|
||||
kd = new SSLSecretDerivation(context, earlySecret);
|
||||
}
|
||||
|
||||
// derive salt secret
|
||||
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
|
||||
|
||||
// derive handshake secret
|
||||
return hkdf.extract(saltSecret, sharedSecret, algorithm);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
if (namedGroupPossession == null || namedGroupCredentials == null) {
|
||||
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No sufficient ECDHE/XDH key agreement " +
|
||||
"parameters negotiated");
|
||||
}
|
||||
|
||||
String alg;
|
||||
switch (namedGroup.type) {
|
||||
case NAMED_GROUP_ECDHE:
|
||||
alg = "ECDH";
|
||||
break;
|
||||
case NAMED_GROUP_XDH:
|
||||
alg = "XDH";
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unexpected named group type");
|
||||
}
|
||||
|
||||
return new KAKeyDerivation(alg, context,
|
||||
namedGroupPossession.getPrivateKey(),
|
||||
namedGroupCredentials.getPublicKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue