8171279: Support X25519 and X448 in TLS
Reviewed-by: xuelei, mullan
This commit is contained in:
parent
3353016bbc
commit
118fd65d44
@ -35,8 +35,8 @@ import static sun.security.ssl.CipherSuite.HashAlg.*;
|
|||||||
import static sun.security.ssl.CipherSuite.KeyExchange.*;
|
import static sun.security.ssl.CipherSuite.KeyExchange.*;
|
||||||
import static sun.security.ssl.CipherSuite.MacAlg.*;
|
import static sun.security.ssl.CipherSuite.MacAlg.*;
|
||||||
import static sun.security.ssl.SSLCipher.*;
|
import static sun.security.ssl.SSLCipher.*;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
|
import static sun.security.ssl.NamedGroup.NamedGroupType.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for SSL/(D)TLS cipher suites.
|
* Enum for SSL/(D)TLS cipher suites.
|
||||||
@ -184,7 +184,7 @@ enum CipherSuite {
|
|||||||
K_DHE_DSS, B_AES_128, M_SHA256, H_SHA256),
|
K_DHE_DSS, B_AES_128, M_SHA256, H_SHA256),
|
||||||
|
|
||||||
//
|
//
|
||||||
// not forward screcy cipher suites.
|
// not forward secret cipher suites.
|
||||||
//
|
//
|
||||||
|
|
||||||
// AES_256(GCM)
|
// AES_256(GCM)
|
||||||
@ -1106,11 +1106,18 @@ enum CipherSuite {
|
|||||||
K_DH_ANON ("DH_anon", true, true, NAMED_GROUP_FFDHE),
|
K_DH_ANON ("DH_anon", true, true, NAMED_GROUP_FFDHE),
|
||||||
K_DH_ANON_EXPORT("DH_anon_EXPORT", true, true, NAMED_GROUP_NONE),
|
K_DH_ANON_EXPORT("DH_anon_EXPORT", true, true, NAMED_GROUP_NONE),
|
||||||
|
|
||||||
K_ECDH_ECDSA ("ECDH_ECDSA", true, false, NAMED_GROUP_ECDHE),
|
// These KeyExchanges can use either ECDHE/XDH, so we'll use a
|
||||||
K_ECDH_RSA ("ECDH_RSA", true, false, NAMED_GROUP_ECDHE),
|
// varargs here.
|
||||||
K_ECDHE_ECDSA ("ECDHE_ECDSA", true, false, NAMED_GROUP_ECDHE),
|
K_ECDH_ECDSA ("ECDH_ECDSA", JsseJce.ALLOW_ECC, false,
|
||||||
K_ECDHE_RSA ("ECDHE_RSA", true, false, NAMED_GROUP_ECDHE),
|
NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
|
||||||
K_ECDH_ANON ("ECDH_anon", true, true, NAMED_GROUP_ECDHE),
|
K_ECDH_RSA ("ECDH_RSA", JsseJce.ALLOW_ECC, false,
|
||||||
|
NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
|
||||||
|
K_ECDHE_ECDSA ("ECDHE_ECDSA", JsseJce.ALLOW_ECC, false,
|
||||||
|
NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
|
||||||
|
K_ECDHE_RSA ("ECDHE_RSA", JsseJce.ALLOW_ECC, false,
|
||||||
|
NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
|
||||||
|
K_ECDH_ANON ("ECDH_anon", JsseJce.ALLOW_ECC, true,
|
||||||
|
NAMED_GROUP_ECDHE, NAMED_GROUP_XDH),
|
||||||
|
|
||||||
// renegotiation protection request signaling cipher suite
|
// renegotiation protection request signaling cipher suite
|
||||||
K_SCSV ("SCSV", true, true, NAMED_GROUP_NONE);
|
K_SCSV ("SCSV", true, true, NAMED_GROUP_NONE);
|
||||||
@ -1118,19 +1125,16 @@ enum CipherSuite {
|
|||||||
// name of the key exchange algorithm, e.g. DHE_DSS
|
// name of the key exchange algorithm, e.g. DHE_DSS
|
||||||
final String name;
|
final String name;
|
||||||
final boolean allowed;
|
final boolean allowed;
|
||||||
final NamedGroupType groupType;
|
final NamedGroupType[] groupTypes;
|
||||||
private final boolean alwaysAvailable;
|
private final boolean alwaysAvailable;
|
||||||
private final boolean isAnonymous;
|
private final boolean isAnonymous;
|
||||||
|
|
||||||
KeyExchange(String name, boolean allowed,
|
KeyExchange(String name, boolean allowed,
|
||||||
boolean isAnonymous, NamedGroupType groupType) {
|
boolean isAnonymous, NamedGroupType... groupTypes) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
if (groupType == NAMED_GROUP_ECDHE) {
|
this.groupTypes = groupTypes;
|
||||||
this.allowed = JsseJce.ALLOW_ECC;
|
this.allowed = allowed;
|
||||||
} else {
|
|
||||||
this.allowed = allowed;
|
|
||||||
}
|
|
||||||
this.groupType = groupType;
|
|
||||||
this.alwaysAvailable = allowed && (!name.startsWith("EC"));
|
this.alwaysAvailable = allowed && (!name.startsWith("EC"));
|
||||||
this.isAnonymous = isAnonymous;
|
this.isAnonymous = isAnonymous;
|
||||||
}
|
}
|
||||||
@ -1140,7 +1144,8 @@ enum CipherSuite {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupType == NAMED_GROUP_ECDHE) {
|
if (NamedGroupType.arrayContains(
|
||||||
|
groupTypes, NamedGroupType.NAMED_GROUP_ECDHE)) {
|
||||||
return (allowed && JsseJce.isEcAvailable());
|
return (allowed && JsseJce.isEcAvailable());
|
||||||
} else {
|
} else {
|
||||||
return allowed;
|
return allowed;
|
||||||
|
@ -42,7 +42,6 @@ import javax.net.ssl.SSLHandshakeException;
|
|||||||
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
||||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.util.HexDumpEncoder;
|
import sun.security.util.HexDumpEncoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,19 +36,12 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import javax.crypto.KeyAgreement;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.interfaces.DHPublicKey;
|
import javax.crypto.interfaces.DHPublicKey;
|
||||||
import javax.crypto.spec.DHParameterSpec;
|
import javax.crypto.spec.DHParameterSpec;
|
||||||
import javax.crypto.spec.DHPublicKeySpec;
|
import javax.crypto.spec.DHPublicKeySpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
import sun.security.ssl.CipherSuite.HashAlg;
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
import sun.security.util.KeyUtil;
|
import sun.security.util.KeyUtil;
|
||||||
@ -61,7 +54,7 @@ final class DHKeyExchange {
|
|||||||
static final SSLKeyAgreementGenerator kaGenerator =
|
static final SSLKeyAgreementGenerator kaGenerator =
|
||||||
new DHEKAGenerator();
|
new DHEKAGenerator();
|
||||||
|
|
||||||
static final class DHECredentials implements SSLCredentials {
|
static final class DHECredentials implements NamedGroupCredentials {
|
||||||
final DHPublicKey popPublicKey;
|
final DHPublicKey popPublicKey;
|
||||||
final NamedGroup namedGroup;
|
final NamedGroup namedGroup;
|
||||||
|
|
||||||
@ -70,6 +63,16 @@ final class DHKeyExchange {
|
|||||||
this.namedGroup = namedGroup;
|
this.namedGroup = namedGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
return popPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NamedGroup getNamedGroup() {
|
||||||
|
return namedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
static DHECredentials valueOf(NamedGroup ng,
|
static DHECredentials valueOf(NamedGroup ng,
|
||||||
byte[] encodedPublic) throws IOException, GeneralSecurityException {
|
byte[] encodedPublic) throws IOException, GeneralSecurityException {
|
||||||
|
|
||||||
@ -98,7 +101,7 @@ final class DHKeyExchange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class DHEPossession implements SSLPossession {
|
static final class DHEPossession implements NamedGroupPossession {
|
||||||
final PrivateKey privateKey;
|
final PrivateKey privateKey;
|
||||||
final DHPublicKey publicKey;
|
final DHPublicKey publicKey;
|
||||||
final NamedGroup namedGroup;
|
final NamedGroup namedGroup;
|
||||||
@ -174,13 +177,13 @@ final class DHKeyExchange {
|
|||||||
// Generate and validate DHPublicKeySpec
|
// Generate and validate DHPublicKeySpec
|
||||||
private KeyPair generateDHKeyPair(
|
private KeyPair generateDHKeyPair(
|
||||||
KeyPairGenerator kpg) throws GeneralSecurityException {
|
KeyPairGenerator kpg) throws GeneralSecurityException {
|
||||||
boolean doExtraValiadtion =
|
boolean doExtraValidation =
|
||||||
(!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
|
(!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
|
||||||
boolean isRecovering = false;
|
boolean isRecovering = false;
|
||||||
for (int i = 0; i <= 2; i++) { // Try to recover from failure.
|
for (int i = 0; i <= 2; i++) { // Try to recover from failure.
|
||||||
KeyPair kp = kpg.generateKeyPair();
|
KeyPair kp = kpg.generateKeyPair();
|
||||||
// validate the Diffie-Hellman public key
|
// validate the Diffie-Hellman public key
|
||||||
if (doExtraValiadtion) {
|
if (doExtraValidation) {
|
||||||
DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
|
DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
|
||||||
try {
|
try {
|
||||||
KeyUtil.validate(spec);
|
KeyUtil.validate(spec);
|
||||||
@ -231,6 +234,21 @@ final class DHKeyExchange {
|
|||||||
|
|
||||||
return encoded;
|
return encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NamedGroup getNamedGroup() {
|
||||||
|
return namedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrivateKey getPrivateKey() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class
|
private static final class
|
||||||
@ -298,7 +316,7 @@ final class DHKeyExchange {
|
|||||||
// Used for ServerKeyExchange, TLS 1.2 and prior versions.
|
// Used for ServerKeyExchange, TLS 1.2 and prior versions.
|
||||||
@Override
|
@Override
|
||||||
public SSLPossession createPossession(HandshakeContext context) {
|
public SSLPossession createPossession(HandshakeContext context) {
|
||||||
NamedGroup preferableNamedGroup = null;
|
NamedGroup preferableNamedGroup;
|
||||||
if (!useLegacyEphemeralDHKeys &&
|
if (!useLegacyEphemeralDHKeys &&
|
||||||
(context.clientRequestedNamedGroups != null) &&
|
(context.clientRequestedNamedGroups != null) &&
|
||||||
(!context.clientRequestedNamedGroups.isEmpty())) {
|
(!context.clientRequestedNamedGroups.isEmpty())) {
|
||||||
@ -306,7 +324,8 @@ final class DHKeyExchange {
|
|||||||
SupportedGroups.getPreferredGroup(
|
SupportedGroups.getPreferredGroup(
|
||||||
context.negotiatedProtocol,
|
context.negotiatedProtocol,
|
||||||
context.algorithmConstraints,
|
context.algorithmConstraints,
|
||||||
NamedGroupType.NAMED_GROUP_FFDHE,
|
new NamedGroupType [] {
|
||||||
|
NamedGroupType.NAMED_GROUP_FFDHE },
|
||||||
context.clientRequestedNamedGroups);
|
context.clientRequestedNamedGroups);
|
||||||
if (preferableNamedGroup != null) {
|
if (preferableNamedGroup != null) {
|
||||||
return new DHEPossession(preferableNamedGroup,
|
return new DHEPossession(preferableNamedGroup,
|
||||||
@ -392,7 +411,7 @@ final class DHKeyExchange {
|
|||||||
|
|
||||||
private static final
|
private static final
|
||||||
class DHEKAGenerator implements SSLKeyAgreementGenerator {
|
class DHEKAGenerator implements SSLKeyAgreementGenerator {
|
||||||
static private DHEKAGenerator instance = new DHEKAGenerator();
|
private static final DHEKAGenerator instance = new DHEKAGenerator();
|
||||||
|
|
||||||
// Prevent instantiation of this class.
|
// Prevent instantiation of this class.
|
||||||
private DHEKAGenerator() {
|
private DHEKAGenerator() {
|
||||||
@ -442,93 +461,8 @@ final class DHKeyExchange {
|
|||||||
"No sufficient DHE key agreement parameters negotiated");
|
"No sufficient DHE key agreement parameters negotiated");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DHEKAKeyDerivation(context,
|
return new KAKeyDerivation("DiffieHellman", context,
|
||||||
dhePossession.privateKey, dheCredentials.popPublicKey);
|
dhePossession.privateKey, dheCredentials.popPublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final
|
|
||||||
class DHEKAKeyDerivation implements SSLKeyDerivation {
|
|
||||||
private final HandshakeContext context;
|
|
||||||
private final PrivateKey localPrivateKey;
|
|
||||||
private final PublicKey peerPublicKey;
|
|
||||||
|
|
||||||
DHEKAKeyDerivation(HandshakeContext context,
|
|
||||||
PrivateKey localPrivateKey,
|
|
||||||
PublicKey peerPublicKey) {
|
|
||||||
this.context = context;
|
|
||||||
this.localPrivateKey = localPrivateKey;
|
|
||||||
this.peerPublicKey = peerPublicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveKey(String algorithm,
|
|
||||||
AlgorithmParameterSpec params) throws IOException {
|
|
||||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
|
||||||
return t12DeriveKey(algorithm, params);
|
|
||||||
} else {
|
|
||||||
return t13DeriveKey(algorithm, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecretKey t12DeriveKey(String algorithm,
|
|
||||||
AlgorithmParameterSpec params) throws IOException {
|
|
||||||
try {
|
|
||||||
KeyAgreement ka = KeyAgreement.getInstance("DiffieHellman");
|
|
||||||
ka.init(localPrivateKey);
|
|
||||||
ka.doPhase(peerPublicKey, true);
|
|
||||||
SecretKey preMasterSecret =
|
|
||||||
ka.generateSecret("TlsPremasterSecret");
|
|
||||||
SSLMasterKeyDerivation mskd =
|
|
||||||
SSLMasterKeyDerivation.valueOf(
|
|
||||||
context.negotiatedProtocol);
|
|
||||||
if (mskd == null) {
|
|
||||||
// unlikely
|
|
||||||
throw new SSLHandshakeException(
|
|
||||||
"No expected master key derivation for protocol: " +
|
|
||||||
context.negotiatedProtocol.name);
|
|
||||||
}
|
|
||||||
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("DiffieHellman");
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,6 @@ import javax.crypto.spec.DHPublicKeySpec;
|
|||||||
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
||||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
import sun.security.util.HexDumpEncoder;
|
import sun.security.util.HexDumpEncoder;
|
||||||
|
@ -27,31 +27,28 @@ package sun.security.ssl;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.AlgorithmConstraints;
|
|
||||||
import java.security.CryptoPrimitive;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.interfaces.ECPublicKey;
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.interfaces.XECPublicKey;
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.security.spec.ECParameterSpec;
|
import java.security.spec.ECParameterSpec;
|
||||||
import java.security.spec.ECPoint;
|
import java.security.spec.NamedParameterSpec;
|
||||||
import java.security.spec.ECPublicKeySpec;
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.crypto.SecretKey;
|
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.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
import sun.security.util.ECUtil;
|
|
||||||
import sun.security.util.HexDumpEncoder;
|
import sun.security.util.HexDumpEncoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pack of the "ClientKeyExchange" handshake message.
|
* Pack of the "ClientKeyExchange" handshake message.
|
||||||
|
*
|
||||||
|
* This file is used by both the ECDH/ECDHE/XDH code since much of the
|
||||||
|
* code is the same between the EC named groups (i.e.
|
||||||
|
* x25519/x448/secp*r1), even though the APIs are very different (i.e.
|
||||||
|
* ECPublicKey/XECPublicKey, KeyExchange.getInstance("EC"/"XDH"), etc.).
|
||||||
*/
|
*/
|
||||||
final class ECDHClientKeyExchange {
|
final class ECDHClientKeyExchange {
|
||||||
static final SSLConsumer ecdhHandshakeConsumer =
|
static final SSLConsumer ecdhHandshakeConsumer =
|
||||||
@ -65,19 +62,17 @@ final class ECDHClientKeyExchange {
|
|||||||
new ECDHEClientKeyExchangeProducer();
|
new ECDHEClientKeyExchangeProducer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ECDH/ECDHE ClientKeyExchange handshake message.
|
* The ECDH/ECDHE/XDH ClientKeyExchange handshake message.
|
||||||
*/
|
*/
|
||||||
private static final
|
private static final
|
||||||
class ECDHClientKeyExchangeMessage extends HandshakeMessage {
|
class ECDHClientKeyExchangeMessage extends HandshakeMessage {
|
||||||
private final byte[] encodedPoint;
|
private final byte[] encodedPoint;
|
||||||
|
|
||||||
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
|
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||||
ECPublicKey publicKey) {
|
byte[] encodedPublicKey) {
|
||||||
super(handshakeContext);
|
super(handshakeContext);
|
||||||
|
|
||||||
ECPoint point = publicKey.getW();
|
this.encodedPoint = encodedPublicKey;
|
||||||
ECParameterSpec params = publicKey.getParams();
|
|
||||||
encodedPoint = ECUtil.encodePoint(point, params.getCurve());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
|
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||||
@ -90,34 +85,6 @@ final class ECDHClientKeyExchange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check constraints of the specified EC public key.
|
|
||||||
static void checkConstraints(AlgorithmConstraints constraints,
|
|
||||||
ECPublicKey publicKey,
|
|
||||||
byte[] encodedPoint) throws SSLHandshakeException {
|
|
||||||
|
|
||||||
try {
|
|
||||||
ECParameterSpec params = publicKey.getParams();
|
|
||||||
ECPoint point =
|
|
||||||
ECUtil.decodePoint(encodedPoint, params.getCurve());
|
|
||||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
|
||||||
|
|
||||||
KeyFactory kf = KeyFactory.getInstance("EC");
|
|
||||||
ECPublicKey peerPublicKey =
|
|
||||||
(ECPublicKey)kf.generatePublic(spec);
|
|
||||||
|
|
||||||
// check constraints of ECPublicKey
|
|
||||||
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
|
@Override
|
||||||
public SSLHandshake handshakeType() {
|
public SSLHandshake handshakeType() {
|
||||||
return SSLHandshake.CLIENT_KEY_EXCHANGE;
|
return SSLHandshake.CLIENT_KEY_EXCHANGE;
|
||||||
@ -194,24 +161,41 @@ final class ECDHClientKeyExchange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PublicKey publicKey = x509Credentials.popPublicKey;
|
PublicKey publicKey = x509Credentials.popPublicKey;
|
||||||
if (!publicKey.getAlgorithm().equals("EC")) {
|
|
||||||
|
NamedGroup namedGroup = null;
|
||||||
|
String algorithm = publicKey.getAlgorithm();
|
||||||
|
|
||||||
|
// Determine which NamedGroup we'll be using, then use
|
||||||
|
// the creator functions.
|
||||||
|
if (algorithm.equals("EC")) {
|
||||||
|
ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
|
||||||
|
namedGroup = NamedGroup.valueOf(params);
|
||||||
|
} else if (algorithm.equals("XDH")) {
|
||||||
|
AlgorithmParameterSpec params =
|
||||||
|
((XECPublicKey)publicKey).getParams();
|
||||||
|
if (params instanceof NamedParameterSpec) {
|
||||||
|
String name = ((NamedParameterSpec)params).getName();
|
||||||
|
namedGroup = NamedGroup.nameOf(name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"Not EC server certificate for ECDH client key exchange");
|
"Not EC/XDH server certificate for " +
|
||||||
|
"ECDH client key exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
|
|
||||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
|
||||||
if (namedGroup == null) {
|
if (namedGroup == null) {
|
||||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"Unsupported EC server cert for ECDH client key exchange");
|
"Unsupported EC/XDH server cert for " +
|
||||||
|
"ECDH client key exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
ECDHEPossession ecdhePossession = new ECDHEPossession(
|
SSLPossession sslPossession = namedGroup.createPossession(
|
||||||
namedGroup, chc.sslContext.getSecureRandom());
|
chc.sslContext.getSecureRandom());
|
||||||
chc.handshakePossessions.add(ecdhePossession);
|
|
||||||
|
chc.handshakePossessions.add(sslPossession);
|
||||||
ECDHClientKeyExchangeMessage cke =
|
ECDHClientKeyExchangeMessage cke =
|
||||||
new ECDHClientKeyExchangeMessage(
|
new ECDHClientKeyExchangeMessage(
|
||||||
chc, ecdhePossession.publicKey);
|
chc, sslPossession.encode());
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine(
|
SSLLogger.fine(
|
||||||
"Produced ECDH ClientKeyExchange handshake message", cke);
|
"Produced ECDH ClientKeyExchange handshake message", cke);
|
||||||
@ -283,18 +267,35 @@ final class ECDHClientKeyExchange {
|
|||||||
"No expected EC server cert for ECDH client key exchange");
|
"No expected EC server cert for ECDH client key exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
ECParameterSpec params = x509Possession.getECParameterSpec();
|
// Determine which NamedGroup we'll be using, then use
|
||||||
if (params == null) {
|
// the creator functions.
|
||||||
// unlikely, have been checked during cipher suite negotiation.
|
NamedGroup namedGroup = null;
|
||||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
|
||||||
"Not EC server cert for ECDH client key exchange");
|
// Iteratively determine the X509Possession type's ParameterSpec.
|
||||||
|
ECParameterSpec ecParams = x509Possession.getECParameterSpec();
|
||||||
|
NamedParameterSpec namedParams = null;
|
||||||
|
if (ecParams != null) {
|
||||||
|
namedGroup = NamedGroup.valueOf(ecParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
// Wasn't EC, try XEC.
|
||||||
if (namedGroup == null) {
|
if (ecParams == null) {
|
||||||
|
namedParams = x509Possession.getXECParameterSpec();
|
||||||
|
namedGroup = NamedGroup.nameOf(namedParams.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't figure this out, bail.
|
||||||
|
if ((ecParams == null) && (namedParams == null)) {
|
||||||
// unlikely, have been checked during cipher suite negotiation.
|
// unlikely, have been checked during cipher suite negotiation.
|
||||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"Unsupported EC server cert for ECDH client key exchange");
|
"Not EC/XDH server cert for ECDH client key exchange");
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlikely, have been checked during cipher suite negotiation.
|
||||||
|
if (namedGroup == null) {
|
||||||
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
|
"Unknown named group in server cert for " +
|
||||||
|
"ECDH client key exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||||
@ -306,7 +307,7 @@ final class ECDHClientKeyExchange {
|
|||||||
"Not supported key exchange type");
|
"Not supported key exchange type");
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the handshake message
|
// parse either handshake message containing either EC/XEC.
|
||||||
ECDHClientKeyExchangeMessage cke =
|
ECDHClientKeyExchangeMessage cke =
|
||||||
new ECDHClientKeyExchangeMessage(shc, message);
|
new ECDHClientKeyExchangeMessage(shc, message);
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
@ -316,27 +317,17 @@ final class ECDHClientKeyExchange {
|
|||||||
|
|
||||||
// create the credentials
|
// create the credentials
|
||||||
try {
|
try {
|
||||||
ECPoint point =
|
NamedGroup ng = namedGroup; // "effectively final" the lambda
|
||||||
ECUtil.decodePoint(cke.encodedPoint, params.getCurve());
|
// AlgorithmConstraints are checked internally.
|
||||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
SSLCredentials sslCredentials = namedGroup.decodeCredentials(
|
||||||
|
cke.encodedPoint, shc.algorithmConstraints,
|
||||||
|
s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||||
|
"ClientKeyExchange " + ng + ": " + s));
|
||||||
|
|
||||||
KeyFactory kf = KeyFactory.getInstance("EC");
|
shc.handshakeCredentials.add(sslCredentials);
|
||||||
ECPublicKey peerPublicKey =
|
} catch (GeneralSecurityException e) {
|
||||||
(ECPublicKey)kf.generatePublic(spec);
|
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||||
|
"Cannot decode ECDH PublicKey: " + namedGroup);
|
||||||
// check constraints of peer ECPublicKey
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the states
|
// update the states
|
||||||
@ -374,25 +365,37 @@ final class ECDHClientKeyExchange {
|
|||||||
// The producing happens in client side only.
|
// The producing happens in client side only.
|
||||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||||
|
|
||||||
ECDHECredentials ecdheCredentials = null;
|
SSLCredentials sslCredentials = null;
|
||||||
|
NamedGroup ng = null;
|
||||||
|
PublicKey publicKey = null;
|
||||||
|
|
||||||
|
// Find a good EC/XEC credential to use, determine the
|
||||||
|
// NamedGroup to use for creating Possessions/Credentials/Keys.
|
||||||
for (SSLCredentials cd : chc.handshakeCredentials) {
|
for (SSLCredentials cd : chc.handshakeCredentials) {
|
||||||
if (cd instanceof ECDHECredentials) {
|
if (cd instanceof NamedGroupCredentials) {
|
||||||
ecdheCredentials = (ECDHECredentials)cd;
|
NamedGroupCredentials creds = (NamedGroupCredentials)cd;
|
||||||
|
ng = creds.getNamedGroup();
|
||||||
|
publicKey = creds.getPublicKey();
|
||||||
|
sslCredentials = cd;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ecdheCredentials == null) {
|
if (sslCredentials == null) {
|
||||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||||
"No ECDHE credentials negotiated for client key exchange");
|
"No ECDHE credentials negotiated for client key exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
ECDHEPossession ecdhePossession = new ECDHEPossession(
|
SSLPossession sslPossession = ng.createPossession(
|
||||||
ecdheCredentials, chc.sslContext.getSecureRandom());
|
chc.sslContext.getSecureRandom());
|
||||||
chc.handshakePossessions.add(ecdhePossession);
|
|
||||||
|
chc.handshakePossessions.add(sslPossession);
|
||||||
|
|
||||||
|
// Write the EC/XEC message.
|
||||||
ECDHClientKeyExchangeMessage cke =
|
ECDHClientKeyExchangeMessage cke =
|
||||||
new ECDHClientKeyExchangeMessage(
|
new ECDHClientKeyExchangeMessage(
|
||||||
chc, ecdhePossession.publicKey);
|
chc, sslPossession.encode());
|
||||||
|
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine(
|
SSLLogger.fine(
|
||||||
"Produced ECDHE ClientKeyExchange handshake message", cke);
|
"Produced ECDHE ClientKeyExchange handshake message", cke);
|
||||||
@ -450,23 +453,29 @@ final class ECDHClientKeyExchange {
|
|||||||
// The consuming happens in server side only.
|
// The consuming happens in server side only.
|
||||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||||
|
|
||||||
ECDHEPossession ecdhePossession = null;
|
SSLPossession sslPossession = null;
|
||||||
|
NamedGroup namedGroup = null;
|
||||||
|
|
||||||
|
// Find a good EC/XEC credential to use, determine the
|
||||||
|
// NamedGroup to use for creating Possessions/Credentials/Keys.
|
||||||
for (SSLPossession possession : shc.handshakePossessions) {
|
for (SSLPossession possession : shc.handshakePossessions) {
|
||||||
if (possession instanceof ECDHEPossession) {
|
if (possession instanceof NamedGroupPossession) {
|
||||||
ecdhePossession = (ECDHEPossession)possession;
|
NamedGroupPossession poss =
|
||||||
|
(NamedGroupPossession)possession;
|
||||||
|
namedGroup = poss.getNamedGroup();
|
||||||
|
sslPossession = poss;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ecdhePossession == null) {
|
|
||||||
|
if (sslPossession == null) {
|
||||||
// unlikely
|
// unlikely
|
||||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||||
"No expected ECDHE possessions for client key exchange");
|
"No expected ECDHE possessions for client key exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
ECParameterSpec params = ecdhePossession.publicKey.getParams();
|
|
||||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
|
||||||
if (namedGroup == null) {
|
if (namedGroup == null) {
|
||||||
// unlikely, have been checked during cipher suite negotiation.
|
// unlikely, have been checked during cipher suite negotiation
|
||||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"Unsupported EC server cert for ECDHE client key exchange");
|
"Unsupported EC server cert for ECDHE client key exchange");
|
||||||
}
|
}
|
||||||
@ -480,7 +489,7 @@ final class ECDHClientKeyExchange {
|
|||||||
"Not supported key exchange type");
|
"Not supported key exchange type");
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the handshake message
|
// parse the EC/XEC handshake message
|
||||||
ECDHClientKeyExchangeMessage cke =
|
ECDHClientKeyExchangeMessage cke =
|
||||||
new ECDHClientKeyExchangeMessage(shc, message);
|
new ECDHClientKeyExchangeMessage(shc, message);
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
@ -490,27 +499,17 @@ final class ECDHClientKeyExchange {
|
|||||||
|
|
||||||
// create the credentials
|
// create the credentials
|
||||||
try {
|
try {
|
||||||
ECPoint point =
|
NamedGroup ng = namedGroup; // "effectively final" the lambda
|
||||||
ECUtil.decodePoint(cke.encodedPoint, params.getCurve());
|
// AlgorithmConstraints are checked internally.
|
||||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
SSLCredentials sslCredentials = namedGroup.decodeCredentials(
|
||||||
|
cke.encodedPoint, shc.algorithmConstraints,
|
||||||
|
s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||||
|
"ClientKeyExchange " + ng + ": " + s));
|
||||||
|
|
||||||
KeyFactory kf = KeyFactory.getInstance("EC");
|
shc.handshakeCredentials.add(sslCredentials);
|
||||||
ECPublicKey peerPublicKey =
|
} catch (GeneralSecurityException e) {
|
||||||
(ECPublicKey)kf.generatePublic(spec);
|
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||||
|
"Cannot decode named group: " + namedGroup);
|
||||||
// check constraints of peer ECPublicKey
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the states
|
// update the states
|
||||||
|
@ -36,7 +36,6 @@ import java.security.PrivateKey;
|
|||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.interfaces.ECPublicKey;
|
import java.security.interfaces.ECPublicKey;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
|
||||||
import java.security.spec.ECGenParameterSpec;
|
import java.security.spec.ECGenParameterSpec;
|
||||||
import java.security.spec.ECParameterSpec;
|
import java.security.spec.ECParameterSpec;
|
||||||
import java.security.spec.ECPoint;
|
import java.security.spec.ECPoint;
|
||||||
@ -44,25 +43,30 @@ import java.security.spec.ECPublicKeySpec;
|
|||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import javax.crypto.KeyAgreement;
|
import javax.crypto.KeyAgreement;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import sun.security.ssl.CipherSuite.HashAlg;
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
|
import sun.security.ssl.XDHKeyExchange.XDHECredentials;
|
||||||
|
import sun.security.ssl.XDHKeyExchange.XDHEPossession;
|
||||||
import sun.security.util.ECUtil;
|
import sun.security.util.ECUtil;
|
||||||
|
|
||||||
final class ECDHKeyExchange {
|
final class ECDHKeyExchange {
|
||||||
static final SSLPossessionGenerator poGenerator =
|
static final SSLPossessionGenerator poGenerator =
|
||||||
new ECDHEPossessionGenerator();
|
new ECDHEPossessionGenerator();
|
||||||
static final SSLKeyAgreementGenerator ecdheKAGenerator =
|
|
||||||
new ECDHEKAGenerator();
|
|
||||||
static final SSLKeyAgreementGenerator ecdhKAGenerator =
|
static final SSLKeyAgreementGenerator ecdhKAGenerator =
|
||||||
new 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 ECPublicKey popPublicKey;
|
||||||
final NamedGroup namedGroup;
|
final NamedGroup namedGroup;
|
||||||
|
|
||||||
@ -71,6 +75,16 @@ final class ECDHKeyExchange {
|
|||||||
this.namedGroup = namedGroup;
|
this.namedGroup = namedGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
return popPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NamedGroup getNamedGroup() {
|
||||||
|
return namedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
static ECDHECredentials valueOf(NamedGroup namedGroup,
|
static ECDHECredentials valueOf(NamedGroup namedGroup,
|
||||||
byte[] encodedPoint) throws IOException, GeneralSecurityException {
|
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 PrivateKey privateKey;
|
||||||
final ECPublicKey publicKey;
|
final ECPublicKey publicKey;
|
||||||
final NamedGroup namedGroup;
|
final NamedGroup namedGroup;
|
||||||
@ -199,6 +213,21 @@ final class ECDHKeyExchange {
|
|||||||
"Could not generate ECPublicKey").initCause(e);
|
"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
|
private static final
|
||||||
@ -210,24 +239,31 @@ final class ECDHKeyExchange {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLPossession createPossession(HandshakeContext context) {
|
public SSLPossession createPossession(HandshakeContext context) {
|
||||||
NamedGroup preferableNamedGroup = null;
|
|
||||||
|
NamedGroup preferableNamedGroup;
|
||||||
|
|
||||||
|
// Find most preferred EC or XEC groups
|
||||||
if ((context.clientRequestedNamedGroups != null) &&
|
if ((context.clientRequestedNamedGroups != null) &&
|
||||||
(!context.clientRequestedNamedGroups.isEmpty())) {
|
(!context.clientRequestedNamedGroups.isEmpty())) {
|
||||||
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
||||||
context.negotiatedProtocol,
|
context.negotiatedProtocol,
|
||||||
context.algorithmConstraints,
|
context.algorithmConstraints,
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
new NamedGroupType[] {
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
NamedGroupType.NAMED_GROUP_XDH },
|
||||||
context.clientRequestedNamedGroups);
|
context.clientRequestedNamedGroups);
|
||||||
} else {
|
} else {
|
||||||
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
||||||
context.negotiatedProtocol,
|
context.negotiatedProtocol,
|
||||||
context.algorithmConstraints,
|
context.algorithmConstraints,
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE);
|
new NamedGroupType[] {
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
NamedGroupType.NAMED_GROUP_XDH });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferableNamedGroup != null) {
|
if (preferableNamedGroup != null) {
|
||||||
return new ECDHEPossession(preferableNamedGroup,
|
return preferableNamedGroup.createPossession(
|
||||||
context.sslContext.getSecureRandom());
|
context.sslContext.getSecureRandom());
|
||||||
}
|
}
|
||||||
|
|
||||||
// no match found, cannot use this cipher suite.
|
// no match found, cannot use this cipher suite.
|
||||||
@ -298,7 +334,7 @@ final class ECDHKeyExchange {
|
|||||||
"No sufficient ECDHE key agreement parameters negotiated");
|
"No sufficient ECDHE key agreement parameters negotiated");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ECDHEKAKeyDerivation(shc,
|
return new KAKeyDerivation("ECDH", shc,
|
||||||
x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);
|
x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,7 +383,7 @@ final class ECDHKeyExchange {
|
|||||||
"No sufficient ECDH key agreement parameters negotiated");
|
"No sufficient ECDH key agreement parameters negotiated");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ECDHEKAKeyDerivation(chc,
|
return new KAKeyDerivation("ECDH", chc,
|
||||||
ecdhePossession.privateKey, x509Credentials.popPublicKey);
|
ecdhePossession.privateKey, x509Credentials.popPublicKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,94 +427,73 @@ final class ECDHKeyExchange {
|
|||||||
"No sufficient ECDHE key agreement parameters negotiated");
|
"No sufficient ECDHE key agreement parameters negotiated");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ECDHEKAKeyDerivation(context,
|
return new KAKeyDerivation("ECDH", context,
|
||||||
ecdhePossession.privateKey, ecdheCredentials.popPublicKey);
|
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
|
private static final
|
||||||
class ECDHEKAKeyDerivation implements SSLKeyDerivation {
|
class ECDHEXDHKAGenerator implements SSLKeyAgreementGenerator {
|
||||||
private final HandshakeContext context;
|
// Prevent instantiation of this class.
|
||||||
private final PrivateKey localPrivateKey;
|
private ECDHEXDHKAGenerator() {
|
||||||
private final PublicKey peerPublicKey;
|
// blank
|
||||||
|
|
||||||
ECDHEKAKeyDerivation(HandshakeContext context,
|
|
||||||
PrivateKey localPrivateKey,
|
|
||||||
PublicKey peerPublicKey) {
|
|
||||||
this.context = context;
|
|
||||||
this.localPrivateKey = localPrivateKey;
|
|
||||||
this.peerPublicKey = peerPublicKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveKey(String algorithm,
|
public SSLKeyDerivation createKeyDerivation(
|
||||||
AlgorithmParameterSpec params) throws IOException {
|
HandshakeContext context) throws IOException {
|
||||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
|
||||||
return t12DeriveKey(algorithm, params);
|
|
||||||
} else {
|
|
||||||
return t13DeriveKey(algorithm, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecretKey t12DeriveKey(String algorithm,
|
NamedGroupPossession namedGroupPossession = null;
|
||||||
AlgorithmParameterSpec params) throws IOException {
|
NamedGroupCredentials namedGroupCredentials = null;
|
||||||
try {
|
NamedGroup namedGroup = null;
|
||||||
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
|
|
||||||
ka.init(localPrivateKey);
|
|
||||||
ka.doPhase(peerPublicKey, true);
|
|
||||||
SecretKey preMasterSecret =
|
|
||||||
ka.generateSecret("TlsPremasterSecret");
|
|
||||||
|
|
||||||
SSLMasterKeyDerivation mskd =
|
// Find a possession/credential combo using the same named group
|
||||||
SSLMasterKeyDerivation.valueOf(
|
search:
|
||||||
context.negotiatedProtocol);
|
for (SSLPossession poss : context.handshakePossessions) {
|
||||||
if (mskd == null) {
|
for (SSLCredentials cred : context.handshakeCredentials) {
|
||||||
// unlikely
|
if (((poss instanceof ECDHEPossession) &&
|
||||||
throw new SSLHandshakeException(
|
(cred instanceof ECDHECredentials)) ||
|
||||||
"No expected master key derivation for protocol: " +
|
(((poss instanceof XDHEPossession) &&
|
||||||
context.negotiatedProtocol.name);
|
(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,
|
if (namedGroupPossession == null || namedGroupCredentials == null) {
|
||||||
AlgorithmParameterSpec params) throws IOException {
|
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||||
try {
|
"No sufficient ECDHE/XDH key agreement " +
|
||||||
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
|
"parameters negotiated");
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,32 +27,21 @@ package sun.security.ssl;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.CryptoPrimitive;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.security.interfaces.ECPublicKey;
|
|
||||||
import java.security.spec.ECParameterSpec;
|
|
||||||
import java.security.spec.ECPoint;
|
|
||||||
import java.security.spec.ECPublicKeySpec;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
|
|
||||||
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
import sun.security.util.ECUtil;
|
|
||||||
import sun.security.util.HexDumpEncoder;
|
import sun.security.util.HexDumpEncoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,14 +69,14 @@ final class ECDHServerKeyExchange {
|
|||||||
// signature bytes, or null if anonymous
|
// signature bytes, or null if anonymous
|
||||||
private final byte[] paramsSignature;
|
private final byte[] paramsSignature;
|
||||||
|
|
||||||
// public key object encapsulated in this message
|
|
||||||
private final ECPublicKey publicKey;
|
|
||||||
|
|
||||||
private final boolean useExplicitSigAlgorithm;
|
private final boolean useExplicitSigAlgorithm;
|
||||||
|
|
||||||
// the signature algorithm used by this ServerKeyExchange message
|
// the signature algorithm used by this ServerKeyExchange message
|
||||||
private final SignatureScheme signatureScheme;
|
private final SignatureScheme signatureScheme;
|
||||||
|
|
||||||
|
// the parsed credential object
|
||||||
|
private SSLCredentials sslCredentials;
|
||||||
|
|
||||||
ECDHServerKeyExchangeMessage(
|
ECDHServerKeyExchangeMessage(
|
||||||
HandshakeContext handshakeContext) throws IOException {
|
HandshakeContext handshakeContext) throws IOException {
|
||||||
super(handshakeContext);
|
super(handshakeContext);
|
||||||
@ -96,38 +85,38 @@ final class ECDHServerKeyExchange {
|
|||||||
ServerHandshakeContext shc =
|
ServerHandshakeContext shc =
|
||||||
(ServerHandshakeContext)handshakeContext;
|
(ServerHandshakeContext)handshakeContext;
|
||||||
|
|
||||||
ECDHEPossession ecdhePossession = null;
|
// Find the Possessions needed
|
||||||
|
NamedGroupPossession namedGroupPossession = null;
|
||||||
X509Possession x509Possession = null;
|
X509Possession x509Possession = null;
|
||||||
for (SSLPossession possession : shc.handshakePossessions) {
|
for (SSLPossession possession : shc.handshakePossessions) {
|
||||||
if (possession instanceof ECDHEPossession) {
|
if (possession instanceof NamedGroupPossession) {
|
||||||
ecdhePossession = (ECDHEPossession)possession;
|
namedGroupPossession = (NamedGroupPossession)possession;
|
||||||
if (x509Possession != null) {
|
if (x509Possession != null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (possession instanceof X509Possession) {
|
} else if (possession instanceof X509Possession) {
|
||||||
x509Possession = (X509Possession)possession;
|
x509Possession = (X509Possession)possession;
|
||||||
if (ecdhePossession != null) {
|
if (namedGroupPossession != null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ecdhePossession == null) {
|
if (namedGroupPossession == null) {
|
||||||
// unlikely
|
// unlikely
|
||||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"No ECDHE credentials negotiated for server key exchange");
|
"No ECDHE credentials negotiated for server key exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
publicKey = ecdhePossession.publicKey;
|
// Find the NamedGroup used for the ephemeral keys.
|
||||||
ECParameterSpec params = publicKey.getParams();
|
namedGroup = namedGroupPossession.getNamedGroup();
|
||||||
ECPoint point = publicKey.getW();
|
publicPoint = namedGroup.encodePossessionPublicKey(
|
||||||
publicPoint = ECUtil.encodePoint(point, params.getCurve());
|
namedGroupPossession);
|
||||||
|
|
||||||
this.namedGroup = NamedGroup.valueOf(params);
|
|
||||||
if ((namedGroup == null) || (namedGroup.oid == null) ) {
|
if ((namedGroup == null) || (namedGroup.oid == null) ) {
|
||||||
// unlikely
|
// unlikely
|
||||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"Unnamed EC parameter spec: " + params);
|
"Missing Named Group");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x509Possession == null) {
|
if (x509Possession == null) {
|
||||||
@ -216,39 +205,23 @@ final class ECDHServerKeyExchange {
|
|||||||
"Unsupported named group: " + namedGroup);
|
"Unsupported named group: " + namedGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namedGroup.oid == null) {
|
|
||||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
|
||||||
"Unknown named EC curve: " + namedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
ECParameterSpec parameters =
|
|
||||||
ECUtil.getECParameterSpec(null, namedGroup.oid);
|
|
||||||
if (parameters == null) {
|
|
||||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
|
||||||
"No supported EC parameter: " + namedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
publicPoint = Record.getBytes8(m);
|
publicPoint = Record.getBytes8(m);
|
||||||
if (publicPoint.length == 0) {
|
if (publicPoint.length == 0) {
|
||||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"Insufficient ECPoint data: " + namedGroup);
|
"Insufficient Point data: " + namedGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
ECPublicKey ecPublicKey = null;
|
|
||||||
try {
|
try {
|
||||||
ECPoint point =
|
sslCredentials = namedGroup.decodeCredentials(
|
||||||
ECUtil.decodePoint(publicPoint, parameters.getCurve());
|
publicPoint, handshakeContext.algorithmConstraints,
|
||||||
KeyFactory factory = KeyFactory.getInstance("EC");
|
s -> chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||||
ecPublicKey = (ECPublicKey)factory.generatePublic(
|
"ServerKeyExchange " + namedGroup + ": " + (s)));
|
||||||
new ECPublicKeySpec(point, parameters));
|
} catch (GeneralSecurityException ex) {
|
||||||
} catch (NoSuchAlgorithmException |
|
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||||
InvalidKeySpecException | IOException ex) {
|
"Cannot decode named group: " +
|
||||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
NamedGroup.nameOf(namedGroupId));
|
||||||
"Invalid ECPoint: " + namedGroup, ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publicKey = ecPublicKey;
|
|
||||||
|
|
||||||
X509Credentials x509Credentials = null;
|
X509Credentials x509Credentials = null;
|
||||||
for (SSLCredentials cd : chc.handshakeCredentials) {
|
for (SSLCredentials cd : chc.handshakeCredentials) {
|
||||||
if (cd instanceof X509Credentials) {
|
if (cd instanceof X509Credentials) {
|
||||||
@ -529,6 +502,7 @@ final class ECDHServerKeyExchange {
|
|||||||
// The consuming happens in client side only.
|
// The consuming happens in client side only.
|
||||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||||
|
|
||||||
|
// AlgorithmConstraints are checked during decoding
|
||||||
ECDHServerKeyExchangeMessage skem =
|
ECDHServerKeyExchangeMessage skem =
|
||||||
new ECDHServerKeyExchangeMessage(chc, message);
|
new ECDHServerKeyExchangeMessage(chc, message);
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
@ -536,23 +510,10 @@ final class ECDHServerKeyExchange {
|
|||||||
"Consuming ECDH ServerKeyExchange handshake message", skem);
|
"Consuming ECDH ServerKeyExchange handshake message", skem);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// validate
|
|
||||||
//
|
|
||||||
// check constraints of EC PublicKey
|
|
||||||
if (!chc.algorithmConstraints.permits(
|
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
|
||||||
skem.publicKey)) {
|
|
||||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
|
||||||
"ECDH ServerKeyExchange does not comply " +
|
|
||||||
"to algorithm constraints");
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// update
|
// update
|
||||||
//
|
//
|
||||||
chc.handshakeCredentials.add(
|
chc.handshakeCredentials.add(skem.sslCredentials);
|
||||||
new ECDHECredentials(skem.publicKey, skem.namedGroup));
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// produce
|
// produce
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -34,7 +34,7 @@ import static sun.security.ssl.SSLExtension.CH_EC_POINT_FORMATS;
|
|||||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pack of the "ec_point_formats" extensions [RFC 4492].
|
* Pack of the "ec_point_formats" extensions [RFC 4492].
|
||||||
|
@ -46,9 +46,8 @@ import javax.crypto.SecretKey;
|
|||||||
import javax.net.ssl.SNIServerName;
|
import javax.net.ssl.SNIServerName;
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
import static sun.security.ssl.NamedGroup.NamedGroupType.*;
|
||||||
import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
|
|
||||||
abstract class HandshakeContext implements ConnectionContext {
|
abstract class HandshakeContext implements ConnectionContext {
|
||||||
@ -519,31 +518,38 @@ abstract class HandshakeContext implements ConnectionContext {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean available;
|
// Is at least one of the group types available?
|
||||||
NamedGroupType groupType = suite.keyExchange.groupType;
|
boolean groupAvailable, retval = false;
|
||||||
if (groupType != NAMED_GROUP_NONE) {
|
NamedGroupType[] groupTypes = suite.keyExchange.groupTypes;
|
||||||
Boolean checkedStatus = cachedStatus.get(groupType);
|
for (NamedGroupType groupType : groupTypes) {
|
||||||
if (checkedStatus == null) {
|
if (groupType != NAMED_GROUP_NONE) {
|
||||||
available = SupportedGroups.isActivatable(
|
Boolean checkedStatus = cachedStatus.get(groupType);
|
||||||
algorithmConstraints, groupType);
|
if (checkedStatus == null) {
|
||||||
cachedStatus.put(groupType, available);
|
groupAvailable = SupportedGroups.isActivatable(
|
||||||
|
algorithmConstraints, groupType);
|
||||||
|
cachedStatus.put(groupType, groupAvailable);
|
||||||
|
|
||||||
if (!available &&
|
if (!groupAvailable &&
|
||||||
SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||||
SSLLogger.fine("No activated named group");
|
SSLLogger.fine(
|
||||||
|
"No activated named group in " + groupType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
groupAvailable = checkedStatus;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
available = checkedStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!available && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
retval |= groupAvailable;
|
||||||
SSLLogger.fine(
|
} else {
|
||||||
"No active named group, ignore " + suite);
|
retval |= true;
|
||||||
}
|
}
|
||||||
return available;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!retval && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||||
|
SSLLogger.fine("No active named group(s), ignore " + suite);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
|
||||||
} else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
} else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||||
SSLLogger.fine("Ignore disabled cipher suite: " + suite);
|
SSLLogger.fine("Ignore disabled cipher suite: " + suite);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package sun.security.ssl;
|
||||||
|
|
||||||
|
import javax.crypto.KeyAgreement;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common class for creating various KeyDerivation types.
|
||||||
|
*/
|
||||||
|
public class KAKeyDerivation implements SSLKeyDerivation {
|
||||||
|
|
||||||
|
private final String algorithmName;
|
||||||
|
private final HandshakeContext context;
|
||||||
|
private final PrivateKey localPrivateKey;
|
||||||
|
private final PublicKey peerPublicKey;
|
||||||
|
|
||||||
|
KAKeyDerivation(String algorithmName,
|
||||||
|
HandshakeContext context,
|
||||||
|
PrivateKey localPrivateKey,
|
||||||
|
PublicKey peerPublicKey) {
|
||||||
|
this.algorithmName = algorithmName;
|
||||||
|
this.context = context;
|
||||||
|
this.localPrivateKey = localPrivateKey;
|
||||||
|
this.peerPublicKey = peerPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecretKey deriveKey(String algorithm,
|
||||||
|
AlgorithmParameterSpec params) throws IOException {
|
||||||
|
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||||
|
return t12DeriveKey(algorithm, params);
|
||||||
|
} else {
|
||||||
|
return t13DeriveKey(algorithm, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the TLSv1-1.2 objects, which don't use the HKDF algorithms.
|
||||||
|
*/
|
||||||
|
private SecretKey t12DeriveKey(String algorithm,
|
||||||
|
AlgorithmParameterSpec params) throws IOException {
|
||||||
|
try {
|
||||||
|
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
|
||||||
|
ka.init(localPrivateKey);
|
||||||
|
ka.doPhase(peerPublicKey, true);
|
||||||
|
SecretKey preMasterSecret
|
||||||
|
= ka.generateSecret("TlsPremasterSecret");
|
||||||
|
SSLMasterKeyDerivation mskd
|
||||||
|
= SSLMasterKeyDerivation.valueOf(
|
||||||
|
context.negotiatedProtocol);
|
||||||
|
if (mskd == null) {
|
||||||
|
// unlikely
|
||||||
|
throw new SSLHandshakeException(
|
||||||
|
"No expected master key derivation for protocol: "
|
||||||
|
+ context.negotiatedProtocol.name);
|
||||||
|
}
|
||||||
|
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||||
|
context, preMasterSecret);
|
||||||
|
return kd.deriveKey("MasterSecret", params);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||||
|
"Could not generate secret").initCause(gse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the TLSv1.3 objects, which use the HKDF algorithms.
|
||||||
|
*/
|
||||||
|
private SecretKey t13DeriveKey(String algorithm,
|
||||||
|
AlgorithmParameterSpec params) throws IOException {
|
||||||
|
try {
|
||||||
|
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
|
||||||
|
ka.init(localPrivateKey);
|
||||||
|
ka.doPhase(peerPublicKey, true);
|
||||||
|
SecretKey sharedSecret
|
||||||
|
= ka.generateSecret("TlsPremasterSecret");
|
||||||
|
|
||||||
|
CipherSuite.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,27 +27,19 @@ package sun.security.ssl;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.CryptoPrimitive;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.net.ssl.SSLProtocolException;
|
import javax.net.ssl.SSLProtocolException;
|
||||||
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
|
||||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
|
||||||
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
|
|
||||||
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
|
||||||
import sun.security.ssl.KeyShareExtension.CHKeyShareSpec;
|
import sun.security.ssl.KeyShareExtension.CHKeyShareSpec;
|
||||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
import sun.security.util.HexDumpEncoder;
|
import sun.security.util.HexDumpEncoder;
|
||||||
|
|
||||||
@ -264,8 +256,7 @@ final class KeyShareExtension {
|
|||||||
for (SSLPossession pos : poses) {
|
for (SSLPossession pos : poses) {
|
||||||
// update the context
|
// update the context
|
||||||
chc.handshakePossessions.add(pos);
|
chc.handshakePossessions.add(pos);
|
||||||
if (!(pos instanceof ECDHEPossession) &&
|
if (!(pos instanceof NamedGroupPossession)) {
|
||||||
!(pos instanceof DHEPossession)) {
|
|
||||||
// May need more possesion types in the future.
|
// May need more possesion types in the future.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -353,46 +344,18 @@ final class KeyShareExtension {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
try {
|
||||||
try {
|
SSLCredentials kaCred =
|
||||||
ECDHECredentials ecdhec =
|
ng.decodeCredentials(entry.keyExchange,
|
||||||
ECDHECredentials.valueOf(ng, entry.keyExchange);
|
shc.algorithmConstraints,
|
||||||
if (ecdhec != null) {
|
s -> SSLLogger.warning(s));
|
||||||
if (!shc.algorithmConstraints.permits(
|
if (kaCred != null) {
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
credentials.add(kaCred);
|
||||||
ecdhec.popPublicKey)) {
|
|
||||||
SSLLogger.warning(
|
|
||||||
"ECDHE key share entry does not " +
|
|
||||||
"comply to algorithm constraints");
|
|
||||||
} else {
|
|
||||||
credentials.add(ecdhec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException | GeneralSecurityException ex) {
|
|
||||||
SSLLogger.warning(
|
|
||||||
"Cannot decode named group: " +
|
|
||||||
NamedGroup.nameOf(entry.namedGroupId));
|
|
||||||
}
|
|
||||||
} else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
|
||||||
try {
|
|
||||||
DHECredentials dhec =
|
|
||||||
DHECredentials.valueOf(ng, entry.keyExchange);
|
|
||||||
if (dhec != null) {
|
|
||||||
if (!shc.algorithmConstraints.permits(
|
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
|
||||||
dhec.popPublicKey)) {
|
|
||||||
SSLLogger.warning(
|
|
||||||
"DHE key share entry does not " +
|
|
||||||
"comply to algorithm constraints");
|
|
||||||
} else {
|
|
||||||
credentials.add(dhec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException | GeneralSecurityException ex) {
|
|
||||||
SSLLogger.warning(
|
|
||||||
"Cannot decode named group: " +
|
|
||||||
NamedGroup.nameOf(entry.namedGroupId));
|
|
||||||
}
|
}
|
||||||
|
} catch (GeneralSecurityException ex) {
|
||||||
|
SSLLogger.warning(
|
||||||
|
"Cannot decode named group: " +
|
||||||
|
NamedGroup.nameOf(entry.namedGroupId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,10 +489,9 @@ final class KeyShareExtension {
|
|||||||
KeyShareEntry keyShare = null;
|
KeyShareEntry keyShare = null;
|
||||||
for (SSLCredentials cd : shc.handshakeCredentials) {
|
for (SSLCredentials cd : shc.handshakeCredentials) {
|
||||||
NamedGroup ng = null;
|
NamedGroup ng = null;
|
||||||
if (cd instanceof ECDHECredentials) {
|
if (cd instanceof NamedGroupCredentials) {
|
||||||
ng = ((ECDHECredentials)cd).namedGroup;
|
NamedGroupCredentials creds = (NamedGroupCredentials)cd;
|
||||||
} else if (cd instanceof DHECredentials) {
|
ng = creds.getNamedGroup();
|
||||||
ng = ((DHECredentials)cd).namedGroup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ng == null) {
|
if (ng == null) {
|
||||||
@ -547,8 +509,7 @@ final class KeyShareExtension {
|
|||||||
|
|
||||||
SSLPossession[] poses = ke.createPossessions(shc);
|
SSLPossession[] poses = ke.createPossessions(shc);
|
||||||
for (SSLPossession pos : poses) {
|
for (SSLPossession pos : poses) {
|
||||||
if (!(pos instanceof ECDHEPossession) &&
|
if (!(pos instanceof NamedGroupPossession)) {
|
||||||
!(pos instanceof DHEPossession)) {
|
|
||||||
// May need more possesion types in the future.
|
// May need more possesion types in the future.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -567,7 +528,7 @@ final class KeyShareExtension {
|
|||||||
me.getKey(), me.getValue());
|
me.getKey(), me.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have got one! Don't forgor to break.
|
// We have got one! Don't forget to break.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -643,49 +604,16 @@ final class KeyShareExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SSLCredentials credentials = null;
|
SSLCredentials credentials = null;
|
||||||
if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
try {
|
||||||
try {
|
SSLCredentials kaCred = ng.decodeCredentials(
|
||||||
ECDHECredentials ecdhec =
|
keyShare.keyExchange, chc.algorithmConstraints,
|
||||||
ECDHECredentials.valueOf(ng, keyShare.keyExchange);
|
s -> chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, s));
|
||||||
if (ecdhec != null) {
|
if (kaCred != null) {
|
||||||
if (!chc.algorithmConstraints.permits(
|
credentials = kaCred;
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
|
||||||
ecdhec.popPublicKey)) {
|
|
||||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
|
||||||
"ECDHE key share entry does not " +
|
|
||||||
"comply to algorithm constraints");
|
|
||||||
} else {
|
|
||||||
credentials = ecdhec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException | GeneralSecurityException ex) {
|
|
||||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
|
||||||
"Cannot decode named group: " +
|
|
||||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
|
||||||
}
|
}
|
||||||
} else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
} catch (GeneralSecurityException ex) {
|
||||||
try {
|
|
||||||
DHECredentials dhec =
|
|
||||||
DHECredentials.valueOf(ng, keyShare.keyExchange);
|
|
||||||
if (dhec != null) {
|
|
||||||
if (!chc.algorithmConstraints.permits(
|
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
|
||||||
dhec.popPublicKey)) {
|
|
||||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
|
||||||
"DHE key share entry does not " +
|
|
||||||
"comply to algorithm constraints");
|
|
||||||
} else {
|
|
||||||
credentials = dhec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException | GeneralSecurityException ex) {
|
|
||||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
|
||||||
"Cannot decode named group: " +
|
|
||||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||||
"Unsupported named group: " +
|
"Cannot decode named group: " +
|
||||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
NamedGroup.nameOf(keyShare.namedGroupId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
781
src/java.base/share/classes/sun/security/ssl/NamedGroup.java
Normal file
781
src/java.base/share/classes/sun/security/ssl/NamedGroup.java
Normal file
@ -0,0 +1,781 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package sun.security.ssl;
|
||||||
|
|
||||||
|
import javax.crypto.spec.DHParameterSpec;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.*;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import javax.crypto.*;
|
||||||
|
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
||||||
|
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
||||||
|
|
||||||
|
import sun.security.util.ECUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enum containing all known named groups for use in TLS.
|
||||||
|
*
|
||||||
|
* The enum also contains the required properties of each group and the
|
||||||
|
* required functions (e.g. encoding/decoding).
|
||||||
|
*/
|
||||||
|
enum NamedGroup {
|
||||||
|
// Elliptic Curves (RFC 4492)
|
||||||
|
//
|
||||||
|
// See sun.security.util.CurveDB for the OIDs
|
||||||
|
// NIST K-163
|
||||||
|
|
||||||
|
SECT163_K1(0x0001, "sect163k1", "1.3.132.0.1",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECT163_R1(0x0002, "sect163r1", "1.3.132.0.2",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST B-163
|
||||||
|
SECT163_R2(0x0003, "sect163r2", "1.3.132.0.15",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECT193_R1(0x0004, "sect193r1", "1.3.132.0.24",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECT193_R2(0x0005, "sect193r2", "1.3.132.0.25",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST K-233
|
||||||
|
SECT233_K1(0x0006, "sect233k1", "1.3.132.0.26",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST B-233
|
||||||
|
SECT233_R1(0x0007, "sect233r1", "1.3.132.0.27",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECT239_K1(0x0008, "sect239k1", "1.3.132.0.3",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST K-283
|
||||||
|
SECT283_K1(0x0009, "sect283k1", "1.3.132.0.16",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST B-283
|
||||||
|
SECT283_R1(0x000A, "sect283r1", "1.3.132.0.17",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST K-409
|
||||||
|
SECT409_K1(0x000B, "sect409k1", "1.3.132.0.36",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST B-409
|
||||||
|
SECT409_R1(0x000C, "sect409r1", "1.3.132.0.37",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST K-571
|
||||||
|
SECT571_K1(0x000D, "sect571k1", "1.3.132.0.38",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST B-571
|
||||||
|
SECT571_R1(0x000E, "sect571r1", "1.3.132.0.39",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECP160_K1(0x000F, "secp160k1", "1.3.132.0.9",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECP160_R1(0x0010, "secp160r1", "1.3.132.0.8",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECP160_R2(0x0011, "secp160r2", "1.3.132.0.30",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECP192_K1(0x0012, "secp192k1", "1.3.132.0.31",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST P-192
|
||||||
|
SECP192_R1(0x0013, "secp192r1", "1.2.840.10045.3.1.1",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECP224_K1(0x0014, "secp224k1", "1.3.132.0.32",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST P-224
|
||||||
|
SECP224_R1(0x0015, "secp224r1", "1.3.132.0.33",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
SECP256_K1(0x0016, "secp256k1", "1.3.132.0.10",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
|
||||||
|
// NIST P-256
|
||||||
|
SECP256_R1(0x0017, "secp256r1", "1.2.840.10045.3.1.7",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
|
||||||
|
// NIST P-384
|
||||||
|
SECP384_R1(0x0018, "secp384r1", "1.3.132.0.34",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
|
||||||
|
// NIST P-521
|
||||||
|
SECP521_R1(0x0019, "secp521r1", "1.3.132.0.35",
|
||||||
|
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
|
||||||
|
// x25519 and x448 (RFC 8422/8446)
|
||||||
|
X25519(0x001D, "x25519", "1.3.101.110",
|
||||||
|
NamedGroupType.NAMED_GROUP_XDH,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
X448(0x001E, "x448", "1.3.101.111",
|
||||||
|
NamedGroupType.NAMED_GROUP_XDH,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
|
||||||
|
// Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
|
||||||
|
FFDHE_2048(0x0100, "ffdhe2048", null,
|
||||||
|
NamedGroupType.NAMED_GROUP_FFDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
FFDHE_3072(0x0101, "ffdhe3072", null,
|
||||||
|
NamedGroupType.NAMED_GROUP_FFDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
FFDHE_4096(0x0102, "ffdhe4096", null,
|
||||||
|
NamedGroupType.NAMED_GROUP_FFDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
FFDHE_6144(0x0103, "ffdhe6144", null,
|
||||||
|
NamedGroupType.NAMED_GROUP_FFDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
FFDHE_8192(0x0104, "ffdhe8192", null,
|
||||||
|
NamedGroupType.NAMED_GROUP_FFDHE,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
|
||||||
|
// Elliptic Curves (RFC 4492)
|
||||||
|
//
|
||||||
|
// arbitrary prime and characteristic-2 curves
|
||||||
|
ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves", null,
|
||||||
|
NamedGroupType.NAMED_GROUP_ARBITRARY,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12),
|
||||||
|
ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves", null,
|
||||||
|
NamedGroupType.NAMED_GROUP_ARBITRARY,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12);
|
||||||
|
|
||||||
|
final int id; // hash + signature
|
||||||
|
final NamedGroupType type; // group type
|
||||||
|
final String name; // literal name
|
||||||
|
final String oid; // object identifier of the named group
|
||||||
|
final String algorithm; // signature algorithm
|
||||||
|
final ProtocolVersion[] supportedProtocols;
|
||||||
|
private final NamedGroupFunctions functions; // may be null
|
||||||
|
|
||||||
|
// Constructor used for all NamedGroup types
|
||||||
|
private NamedGroup(int id, String name, String oid,
|
||||||
|
NamedGroupType namedGroupType,
|
||||||
|
ProtocolVersion[] supportedProtocols) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.oid = oid;
|
||||||
|
this.type = namedGroupType;
|
||||||
|
this.supportedProtocols = supportedProtocols;
|
||||||
|
|
||||||
|
if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
||||||
|
this.functions = ECDHFunctions.getInstance();
|
||||||
|
this.algorithm = "EC";
|
||||||
|
} else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
||||||
|
this.functions = FFDHFunctions.getInstance();
|
||||||
|
this.algorithm = "DiffieHellman";
|
||||||
|
} else if (this.type == NamedGroupType.NAMED_GROUP_XDH) {
|
||||||
|
this.functions = XDHFunctions.getInstance();
|
||||||
|
this.algorithm = "XDH";
|
||||||
|
} else if (this.type == NamedGroupType.NAMED_GROUP_ARBITRARY) {
|
||||||
|
this.functions = null;
|
||||||
|
this.algorithm = "EC";
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Unexpected Named Group Type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<NamedGroupFunctions> getFunctions() {
|
||||||
|
return Optional.ofNullable(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next set of methods search & retrieve NamedGroups.
|
||||||
|
|
||||||
|
static NamedGroup valueOf(int id) {
|
||||||
|
for (NamedGroup group : NamedGroup.values()) {
|
||||||
|
if (group.id == id) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NamedGroup valueOf(ECParameterSpec params) {
|
||||||
|
String oid = ECUtil.getCurveName(null, params);
|
||||||
|
if ((oid != null) && (!oid.isEmpty())) {
|
||||||
|
for (NamedGroup group : NamedGroup.values()) {
|
||||||
|
if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE)
|
||||||
|
&& oid.equals(group.oid)) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NamedGroup valueOf(DHParameterSpec params) {
|
||||||
|
for (NamedGroup ng : NamedGroup.values()) {
|
||||||
|
if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DHParameterSpec ngParams = null;
|
||||||
|
// functions is non-null for FFDHE type
|
||||||
|
AlgorithmParameters aps = ng.functions.getParameters(ng);
|
||||||
|
try {
|
||||||
|
ngParams = aps.getParameterSpec(DHParameterSpec.class);
|
||||||
|
} catch (InvalidParameterSpecException ipse) {
|
||||||
|
// should be unlikely
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngParams == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngParams.getP().equals(params.getP())
|
||||||
|
&& ngParams.getG().equals(params.getG())) {
|
||||||
|
return ng;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NamedGroup nameOf(String name) {
|
||||||
|
for (NamedGroup group : NamedGroup.values()) {
|
||||||
|
if (group.name.equals(name)) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String nameOf(int id) {
|
||||||
|
for (NamedGroup group : NamedGroup.values()) {
|
||||||
|
if (group.id == id) {
|
||||||
|
return group.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "UNDEFINED-NAMED-GROUP(" + id + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are the NamedGroups available for the protocol desired?
|
||||||
|
|
||||||
|
boolean isAvailable(List<ProtocolVersion> protocolVersions) {
|
||||||
|
for (ProtocolVersion pv : supportedProtocols) {
|
||||||
|
if (protocolVersions.contains(pv)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAvailable(ProtocolVersion protocolVersion) {
|
||||||
|
for (ProtocolVersion pv : supportedProtocols) {
|
||||||
|
if (protocolVersion == pv) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are the NamedGroups available for the ciphersuites desired?
|
||||||
|
|
||||||
|
boolean isSupported(List<CipherSuite> cipherSuites) {
|
||||||
|
for (CipherSuite cs : cipherSuites) {
|
||||||
|
boolean isMatch = isAvailable(cs.supportedProtocols);
|
||||||
|
if (isMatch && ((cs.keyExchange == null)
|
||||||
|
|| (NamedGroupType.arrayContains(
|
||||||
|
cs.keyExchange.groupTypes, type)))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lazy loading of parameters
|
||||||
|
AlgorithmParameters getParameters() {
|
||||||
|
Optional<NamedGroupFunctions> ngf = getFunctions();
|
||||||
|
if (ngf.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ngf.get().getParameters(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next set of methods use the NamedGroupFunctions table
|
||||||
|
// to do various operations in a consistent way.
|
||||||
|
|
||||||
|
AlgorithmParameterSpec getParameterSpec() {
|
||||||
|
Optional<NamedGroupFunctions> ngf = getFunctions();
|
||||||
|
if (ngf.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ngf.get().getParameterSpec(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] encodePossessionPublicKey(
|
||||||
|
NamedGroupPossession namedGroupPossession) {
|
||||||
|
|
||||||
|
Optional<NamedGroupFunctions> ngf = getFunctions();
|
||||||
|
if (ngf.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ngf.get().encodePossessionPublicKey(namedGroupPossession);
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLCredentials decodeCredentials(byte[] encoded,
|
||||||
|
AlgorithmConstraints constraints,
|
||||||
|
ExceptionSupplier onConstraintFail)
|
||||||
|
throws IOException, GeneralSecurityException {
|
||||||
|
|
||||||
|
Optional<NamedGroupFunctions> ngf = getFunctions();
|
||||||
|
if (ngf.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ngf.get().decodeCredentials(this, encoded, constraints,
|
||||||
|
onConstraintFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLPossession createPossession(SecureRandom random) {
|
||||||
|
|
||||||
|
Optional<NamedGroupFunctions> ngf = getFunctions();
|
||||||
|
if (ngf.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ngf.get().createPossession(this, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
Optional<NamedGroupFunctions> ngf = getFunctions();
|
||||||
|
if (ngf.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ngf.get().createKeyDerivation(hc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAvailableGroup() {
|
||||||
|
Optional<NamedGroupFunctions> ngfOpt = getFunctions();
|
||||||
|
if (ngfOpt.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NamedGroupFunctions ngf = ngfOpt.get();
|
||||||
|
return ngf.isAvailable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NamedGroupType {
|
||||||
|
NAMED_GROUP_ECDHE, // Elliptic Curve Groups (ECDHE)
|
||||||
|
NAMED_GROUP_FFDHE, // Finite Field Groups (DHE)
|
||||||
|
NAMED_GROUP_XDH, // Finite Field Groups (XDH)
|
||||||
|
NAMED_GROUP_ARBITRARY, // arbitrary prime and curves (ECDHE)
|
||||||
|
NAMED_GROUP_NONE; // Not predefined named group
|
||||||
|
|
||||||
|
boolean isSupported(List<CipherSuite> cipherSuites) {
|
||||||
|
for (CipherSuite cs : cipherSuites) {
|
||||||
|
if (cs.keyExchange == null ||
|
||||||
|
arrayContains(cs.keyExchange.groupTypes, this)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean arrayContains(NamedGroupType[] namedGroupTypes,
|
||||||
|
NamedGroupType namedGroupType) {
|
||||||
|
for (NamedGroupType ng : namedGroupTypes) {
|
||||||
|
if (ng == namedGroupType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExceptionSupplier {
|
||||||
|
void apply(String s) throws SSLException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A list of functions to do NamedGroup operations in a
|
||||||
|
* algorithm-independent and consistent way.
|
||||||
|
*/
|
||||||
|
private static abstract class NamedGroupFunctions {
|
||||||
|
|
||||||
|
// cache to speed up the parameters construction
|
||||||
|
protected static final Map<NamedGroup, AlgorithmParameters>
|
||||||
|
namedGroupParams = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
protected void checkConstraints(PublicKey publicKey,
|
||||||
|
AlgorithmConstraints constraints,
|
||||||
|
ExceptionSupplier onConstraintFail)
|
||||||
|
throws SSLException {
|
||||||
|
|
||||||
|
if (!constraints.permits(
|
||||||
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||||
|
publicKey)) {
|
||||||
|
|
||||||
|
onConstraintFail.apply("key share entry does not "
|
||||||
|
+ "comply with algorithm constraints");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlgorithmParameters getParameters(NamedGroup ng) {
|
||||||
|
|
||||||
|
AlgorithmParameters result = namedGroupParams.get(ng);
|
||||||
|
if (result == null) {
|
||||||
|
Optional<AlgorithmParameters> paramsOpt = getParametersImpl(ng);
|
||||||
|
if (paramsOpt.isPresent()) {
|
||||||
|
result = paramsOpt.get();
|
||||||
|
namedGroupParams.put(ng, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract byte[] encodePossessionPublicKey(
|
||||||
|
NamedGroupPossession namedGroupPossession);
|
||||||
|
|
||||||
|
public abstract SSLCredentials decodeCredentials(
|
||||||
|
NamedGroup ng, byte[] encoded,
|
||||||
|
AlgorithmConstraints constraints,
|
||||||
|
ExceptionSupplier onConstraintFail)
|
||||||
|
throws IOException, GeneralSecurityException;
|
||||||
|
|
||||||
|
public abstract SSLPossession createPossession(NamedGroup ng,
|
||||||
|
SecureRandom random);
|
||||||
|
|
||||||
|
public abstract SSLKeyDerivation createKeyDerivation(
|
||||||
|
HandshakeContext hc) throws IOException;
|
||||||
|
|
||||||
|
protected abstract Optional<AlgorithmParameters> getParametersImpl(
|
||||||
|
NamedGroup ng);
|
||||||
|
|
||||||
|
public abstract AlgorithmParameterSpec getParameterSpec(NamedGroup ng);
|
||||||
|
|
||||||
|
public abstract boolean isAvailable(NamedGroup ng);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FFDHFunctions extends NamedGroupFunctions {
|
||||||
|
|
||||||
|
// lazy initialization
|
||||||
|
private static class FunctionsHolder {
|
||||||
|
private static final FFDHFunctions instance = new FFDHFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FFDHFunctions getInstance() {
|
||||||
|
return FunctionsHolder.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encodePossessionPublicKey(
|
||||||
|
NamedGroupPossession namedGroupPossession) {
|
||||||
|
return ((DHEPossession)namedGroupPossession).encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
|
||||||
|
AlgorithmConstraints constraints,
|
||||||
|
ExceptionSupplier onConstraintFail)
|
||||||
|
throws IOException, GeneralSecurityException {
|
||||||
|
|
||||||
|
DHKeyExchange.DHECredentials result
|
||||||
|
= DHKeyExchange.DHECredentials.valueOf(ng, encoded);
|
||||||
|
|
||||||
|
checkConstraints(result.getPublicKey(), constraints,
|
||||||
|
onConstraintFail);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLPossession createPossession(
|
||||||
|
NamedGroup ng, SecureRandom random) {
|
||||||
|
return new DHKeyExchange.DHEPossession(ng, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) {
|
||||||
|
return getDHParameterSpec(ng);
|
||||||
|
}
|
||||||
|
|
||||||
|
DHParameterSpec getDHParameterSpec(NamedGroup ng) {
|
||||||
|
|
||||||
|
AlgorithmParameters params = getParameters(ng);
|
||||||
|
try {
|
||||||
|
return params.getParameterSpec(DHParameterSpec.class);
|
||||||
|
} catch (InvalidParameterSpecException ipse) {
|
||||||
|
// should be unlikely
|
||||||
|
return getPredefinedDHParameterSpec(ng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DHParameterSpec getFFDHEDHParameterSpec(
|
||||||
|
NamedGroup namedGroup) {
|
||||||
|
|
||||||
|
DHParameterSpec spec = null;
|
||||||
|
switch (namedGroup) {
|
||||||
|
case FFDHE_2048:
|
||||||
|
spec = PredefinedDHParameterSpecs.ffdheParams.get(2048);
|
||||||
|
break;
|
||||||
|
case FFDHE_3072:
|
||||||
|
spec = PredefinedDHParameterSpecs.ffdheParams.get(3072);
|
||||||
|
break;
|
||||||
|
case FFDHE_4096:
|
||||||
|
spec = PredefinedDHParameterSpecs.ffdheParams.get(4096);
|
||||||
|
break;
|
||||||
|
case FFDHE_6144:
|
||||||
|
spec = PredefinedDHParameterSpecs.ffdheParams.get(6144);
|
||||||
|
break;
|
||||||
|
case FFDHE_8192:
|
||||||
|
spec = PredefinedDHParameterSpecs.ffdheParams.get(8192);
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DHParameterSpec getPredefinedDHParameterSpec(
|
||||||
|
NamedGroup namedGroup) {
|
||||||
|
|
||||||
|
DHParameterSpec spec = null;
|
||||||
|
switch (namedGroup) {
|
||||||
|
case FFDHE_2048:
|
||||||
|
spec = PredefinedDHParameterSpecs.definedParams.get(2048);
|
||||||
|
break;
|
||||||
|
case FFDHE_3072:
|
||||||
|
spec = PredefinedDHParameterSpecs.definedParams.get(3072);
|
||||||
|
break;
|
||||||
|
case FFDHE_4096:
|
||||||
|
spec = PredefinedDHParameterSpecs.definedParams.get(4096);
|
||||||
|
break;
|
||||||
|
case FFDHE_6144:
|
||||||
|
spec = PredefinedDHParameterSpecs.definedParams.get(6144);
|
||||||
|
break;
|
||||||
|
case FFDHE_8192:
|
||||||
|
spec = PredefinedDHParameterSpecs.definedParams.get(8192);
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable(NamedGroup ng) {
|
||||||
|
|
||||||
|
AlgorithmParameters params = getParameters(ng);
|
||||||
|
return params != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<AlgorithmParameters> getParametersImpl(
|
||||||
|
NamedGroup ng) {
|
||||||
|
try {
|
||||||
|
AlgorithmParameters params
|
||||||
|
= AlgorithmParameters.getInstance("DiffieHellman");
|
||||||
|
AlgorithmParameterSpec spec
|
||||||
|
= getFFDHEDHParameterSpec(ng);
|
||||||
|
params.init(spec);
|
||||||
|
return Optional.of(params);
|
||||||
|
} catch (InvalidParameterSpecException
|
||||||
|
| NoSuchAlgorithmException ex) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ECDHFunctions extends NamedGroupFunctions {
|
||||||
|
|
||||||
|
// lazy initialization
|
||||||
|
private static class FunctionsHolder {
|
||||||
|
private static final ECDHFunctions instance = new ECDHFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ECDHFunctions getInstance() {
|
||||||
|
return FunctionsHolder.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encodePossessionPublicKey(
|
||||||
|
NamedGroupPossession namedGroupPossession) {
|
||||||
|
return ((ECDHEPossession)namedGroupPossession).encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
|
||||||
|
AlgorithmConstraints constraints,
|
||||||
|
ExceptionSupplier onConstraintFail)
|
||||||
|
throws IOException, GeneralSecurityException {
|
||||||
|
|
||||||
|
ECDHKeyExchange.ECDHECredentials result
|
||||||
|
= ECDHKeyExchange.ECDHECredentials.valueOf(ng, encoded);
|
||||||
|
|
||||||
|
checkConstraints(result.getPublicKey(), constraints,
|
||||||
|
onConstraintFail);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLPossession createPossession(
|
||||||
|
NamedGroup ng, SecureRandom random) {
|
||||||
|
return new ECDHKeyExchange.ECDHEPossession(ng, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) {
|
||||||
|
return SupportedGroupsExtension.SupportedGroups
|
||||||
|
.getECGenParamSpec(ng);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable(NamedGroup ng) {
|
||||||
|
|
||||||
|
AlgorithmParameters params = getParameters(ng);
|
||||||
|
return params != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<AlgorithmParameters> getParametersImpl(
|
||||||
|
NamedGroup ng) {
|
||||||
|
try {
|
||||||
|
AlgorithmParameters params
|
||||||
|
= AlgorithmParameters.getInstance("EC");
|
||||||
|
AlgorithmParameterSpec spec
|
||||||
|
= new ECGenParameterSpec(ng.oid);
|
||||||
|
params.init(spec);
|
||||||
|
return Optional.of(params);
|
||||||
|
} catch (InvalidParameterSpecException
|
||||||
|
| NoSuchAlgorithmException ex) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class XDHFunctions extends NamedGroupFunctions {
|
||||||
|
|
||||||
|
// lazy initialization
|
||||||
|
private static class FunctionsHolder {
|
||||||
|
private static final XDHFunctions instance = new XDHFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static XDHFunctions getInstance() {
|
||||||
|
return FunctionsHolder.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encodePossessionPublicKey(NamedGroupPossession poss) {
|
||||||
|
return ((XDHKeyExchange.XDHEPossession)poss).encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
|
||||||
|
AlgorithmConstraints constraints,
|
||||||
|
ExceptionSupplier onConstraintFail)
|
||||||
|
throws IOException, GeneralSecurityException {
|
||||||
|
|
||||||
|
XDHKeyExchange.XDHECredentials result
|
||||||
|
= XDHKeyExchange.XDHECredentials.valueOf(ng, encoded);
|
||||||
|
|
||||||
|
checkConstraints(result.getPublicKey(), constraints,
|
||||||
|
onConstraintFail);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLPossession createPossession(
|
||||||
|
NamedGroup ng, SecureRandom random) {
|
||||||
|
return new XDHKeyExchange.XDHEPossession(ng, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
|
||||||
|
throws IOException {
|
||||||
|
return XDHKeyExchange.xdheKAGenerator.createKeyDerivation(hc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) {
|
||||||
|
return new NamedParameterSpec(ng.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable(NamedGroup ng) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
KeyAgreement.getInstance(ng.algorithm);
|
||||||
|
return true;
|
||||||
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<AlgorithmParameters> getParametersImpl(
|
||||||
|
NamedGroup ng) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sun.security.ssl;
|
||||||
|
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
interface NamedGroupCredentials extends SSLCredentials {
|
||||||
|
|
||||||
|
PublicKey getPublicKey();
|
||||||
|
|
||||||
|
NamedGroup getNamedGroup();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sun.security.ssl;
|
||||||
|
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
interface NamedGroupPossession extends SSLPossession {
|
||||||
|
|
||||||
|
NamedGroup getNamedGroup();
|
||||||
|
|
||||||
|
PublicKey getPublicKey();
|
||||||
|
|
||||||
|
PrivateKey getPrivateKey();
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -484,6 +484,25 @@ enum SSLExtension implements SSLStringizer {
|
|||||||
final SSLHandshake handshakeType;
|
final SSLHandshake handshakeType;
|
||||||
final String name;
|
final String name;
|
||||||
final ProtocolVersion[] supportedProtocols;
|
final ProtocolVersion[] supportedProtocols;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* networkProducer: produces outbound handshake data.
|
||||||
|
*
|
||||||
|
* onLoadConsumer: parses inbound data. It may not be appropriate
|
||||||
|
* to act until all of the message inputs have
|
||||||
|
* been parsed. (e.g. parsing keyShares and choosing
|
||||||
|
* a local value without having seen the SupportedGroups
|
||||||
|
* extension.)
|
||||||
|
*
|
||||||
|
* onLoadAbsence: if a missing message needs special handling
|
||||||
|
* during the load phase.
|
||||||
|
*
|
||||||
|
* onTradeConsumer: act on the parsed message once all inbound data has
|
||||||
|
* been traded and parsed.
|
||||||
|
*
|
||||||
|
* onTradeAbsence: if a missing message needs special handling
|
||||||
|
* during the trade phase.
|
||||||
|
*/
|
||||||
final HandshakeProducer networkProducer;
|
final HandshakeProducer networkProducer;
|
||||||
final ExtensionConsumer onLoadConsumer;
|
final ExtensionConsumer onLoadConsumer;
|
||||||
final HandshakeAbsence onLoadAbsence;
|
final HandshakeAbsence onLoadAbsence;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,10 +30,6 @@ import java.util.AbstractMap.SimpleImmutableEntry;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
|
||||||
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
|
|
||||||
@ -243,8 +239,7 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
|||||||
static SSLKeyExchange valueOf(NamedGroup namedGroup) {
|
static SSLKeyExchange valueOf(NamedGroup namedGroup) {
|
||||||
SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup);
|
SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup);
|
||||||
if (ka != null) {
|
if (ka != null) {
|
||||||
return new SSLKeyExchange(
|
return new SSLKeyExchange(null, ka);
|
||||||
null, T13KeyAgreement.valueOf(namedGroup));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -337,7 +332,7 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
|||||||
ECDH ("ecdh", null,
|
ECDH ("ecdh", null,
|
||||||
ECDHKeyExchange.ecdhKAGenerator),
|
ECDHKeyExchange.ecdhKAGenerator),
|
||||||
ECDHE ("ecdhe", ECDHKeyExchange.poGenerator,
|
ECDHE ("ecdhe", ECDHKeyExchange.poGenerator,
|
||||||
ECDHKeyExchange.ecdheKAGenerator);
|
ECDHKeyExchange.ecdheXdhKAGenerator);
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final SSLPossessionGenerator possessionGenerator;
|
final SSLPossessionGenerator possessionGenerator;
|
||||||
@ -570,27 +565,13 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLPossession createPossession(HandshakeContext hc) {
|
public SSLPossession createPossession(HandshakeContext hc) {
|
||||||
if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
return namedGroup.createPossession(hc.sslContext.getSecureRandom());
|
||||||
return new ECDHEPossession(
|
|
||||||
namedGroup, hc.sslContext.getSecureRandom());
|
|
||||||
} else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
|
||||||
return new DHEPossession(
|
|
||||||
namedGroup, hc.sslContext.getSecureRandom());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLKeyDerivation createKeyDerivation(
|
public SSLKeyDerivation createKeyDerivation(
|
||||||
HandshakeContext hc) throws IOException {
|
HandshakeContext hc) throws IOException {
|
||||||
if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
return namedGroup.createKeyDerivation(hc);
|
||||||
return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
|
|
||||||
} else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
|
||||||
return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -313,7 +313,9 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
|
|||||||
handshakeBuffer.put(handshakeFrag);
|
handshakeBuffer.put(handshakeFrag);
|
||||||
handshakeBuffer.rewind();
|
handshakeBuffer.rewind();
|
||||||
break;
|
break;
|
||||||
} if (remaining == handshakeMessageLen) {
|
}
|
||||||
|
|
||||||
|
if (remaining == handshakeMessageLen) {
|
||||||
if (handshakeHash.isHashable(handshakeType)) {
|
if (handshakeHash.isHashable(handshakeType)) {
|
||||||
handshakeHash.receive(handshakeFrag);
|
handshakeHash.receive(handshakeFrag);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
package sun.security.ssl;
|
package sun.security.ssl;
|
||||||
|
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.security.interfaces.ECPrivateKey;
|
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.security.spec.ECParameterSpec;
|
import java.security.spec.ECParameterSpec;
|
||||||
import java.security.spec.MGF1ParameterSpec;
|
import java.security.spec.MGF1ParameterSpec;
|
||||||
@ -39,8 +38,7 @@ import java.util.EnumSet;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
import sun.security.util.KeyUtil;
|
import sun.security.util.KeyUtil;
|
||||||
import sun.security.util.SignatureUtil;
|
import sun.security.util.SignatureUtil;
|
||||||
@ -432,10 +430,10 @@ enum SignatureScheme {
|
|||||||
}
|
}
|
||||||
for (SignatureScheme ss : schemes) {
|
for (SignatureScheme ss : schemes) {
|
||||||
if (ss.isAvailable && (keySize >= ss.minimalKeySize) &&
|
if (ss.isAvailable && (keySize >= ss.minimalKeySize) &&
|
||||||
ss.handshakeSupportedProtocols.contains(version) &&
|
ss.handshakeSupportedProtocols.contains(version) &&
|
||||||
keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
|
keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
|
||||||
if (ss.namedGroup != null &&
|
if ((ss.namedGroup != null) && (ss.namedGroup.type ==
|
||||||
ss.namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
NamedGroupType.NAMED_GROUP_ECDHE)) {
|
||||||
ECParameterSpec params =
|
ECParameterSpec params =
|
||||||
x509Possession.getECParameterSpec();
|
x509Possession.getECParameterSpec();
|
||||||
if (params != null &&
|
if (params != null &&
|
||||||
|
@ -30,29 +30,24 @@ import java.nio.ByteBuffer;
|
|||||||
import java.security.AlgorithmConstraints;
|
import java.security.AlgorithmConstraints;
|
||||||
import java.security.AlgorithmParameters;
|
import java.security.AlgorithmParameters;
|
||||||
import java.security.CryptoPrimitive;
|
import java.security.CryptoPrimitive;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
|
||||||
import java.security.spec.ECGenParameterSpec;
|
import java.security.spec.ECGenParameterSpec;
|
||||||
import java.security.spec.ECParameterSpec;
|
|
||||||
import java.security.spec.InvalidParameterSpecException;
|
import java.security.spec.InvalidParameterSpecException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import javax.crypto.spec.DHParameterSpec;
|
|
||||||
import javax.net.ssl.SSLProtocolException;
|
import javax.net.ssl.SSLProtocolException;
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
|
import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
|
||||||
import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
|
import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
|
||||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.util.ECUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pack of the "supported_groups" extensions [RFC 4492/7919].
|
* Pack of the "supported_groups" extensions [RFC 4492/7919].
|
||||||
@ -158,320 +153,11 @@ final class SupportedGroupsExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum NamedGroupType {
|
|
||||||
NAMED_GROUP_ECDHE ("EC"),
|
|
||||||
NAMED_GROUP_FFDHE ("DiffieHellman"),
|
|
||||||
NAMED_GROUP_X25519 ("x25519"),
|
|
||||||
NAMED_GROUP_X448 ("x448"),
|
|
||||||
NAMED_GROUP_ARBITRARY ("EC"),
|
|
||||||
NAMED_GROUP_NONE ("");
|
|
||||||
|
|
||||||
private final String algorithm;
|
|
||||||
|
|
||||||
private NamedGroupType(String algorithm) {
|
|
||||||
this.algorithm = algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isSupported(List<CipherSuite> cipherSuites) {
|
|
||||||
for (CipherSuite cs : cipherSuites) {
|
|
||||||
if (cs.keyExchange == null ||
|
|
||||||
cs.keyExchange.groupType == this) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum NamedGroup {
|
|
||||||
// Elliptic Curves (RFC 4492)
|
|
||||||
//
|
|
||||||
// See sun.security.util.CurveDB for the OIDs
|
|
||||||
// NIST K-163
|
|
||||||
SECT163_K1 (0x0001, "sect163k1", "1.3.132.0.1",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECT163_R1 (0x0002, "sect163r1", "1.3.132.0.2",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST B-163
|
|
||||||
SECT163_R2 (0x0003, "sect163r2", "1.3.132.0.15",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECT193_R1 (0x0004, "sect193r1", "1.3.132.0.24",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECT193_R2 (0x0005, "sect193r2", "1.3.132.0.25",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST K-233
|
|
||||||
SECT233_K1 (0x0006, "sect233k1", "1.3.132.0.26",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST B-233
|
|
||||||
SECT233_R1 (0x0007, "sect233r1", "1.3.132.0.27",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECT239_K1 (0x0008, "sect239k1", "1.3.132.0.3",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST K-283
|
|
||||||
SECT283_K1 (0x0009, "sect283k1", "1.3.132.0.16",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST B-283
|
|
||||||
SECT283_R1 (0x000A, "sect283r1", "1.3.132.0.17",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST K-409
|
|
||||||
SECT409_K1 (0x000B, "sect409k1", "1.3.132.0.36",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST B-409
|
|
||||||
SECT409_R1 (0x000C, "sect409r1", "1.3.132.0.37",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST K-571
|
|
||||||
SECT571_K1 (0x000D, "sect571k1", "1.3.132.0.38",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST B-571
|
|
||||||
SECT571_R1 (0x000E, "sect571r1", "1.3.132.0.39",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECP160_K1 (0x000F, "secp160k1", "1.3.132.0.9",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECP160_R1 (0x0010, "secp160r1", "1.3.132.0.8",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECP160_R2 (0x0011, "secp160r2", "1.3.132.0.30",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECP192_K1 (0x0012, "secp192k1", "1.3.132.0.31",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST P-192
|
|
||||||
SECP192_R1 (0x0013, "secp192r1", "1.2.840.10045.3.1.1",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECP224_K1 (0x0014, "secp224k1", "1.3.132.0.32",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
// NIST P-224
|
|
||||||
SECP224_R1 (0x0015, "secp224r1", "1.3.132.0.33",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
SECP256_K1 (0x0016, "secp256k1", "1.3.132.0.10",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
|
|
||||||
// NIST P-256
|
|
||||||
SECP256_R1 (0x0017, "secp256r1", "1.2.840.10045.3.1.7",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
|
|
||||||
// NIST P-384
|
|
||||||
SECP384_R1 (0x0018, "secp384r1", "1.3.132.0.34",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
|
|
||||||
// NIST P-521
|
|
||||||
SECP521_R1 (0x0019, "secp521r1", "1.3.132.0.35",
|
|
||||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
|
|
||||||
// x25519 and x448
|
|
||||||
X25519 (0x001D, "x25519", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_X25519,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
X448 (0x001E, "x448", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_X448,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
|
|
||||||
// Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
|
|
||||||
FFDHE_2048 (0x0100, "ffdhe2048", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_FFDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
FFDHE_3072 (0x0101, "ffdhe3072", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_FFDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
FFDHE_4096 (0x0102, "ffdhe4096", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_FFDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
FFDHE_6144 (0x0103, "ffdhe6144", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_FFDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
FFDHE_8192 (0x0104, "ffdhe8192", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_FFDHE,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
|
||||||
|
|
||||||
// Elliptic Curves (RFC 4492)
|
|
||||||
//
|
|
||||||
// arbitrary prime and characteristic-2 curves
|
|
||||||
ARBITRARY_PRIME (0xFF01, "arbitrary_explicit_prime_curves", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_ARBITRARY,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12),
|
|
||||||
ARBITRARY_CHAR2 (0xFF02, "arbitrary_explicit_char2_curves", null,
|
|
||||||
NamedGroupType.NAMED_GROUP_ARBITRARY,
|
|
||||||
ProtocolVersion.PROTOCOLS_TO_12);
|
|
||||||
|
|
||||||
final int id; // hash + signature
|
|
||||||
final NamedGroupType type; // group type
|
|
||||||
final String name; // literal name
|
|
||||||
final String oid; // object identifier of the named group
|
|
||||||
final String algorithm; // signature algorithm
|
|
||||||
final ProtocolVersion[] supportedProtocols;
|
|
||||||
|
|
||||||
private NamedGroup(int id, String name, String oid,
|
|
||||||
NamedGroupType namedGroupType,
|
|
||||||
ProtocolVersion[] supportedProtocols) {
|
|
||||||
this.id = id;
|
|
||||||
this.type = namedGroupType;
|
|
||||||
this.name = name;
|
|
||||||
this.oid = oid;
|
|
||||||
this.algorithm = namedGroupType.algorithm;
|
|
||||||
this.supportedProtocols = supportedProtocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NamedGroup valueOf(int id) {
|
|
||||||
for (NamedGroup group : NamedGroup.values()) {
|
|
||||||
if (group.id == id) {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NamedGroup valueOf(ECParameterSpec params) {
|
|
||||||
String oid = ECUtil.getCurveName(null, params);
|
|
||||||
if ((oid != null) && (!oid.isEmpty())) {
|
|
||||||
for (NamedGroup group : NamedGroup.values()) {
|
|
||||||
if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) &&
|
|
||||||
oid.equals(group.oid)) {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NamedGroup valueOf(DHParameterSpec params) {
|
|
||||||
for (Map.Entry<NamedGroup, AlgorithmParameters> me :
|
|
||||||
SupportedGroups.namedGroupParams.entrySet()) {
|
|
||||||
NamedGroup ng = me.getKey();
|
|
||||||
if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DHParameterSpec ngParams = null;
|
|
||||||
AlgorithmParameters aps = me.getValue();
|
|
||||||
try {
|
|
||||||
ngParams = aps.getParameterSpec(DHParameterSpec.class);
|
|
||||||
} catch (InvalidParameterSpecException ipse) {
|
|
||||||
// should be unlikely
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ngParams == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ngParams.getP().equals(params.getP()) &&
|
|
||||||
ngParams.getG().equals(params.getG())) {
|
|
||||||
return ng;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NamedGroup nameOf(String name) {
|
|
||||||
for (NamedGroup group : NamedGroup.values()) {
|
|
||||||
if (group.name.equals(name)) {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String nameOf(int id) {
|
|
||||||
for (NamedGroup group : NamedGroup.values()) {
|
|
||||||
if (group.id == id) {
|
|
||||||
return group.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "UNDEFINED-NAMED-GROUP(" + id + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isAvailable(List<ProtocolVersion> protocolVersions) {
|
|
||||||
for (ProtocolVersion pv : supportedProtocols) {
|
|
||||||
if (protocolVersions.contains(pv)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isAvailable(ProtocolVersion protocolVersion) {
|
|
||||||
for (ProtocolVersion pv : supportedProtocols) {
|
|
||||||
if (protocolVersion == pv) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isSupported(List<CipherSuite> cipherSuites) {
|
|
||||||
for (CipherSuite cs : cipherSuites) {
|
|
||||||
boolean isMatch = isAvailable(cs.supportedProtocols);
|
|
||||||
if (isMatch && (cs.keyExchange == null ||
|
|
||||||
cs.keyExchange.groupType == type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lazy loading of parameters
|
|
||||||
AlgorithmParameters getParameters() {
|
|
||||||
return SupportedGroups.namedGroupParams.get(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
AlgorithmParameterSpec getParameterSpec() {
|
|
||||||
if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
|
||||||
return SupportedGroups.getECGenParamSpec(this);
|
|
||||||
} else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
|
||||||
return SupportedGroups.getDHParameterSpec(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class SupportedGroups {
|
static class SupportedGroups {
|
||||||
// To switch off the supported_groups extension for DHE cipher suite.
|
// To switch off the supported_groups extension for DHE cipher suite.
|
||||||
static final boolean enableFFDHE =
|
static final boolean enableFFDHE =
|
||||||
Utilities.getBooleanProperty("jsse.enableFFDHE", true);
|
Utilities.getBooleanProperty("jsse.enableFFDHE", true);
|
||||||
|
|
||||||
// cache to speed up the parameters construction
|
|
||||||
static final Map<NamedGroup,
|
|
||||||
AlgorithmParameters> namedGroupParams = new HashMap<>();
|
|
||||||
|
|
||||||
// the supported named groups
|
// the supported named groups
|
||||||
static final NamedGroup[] supportedNamedGroups;
|
static final NamedGroup[] supportedNamedGroups;
|
||||||
|
|
||||||
@ -516,10 +202,19 @@ final class SupportedGroupsExtension {
|
|||||||
}
|
}
|
||||||
} else { // default groups
|
} else { // default groups
|
||||||
NamedGroup[] groups = new NamedGroup[] {
|
NamedGroup[] groups = new NamedGroup[] {
|
||||||
// NIST curves first
|
|
||||||
|
// Primary XDH (RFC 7748) curves
|
||||||
|
NamedGroup.X25519,
|
||||||
|
|
||||||
|
// Primary NIST curves (e.g. used in TLSv1.3)
|
||||||
NamedGroup.SECP256_R1,
|
NamedGroup.SECP256_R1,
|
||||||
NamedGroup.SECP384_R1,
|
NamedGroup.SECP384_R1,
|
||||||
NamedGroup.SECP521_R1,
|
NamedGroup.SECP521_R1,
|
||||||
|
|
||||||
|
// Secondary XDH curves
|
||||||
|
NamedGroup.X448,
|
||||||
|
|
||||||
|
// Secondary NIST curves
|
||||||
NamedGroup.SECT283_K1,
|
NamedGroup.SECT283_K1,
|
||||||
NamedGroup.SECT283_R1,
|
NamedGroup.SECT283_R1,
|
||||||
NamedGroup.SECT409_K1,
|
NamedGroup.SECT409_K1,
|
||||||
@ -530,7 +225,7 @@ final class SupportedGroupsExtension {
|
|||||||
// non-NIST curves
|
// non-NIST curves
|
||||||
NamedGroup.SECP256_K1,
|
NamedGroup.SECP256_K1,
|
||||||
|
|
||||||
// FFDHE 2048
|
// FFDHE (RFC 7919)
|
||||||
NamedGroup.FFDHE_2048,
|
NamedGroup.FFDHE_2048,
|
||||||
NamedGroup.FFDHE_3072,
|
NamedGroup.FFDHE_3072,
|
||||||
NamedGroup.FFDHE_4096,
|
NamedGroup.FFDHE_4096,
|
||||||
@ -560,126 +255,27 @@ final class SupportedGroupsExtension {
|
|||||||
|
|
||||||
// check whether the group is supported by the underlying providers
|
// check whether the group is supported by the underlying providers
|
||||||
private static boolean isAvailableGroup(NamedGroup namedGroup) {
|
private static boolean isAvailableGroup(NamedGroup namedGroup) {
|
||||||
AlgorithmParameters params = null;
|
return namedGroup.isAvailableGroup();
|
||||||
AlgorithmParameterSpec spec = null;
|
|
||||||
if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
|
||||||
if (namedGroup.oid != null) {
|
|
||||||
try {
|
|
||||||
params = AlgorithmParameters.getInstance("EC");
|
|
||||||
spec = new ECGenParameterSpec(namedGroup.oid);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
|
||||||
try {
|
|
||||||
params = AlgorithmParameters.getInstance("DiffieHellman");
|
|
||||||
spec = getFFDHEDHParameterSpec(namedGroup);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // Otherwise, unsupported.
|
|
||||||
|
|
||||||
if ((params != null) && (spec != null)) {
|
|
||||||
try {
|
|
||||||
params.init(spec);
|
|
||||||
} catch (InvalidParameterSpecException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cache the parameters
|
|
||||||
namedGroupParams.put(namedGroup, params);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DHParameterSpec getFFDHEDHParameterSpec(
|
static ECGenParameterSpec getECGenParamSpec(NamedGroup ng) {
|
||||||
NamedGroup namedGroup) {
|
if (ng.type != NamedGroupType.NAMED_GROUP_ECDHE) {
|
||||||
DHParameterSpec spec = null;
|
throw new RuntimeException(
|
||||||
switch (namedGroup) {
|
"Not a named EC group: " + ng);
|
||||||
case FFDHE_2048:
|
|
||||||
spec = PredefinedDHParameterSpecs.ffdheParams.get(2048);
|
|
||||||
break;
|
|
||||||
case FFDHE_3072:
|
|
||||||
spec = PredefinedDHParameterSpecs.ffdheParams.get(3072);
|
|
||||||
break;
|
|
||||||
case FFDHE_4096:
|
|
||||||
spec = PredefinedDHParameterSpecs.ffdheParams.get(4096);
|
|
||||||
break;
|
|
||||||
case FFDHE_6144:
|
|
||||||
spec = PredefinedDHParameterSpecs.ffdheParams.get(6144);
|
|
||||||
break;
|
|
||||||
case FFDHE_8192:
|
|
||||||
spec = PredefinedDHParameterSpecs.ffdheParams.get(8192);
|
|
||||||
}
|
|
||||||
|
|
||||||
return spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DHParameterSpec getPredefinedDHParameterSpec(
|
|
||||||
NamedGroup namedGroup) {
|
|
||||||
DHParameterSpec spec = null;
|
|
||||||
switch (namedGroup) {
|
|
||||||
case FFDHE_2048:
|
|
||||||
spec = PredefinedDHParameterSpecs.definedParams.get(2048);
|
|
||||||
break;
|
|
||||||
case FFDHE_3072:
|
|
||||||
spec = PredefinedDHParameterSpecs.definedParams.get(3072);
|
|
||||||
break;
|
|
||||||
case FFDHE_4096:
|
|
||||||
spec = PredefinedDHParameterSpecs.definedParams.get(4096);
|
|
||||||
break;
|
|
||||||
case FFDHE_6144:
|
|
||||||
spec = PredefinedDHParameterSpecs.definedParams.get(6144);
|
|
||||||
break;
|
|
||||||
case FFDHE_8192:
|
|
||||||
spec = PredefinedDHParameterSpecs.definedParams.get(8192);
|
|
||||||
}
|
|
||||||
|
|
||||||
return spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) {
|
|
||||||
if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Not a named EC group: " + namedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
AlgorithmParameters params = namedGroupParams.get(namedGroup);
|
|
||||||
if (params == null) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Not a supported EC named group: " + namedGroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parameters are non-null
|
||||||
|
AlgorithmParameters params = ng.getParameters();
|
||||||
try {
|
try {
|
||||||
return params.getParameterSpec(ECGenParameterSpec.class);
|
return params.getParameterSpec(ECGenParameterSpec.class);
|
||||||
} catch (InvalidParameterSpecException ipse) {
|
} catch (InvalidParameterSpecException ipse) {
|
||||||
// should be unlikely
|
// should be unlikely
|
||||||
return new ECGenParameterSpec(namedGroup.oid);
|
return new ECGenParameterSpec(ng.oid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) {
|
static AlgorithmParameters getParameters(NamedGroup ng) {
|
||||||
if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) {
|
return ng.getParameters();
|
||||||
throw new RuntimeException(
|
|
||||||
"Not a named DH group: " + namedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
AlgorithmParameters params = namedGroupParams.get(namedGroup);
|
|
||||||
if (params == null) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Not a supported DH named group: " + namedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return params.getParameterSpec(DHParameterSpec.class);
|
|
||||||
} catch (InvalidParameterSpecException ipse) {
|
|
||||||
// should be unlikely
|
|
||||||
return getPredefinedDHParameterSpec(namedGroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is there any supported group permitted by the constraints?
|
// Is there any supported group permitted by the constraints?
|
||||||
@ -692,7 +288,7 @@ final class SupportedGroupsExtension {
|
|||||||
if (constraints.permits(
|
if (constraints.permits(
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||||
namedGroup.algorithm,
|
namedGroup.algorithm,
|
||||||
namedGroupParams.get(namedGroup))) {
|
getParameters(namedGroup))) {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -723,7 +319,7 @@ final class SupportedGroupsExtension {
|
|||||||
return constraints.permits(
|
return constraints.permits(
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||||
namedGroup.algorithm,
|
namedGroup.algorithm,
|
||||||
namedGroupParams.get(namedGroup));
|
getParameters(namedGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the named group supported?
|
// Is the named group supported?
|
||||||
@ -739,16 +335,16 @@ final class SupportedGroupsExtension {
|
|||||||
|
|
||||||
static NamedGroup getPreferredGroup(
|
static NamedGroup getPreferredGroup(
|
||||||
ProtocolVersion negotiatedProtocol,
|
ProtocolVersion negotiatedProtocol,
|
||||||
AlgorithmConstraints constraints, NamedGroupType type,
|
AlgorithmConstraints constraints, NamedGroupType[] types,
|
||||||
List<NamedGroup> requestedNamedGroups) {
|
List<NamedGroup> requestedNamedGroups) {
|
||||||
for (NamedGroup namedGroup : requestedNamedGroups) {
|
for (NamedGroup namedGroup : requestedNamedGroups) {
|
||||||
if ((namedGroup.type == type) &&
|
if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
|
||||||
namedGroup.isAvailable(negotiatedProtocol) &&
|
namedGroup.isAvailable(negotiatedProtocol) &&
|
||||||
isSupported(namedGroup) &&
|
isSupported(namedGroup) &&
|
||||||
constraints.permits(
|
constraints.permits(
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||||
namedGroup.algorithm,
|
namedGroup.algorithm,
|
||||||
namedGroupParams.get(namedGroup))) {
|
getParameters(namedGroup))) {
|
||||||
return namedGroup;
|
return namedGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -758,14 +354,14 @@ final class SupportedGroupsExtension {
|
|||||||
|
|
||||||
static NamedGroup getPreferredGroup(
|
static NamedGroup getPreferredGroup(
|
||||||
ProtocolVersion negotiatedProtocol,
|
ProtocolVersion negotiatedProtocol,
|
||||||
AlgorithmConstraints constraints, NamedGroupType type) {
|
AlgorithmConstraints constraints, NamedGroupType[] types) {
|
||||||
for (NamedGroup namedGroup : supportedNamedGroups) {
|
for (NamedGroup namedGroup : supportedNamedGroups) {
|
||||||
if ((namedGroup.type == type) &&
|
if ((NamedGroupType.arrayContains(types, namedGroup.type)) &&
|
||||||
namedGroup.isAvailable(negotiatedProtocol) &&
|
namedGroup.isAvailable(negotiatedProtocol) &&
|
||||||
constraints.permits(
|
constraints.permits(
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||||
namedGroup.algorithm,
|
namedGroup.algorithm,
|
||||||
namedGroupParams.get(namedGroup))) {
|
getParameters(namedGroup))) {
|
||||||
return namedGroup;
|
return namedGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -813,7 +409,7 @@ final class SupportedGroupsExtension {
|
|||||||
ng.isSupported(chc.activeCipherSuites) &&
|
ng.isSupported(chc.activeCipherSuites) &&
|
||||||
chc.algorithmConstraints.permits(
|
chc.algorithmConstraints.permits(
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||||
ng.algorithm, namedGroupParams.get(ng))) {
|
ng.algorithm, getParameters(ng))) {
|
||||||
namedGroups.add(ng);
|
namedGroups.add(ng);
|
||||||
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine(
|
SSLLogger.fine(
|
||||||
@ -940,7 +536,7 @@ final class SupportedGroupsExtension {
|
|||||||
ng.isSupported(shc.activeCipherSuites) &&
|
ng.isSupported(shc.activeCipherSuites) &&
|
||||||
shc.algorithmConstraints.permits(
|
shc.algorithmConstraints.permits(
|
||||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||||
ng.algorithm, namedGroupParams.get(ng))) {
|
ng.algorithm, getParameters(ng))) {
|
||||||
namedGroups.add(ng);
|
namedGroups.add(ng);
|
||||||
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine(
|
SSLLogger.fine(
|
||||||
|
@ -39,7 +39,6 @@ import javax.net.ssl.HandshakeCompletedListener;
|
|||||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSL/(D)TLS transportation context.
|
* SSL/(D)TLS transportation context.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -61,8 +61,8 @@ final class Utilities {
|
|||||||
|
|
||||||
int size = serverNames.size();
|
int size = serverNames.size();
|
||||||
List<SNIServerName> sniList = (size != 0) ?
|
List<SNIServerName> sniList = (size != 0) ?
|
||||||
new ArrayList<SNIServerName>(serverNames) :
|
new ArrayList<>(serverNames) :
|
||||||
new ArrayList<SNIServerName>(1);
|
new ArrayList<>(1);
|
||||||
|
|
||||||
boolean reset = false;
|
boolean reset = false;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
@ -147,7 +147,7 @@ final class Utilities {
|
|||||||
static String indent(String source, String prefix) {
|
static String indent(String source, String prefix) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
builder.append("\n" + prefix + "<blank message>");
|
builder.append("\n").append(prefix).append("<blank message>");
|
||||||
} else {
|
} else {
|
||||||
String[] lines = lineBreakPatern.split(source);
|
String[] lines = lineBreakPatern.split(source);
|
||||||
boolean isFirst = true;
|
boolean isFirst = true;
|
||||||
@ -232,4 +232,21 @@ final class Utilities {
|
|||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reverseBytes(byte[] arr) {
|
||||||
|
int i = 0;
|
||||||
|
int j = arr.length - 1;
|
||||||
|
|
||||||
|
while (i < j) {
|
||||||
|
swap(arr, i, j);
|
||||||
|
i++;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void swap(byte[] arr, int i, int j) {
|
||||||
|
byte tmp = arr[i];
|
||||||
|
arr[i] = arr[j];
|
||||||
|
arr[j] = tmp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,13 +30,15 @@ import java.security.PublicKey;
|
|||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.interfaces.ECKey;
|
import java.security.interfaces.ECKey;
|
||||||
import java.security.interfaces.ECPublicKey;
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.interfaces.XECKey;
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.security.spec.ECParameterSpec;
|
import java.security.spec.ECParameterSpec;
|
||||||
|
import java.security.spec.NamedParameterSpec;
|
||||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import javax.net.ssl.X509ExtendedKeyManager;
|
import javax.net.ssl.X509ExtendedKeyManager;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
|
|
||||||
enum X509Authentication implements SSLAuthentication {
|
enum X509Authentication implements SSLAuthentication {
|
||||||
@ -148,6 +150,35 @@ enum X509Authentication implements SSLAuthentication {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Similar to above, but for XEC.
|
||||||
|
NamedParameterSpec getXECParameterSpec() {
|
||||||
|
if (popPrivateKey == null ||
|
||||||
|
!"XEC".equals(popPrivateKey.getAlgorithm())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popPrivateKey instanceof XECKey) {
|
||||||
|
AlgorithmParameterSpec params =
|
||||||
|
((XECKey)popPrivateKey).getParams();
|
||||||
|
if (params instanceof NamedParameterSpec){
|
||||||
|
return (NamedParameterSpec)params;
|
||||||
|
}
|
||||||
|
} else if (popCerts != null && popCerts.length != 0) {
|
||||||
|
// The private key not extractable, get the parameters from
|
||||||
|
// the X.509 certificate.
|
||||||
|
PublicKey publicKey = popCerts[0].getPublicKey();
|
||||||
|
if (publicKey instanceof XECKey) {
|
||||||
|
AlgorithmParameterSpec params =
|
||||||
|
((XECKey)publicKey).getParams();
|
||||||
|
if (params instanceof NamedParameterSpec){
|
||||||
|
return (NamedParameterSpec)params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class X509Credentials implements SSLCredentials {
|
static final class X509Credentials implements SSLCredentials {
|
||||||
|
210
src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java
Normal file
210
src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package sun.security.ssl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.interfaces.XECPublicKey;
|
||||||
|
import java.security.spec.*;
|
||||||
|
import sun.security.ssl.NamedGroup.NamedGroupType;
|
||||||
|
import sun.security.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifics for XEC/XDH Keys/Exchanges
|
||||||
|
*/
|
||||||
|
final class XDHKeyExchange {
|
||||||
|
|
||||||
|
static final SSLKeyAgreementGenerator xdheKAGenerator
|
||||||
|
= new XDHEKAGenerator();
|
||||||
|
|
||||||
|
static final class XDHECredentials implements NamedGroupCredentials {
|
||||||
|
|
||||||
|
final XECPublicKey popPublicKey;
|
||||||
|
final NamedGroup namedGroup;
|
||||||
|
|
||||||
|
XDHECredentials(XECPublicKey popPublicKey, NamedGroup namedGroup) {
|
||||||
|
this.popPublicKey = popPublicKey;
|
||||||
|
this.namedGroup = namedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
return popPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NamedGroup getNamedGroup() {
|
||||||
|
return namedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the encoded Point into the XDHECredentials using the
|
||||||
|
* namedGroup.
|
||||||
|
*/
|
||||||
|
static XDHECredentials valueOf(NamedGroup namedGroup,
|
||||||
|
byte[] encodedPoint) throws IOException,
|
||||||
|
GeneralSecurityException {
|
||||||
|
|
||||||
|
if (namedGroup.type != NamedGroupType.NAMED_GROUP_XDH) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Credentials decoding: Not XDH named group");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encodedPoint == null || encodedPoint.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] uBytes = encodedPoint.clone();
|
||||||
|
Utilities.reverseBytes(uBytes);
|
||||||
|
BigInteger u = new BigInteger(1, uBytes);
|
||||||
|
|
||||||
|
XECPublicKeySpec xecPublicKeySpec = new XECPublicKeySpec(
|
||||||
|
new NamedParameterSpec(namedGroup.name), u);
|
||||||
|
KeyFactory factory = KeyFactory.getInstance(namedGroup.algorithm);
|
||||||
|
XECPublicKey publicKey = (XECPublicKey) factory.generatePublic(
|
||||||
|
xecPublicKeySpec);
|
||||||
|
|
||||||
|
return new XDHECredentials(publicKey, namedGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class XDHEPossession implements NamedGroupPossession {
|
||||||
|
|
||||||
|
final PrivateKey privateKey;
|
||||||
|
final XECPublicKey publicKey;
|
||||||
|
final NamedGroup namedGroup;
|
||||||
|
|
||||||
|
XDHEPossession(NamedGroup namedGroup, SecureRandom random) {
|
||||||
|
try {
|
||||||
|
KeyPairGenerator kpg
|
||||||
|
= KeyPairGenerator.getInstance(namedGroup.algorithm);
|
||||||
|
AlgorithmParameterSpec params = namedGroup.getParameterSpec();
|
||||||
|
kpg.initialize(params, random);
|
||||||
|
KeyPair kp = kpg.generateKeyPair();
|
||||||
|
privateKey = kp.getPrivate();
|
||||||
|
publicKey = (XECPublicKey) kp.getPublic();
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Could not generate XDH keypair", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.namedGroup = namedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encode() {
|
||||||
|
|
||||||
|
byte[] uBytes = ECUtil.trimZeroes(publicKey.getU().toByteArray());
|
||||||
|
|
||||||
|
int expLength;
|
||||||
|
switch (namedGroup) {
|
||||||
|
case X25519:
|
||||||
|
expLength = 32;
|
||||||
|
break;
|
||||||
|
case X448:
|
||||||
|
expLength = 56;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Invalid XDH group");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uBytes.length > expLength) {
|
||||||
|
throw new RuntimeException("Encoded XDH key too large");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uBytes.length != expLength) {
|
||||||
|
byte[] tmp = new byte[expLength];
|
||||||
|
System.arraycopy(uBytes, 0, tmp,
|
||||||
|
expLength - uBytes.length, uBytes.length);
|
||||||
|
uBytes = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utilities.reverseBytes(uBytes);
|
||||||
|
return (uBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NamedGroup getNamedGroup() {
|
||||||
|
return namedGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrivateKey getPrivateKey() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class XDHEKAGenerator
|
||||||
|
implements SSLKeyAgreementGenerator {
|
||||||
|
|
||||||
|
// Prevent instantiation of this class.
|
||||||
|
private XDHEKAGenerator() {
|
||||||
|
// blank
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLKeyDerivation createKeyDerivation(
|
||||||
|
HandshakeContext context) throws IOException {
|
||||||
|
XDHEPossession xdhePossession = null;
|
||||||
|
XDHECredentials xdheCredentials = null;
|
||||||
|
for (SSLPossession poss : context.handshakePossessions) {
|
||||||
|
if (!(poss instanceof XDHEPossession)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NamedGroup ng = ((XDHEPossession) poss).namedGroup;
|
||||||
|
for (SSLCredentials cred : context.handshakeCredentials) {
|
||||||
|
if (!(cred instanceof XDHECredentials)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ng.equals(((XDHECredentials) cred).namedGroup)) {
|
||||||
|
xdheCredentials = (XDHECredentials) cred;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xdheCredentials != null) {
|
||||||
|
xdhePossession = (XDHEPossession) poss;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xdhePossession == null || xdheCredentials == null) {
|
||||||
|
context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||||
|
"No sufficient XDHE key agreement "
|
||||||
|
+ "parameters negotiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new KAKeyDerivation("XDH", context,
|
||||||
|
xdhePossession.privateKey, xdheCredentials.popPublicKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -38,6 +38,7 @@ import java.security.spec.NamedParameterSpec;
|
|||||||
import javax.crypto.KeyAgreementSpi;
|
import javax.crypto.KeyAgreementSpi;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.ShortBufferException;
|
import javax.crypto.ShortBufferException;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class XDHKeyAgreement extends KeyAgreementSpi {
|
public class XDHKeyAgreement extends KeyAgreementSpi {
|
||||||
@ -202,7 +203,15 @@ public class XDHKeyAgreement extends KeyAgreementSpi {
|
|||||||
throws IllegalStateException, NoSuchAlgorithmException,
|
throws IllegalStateException, NoSuchAlgorithmException,
|
||||||
InvalidKeyException {
|
InvalidKeyException {
|
||||||
|
|
||||||
throw new NoSuchAlgorithmException("Not supported");
|
if (algorithm == null) {
|
||||||
|
throw new NoSuchAlgorithmException("Algorithm must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(algorithm.equals("TlsPremasterSecret"))) {
|
||||||
|
throw new NoSuchAlgorithmException(
|
||||||
|
"Only supported for algorithm TlsPremasterSecret");
|
||||||
|
}
|
||||||
|
return new SecretKeySpec(engineGenerateSecret(), algorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class X25519 extends XDHKeyAgreement {
|
static class X25519 extends XDHKeyAgreement {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -156,7 +156,7 @@ public class XECParameters {
|
|||||||
new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name);
|
new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name);
|
||||||
bySize.put(bits, params);
|
bySize.put(bits, params);
|
||||||
byOid.put(oid, params);
|
byOid.put(oid, params);
|
||||||
byName.put(name, params);
|
byName.put(name.toLowerCase(), params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<XECParameters> getByOid(ObjectIdentifier id) {
|
public static Optional<XECParameters> getByOid(ObjectIdentifier id) {
|
||||||
@ -166,7 +166,7 @@ public class XECParameters {
|
|||||||
return Optional.ofNullable(SIZE_MAP.get(size));
|
return Optional.ofNullable(SIZE_MAP.get(size));
|
||||||
}
|
}
|
||||||
public static Optional<XECParameters> getByName(String name) {
|
public static Optional<XECParameters> getByName(String name) {
|
||||||
return Optional.ofNullable(NAME_MAP.get(name));
|
return Optional.ofNullable(NAME_MAP.get(name.toLowerCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean oidEquals(XECParameters other) {
|
boolean oidEquals(XECParameters other) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -188,11 +188,16 @@ public class SSLSocketTemplate {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure the client side socket.
|
||||||
|
*/
|
||||||
|
protected void configureClientSocket(SSLSocket socket) {
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure the server side socket.
|
* Configure the server side socket.
|
||||||
*/
|
*/
|
||||||
protected void configureServerSocket(SSLServerSocket socket) {
|
protected void configureServerSocket(SSLServerSocket socket) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -317,6 +322,7 @@ public class SSLSocketTemplate {
|
|||||||
|
|
||||||
try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
|
try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
|
||||||
try {
|
try {
|
||||||
|
configureClientSocket(sslSocket);
|
||||||
sslSocket.connect(
|
sslSocket.connect(
|
||||||
new InetSocketAddress("localhost", serverPort), 15000);
|
new InetSocketAddress("localhost", serverPort), 15000);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
87
test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java
Normal file
87
test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8171279
|
||||||
|
* @library /javax/net/ssl/templates
|
||||||
|
* @summary Test TLS connection with each individual supported group
|
||||||
|
* @run main/othervm SupportedGroups x25519
|
||||||
|
* @run main/othervm SupportedGroups x448
|
||||||
|
* @run main/othervm SupportedGroups secp256r1
|
||||||
|
* @run main/othervm SupportedGroups secp384r1
|
||||||
|
* @run main/othervm SupportedGroups secp521r1
|
||||||
|
* @run main/othervm SupportedGroups ffdhe2048
|
||||||
|
* @run main/othervm SupportedGroups ffdhe3072
|
||||||
|
* @run main/othervm SupportedGroups ffdhe4096
|
||||||
|
* @run main/othervm SupportedGroups ffdhe6144
|
||||||
|
* @run main/othervm SupportedGroups ffdhe8192
|
||||||
|
*/
|
||||||
|
import java.util.Arrays;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLServerSocket;
|
||||||
|
|
||||||
|
public class SupportedGroups extends SSLSocketTemplate {
|
||||||
|
|
||||||
|
private static volatile int index;
|
||||||
|
private static final String[][][] protocols = {
|
||||||
|
{{"TLSv1.3"}, {"TLSv1.3"}},
|
||||||
|
{{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}},
|
||||||
|
{{"TLSv1.2"}, {"TLSv1.3", "TLSv1.2"}},
|
||||||
|
{{"TLSv1.2"}, {"TLSv1.2"}}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Servers are configured before clients, increment test case after.
|
||||||
|
@Override
|
||||||
|
protected void configureClientSocket(SSLSocket socket) {
|
||||||
|
String[] ps = protocols[index][0];
|
||||||
|
|
||||||
|
System.out.print("Setting client protocol(s): ");
|
||||||
|
Arrays.stream(ps).forEachOrdered(System.out::print);
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
socket.setEnabledProtocols(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureServerSocket(SSLServerSocket serverSocket) {
|
||||||
|
String[] ps = protocols[index][1];
|
||||||
|
|
||||||
|
System.out.print("Setting server protocol(s): ");
|
||||||
|
Arrays.stream(ps).forEachOrdered(System.out::print);
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
serverSocket.setEnabledProtocols(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run the test case.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
System.setProperty("jdk.tls.namedGroups", args[0]);
|
||||||
|
|
||||||
|
for (index = 0; index < protocols.length; index++) {
|
||||||
|
(new SupportedGroups()).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user