8281236: (D)TLS key exchange named groups

Reviewed-by: mullan
This commit is contained in:
Xue-Lei Andrew Fan 2022-12-07 20:16:45 +00:00
parent 10356e767a
commit 5d4c71c8bd
17 changed files with 806 additions and 236 deletions

View File

@ -34,8 +34,8 @@ import java.util.*;
* the list of protocols to be allowed, the endpoint identification
* algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI),
* the maximum network packet size, the algorithm constraints, the signature
* schemes and whether SSL/TLS/DTLS servers should request or require client
* authentication, etc.
* schemes, the key exchange named groups and whether SSL/TLS/DTLS servers
* should request or require client authentication, etc.
* <p>
* {@code SSLParameter} objects can be created via the constructors in this
* class, and can be described as pre-populated objects. {@code SSLParameter}
@ -85,6 +85,7 @@ public class SSLParameters {
private int maximumPacketSize = 0;
private String[] applicationProtocols = new String[0];
private String[] signatureSchemes = null;
private String[] namedGroups = null;
/**
* Constructs SSLParameters.
@ -810,4 +811,130 @@ public class SSLParameters {
this.signatureSchemes = tempSchemes;
}
/**
* Returns a prioritized array of key exchange named groups names that
* can be used over the SSL/TLS/DTLS protocols.
* <p>
* Note that the standard list of key exchange named groups are defined
* in the <a href=
* "{@docRoot}/../specs/security/standard-names.html#named-groups">
* Named Groups</a> section of the Java Security Standard Algorithm
* Names Specification. Providers may support named groups not defined
* in this list or may not use the recommended name for a certain named
* group.
* <p>
* The set of named groups that will be used over the SSL/TLS/DTLS
* connections is determined by the returned array of this method and the
* underlying provider-specific default named groups.
* <p>
* If the returned array is {@code null}, then the underlying
* provider-specific default named groups will be used over the
* SSL/TLS/DTLS connections.
* <p>
* If the returned array is empty (zero-length), then the named group
* negotiation mechanism is turned off for SSL/TLS/DTLS protocols, and
* the connections may not be able to be established if the negotiation
* mechanism is required by a certain SSL/TLS/DTLS protocol. This
* parameter will override the underlying provider-specific default
* name groups.
* <p>
* If the returned array is not {@code null} or empty (zero-length),
* then the named groups in the returned array will be used over
* the SSL/TLS/DTLS connections. This parameter will override the
* underlying provider-specific default named groups.
* <p>
* This method returns the most recent value passed to
* {@link #setNamedGroups} if that method has been called and otherwise
* returns the default named groups for connection populated objects,
* or {@code null} for pre-populated objects.
*
* @apiNote
* Note that a provider may not have been updated to support this method
* and in that case may return {@code null} instead of the default
* named groups for connection populated objects.
*
* @implNote
* The SunJSSE provider supports this method.
*
* @implNote
* Note that applications may use the
* {@systemProperty jdk.tls.namedGroups} system property with the SunJSSE
* provider to override the provider-specific default named groups.
*
* @return an array of key exchange named group names {@code Strings} or
* {@code null} if none have been set. For non-null returns, this
* method will return a new array each time it is invoked. The
* array is ordered based on named group preference, with the first
* entry being the most preferred. Providers should ignore unknown
* named group names while establishing the SSL/TLS/DTLS
* connections.
* @see #setNamedGroups
*
* @since 20
*/
public String[] getNamedGroups() {
return clone(namedGroups);
}
/**
* Sets the prioritized array of key exchange named groups names that
* can be used over the SSL/TLS/DTLS protocols.
* <p>
* Note that the standard list of key exchange named groups are defined in
* the <a href=
* "{@docRoot}/../specs/security/standard-names.html#named-groups">
* Named Groups</a> section of the Java Security Standard Algorithm
* Names Specification. Providers may support named groups not defined
* in this list or may not use the recommended name for a certain named
* group.
* <p>
* The set of named groups that will be used over the SSL/TLS/DTLS
* connections is determined by the input parameter {@code namedGroups}
* array and the underlying provider-specific default named groups.
* See {@link #getNamedGroups} for specific details on how the
* parameters are used in SSL/TLS/DTLS connections.
*
* @apiNote
* Note that a provider may not have been updated to support this method
* and in that case may ignore the named groups that are set.
*
* @implNote
* The SunJSSE provider supports this method.
*
* @param namedGroups an ordered array of key exchange named group names
* with the first entry being the most preferred, or {@code null}.
* This method will make a copy of this array. Providers should
* ignore unknown named group scheme names while establishing the
* SSL/TLS/DTLS connections.
* @throws IllegalArgumentException if any element in the
* {@code namedGroups} array is a duplicate, {@code null} or
* {@linkplain String#isBlank() blank}.
*
* @see #getNamedGroups
*
* @since 20
*/
public void setNamedGroups(String[] namedGroups) {
String[] tempGroups = null;
if (namedGroups != null) {
tempGroups = namedGroups.clone();
Set<String> groupsSet = new HashSet<>();
for (String namedGroup : tempGroups) {
if (namedGroup == null || namedGroup.isBlank()) {
throw new IllegalArgumentException(
"An element of namedGroups is null or blank");
}
if (groupsSet.contains(namedGroup)) {
throw new IllegalArgumentException(
"Duplicate element of namedGroups: " + namedGroup);
}
groupsSet.add(namedGroup);
}
}
this.namedGroups = tempGroups;
}
}

View File

@ -590,6 +590,7 @@ final class CertificateVerify {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
SignatureScheme.getSignerOfPreferableAlgorithm(
chc.sslConfig,
chc.algorithmConstraints,
chc.peerRequestedSignatureSchemes,
x509Possession,
@ -901,6 +902,7 @@ final class CertificateVerify {
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
SignatureScheme.getSignerOfPreferableAlgorithm(
context.sslConfig,
context.algorithmConstraints,
context.peerRequestedSignatureSchemes,
x509Possession,

View File

@ -42,7 +42,6 @@ import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.NamedGroup.NamedGroupSpec;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.security.util.KeyUtil;
@ -313,12 +312,13 @@ final class DHKeyExchange {
if (!useLegacyEphemeralDHKeys &&
(context.clientRequestedNamedGroups != null) &&
(!context.clientRequestedNamedGroups.isEmpty())) {
preferableNamedGroup =
SupportedGroups.getPreferredGroup(context.negotiatedProtocol,
context.algorithmConstraints,
new NamedGroupSpec [] {
NamedGroupSpec.NAMED_GROUP_FFDHE },
context.clientRequestedNamedGroups);
preferableNamedGroup = NamedGroup.getPreferredGroup(
context.sslConfig,
context.negotiatedProtocol,
context.algorithmConstraints,
new NamedGroupSpec [] {
NamedGroupSpec.NAMED_GROUP_FFDHE },
context.clientRequestedNamedGroups);
if (preferableNamedGroup != null) {
return new DHEPossession(preferableNamedGroup,
context.sslContext.getSecureRandom());

View File

@ -127,6 +127,7 @@ final class DHServerKeyExchange {
if (useExplicitSigAlgorithm) {
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
SignatureScheme.getSignerOfPreferableAlgorithm(
shc.sslConfig,
shc.algorithmConstraints,
shc.peerRequestedSignatureSchemes,
x509Possession,

View File

@ -44,7 +44,6 @@ import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.NamedGroup.NamedGroupSpec;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.security.ssl.XDHKeyExchange.XDHECredentials;
@ -236,7 +235,8 @@ final class ECDHKeyExchange {
// Find most preferred EC or XEC groups
if ((context.clientRequestedNamedGroups != null) &&
(!context.clientRequestedNamedGroups.isEmpty())) {
preferableNamedGroup = SupportedGroups.getPreferredGroup(
preferableNamedGroup = NamedGroup.getPreferredGroup(
context.sslConfig,
context.negotiatedProtocol,
context.algorithmConstraints,
new NamedGroupSpec[] {
@ -244,7 +244,8 @@ final class ECDHKeyExchange {
NamedGroupSpec.NAMED_GROUP_XDH },
context.clientRequestedNamedGroups);
} else {
preferableNamedGroup = SupportedGroups.getPreferredGroup(
preferableNamedGroup = NamedGroup.getPreferredGroup(
context.sslConfig,
context.negotiatedProtocol,
context.algorithmConstraints,
new NamedGroupSpec[] {

View File

@ -42,7 +42,6 @@ import java.util.EnumSet;
import java.util.Locale;
import java.util.Map;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.security.util.HexDumpEncoder;
@ -139,6 +138,7 @@ final class ECDHServerKeyExchange {
if (useExplicitSigAlgorithm) {
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
SignatureScheme.getSignerOfPreferableAlgorithm(
shc.sslConfig,
shc.algorithmConstraints,
shc.peerRequestedSignatureSchemes,
x509Possession,
@ -204,7 +204,7 @@ final class ECDHServerKeyExchange {
"Unknown named group ID: " + namedGroupId);
}
if (!SupportedGroups.isSupported(namedGroup)) {
if (!NamedGroup.isEnabled(chc.sslConfig, namedGroup)) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported named group: " + namedGroup);
}

View File

@ -39,7 +39,6 @@ import javax.net.ssl.SSLHandshakeException;
import javax.security.auth.x500.X500Principal;
import sun.security.ssl.NamedGroup.NamedGroupSpec;
import static sun.security.ssl.NamedGroup.NamedGroupSpec.*;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
abstract class HandshakeContext implements ConnectionContext {
// System properties
@ -157,8 +156,8 @@ abstract class HandshakeContext implements ConnectionContext {
this.algorithmConstraints = SSLAlgorithmConstraints.wrap(
sslConfig.userSpecifiedAlgorithmConstraints);
this.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols,
sslConfig.enabledCipherSuites, algorithmConstraints);
this.activeProtocols =
getActiveProtocols(sslConfig, algorithmConstraints);
if (activeProtocols.isEmpty()) {
throw new SSLHandshakeException(
"No appropriate protocol (protocol is disabled or " +
@ -173,8 +172,8 @@ abstract class HandshakeContext implements ConnectionContext {
}
}
this.maximumActiveProtocol = maximumVersion;
this.activeCipherSuites = getActiveCipherSuites(this.activeProtocols,
sslConfig.enabledCipherSuites, algorithmConstraints);
this.activeCipherSuites = getActiveCipherSuites(sslConfig,
this.activeProtocols, algorithmConstraints);
if (activeCipherSuites.isEmpty()) {
throw new SSLHandshakeException("No appropriate cipher suite");
}
@ -256,12 +255,11 @@ abstract class HandshakeContext implements ConnectionContext {
}
private static List<ProtocolVersion> getActiveProtocols(
List<ProtocolVersion> enabledProtocols,
List<CipherSuite> enabledCipherSuites,
SSLConfiguration sslConfig,
AlgorithmConstraints algorithmConstraints) {
boolean enabledSSL20Hello = false;
ArrayList<ProtocolVersion> protocols = new ArrayList<>(4);
for (ProtocolVersion protocol : enabledProtocols) {
for (ProtocolVersion protocol : sslConfig.enabledProtocols) {
if (!enabledSSL20Hello && protocol == ProtocolVersion.SSL20Hello) {
enabledSSL20Hello = true;
continue;
@ -277,9 +275,9 @@ abstract class HandshakeContext implements ConnectionContext {
boolean found = false;
Map<NamedGroupSpec, Boolean> cachedStatus =
new EnumMap<>(NamedGroupSpec.class);
for (CipherSuite suite : enabledCipherSuites) {
for (CipherSuite suite : sslConfig.enabledCipherSuites) {
if (suite.isAvailable() && suite.supports(protocol)) {
if (isActivatable(suite,
if (isActivatable(sslConfig, suite,
algorithmConstraints, cachedStatus)) {
protocols.add(protocol);
found = true;
@ -309,15 +307,15 @@ abstract class HandshakeContext implements ConnectionContext {
}
private static List<CipherSuite> getActiveCipherSuites(
SSLConfiguration sslConfig,
List<ProtocolVersion> enabledProtocols,
List<CipherSuite> enabledCipherSuites,
AlgorithmConstraints algorithmConstraints) {
List<CipherSuite> suites = new LinkedList<>();
if (enabledProtocols != null && !enabledProtocols.isEmpty()) {
Map<NamedGroupSpec, Boolean> cachedStatus =
new EnumMap<>(NamedGroupSpec.class);
for (CipherSuite suite : enabledCipherSuites) {
for (CipherSuite suite : sslConfig.enabledCipherSuites) {
if (!suite.isAvailable()) {
continue;
}
@ -327,7 +325,7 @@ abstract class HandshakeContext implements ConnectionContext {
if (!suite.supports(protocol)) {
continue;
}
if (isActivatable(suite,
if (isActivatable(sslConfig, suite,
algorithmConstraints, cachedStatus)) {
suites.add(suite);
isSupported = true;
@ -525,7 +523,9 @@ abstract class HandshakeContext implements ConnectionContext {
return activeProtocols.contains(protocolVersion);
}
private static boolean isActivatable(CipherSuite suite,
private static boolean isActivatable(
SSLConfiguration sslConfig,
CipherSuite suite,
AlgorithmConstraints algorithmConstraints,
Map<NamedGroupSpec, Boolean> cachedStatus) {
@ -543,8 +543,8 @@ abstract class HandshakeContext implements ConnectionContext {
if (groupType != NAMED_GROUP_NONE) {
Boolean checkedStatus = cachedStatus.get(groupType);
if (checkedStatus == null) {
groupAvailable = SupportedGroups.isActivatable(
algorithmConstraints, groupType);
groupAvailable = NamedGroup.isActivatable(
sslConfig, algorithmConstraints, groupType);
cachedStatus.put(groupType, groupAvailable);
if (!groupAvailable &&

View File

@ -36,7 +36,6 @@ import sun.security.ssl.NamedGroup.NamedGroupSpec;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.util.HexDumpEncoder;
/**
@ -345,7 +344,7 @@ final class KeyShareExtension {
List<SSLCredentials> credentials = new LinkedList<>();
for (KeyShareEntry entry : spec.clientShares) {
NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
if (ng == null || !SupportedGroups.isActivatable(
if (ng == null || !NamedGroup.isActivatable(shc.sslConfig,
shc.algorithmConstraints, ng)) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
@ -647,7 +646,7 @@ final class KeyShareExtension {
SHKeyShareSpec spec = new SHKeyShareSpec(chc, buffer);
KeyShareEntry keyShare = spec.serverShare;
NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
if (ng == null || !SupportedGroups.isActivatable(
if (ng == null || !NamedGroup.isActivatable(chc.sslConfig,
chc.algorithmConstraints, ng)) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported named group: " +
@ -800,7 +799,7 @@ final class KeyShareExtension {
NamedGroup selectedGroup = null;
for (NamedGroup ng : shc.clientRequestedNamedGroups) {
if (SupportedGroups.isActivatable(
if (NamedGroup.isActivatable(shc.sslConfig,
shc.algorithmConstraints, ng)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(

View File

@ -33,12 +33,13 @@ import java.security.spec.NamedParameterSpec;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import javax.crypto.KeyAgreement;
import javax.crypto.spec.DHParameterSpec;
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
import sun.security.util.CurveDB;
import sun.security.action.GetPropertyAction;
/**
* An enum containing all known named groups for use in TLS.
@ -241,9 +242,9 @@ enum NamedGroup {
// Constructor used for all NamedGroup types
NamedGroup(int id, String name,
NamedGroupSpec namedGroupSpec,
ProtocolVersion[] supportedProtocols,
AlgorithmParameterSpec keAlgParamSpec) {
NamedGroupSpec namedGroupSpec,
ProtocolVersion[] supportedProtocols,
AlgorithmParameterSpec keAlgParamSpec) {
this.id = id;
this.name = name;
this.spec = namedGroupSpec;
@ -369,6 +370,126 @@ enum NamedGroup {
return "UNDEFINED-NAMED-GROUP(" + id + ")";
}
public static List<NamedGroup> namesOf(String[] namedGroups) {
if (namedGroups == null) {
return null;
}
if (namedGroups.length == 0) {
return List.of();
}
List<NamedGroup> ngs = new ArrayList<>(namedGroups.length);
for (String ss : namedGroups) {
NamedGroup ng = NamedGroup.nameOf(ss);
if (ng == null || !ng.isAvailable) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest(
"Ignore the named group (" + ss
+ "), unsupported or unavailable");
}
continue;
}
ngs.add(ng);
}
return Collections.unmodifiableList(ngs);
}
// Is there any supported group permitted by the constraints?
static boolean isActivatable(SSLConfiguration sslConfig,
AlgorithmConstraints constraints, NamedGroupSpec type) {
boolean hasFFDHEGroups = false;
for (String ng : sslConfig.namedGroups) {
NamedGroup namedGroup = NamedGroup.nameOf(ng);
if (namedGroup != null &&
namedGroup.isAvailable && namedGroup.spec == type) {
if (namedGroup.isPermitted(constraints)) {
return true;
}
if (!hasFFDHEGroups &&
(type == NamedGroupSpec.NAMED_GROUP_FFDHE)) {
hasFFDHEGroups = true;
}
}
}
// For compatibility, if no FFDHE groups are defined, the non-FFDHE
// compatible mode (using DHE cipher suite without FFDHE extension)
// is allowed.
//
// Note that the constraints checking on DHE parameters will be
// performed during key exchanging in a handshake.
return !hasFFDHEGroups && type == NamedGroupSpec.NAMED_GROUP_FFDHE;
}
// Is the named group permitted by the constraints?
static boolean isActivatable(
SSLConfiguration sslConfig,
AlgorithmConstraints constraints, NamedGroup namedGroup) {
if (!namedGroup.isAvailable || !isEnabled(sslConfig, namedGroup)) {
return false;
}
return namedGroup.isPermitted(constraints);
}
// Is the named group supported?
static boolean isEnabled(SSLConfiguration sslConfig,
NamedGroup namedGroup) {
for (String ng : sslConfig.namedGroups) {
if (namedGroup.name.equalsIgnoreCase(ng)) {
return true;
}
}
return false;
}
// Get preferred named group from the configured named groups for the
// negotiated protocol and named group types.
static NamedGroup getPreferredGroup(
SSLConfiguration sslConfig,
ProtocolVersion negotiatedProtocol,
AlgorithmConstraints constraints, NamedGroupSpec[] types) {
for (String name : sslConfig.namedGroups) {
NamedGroup ng = NamedGroup.nameOf(name);
if (ng != null && ng.isAvailable &&
(NamedGroupSpec.arrayContains(types, ng.spec)) &&
ng.isAvailable(negotiatedProtocol) &&
ng.isPermitted(constraints)) {
return ng;
}
}
return null;
}
// Get preferred named group from the requested and configured named
// groups for the negotiated protocol and named group types.
static NamedGroup getPreferredGroup(
SSLConfiguration sslConfig,
ProtocolVersion negotiatedProtocol,
AlgorithmConstraints constraints, NamedGroupSpec[] types,
List<NamedGroup> requestedNamedGroups) {
for (NamedGroup namedGroup : requestedNamedGroups) {
if ((namedGroup.isAvailable &&
NamedGroupSpec.arrayContains(types, namedGroup.spec)) &&
namedGroup.isAvailable(negotiatedProtocol) &&
isEnabled(sslConfig, namedGroup) &&
namedGroup.isPermitted(constraints)) {
return namedGroup;
}
}
return null;
}
// Is the NamedGroup available for the protocols desired?
boolean isAvailable(List<ProtocolVersion> protocolVersions) {
if (this.isAvailable) {
@ -618,4 +739,86 @@ enum NamedGroup {
return XDHKeyExchange.xdheKAGenerator.createKeyDerivation(hc);
}
}
static final class SupportedGroups {
// the supported named groups, non-null immutable list
static final String[] namedGroups;
static {
// The value of the System Property defines a list of enabled named
// groups in preference order, separated with comma. For example:
//
// jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
//
// If the System Property is not defined or the value is empty, the
// default groups and preferences will be used.
String property = GetPropertyAction
.privilegedGetProperty("jdk.tls.namedGroups");
if (property != null && !property.isEmpty()) {
// remove double quote marks from beginning/end of the property
if (property.length() > 1 && property.charAt(0) == '"' &&
property.charAt(property.length() - 1) == '"') {
property = property.substring(1, property.length() - 1);
}
}
ArrayList<String> groupList;
if (property != null && !property.isEmpty()) {
String[] groups = property.split(",");
groupList = new ArrayList<>(groups.length);
for (String group : groups) {
group = group.trim();
if (!group.isEmpty()) {
NamedGroup namedGroup = nameOf(group);
if (namedGroup != null) {
if (namedGroup.isAvailable) {
groupList.add(namedGroup.name);
}
} // ignore unknown groups
}
}
if (groupList.isEmpty()) {
throw new IllegalArgumentException(
"System property jdk.tls.namedGroups(" +
property + ") contains no supported named groups");
}
} else { // default groups
NamedGroup[] groups = new NamedGroup[] {
// Primary XDH (RFC 7748) curves
X25519,
// Primary NIST Suite B curves
SECP256_R1,
SECP384_R1,
SECP521_R1,
// Secondary XDH curves
X448,
// FFDHE (RFC 7919)
FFDHE_2048,
FFDHE_3072,
FFDHE_4096,
FFDHE_6144,
FFDHE_8192,
};
groupList = new ArrayList<>(groups.length);
for (NamedGroup group : groups) {
if (group.isAvailable) {
groupList.add(group.name);
}
}
if (groupList.isEmpty() &&
SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("No default named groups");
}
}
namedGroups = groupList.toArray(new String[0]);
}
}
}

View File

@ -64,6 +64,9 @@ final class SSLConfiguration implements Cloneable {
// "signature_algorithms_cert" extensions
String[] signatureSchemes;
// the configured named groups for the "supported_groups" extensions
String[] namedGroups;
// the maximum protocol version of enabled protocols
ProtocolVersion maximumProtocolVersion;
@ -109,6 +112,10 @@ final class SSLConfiguration implements Cloneable {
static final int maxCertificateChainLength = GetIntegerAction.privilegedGetProperty(
"jdk.tls.maxCertificateChainLength", 10);
// To switch off the supported_groups extension for DHE cipher suite.
static final boolean enableFFDHE =
Utilities.getBooleanProperty("jsse.enableFFDHE", true);
// Is the extended_master_secret extension supported?
static {
boolean supportExtendedMasterSecret = Utilities.getBooleanProperty(
@ -146,6 +153,7 @@ final class SSLConfiguration implements Cloneable {
this.signatureSchemes = isClientMode ?
CustomizedClientSignatureSchemes.signatureSchemes :
CustomizedServerSignatureSchemes.signatureSchemes;
this.namedGroups = NamedGroup.SupportedGroups.namedGroups;
this.maximumProtocolVersion = ProtocolVersion.NONE;
for (ProtocolVersion pv : enabledProtocols) {
if (pv.compareTo(maximumProtocolVersion) > 0) {
@ -201,6 +209,7 @@ final class SSLConfiguration implements Cloneable {
params.setEnableRetransmissions(this.enableRetransmissions);
params.setMaximumPacketSize(this.maximumPacketSize);
params.setSignatureSchemes(this.signatureSchemes);
params.setNamedGroups(this.namedGroups);
return params;
}
@ -265,6 +274,15 @@ final class SSLConfiguration implements Cloneable {
this.signatureSchemes = ss;
} // Otherwise, use the default values
String[] ngs = params.getNamedGroups();
if (ngs != null) {
// Note if 'ngs' is empty, then no named groups should be
// specified over the connections.
this.namedGroups = ngs;
} else { // Otherwise, use the default values.
this.namedGroups = NamedGroup.SupportedGroups.namedGroups;
}
this.preferLocalCipherSuites = params.getUseCipherSuitesOrder();
this.enableRetransmissions = params.getEnableRetransmissions();
this.maximumPacketSize = params.getMaximumPacketSize();

View File

@ -28,8 +28,6 @@ package sun.security.ssl;
import java.io.IOException;
import java.util.*;
import java.util.AbstractMap.SimpleImmutableEntry;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Possession;
final class SSLKeyExchange implements SSLKeyAgreementGenerator,
@ -561,23 +559,13 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
private static final class T13KeyAgreement implements SSLKeyAgreement {
private final NamedGroup namedGroup;
static final Map<NamedGroup, T13KeyAgreement>
supportedKeyShares = new HashMap<>();
static {
for (NamedGroup namedGroup :
SupportedGroups.supportedNamedGroups) {
supportedKeyShares.put(
namedGroup, new T13KeyAgreement(namedGroup));
}
}
private T13KeyAgreement(NamedGroup namedGroup) {
this.namedGroup = namedGroup;
}
static T13KeyAgreement valueOf(NamedGroup namedGroup) {
return supportedKeyShares.get(namedGroup);
return new T13KeyAgreement(namedGroup);
}
@Override

View File

@ -41,7 +41,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import sun.security.ssl.NamedGroup.NamedGroupSpec;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.security.util.KeyUtil;
import sun.security.util.SignatureUtil;
@ -466,6 +465,7 @@ enum SignatureScheme {
}
static Map.Entry<SignatureScheme, Signature> getSignerOfPreferableAlgorithm(
SSLConfiguration sslConfig,
AlgorithmConstraints constraints,
List<SignatureScheme> schemes,
X509Possession x509Possession,
@ -519,7 +519,7 @@ enum SignatureScheme {
if (params != null) {
NamedGroup keyGroup = NamedGroup.valueOf(params);
if (keyGroup != null &&
SupportedGroups.isSupported(keyGroup)) {
NamedGroup.isEnabled(sslConfig, keyGroup)) {
Signature signer = ss.getSigner(signingKey);
if (signer != null) {
return new SimpleImmutableEntry<>(ss, signer);

View File

@ -27,11 +27,10 @@ package sun.security.ssl;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.AlgorithmConstraints;
import java.text.MessageFormat;
import java.util.*;
import javax.net.ssl.SSLProtocolException;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.NamedGroup.NamedGroupSpec;
import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS;
import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS;
@ -64,10 +63,6 @@ final class SupportedGroupsExtension {
static final class SupportedGroupsSpec implements SSLExtensionSpec {
final int[] namedGroupsIds;
private SupportedGroupsSpec(int[] namedGroupsIds) {
this.namedGroupsIds = namedGroupsIds;
}
private SupportedGroupsSpec(List<NamedGroup> namedGroups) {
this.namedGroupsIds = new int[namedGroups.size()];
int i = 0;
@ -150,174 +145,6 @@ final class SupportedGroupsExtension {
}
}
static class SupportedGroups {
// To switch off the supported_groups extension for DHE cipher suite.
static final boolean enableFFDHE =
Utilities.getBooleanProperty("jsse.enableFFDHE", true);
// the supported named groups
static final NamedGroup[] supportedNamedGroups;
static {
// The value of the System Property defines a list of enabled named
// groups in preference order, separated with comma. For example:
//
// jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048"
//
// If the System Property is not defined or the value is empty, the
// default groups and preferences will be used.
String property = GetPropertyAction
.privilegedGetProperty("jdk.tls.namedGroups");
if (property != null && !property.isEmpty()) {
// remove double quote marks from beginning/end of the property
if (property.length() > 1 && property.charAt(0) == '"' &&
property.charAt(property.length() - 1) == '"') {
property = property.substring(1, property.length() - 1);
}
}
ArrayList<NamedGroup> groupList;
if (property != null && !property.isEmpty()) {
String[] groups = property.split(",");
groupList = new ArrayList<>(groups.length);
for (String group : groups) {
group = group.trim();
if (!group.isEmpty()) {
NamedGroup namedGroup = NamedGroup.nameOf(group);
if (namedGroup != null) {
if (namedGroup.isAvailable) {
groupList.add(namedGroup);
}
} // ignore unknown groups
}
}
if (groupList.isEmpty()) {
throw new IllegalArgumentException(
"System property jdk.tls.namedGroups(" +
property + ") contains no supported named groups");
}
} else { // default groups
NamedGroup[] groups = new NamedGroup[] {
// Primary XDH (RFC 7748) curves
NamedGroup.X25519,
// Primary NIST Suite B curves
NamedGroup.SECP256_R1,
NamedGroup.SECP384_R1,
NamedGroup.SECP521_R1,
// Secondary XDH curves
NamedGroup.X448,
// FFDHE (RFC 7919)
NamedGroup.FFDHE_2048,
NamedGroup.FFDHE_3072,
NamedGroup.FFDHE_4096,
NamedGroup.FFDHE_6144,
NamedGroup.FFDHE_8192,
};
groupList = new ArrayList<>(groups.length);
for (NamedGroup group : groups) {
if (group.isAvailable) {
groupList.add(group);
}
}
if (groupList.isEmpty() &&
SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("No default named groups");
}
}
supportedNamedGroups = new NamedGroup[groupList.size()];
int i = 0;
for (NamedGroup namedGroup : groupList) {
supportedNamedGroups[i++] = namedGroup;
}
}
// Is there any supported group permitted by the constraints?
static boolean isActivatable(
AlgorithmConstraints constraints, NamedGroupSpec type) {
boolean hasFFDHEGroups = false;
for (NamedGroup namedGroup : supportedNamedGroups) {
if (namedGroup.isAvailable && namedGroup.spec == type) {
if (namedGroup.isPermitted(constraints)) {
return true;
}
if (!hasFFDHEGroups &&
(type == NamedGroupSpec.NAMED_GROUP_FFDHE)) {
hasFFDHEGroups = true;
}
}
}
// For compatibility, if no FFDHE groups are defined, the non-FFDHE
// compatible mode (using DHE cipher suite without FFDHE extension)
// is allowed.
//
// Note that the constraints checking on DHE parameters will be
// performed during key exchanging in a handshake.
return !hasFFDHEGroups && type == NamedGroupSpec.NAMED_GROUP_FFDHE;
}
// Is the named group permitted by the constraints?
static boolean isActivatable(
AlgorithmConstraints constraints, NamedGroup namedGroup) {
if (!namedGroup.isAvailable || !isSupported(namedGroup)) {
return false;
}
return namedGroup.isPermitted(constraints);
}
// Is the named group supported?
static boolean isSupported(NamedGroup namedGroup) {
for (NamedGroup group : supportedNamedGroups) {
if (namedGroup.id == group.id) {
return true;
}
}
return false;
}
static NamedGroup getPreferredGroup(
ProtocolVersion negotiatedProtocol,
AlgorithmConstraints constraints, NamedGroupSpec[] types,
List<NamedGroup> requestedNamedGroups) {
for (NamedGroup namedGroup : requestedNamedGroups) {
if ((NamedGroupSpec.arrayContains(types, namedGroup.spec)) &&
namedGroup.isAvailable(negotiatedProtocol) &&
isSupported(namedGroup) &&
namedGroup.isPermitted(constraints)) {
return namedGroup;
}
}
return null;
}
static NamedGroup getPreferredGroup(
ProtocolVersion negotiatedProtocol,
AlgorithmConstraints constraints, NamedGroupSpec[] types) {
for (NamedGroup namedGroup : supportedNamedGroups) {
if ((NamedGroupSpec.arrayContains(types, namedGroup.spec)) &&
namedGroup.isAvailable(negotiatedProtocol) &&
namedGroup.isPermitted(constraints)) {
return namedGroup;
}
}
return null;
}
}
/**
* Network data producer of a "supported_groups" extension in
* the ClientHello handshake message.
@ -346,9 +173,18 @@ final class SupportedGroupsExtension {
// Produce the extension.
ArrayList<NamedGroup> namedGroups =
new ArrayList<>(SupportedGroups.supportedNamedGroups.length);
for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
if ((!SupportedGroups.enableFFDHE) &&
new ArrayList<>(chc.sslConfig.namedGroups.length);
for (String name : chc.sslConfig.namedGroups) {
NamedGroup ng = NamedGroup.nameOf(name);
if (ng == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unspecified named group: " + name);
}
continue;
}
if ((!SSLConfiguration.enableFFDHE) &&
(ng.spec == NamedGroupSpec.NAMED_GROUP_FFDHE)) {
continue;
}
@ -495,9 +331,19 @@ final class SupportedGroupsExtension {
// Contains all groups the server supports, regardless of whether
// they are currently supported by the client.
ArrayList<NamedGroup> namedGroups = new ArrayList<>(
SupportedGroups.supportedNamedGroups.length);
for (NamedGroup ng : SupportedGroups.supportedNamedGroups) {
if ((!SupportedGroups.enableFFDHE) &&
shc.sslConfig.namedGroups.length);
for (String name : shc.sslConfig.namedGroups) {
NamedGroup ng = NamedGroup.nameOf(name);
if (ng == null) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unspecified named group: " + name);
}
continue;
}
if ((!SSLConfiguration.enableFFDHE) &&
(ng.spec == NamedGroupSpec.NAMED_GROUP_FFDHE)) {
continue;
}

View File

@ -39,8 +39,6 @@ import java.util.Arrays;
import java.util.Map;
import javax.net.ssl.X509ExtendedKeyManager;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
enum X509Authentication implements SSLAuthentication {
// Require rsaEncryption public key
RSA ("RSA", "RSA"),
@ -344,7 +342,7 @@ enum X509Authentication implements SSLAuthentication {
((ECPublicKey) serverPublicKey).getParams();
NamedGroup namedGroup = NamedGroup.valueOf(params);
if ((namedGroup == null) ||
(!SupportedGroups.isSupported(namedGroup)) ||
(!NamedGroup.isEnabled(shc.sslConfig, namedGroup)) ||
((shc.clientRequestedNamedGroups != null) &&
!shc.clientRequestedNamedGroups.contains(namedGroup))) {

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2022 THL A29 Limited, a Tencent company. 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8281236
* @summary Check DTLS connection behaviors for named groups configuration
* @modules java.base/sun.security.util
* @library /test/lib
* @build DTLSOverDatagram
* @run main/othervm DTLSNamedGroups
*/
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import java.security.Security;
/**
* Test DTLS client authentication.
*/
public class DTLSNamedGroups extends DTLSOverDatagram {
// Make sure default DH(E) key exchange is not used for DTLS v1.2.
private static String[] cipherSuites = new String[] {
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
};
private final String[] serverNamedGroups;
private final String[] clientNamedGroups;
public DTLSNamedGroups(String[] serverNamedGroups,
String[] clientNamedGroups) {
this.serverNamedGroups = serverNamedGroups;
this.clientNamedGroups = clientNamedGroups;
}
@Override
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLEngine engine = super.createSSLEngine(isClient);
SSLParameters sslParameters = engine.getSSLParameters();
if (isClient) {
sslParameters.setNamedGroups(clientNamedGroups);
sslParameters.setCipherSuites(cipherSuites);
} else {
sslParameters.setNamedGroups(serverNamedGroups);
}
engine.setSSLParameters(sslParameters);
return engine;
}
public static void main(String[] args) throws Exception {
Security.setProperty("jdk.tls.disabledAlgorithms", "");
runTest(new String[] {
"x25519",
"secp256r1"
},
new String[] {
"x25519",
"secp256r1"
},
false);
runTest(new String[] {
"secp256r1"
},
new String[] {
"secp256r1"
},
false);
runTest(null,
new String[] {
"secp256r1"
},
false);
runTest(new String[] {
"secp256r1"
},
null,
false);
runTest(new String[0],
new String[] {
"secp256r1"
},
true);
runTest(new String[] {
"secp256r1"
},
new String[0],
true);
runTest(new String[] {
"secp256NA"
},
new String[] {
"secp256r1"
},
true);
}
private static void runTest(String[] serverNamedGroups,
String[] clientNamedGroups,
boolean exceptionExpected) throws Exception {
DTLSNamedGroups testCase = new DTLSNamedGroups(
serverNamedGroups, clientNamedGroups);
try {
testCase.runTest(testCase);
} catch (Exception e) {
if (!exceptionExpected) {
throw e;
} else { // Otherwise, swallow the expected exception and return.
return;
}
}
if (exceptionExpected) {
throw new RuntimeException("Unexpected success!");
}
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (C) 2022 THL A29 Limited, a Tencent company. 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8281236
* @summary Check TLS connection behaviors for named groups configuration
* @library /javax/net/ssl/templates
* @run main/othervm NamedGroups
*/
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import java.security.Security;
public class NamedGroups extends SSLSocketTemplate {
private final String[] serverNamedGroups;
private final String[] clientNamedGroups;
private final boolean exceptionExpected;
public NamedGroups(String[] serverNamedGroups,
String[] clientNamedGroups,
boolean exceptionExpected) {
this.serverNamedGroups = serverNamedGroups;
this.clientNamedGroups = clientNamedGroups;
this.exceptionExpected = exceptionExpected;
}
@Override
protected void configureServerSocket(SSLServerSocket sslServerSocket) {
SSLParameters sslParameters = sslServerSocket.getSSLParameters();
sslParameters.setNamedGroups(serverNamedGroups);
sslServerSocket.setSSLParameters(sslParameters);
}
@Override
protected void configureClientSocket(SSLSocket socket) {
SSLParameters sslParameters = socket.getSSLParameters();
sslParameters.setNamedGroups(clientNamedGroups);
socket.setSSLParameters(sslParameters);
}
@Override
protected void runServerApplication(SSLSocket socket) {
try {
super.runServerApplication(socket);
} catch (Exception ex) {
// Just ignore, let the client handle the failure information.
}
}
@Override
protected void runClientApplication(SSLSocket sslSocket) throws Exception {
try {
super.runClientApplication(sslSocket);
} catch (Exception ex) {
if (!exceptionExpected) {
throw ex;
} else { // Otherwise, swallow the exception and return.
return;
}
}
if (exceptionExpected) {
throw new RuntimeException("Unexpected success!");
}
}
public static void main(String[] args) throws Exception {
Security.setProperty("jdk.tls.disabledAlgorithms", "");
runTest(new String[] {
"x25519",
"secp256r1"
},
new String[] {
"x25519",
"secp256r1"
},
false);
runTest(new String[] {
"secp256r1"
},
new String[] {
"secp256r1"
},
false);
runTest(null,
new String[] {
"secp256r1"
},
false);
runTest(new String[] {
"secp256r1"
},
null,
false);
runTest(new String[0],
new String[] {
"secp256r1"
},
true);
runTest(new String[] {
"secp256r1"
},
new String[0],
true);
runTest(new String[] {
"secp256NA"
},
new String[] {
"secp256r1"
},
true);
}
private static void runTest(String[] serverNamedGroups,
String[] clientNamedGroups,
boolean exceptionExpected) throws Exception {
new NamedGroups(serverNamedGroups,
clientNamedGroups, exceptionExpected).run();
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2022 THL A29 Limited, a Tencent company. 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 8281236
* @summary check SSLParameters.setNamedGroups() implementation
*/
import javax.net.ssl.SSLParameters;
import java.util.Arrays;
public class NamedGroupsSpec {
public static void main(String[] args) throws Exception {
runTest(null, // null array should be allowed.
false);
runTest(new String[] { // empty array should be allowed
// blank line
},
false);
runTest(new String[] { // multiple elements should be fine
"x25519",
"secp256r1"
},
false);
runTest(new String[] { // no duplicate element should be allowed
"x25519",
"x25519"
},
true);
runTest(new String[] { // no null element should be allowed
null
},
true);
runTest(new String[] { // no blank element should be allowed
""
},
true);
runTest(new String[] { // no blank element should be allowed
"x25519",
""
},
true);
runTest(new String[] { // no null element should be allowed.
"x25519",
null
},
true);
}
private static void runTest(String[] namedGroups,
boolean exceptionExpected) throws Exception {
SSLParameters sslParams = new SSLParameters();
try {
sslParams.setNamedGroups(namedGroups);
} catch (Exception ex) {
if (!exceptionExpected ||
!(ex instanceof IllegalArgumentException)) {
throw ex;
} else { // Otherwise, swallow the exception and return.
return;
}
}
if (exceptionExpected) {
throw new RuntimeException("Unexpected success!");
}
// Check if the getNamedGroups() method returns the same elements.
String[] configuredNamedGroups = sslParams.getNamedGroups();
if (!Arrays.equals(namedGroups, configuredNamedGroups)) {
throw new RuntimeException(
"SSLParameters.getNamedGroups() method does not return "
+ "the same elements as set with setNamedGroups()");
}
}
}