From c8831fbc531af8dd7ab669d653e20ad2b35c99f5 Mon Sep 17 00:00:00 2001 From: Andreas Chmielewski Date: Thu, 2 Jul 2026 19:33:56 +0200 Subject: [PATCH 1/4] Backport 3f52251c9d82991b14f0bbf34c81627911b0fdbf --- .../security/ssl/SSLAlgorithmDecomposer.java | 10 + .../BulkCipherDisabledAlgorithms.java | 218 ++++++++++++++++++ .../TLS13BulkCipherDisabledCipherSuite.java | 79 +++++++ 3 files changed, 307 insertions(+) create mode 100644 test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java create mode 100644 test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java index 565ed8f6128..61b1236e9bc 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corporation. 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 @@ -172,9 +173,18 @@ private Set decomposes(SSLCipher bulkCipher) { case B_AES_128_GCM: components.add("AES_128_GCM"); break; + case B_AES_128_GCM_IV: + components.add("AES_128_GCM"); + break; case B_AES_256_GCM: components.add("AES_256_GCM"); break; + case B_AES_256_GCM_IV: + components.add("AES_256_GCM"); + break; + case B_CC20_P1305: + components.add("CHACHA20_POLY1305"); + break; } return components; diff --git a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java new file mode 100644 index 00000000000..11f3efb1518 --- /dev/null +++ b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2026, IBM Corporation. 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 8387124 + * @summary Test TLS cipher suite disabling via jdk.tls.disabledAlgorithms, + * including matching on bulk cipher components, covering both + * visibility and handshake behavior. + * @library /test/lib + * /javax/net/ssl/TLSCommon + * /javax/net/ssl/templates + * @run main/othervm BulkCipherDisabledAlgorithms visibility + * @run main/othervm BulkCipherDisabledAlgorithms handshake + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.*; + +import jdk.test.lib.process.Proc; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +public class BulkCipherDisabledAlgorithms { + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new RuntimeException("Missing mode argument"); + } + + String mode = args[0]; + boolean isVisibilityTest = "visibility".equals(mode); + boolean isHandshakeTest = "handshake".equals(mode); + + if (args.length == 1) { + List tests = buildTests(isVisibilityTest); + + for (String[] test : tests) { + String suite = test[0]; + String disabled = test[1]; + String expected = test[2]; + + System.out.println("================================================="); + System.out.println("Testing: " + mode + + ", suite=" + suite + + ", disabled=" + disabled + + ", expected=" + expected); + + Proc p = Proc.create( + BulkCipherDisabledAlgorithms.class.getName()) + .args(mode, suite, expected) + .secprop("jdk.tls.disabledAlgorithms", disabled) + .inheritIO(); + + p.start().waitFor(0); + } + + System.out.println("TEST PASS - OK"); + return; + } + + String suite = args[1]; + String expected = args[2]; + boolean expectedDisabled = "disabled".equals(expected); + + if (isVisibilityTest) { + testCipherSuiteVisibility(suite, expectedDisabled); + } + + if (isHandshakeTest) { + testHandshake(suite, expectedDisabled); + } + } + + // Returns cipher suites for testing. + // - true: use all supported suites (independent of disabledAlgorithms) + // - false: use default enabled suites (candidates for handshake) + private static CipherSuite[] getCipherSuites(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + String[] suites = useSupportedSuites + ? engine.getSupportedCipherSuites() + : engine.getEnabledCipherSuites(); + + return Arrays.stream(suites) + .map(CipherSuite::cipherSuite) + .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .toArray(CipherSuite[]::new); + } + + private static List buildTests(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + if (useSupportedSuites) { + // disabledAlgorithms limits supported suites; clear to list all + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + List tests = new ArrayList<>(); + CipherSuite[] suites = getCipherSuites(useSupportedSuites); + + for (CipherSuite suite : suites) { + String suiteName = suite.name(); + String bulk = extractBulkCipher(suiteName); + + tests.add(new String[] { suiteName, suiteName, "disabled" }); + tests.add(new String[] { suiteName, bulk, "disabled" }); + + for (CipherSuite other : suites) { + // Negative test case: disable a different bulk cipher than the one + // used by the current suite. This ensures that the suite remains + // enabled and a successful TLS handshake can still be negotiated. + if (other == suite) { + continue; + } + + String otherBulk = extractBulkCipher(other.name()); + + if (!bulk.equals(otherBulk) + && !suiteName.contains(otherBulk)) { + tests.add(new String[] { suiteName, otherBulk, "enabled" }); + break; + } + } + } + + return tests; + } + + /** + * Separator used in TLS cipher suite names to mark the start of + * the bulk cipher component (e.g. TLS_RSA_WITH_AES_128_CBC_SHA). + */ + private static final String WITH = "_WITH_"; + + private static String extractBulkCipher(String suite) { + if (suite.contains(WITH)) { + String after = suite.substring(suite.indexOf(WITH) + WITH.length()); + int last = after.lastIndexOf('_'); + return after.substring(0, last); + } else { + int first = suite.indexOf('_'); + int last = suite.lastIndexOf('_'); + return suite.substring(first + 1, last); + } + } + + private static void testCipherSuiteVisibility(String suite, boolean expectedDisabled) + throws NoSuchAlgorithmException { + boolean visible = Arrays.asList(getCipherSuites(true)) + .contains(CipherSuite.cipherSuite(suite)); + + if (!expectedDisabled && !visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' not visible but expected to be enabled"); + } else if (expectedDisabled && visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' visible but expected to be disabled"); + } + } + + private static void testHandshake(String suite, boolean expectedDisabled) throws Exception { + try { + new TLSHandshakeTest(suite).run(); + + if (expectedDisabled) { + throw new RuntimeException( + "Handshake succeeded but should fail: " + suite); + } + } catch (SSLHandshakeException e) { + if (!expectedDisabled) { + throw new RuntimeException( + "Handshake failed unexpectedly: " + suite, e); + } + } + } + + private static class TLSHandshakeTest extends SSLSocketTemplate { + private final String suite; + + TLSHandshakeTest(String suite) { + this.suite = suite; + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + + @Override + protected void configureServerSocket(SSLServerSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + } +} diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java new file mode 100644 index 00000000000..87a6f156152 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, IBM Corporation. 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 8387124 + * @summary Test disabling TLS 1.3 cipher suites with bulk ciphers names + * @run testng/othervm TLS13BulkCipherDisabledCipherSuite + */ + +import static org.testng.AssertJUnit.assertTrue; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.security.Security; +import java.util.List; + +public class TLS13BulkCipherDisabledCipherSuite extends AbstractDisableCipherSuites { + + private static final String SECURITY_PROPERTY = "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = "AES_256_GCM," + + " AES_128_GCM," + + " CHACHA20_POLY1305"; + private static final String[] CIPHER_SUITES = new String[] { + "TLS_AES_256_GCM_SHA384", + "TLS_AES_128_GCM_SHA256", + "TLS_CHACHA20_POLY1305_SHA256" + }; + static final List CIPHER_SUITES_IDS = List.of( + 0x1301, + 0x1302, + 0x1303); + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + @BeforeTest + void setUp() throws Exception { + Security.setProperty(SECURITY_PROPERTY, TEST_ALGORITHMS); + } + + @Test + public void testDefault() throws Exception { + assertTrue(testDefaultCase(CIPHER_SUITES_IDS)); + } + + @Test + public void testAddDisabled() throws Exception { + assertTrue(testEngAddDisabled(CIPHER_SUITES, CIPHER_SUITES_IDS)); + } + + @Test + public void testOnlyDisabled() throws Exception { + assertTrue(testEngOnlyDisabled(CIPHER_SUITES)); + } +} From d6a61def494d3e9de5dc92f7f8c87ac2864b743d Mon Sep 17 00:00:00 2001 From: Andreas Chmielewski Date: Fri, 3 Jul 2026 10:05:40 +0200 Subject: [PATCH 2/4] trigger tests From 91bc1e74d44568856dd9c4e9cc81e2b902050591 Mon Sep 17 00:00:00 2001 From: Andreas Chmielewski Date: Mon, 20 Jul 2026 11:58:02 +0000 Subject: [PATCH 3/4] 8387874: Timeout when running test BulkCipherDisabledAlgorithms Reviewed-by: abarashev, mullan --- .../BulkCipherDisabledAlgorithms.java | 161 ++++++++---------- .../BulkCipherDecomposition.java | 109 ++++++++++++ 2 files changed, 179 insertions(+), 91 deletions(-) create mode 100644 test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java diff --git a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java index 11f3efb1518..8cf14a38af1 100644 --- a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java +++ b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java @@ -24,56 +24,56 @@ /* * @test * @bug 8387124 - * @summary Test TLS cipher suite disabling via jdk.tls.disabledAlgorithms, - * including matching on bulk cipher components, covering both - * visibility and handshake behavior. + * @summary Verify that disabling bulk cipher algorithms in + * jdk.tls.disabledAlgorithms disables associated TLS cipher suites. * @library /test/lib * /javax/net/ssl/TLSCommon * /javax/net/ssl/templates - * @run main/othervm BulkCipherDisabledAlgorithms visibility - * @run main/othervm BulkCipherDisabledAlgorithms handshake */ +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; -import javax.net.ssl.*; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; import jdk.test.lib.process.Proc; -import java.security.NoSuchAlgorithmException; -import java.security.Security; - +/* + * Each test case is executed in a separate JVM because + * jdk.tls.disabledAlgorithms is evaluated during JSSE initialization and + * cannot be reliably reconfigured within the same VM. + */ public class BulkCipherDisabledAlgorithms { public static void main(String[] args) throws Exception { if (args.length == 0) { - throw new RuntimeException("Missing mode argument"); - } - - String mode = args[0]; - boolean isVisibilityTest = "visibility".equals(mode); - boolean isHandshakeTest = "handshake".equals(mode); + // Sanity check: all enabled cipher suites should work before + // applying any jdk.tls.disabledAlgorithms restrictions. + testAllCipherSuitesEnabled(); - if (args.length == 1) { - List tests = buildTests(isVisibilityTest); + // Verify that disabling a bulk cipher algorithm disables cipher + // suites that use that algorithm. + Map> cipherSuitesByBulkCipher = groupCipherSuitesByBulkCipher(); - for (String[] test : tests) { - String suite = test[0]; - String disabled = test[1]; - String expected = test[2]; - - System.out.println("================================================="); - System.out.println("Testing: " + mode + - ", suite=" + suite + - ", disabled=" + disabled + - ", expected=" + expected); + for (Map.Entry> entry : cipherSuitesByBulkCipher.entrySet()) { + String disabledBulkCipher = entry.getKey(); + List disabledCipherSuites = entry.getValue(); + // Verify that all cipher suites associated with the disabled + // bulk cipher become unavailable. Proc p = Proc.create( BulkCipherDisabledAlgorithms.class.getName()) - .args(mode, suite, expected) - .secprop("jdk.tls.disabledAlgorithms", disabled) + .args(disabledBulkCipher, + String.join(",", disabledCipherSuites)) + .secprop("jdk.tls.disabledAlgorithms", disabledBulkCipher) .inheritIO(); p.start().waitFor(0); @@ -83,71 +83,54 @@ public static void main(String[] args) throws Exception { return; } - String suite = args[1]; - String expected = args[2]; - boolean expectedDisabled = "disabled".equals(expected); + String disabledBulkCipher = args[0]; + List disabledCipherSuites = Arrays.asList(args[1].split(",")); + + for (String disabledCipherSuite : disabledCipherSuites) { + System.out.println("================================================="); + System.out.println("Testing: suite=" + disabledCipherSuite + + ", disabled bulk cipher=" + disabledBulkCipher); - if (isVisibilityTest) { - testCipherSuiteVisibility(suite, expectedDisabled); + testCipherSuiteDisabled(disabledCipherSuite); + testHandshake(disabledCipherSuite, true); } + } + + private static void testAllCipherSuitesEnabled() throws Exception { + CipherSuite[] suites = getCipherSuites(); - if (isHandshakeTest) { - testHandshake(suite, expectedDisabled); + for (CipherSuite suite : suites) { + testHandshake(suite.name(), false); } } - // Returns cipher suites for testing. - // - true: use all supported suites (independent of disabledAlgorithms) - // - false: use default enabled suites (candidates for handshake) - private static CipherSuite[] getCipherSuites(boolean useSupportedSuites) - throws NoSuchAlgorithmException { + private static CipherSuite[] getCipherSuites() throws NoSuchAlgorithmException { SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - String[] suites = useSupportedSuites - ? engine.getSupportedCipherSuites() - : engine.getEnabledCipherSuites(); - + String[] suites = engine.getEnabledCipherSuites(); return Arrays.stream(suites) .map(CipherSuite::cipherSuite) .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) .toArray(CipherSuite[]::new); } - private static List buildTests(boolean useSupportedSuites) - throws NoSuchAlgorithmException { - if (useSupportedSuites) { - // disabledAlgorithms limits supported suites; clear to list all - Security.setProperty("jdk.tls.disabledAlgorithms", ""); - } - - List tests = new ArrayList<>(); - CipherSuite[] suites = getCipherSuites(useSupportedSuites); + private static Map> groupCipherSuitesByBulkCipher() throws NoSuchAlgorithmException { + Map> cipherSuitesByBulkCipher = new LinkedHashMap<>(); + CipherSuite[] suites = getCipherSuites(); for (CipherSuite suite : suites) { String suiteName = suite.name(); - String bulk = extractBulkCipher(suiteName); - - tests.add(new String[] { suiteName, suiteName, "disabled" }); - tests.add(new String[] { suiteName, bulk, "disabled" }); - - for (CipherSuite other : suites) { - // Negative test case: disable a different bulk cipher than the one - // used by the current suite. This ensures that the suite remains - // enabled and a successful TLS handshake can still be negotiated. - if (other == suite) { - continue; - } - - String otherBulk = extractBulkCipher(other.name()); - - if (!bulk.equals(otherBulk) - && !suiteName.contains(otherBulk)) { - tests.add(new String[] { suiteName, otherBulk, "enabled" }); - break; - } + String bulkCipher = extractBulkCipher(suiteName); + List suitesForBulk = cipherSuitesByBulkCipher.get(bulkCipher); + + if (suitesForBulk == null) { + suitesForBulk = new ArrayList<>(); + cipherSuitesByBulkCipher.put(bulkCipher, suitesForBulk); } + + suitesForBulk.add(suiteName); } - return tests; + return cipherSuitesByBulkCipher; } /** @@ -168,51 +151,47 @@ private static String extractBulkCipher(String suite) { } } - private static void testCipherSuiteVisibility(String suite, boolean expectedDisabled) - throws NoSuchAlgorithmException { - boolean visible = Arrays.asList(getCipherSuites(true)) + private static void testCipherSuiteDisabled(String suite) throws NoSuchAlgorithmException { + boolean visible = Arrays.asList(getCipherSuites()) .contains(CipherSuite.cipherSuite(suite)); - if (!expectedDisabled && !visible) { - throw new RuntimeException( - "Cipher suite '" + suite + "' not visible but expected to be enabled"); - } else if (expectedDisabled && visible) { + if (visible) { throw new RuntimeException( "Cipher suite '" + suite + "' visible but expected to be disabled"); } } - private static void testHandshake(String suite, boolean expectedDisabled) throws Exception { + private static void testHandshake(String cipherSuite, boolean expectedDisabled) throws Exception { try { - new TLSHandshakeTest(suite).run(); + new TLSHandshakeTest(cipherSuite).run(); if (expectedDisabled) { throw new RuntimeException( - "Handshake succeeded but should fail: " + suite); + "Handshake succeeded but should fail: " + cipherSuite); } } catch (SSLHandshakeException e) { if (!expectedDisabled) { throw new RuntimeException( - "Handshake failed unexpectedly: " + suite, e); + "Handshake failed unexpectedly: " + cipherSuite, e); } } } private static class TLSHandshakeTest extends SSLSocketTemplate { - private final String suite; + private final String cipherSuite; - TLSHandshakeTest(String suite) { - this.suite = suite; + TLSHandshakeTest(String cipherSuite) { + this.cipherSuite = cipherSuite; } @Override protected void configureClientSocket(SSLSocket socket) { - socket.setEnabledCipherSuites(new String[] { suite }); + socket.setEnabledCipherSuites(new String[] { cipherSuite }); } @Override protected void configureServerSocket(SSLServerSocket socket) { - socket.setEnabledCipherSuites(new String[] { suite }); + socket.setEnabledCipherSuites(new String[] { cipherSuite }); } } } diff --git a/test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java b/test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java new file mode 100644 index 00000000000..cb0bb9ecc3b --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2026, IBM Corporation. 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 8387124 + * @summary Verify SSLAlgorithmDecomposer bulk cipher decomposition + * @library /javax/net/ssl/TLSCommon + * @run main/othervm + * --add-opens java.base/sun.security.ssl=ALL-UNNAMED + * BulkCipherDecomposition + */ + +import java.lang.reflect.Method; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.Arrays; +import java.util.Set; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + +public class BulkCipherDecomposition { + + private static void testDecomposition(Object instance, Method decomposeMethod, String suite) + throws Exception { + @SuppressWarnings("unchecked") + Set result = (Set) decomposeMethod.invoke(instance, suite); + + String expectedBulk = extractBulkCipher(suite); + + System.out.println("================================================="); + System.out.println("Testing suite : " + suite); + System.out.println("Expected bulk : " + expectedBulk); + System.out.println("Decomposition : " + result); + + if (!result.contains(expectedBulk)) { + throw new RuntimeException( + "Missing bulk cipher decomposition\n" + + "Suite: " + suite + "\n" + + "Expected: " + expectedBulk + "\n" + + "Actual: " + result); + } + } + + /** + * Separator used in TLS cipher suite names to mark the start of + * the bulk cipher component (e.g. TLS_RSA_WITH_AES_128_CBC_SHA). + */ + private static final String WITH = "_WITH_"; + + private static String extractBulkCipher(String suite) { + if (suite.contains(WITH)) { + String after = suite.substring(suite.indexOf(WITH) + WITH.length()); + int last = after.lastIndexOf('_'); + return after.substring(0, last); + } else { + int first = suite.indexOf('_'); + int last = suite.lastIndexOf('_'); + return suite.substring(first + 1, last); + } + } + + private static String[] getCipherSuites() throws NoSuchAlgorithmException { + SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + return Arrays.stream(engine.getSupportedCipherSuites()) + .map(CipherSuite::cipherSuite) + .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .map(CipherSuite::name) + .toArray(String[]::new); + } + + public static void main(String[] args) throws Exception { + // disabledAlgorithms limits supported suites; clear to list all + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + Class c = Class.forName("sun.security.ssl.SSLAlgorithmDecomposer"); + var ctor = c.getDeclaredConstructor(); + ctor.setAccessible(true); + Object instance = ctor.newInstance(); + Method decomposeMethod = c.getDeclaredMethod("decompose", String.class); + decomposeMethod.setAccessible(true); + + for (String suite : getCipherSuites()) { + testDecomposition(instance, decomposeMethod, suite); + } + + System.out.println("PASS"); + } +} From 1665db9159852876391c115e36c2b4a43c55444d Mon Sep 17 00:00:00 2001 From: Andreas Chmielewski Date: Fri, 24 Jul 2026 08:09:19 +0200 Subject: [PATCH 4/4] Revert "8387874: Timeout when running test BulkCipherDisabledAlgorithms" This reverts commit 91bc1e74d44568856dd9c4e9cc81e2b902050591. --- .../BulkCipherDisabledAlgorithms.java | 161 ++++++++++-------- .../BulkCipherDecomposition.java | 109 ------------ 2 files changed, 91 insertions(+), 179 deletions(-) delete mode 100644 test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java diff --git a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java index 8cf14a38af1..11f3efb1518 100644 --- a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java +++ b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java @@ -24,56 +24,56 @@ /* * @test * @bug 8387124 - * @summary Verify that disabling bulk cipher algorithms in - * jdk.tls.disabledAlgorithms disables associated TLS cipher suites. + * @summary Test TLS cipher suite disabling via jdk.tls.disabledAlgorithms, + * including matching on bulk cipher components, covering both + * visibility and handshake behavior. * @library /test/lib * /javax/net/ssl/TLSCommon * /javax/net/ssl/templates + * @run main/othervm BulkCipherDisabledAlgorithms visibility + * @run main/othervm BulkCipherDisabledAlgorithms handshake */ -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLSocket; +import javax.net.ssl.*; import jdk.test.lib.process.Proc; -/* - * Each test case is executed in a separate JVM because - * jdk.tls.disabledAlgorithms is evaluated during JSSE initialization and - * cannot be reliably reconfigured within the same VM. - */ +import java.security.NoSuchAlgorithmException; +import java.security.Security; + public class BulkCipherDisabledAlgorithms { public static void main(String[] args) throws Exception { if (args.length == 0) { - // Sanity check: all enabled cipher suites should work before - // applying any jdk.tls.disabledAlgorithms restrictions. - testAllCipherSuitesEnabled(); + throw new RuntimeException("Missing mode argument"); + } + + String mode = args[0]; + boolean isVisibilityTest = "visibility".equals(mode); + boolean isHandshakeTest = "handshake".equals(mode); - // Verify that disabling a bulk cipher algorithm disables cipher - // suites that use that algorithm. - Map> cipherSuitesByBulkCipher = groupCipherSuitesByBulkCipher(); + if (args.length == 1) { + List tests = buildTests(isVisibilityTest); - for (Map.Entry> entry : cipherSuitesByBulkCipher.entrySet()) { - String disabledBulkCipher = entry.getKey(); - List disabledCipherSuites = entry.getValue(); + for (String[] test : tests) { + String suite = test[0]; + String disabled = test[1]; + String expected = test[2]; + + System.out.println("================================================="); + System.out.println("Testing: " + mode + + ", suite=" + suite + + ", disabled=" + disabled + + ", expected=" + expected); - // Verify that all cipher suites associated with the disabled - // bulk cipher become unavailable. Proc p = Proc.create( BulkCipherDisabledAlgorithms.class.getName()) - .args(disabledBulkCipher, - String.join(",", disabledCipherSuites)) - .secprop("jdk.tls.disabledAlgorithms", disabledBulkCipher) + .args(mode, suite, expected) + .secprop("jdk.tls.disabledAlgorithms", disabled) .inheritIO(); p.start().waitFor(0); @@ -83,54 +83,71 @@ public static void main(String[] args) throws Exception { return; } - String disabledBulkCipher = args[0]; - List disabledCipherSuites = Arrays.asList(args[1].split(",")); - - for (String disabledCipherSuite : disabledCipherSuites) { - System.out.println("================================================="); - System.out.println("Testing: suite=" + disabledCipherSuite + - ", disabled bulk cipher=" + disabledBulkCipher); + String suite = args[1]; + String expected = args[2]; + boolean expectedDisabled = "disabled".equals(expected); - testCipherSuiteDisabled(disabledCipherSuite); - testHandshake(disabledCipherSuite, true); + if (isVisibilityTest) { + testCipherSuiteVisibility(suite, expectedDisabled); } - } - - private static void testAllCipherSuitesEnabled() throws Exception { - CipherSuite[] suites = getCipherSuites(); - for (CipherSuite suite : suites) { - testHandshake(suite.name(), false); + if (isHandshakeTest) { + testHandshake(suite, expectedDisabled); } } - private static CipherSuite[] getCipherSuites() throws NoSuchAlgorithmException { + // Returns cipher suites for testing. + // - true: use all supported suites (independent of disabledAlgorithms) + // - false: use default enabled suites (candidates for handshake) + private static CipherSuite[] getCipherSuites(boolean useSupportedSuites) + throws NoSuchAlgorithmException { SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - String[] suites = engine.getEnabledCipherSuites(); + String[] suites = useSupportedSuites + ? engine.getSupportedCipherSuites() + : engine.getEnabledCipherSuites(); + return Arrays.stream(suites) .map(CipherSuite::cipherSuite) .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) .toArray(CipherSuite[]::new); } - private static Map> groupCipherSuitesByBulkCipher() throws NoSuchAlgorithmException { - Map> cipherSuitesByBulkCipher = new LinkedHashMap<>(); - CipherSuite[] suites = getCipherSuites(); + private static List buildTests(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + if (useSupportedSuites) { + // disabledAlgorithms limits supported suites; clear to list all + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + List tests = new ArrayList<>(); + CipherSuite[] suites = getCipherSuites(useSupportedSuites); for (CipherSuite suite : suites) { String suiteName = suite.name(); - String bulkCipher = extractBulkCipher(suiteName); - List suitesForBulk = cipherSuitesByBulkCipher.get(bulkCipher); - - if (suitesForBulk == null) { - suitesForBulk = new ArrayList<>(); - cipherSuitesByBulkCipher.put(bulkCipher, suitesForBulk); + String bulk = extractBulkCipher(suiteName); + + tests.add(new String[] { suiteName, suiteName, "disabled" }); + tests.add(new String[] { suiteName, bulk, "disabled" }); + + for (CipherSuite other : suites) { + // Negative test case: disable a different bulk cipher than the one + // used by the current suite. This ensures that the suite remains + // enabled and a successful TLS handshake can still be negotiated. + if (other == suite) { + continue; + } + + String otherBulk = extractBulkCipher(other.name()); + + if (!bulk.equals(otherBulk) + && !suiteName.contains(otherBulk)) { + tests.add(new String[] { suiteName, otherBulk, "enabled" }); + break; + } } - - suitesForBulk.add(suiteName); } - return cipherSuitesByBulkCipher; + return tests; } /** @@ -151,47 +168,51 @@ private static String extractBulkCipher(String suite) { } } - private static void testCipherSuiteDisabled(String suite) throws NoSuchAlgorithmException { - boolean visible = Arrays.asList(getCipherSuites()) + private static void testCipherSuiteVisibility(String suite, boolean expectedDisabled) + throws NoSuchAlgorithmException { + boolean visible = Arrays.asList(getCipherSuites(true)) .contains(CipherSuite.cipherSuite(suite)); - if (visible) { + if (!expectedDisabled && !visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' not visible but expected to be enabled"); + } else if (expectedDisabled && visible) { throw new RuntimeException( "Cipher suite '" + suite + "' visible but expected to be disabled"); } } - private static void testHandshake(String cipherSuite, boolean expectedDisabled) throws Exception { + private static void testHandshake(String suite, boolean expectedDisabled) throws Exception { try { - new TLSHandshakeTest(cipherSuite).run(); + new TLSHandshakeTest(suite).run(); if (expectedDisabled) { throw new RuntimeException( - "Handshake succeeded but should fail: " + cipherSuite); + "Handshake succeeded but should fail: " + suite); } } catch (SSLHandshakeException e) { if (!expectedDisabled) { throw new RuntimeException( - "Handshake failed unexpectedly: " + cipherSuite, e); + "Handshake failed unexpectedly: " + suite, e); } } } private static class TLSHandshakeTest extends SSLSocketTemplate { - private final String cipherSuite; + private final String suite; - TLSHandshakeTest(String cipherSuite) { - this.cipherSuite = cipherSuite; + TLSHandshakeTest(String suite) { + this.suite = suite; } @Override protected void configureClientSocket(SSLSocket socket) { - socket.setEnabledCipherSuites(new String[] { cipherSuite }); + socket.setEnabledCipherSuites(new String[] { suite }); } @Override protected void configureServerSocket(SSLServerSocket socket) { - socket.setEnabledCipherSuites(new String[] { cipherSuite }); + socket.setEnabledCipherSuites(new String[] { suite }); } } } diff --git a/test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java b/test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java deleted file mode 100644 index cb0bb9ecc3b..00000000000 --- a/test/jdk/sun/security/ssl/SSLAlgorithmDecomposer/BulkCipherDecomposition.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2026, IBM Corporation. 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 8387124 - * @summary Verify SSLAlgorithmDecomposer bulk cipher decomposition - * @library /javax/net/ssl/TLSCommon - * @run main/othervm - * --add-opens java.base/sun.security.ssl=ALL-UNNAMED - * BulkCipherDecomposition - */ - -import java.lang.reflect.Method; -import java.security.NoSuchAlgorithmException; -import java.security.Security; -import java.util.Arrays; -import java.util.Set; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; - -public class BulkCipherDecomposition { - - private static void testDecomposition(Object instance, Method decomposeMethod, String suite) - throws Exception { - @SuppressWarnings("unchecked") - Set result = (Set) decomposeMethod.invoke(instance, suite); - - String expectedBulk = extractBulkCipher(suite); - - System.out.println("================================================="); - System.out.println("Testing suite : " + suite); - System.out.println("Expected bulk : " + expectedBulk); - System.out.println("Decomposition : " + result); - - if (!result.contains(expectedBulk)) { - throw new RuntimeException( - "Missing bulk cipher decomposition\n" + - "Suite: " + suite + "\n" + - "Expected: " + expectedBulk + "\n" + - "Actual: " + result); - } - } - - /** - * Separator used in TLS cipher suite names to mark the start of - * the bulk cipher component (e.g. TLS_RSA_WITH_AES_128_CBC_SHA). - */ - private static final String WITH = "_WITH_"; - - private static String extractBulkCipher(String suite) { - if (suite.contains(WITH)) { - String after = suite.substring(suite.indexOf(WITH) + WITH.length()); - int last = after.lastIndexOf('_'); - return after.substring(0, last); - } else { - int first = suite.indexOf('_'); - int last = suite.lastIndexOf('_'); - return suite.substring(first + 1, last); - } - } - - private static String[] getCipherSuites() throws NoSuchAlgorithmException { - SSLEngine engine = SSLContext.getDefault().createSSLEngine(); - return Arrays.stream(engine.getSupportedCipherSuites()) - .map(CipherSuite::cipherSuite) - .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) - .map(CipherSuite::name) - .toArray(String[]::new); - } - - public static void main(String[] args) throws Exception { - // disabledAlgorithms limits supported suites; clear to list all - Security.setProperty("jdk.tls.disabledAlgorithms", ""); - - Class c = Class.forName("sun.security.ssl.SSLAlgorithmDecomposer"); - var ctor = c.getDeclaredConstructor(); - ctor.setAccessible(true); - Object instance = ctor.newInstance(); - Method decomposeMethod = c.getDeclaredMethod("decompose", String.class); - decomposeMethod.setAccessible(true); - - for (String suite : getCipherSuites()) { - testDecomposition(instance, decomposeMethod, suite); - } - - System.out.println("PASS"); - } -}