diff --git a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index b4fbdf9a43792..0d4872f90510f 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -34,8 +34,8 @@ * 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. *

* {@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 void setSignatureSchemes(String[] signatureSchemes) { this.signatureSchemes = tempSchemes; } + + /** + * Returns a prioritized array of key exchange named groups names that + * can be used over the SSL/TLS/DTLS protocols. + *

+ * Note that the standard list of key exchange named groups are defined + * in the + * Named Groups 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. + *

+ * 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. + *

+ * If the returned array is {@code null}, then the underlying + * provider-specific default named groups will be used over the + * SSL/TLS/DTLS connections. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * Note that the standard list of key exchange named groups are defined in + * the + * Named Groups 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. + *

+ * 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 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; + } } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java index 97c513535c58b..518a525b12fc0 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java @@ -590,6 +590,7 @@ static final class T12CertificateVerifyMessage extends HandshakeMessage { ClientHandshakeContext chc = (ClientHandshakeContext)context; Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + chc.sslConfig, chc.algorithmConstraints, chc.peerRequestedSignatureSchemes, x509Possession, @@ -901,6 +902,7 @@ static final class T13CertificateVerifyMessage extends HandshakeMessage { Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + context.sslConfig, context.algorithmConstraints, context.peerRequestedSignatureSchemes, x509Possession, diff --git a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java index fc45921cff0ae..ef39341158a57 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java @@ -42,7 +42,6 @@ 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 @@ public SSLPossession createPossession(HandshakeContext context) { 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()); diff --git a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java index 019d53c75c045..2df62d50fb845 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java @@ -127,6 +127,7 @@ class DHServerKeyExchangeMessage extends HandshakeMessage { if (useExplicitSigAlgorithm) { Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + shc.sslConfig, shc.algorithmConstraints, shc.peerRequestedSignatureSchemes, x509Possession, diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java index b38b98d4d5999..0b415f655bea4 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java @@ -44,7 +44,6 @@ 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 @@ public SSLPossession createPossession(HandshakeContext context) { // 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 @@ public SSLPossession createPossession(HandshakeContext context) { NamedGroupSpec.NAMED_GROUP_XDH }, context.clientRequestedNamedGroups); } else { - preferableNamedGroup = SupportedGroups.getPreferredGroup( + preferableNamedGroup = NamedGroup.getPreferredGroup( + context.sslConfig, context.negotiatedProtocol, context.algorithmConstraints, new NamedGroupSpec[] { diff --git a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java index 07aaa95a4623a..b31c0ba9cb9de 100644 --- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java @@ -42,7 +42,6 @@ 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 @@ class ECDHServerKeyExchangeMessage extends HandshakeMessage { if (useExplicitSigAlgorithm) { Map.Entry schemeAndSigner = SignatureScheme.getSignerOfPreferableAlgorithm( + shc.sslConfig, shc.algorithmConstraints, shc.peerRequestedSignatureSchemes, x509Possession, @@ -204,7 +204,7 @@ class ECDHServerKeyExchangeMessage extends HandshakeMessage { "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); } diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index e9821c26eb25e..4699c5ce309cf 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -39,7 +39,6 @@ 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 @@ protected HandshakeContext(SSLContextImpl sslContext, 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 @@ protected HandshakeContext(SSLContextImpl sslContext, } } 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 @@ private void initialize() { } private static List getActiveProtocols( - List enabledProtocols, - List enabledCipherSuites, + SSLConfiguration sslConfig, AlgorithmConstraints algorithmConstraints) { boolean enabledSSL20Hello = false; ArrayList 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 @@ private static List getActiveProtocols( boolean found = false; Map 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 @@ private static List getActiveProtocols( } private static List getActiveCipherSuites( + SSLConfiguration sslConfig, List enabledProtocols, - List enabledCipherSuites, AlgorithmConstraints algorithmConstraints) { List suites = new LinkedList<>(); if (enabledProtocols != null && !enabledProtocols.isEmpty()) { Map cachedStatus = new EnumMap<>(NamedGroupSpec.class); - for (CipherSuite suite : enabledCipherSuites) { + for (CipherSuite suite : sslConfig.enabledCipherSuites) { if (!suite.isAvailable()) { continue; } @@ -327,7 +325,7 @@ private static List getActiveCipherSuites( if (!suite.supports(protocol)) { continue; } - if (isActivatable(suite, + if (isActivatable(sslConfig, suite, algorithmConstraints, cachedStatus)) { suites.add(suite); isSupported = true; @@ -525,7 +523,9 @@ boolean isNegotiable(ProtocolVersion protocolVersion) { return activeProtocols.contains(protocolVersion); } - private static boolean isActivatable(CipherSuite suite, + private static boolean isActivatable( + SSLConfiguration sslConfig, + CipherSuite suite, AlgorithmConstraints algorithmConstraints, Map cachedStatus) { @@ -543,8 +543,8 @@ private static boolean isActivatable(CipherSuite suite, 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 && diff --git a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java index 02a667503c62e..2df6d26ff31c9 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java @@ -36,7 +36,6 @@ 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 @@ public void consume(ConnectionContext context, List 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 @@ public void consume(ConnectionContext context, 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 @@ public byte[] produce(ConnectionContext context, 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( diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java index 143d8689f95be..2a6c966a25435 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -33,12 +33,13 @@ 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 @@ static String nameOf(int id) { return "UNDEFINED-NAMED-GROUP(" + id + ")"; } + public static List namesOf(String[] namedGroups) { + if (namedGroups == null) { + return null; + } + + if (namedGroups.length == 0) { + return List.of(); + } + + List 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 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 protocolVersions) { if (this.isAvailable) { @@ -618,4 +739,86 @@ public SSLKeyDerivation createKeyDerivation( 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 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]); + } + } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 056f00a5a3c36..ea2d2efcb4f8b 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -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 @@ SSLParameters getSSLParameters() { params.setEnableRetransmissions(this.enableRetransmissions); params.setMaximumPacketSize(this.maximumPacketSize); params.setSignatureSchemes(this.signatureSchemes); + params.setNamedGroups(this.namedGroups); return params; } @@ -265,6 +274,15 @@ void setSSLParameters(SSLParameters params) { 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(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java index e0d2733c155c1..22a44590ce3c8 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java @@ -28,8 +28,6 @@ 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 @@ public Map.Entry[] getHandshakeConsumers( private static final class T13KeyAgreement implements SSLKeyAgreement { private final NamedGroup namedGroup; - static final Map - 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 diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java index 4897258a93d31..4db3f31bae839 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -41,7 +41,6 @@ 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 @@ static SignatureScheme getPreferableAlgorithm( } static Map.Entry getSignerOfPreferableAlgorithm( + SSLConfiguration sslConfig, AlgorithmConstraints constraints, List schemes, X509Possession x509Possession, @@ -519,7 +519,7 @@ static Map.Entry getSignerOfPreferableAlgorithm( 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); diff --git a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java index ecf2f0bb35e08..d6e1391d09bd9 100644 --- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java @@ -27,11 +27,10 @@ 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 namedGroups) { this.namedGroupsIds = new int[namedGroups.size()]; int i = 0; @@ -150,174 +145,6 @@ public String toString(HandshakeContext hc, ByteBuffer buffer) { } } - 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 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 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 @@ public byte[] produce(ConnectionContext context, // Produce the extension. ArrayList 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 @@ public byte[] produce(ConnectionContext context, // Contains all groups the server supports, regardless of whether // they are currently supported by the client. ArrayList 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; } diff --git a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java index 164fb1c6be210..6c29db30c60a3 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java +++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java @@ -39,8 +39,6 @@ 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 @@ private static SSLPossession createServerPossession( ((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))) { diff --git a/test/jdk/javax/net/ssl/DTLS/DTLSNamedGroups.java b/test/jdk/javax/net/ssl/DTLS/DTLSNamedGroups.java new file mode 100644 index 0000000000000..e9165fa391cd3 --- /dev/null +++ b/test/jdk/javax/net/ssl/DTLS/DTLSNamedGroups.java @@ -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!"); + } + } +} + diff --git a/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java new file mode 100644 index 0000000000000..fc5001e89b818 --- /dev/null +++ b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java @@ -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(); + } +} diff --git a/test/jdk/javax/net/ssl/SSLParameters/NamedGroupsSpec.java b/test/jdk/javax/net/ssl/SSLParameters/NamedGroupsSpec.java new file mode 100644 index 0000000000000..9146e7b5f7b39 --- /dev/null +++ b/test/jdk/javax/net/ssl/SSLParameters/NamedGroupsSpec.java @@ -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()"); + } + } +}