From 025041ba04f3ae3a149b9d57d0dde4afaef37f4c Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Thu, 22 Jan 2026 13:11:42 +0000 Subject: [PATCH] 8370885: Default namedGroups values are not being filtered against algorithm constraints Reviewed-by: hchao --- .../classes/sun/security/ssl/NamedGroup.java | 177 ++++++++++-------- .../ssl/CipherSuite/DefaultNamedGroups.java | 85 +++++++++ 2 files changed, 189 insertions(+), 73 deletions(-) create mode 100644 test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.java 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 abf973727f3..02524e67656 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,12 @@ import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.security.spec.NamedParameterSpec; +import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.ArrayList; +import java.util.Objects; import java.util.Set; import javax.crypto.KeyAgreement; import javax.crypto.spec.DHParameterSpec; @@ -463,10 +465,9 @@ enum NamedGroup { 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) { + for (NamedGroup namedGroup : + SupportedGroups.getGroupsFromConfig(sslConfig)) { + if (namedGroup.isAvailable && namedGroup.spec == type) { if (namedGroup.isPermitted(constraints)) { return true; } @@ -501,8 +502,8 @@ enum NamedGroup { // Is the named group supported? static boolean isEnabled(SSLConfiguration sslConfig, NamedGroup namedGroup) { - for (String ng : sslConfig.namedGroups) { - if (namedGroup.name.equalsIgnoreCase(ng)) { + for (NamedGroup ng : SupportedGroups.getGroupsFromConfig(sslConfig)) { + if (namedGroup.equals(ng)) { return true; } } @@ -516,12 +517,10 @@ enum NamedGroup { 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)) { + for (NamedGroup ng : SupportedGroups.getGroupsFromConfig(sslConfig)) { + if (ng.isAvailable && NamedGroupSpec.arrayContains(types, ng.spec) + && ng.isAvailable(negotiatedProtocol) + && ng.isPermitted(constraints)) { return ng; } } @@ -857,19 +856,92 @@ enum NamedGroup { } } + // Inner class encapsulating supported named groups. static final class SupportedGroups { - // the supported named groups, non-null immutable list + + // Default named groups. + private static final NamedGroup[] defaultGroups = new NamedGroup[]{ + // Hybrid key agreement + X25519MLKEM768, + + // 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 + }; + + // Filter default groups names against default constraints. + // Those are the values being displayed to the user with + // "java -XshowSettings:security:tls" command. + private static final String[] defaultNames = Arrays.stream( + defaultGroups) + .filter(ng -> ng.isAvailable) + .filter(ng -> ng.isPermitted(SSLAlgorithmConstraints.DEFAULT)) + .map(ng -> ng.name) + .toArray(String[]::new); + + private static final NamedGroup[] customizedGroups = + getCustomizedNamedGroups(); + + // Note: user-passed groups are not being filtered against default + // algorithm constraints here. They will be displayed as-is. + private static final String[] customizedNames = + customizedGroups == null ? + null : Arrays.stream(customizedGroups) + .map(ng -> ng.name) + .toArray(String[]::new); + + // Named group names for SSLConfiguration. 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. + if (customizedNames != null) { + namedGroups = customizedNames; + } else { + if (defaultNames.length == 0) { + SSLLogger.logWarning("ssl", "No default named groups"); + } + namedGroups = defaultNames; + } + } + + // Avoid the group lookup for default and customized groups. + static NamedGroup[] getGroupsFromConfig(SSLConfiguration sslConfig) { + if (sslConfig.namedGroups == defaultNames) { + return defaultGroups; + } else if (sslConfig.namedGroups == customizedNames) { + return customizedGroups; + } else { + return Arrays.stream(sslConfig.namedGroups) + .map(NamedGroup::nameOf) + .filter(Objects::nonNull) + .toArray(NamedGroup[]::new); + } + } + + // 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. + private static NamedGroup[] getCustomizedNamedGroups() { String property = System.getProperty("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) == '"' && @@ -878,66 +950,25 @@ enum NamedGroup { } } - 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 - } - } + NamedGroup[] ret = Arrays.stream(property.split(",")) + .map(String::trim) + .map(NamedGroup::nameOf) + .filter(Objects::nonNull) + .filter(ng -> ng.isAvailable) + .toArray(NamedGroup[]::new); - if (groupList.isEmpty()) { + if (ret.length == 0) { throw new IllegalArgumentException( "System property jdk.tls.namedGroups(" + - property + ") contains no supported named groups"); - } - } else { // default groups - NamedGroup[] groups = new NamedGroup[] { - - // Hybrid key agreement - X25519MLKEM768, - - // 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); - } + property + + ") contains no supported named groups"); } - if (groupList.isEmpty() && - SSLLogger.isOn() && SSLLogger.isOn("ssl")) { - SSLLogger.warning("No default named groups"); - } + return ret; } - namedGroups = groupList.toArray(new String[0]); + return null; } } } diff --git a/test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.java b/test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.java new file mode 100644 index 00000000000..44c9d566dc0 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/DefaultNamedGroups.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import javax.net.ssl.SSLEngine; +import jdk.test.lib.security.SecurityUtils; + +/* + * @test + * @bug 8370885 + * @summary Default namedGroups values are not being filtered against + * algorithm constraints + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DefaultNamedGroups + */ + +public class DefaultNamedGroups extends SSLEngineTemplate { + + protected static final String DISABLED_NG = "secp256r1"; + protected static final List REFERENCE_NG = Stream.of( + "X25519MLKEM768", + "x25519", + "secp256r1", + "secp384r1", + "secp521r1", + "x448", + "ffdhe2048", + "ffdhe3072", + "ffdhe4096", + "ffdhe6144", + "ffdhe8192") + .sorted() + .toList(); + + protected DefaultNamedGroups() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + SecurityUtils.addToDisabledTlsAlgs(DISABLED_NG); + var test = new DefaultNamedGroups(); + + for (SSLEngine engine : + new SSLEngine[]{test.serverEngine, test.clientEngine}) { + checkEngineDefaultNG(engine); + } + } + + private static void checkEngineDefaultNG(SSLEngine engine) { + var defaultConfigNG = new ArrayList<>(List.of( + engine.getSSLParameters().getNamedGroups())); + + assertFalse(defaultConfigNG.contains(DISABLED_NG)); + defaultConfigNG.add(DISABLED_NG); + assertTrue(REFERENCE_NG.equals( + defaultConfigNG.stream().sorted().toList()), + "Named groups returned by engine: " + defaultConfigNG); + } +}