diff --git a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java index bc463d01faf..7612242733e 100644 --- a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, 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 @@ -42,21 +42,21 @@ import java.security.spec.NamedParameterSpec; import java.security.spec.PSSParameterSpec; import java.time.DateTimeException; import java.time.Instant; -import java.time.ZonedDateTime; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.Collection; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Algorithm constraints for disabled algorithms property @@ -101,6 +101,7 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { } private final Set disabledAlgorithms; + private final List disabledPatterns; private final Constraints algorithmConstraints; private volatile SoftReference> cacheRef = new SoftReference<>(null); @@ -136,6 +137,13 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { super(decomposer); disabledAlgorithms = getAlgorithms(propertyName); + // Support patterns only for jdk.tls.disabledAlgorithms + if (PROPERTY_TLS_DISABLED_ALGS.equals(propertyName)) { + disabledPatterns = getDisabledPatterns(); + } else { + disabledPatterns = null; + } + // Check for alias for (String s : disabledAlgorithms) { Matcher matcher = INCLUDE_PATTERN.matcher(s); @@ -967,11 +975,48 @@ public class DisabledAlgorithmConstraints extends AbstractAlgorithmConstraints { if (result != null) { return result; } - result = checkAlgorithm(disabledAlgorithms, algorithm, decomposer); + // We won't check patterns if algorithm check fails. + result = checkAlgorithm(disabledAlgorithms, algorithm, decomposer) + && checkDisabledPatterns(algorithm); cache.put(algorithm, result); return result; } + private boolean checkDisabledPatterns(final String algorithm) { + return disabledPatterns == null || disabledPatterns.stream().noneMatch( + p -> p.matcher(algorithm).matches()); + } + + private List getDisabledPatterns() { + List ret = null; + List patternStrings = new ArrayList<>(4); + + for (String p : disabledAlgorithms) { + if (p.contains("*")) { + if (!p.startsWith("TLS_")) { + throw new IllegalArgumentException( + "Wildcard pattern must start with \"TLS_\""); + } + patternStrings.add(p); + } + } + + if (!patternStrings.isEmpty()) { + ret = new ArrayList<>(patternStrings.size()); + + for (String p : patternStrings) { + // Exclude patterns from algorithm code flow. + disabledAlgorithms.remove(p); + + // Ignore all regex characters but asterisk. + ret.add(Pattern.compile( + "^\\Q" + p.replace("*", "\\E.*\\Q") + "\\E$")); + } + } + + return ret; + } + /* * This constraint is used for the complete disabling of the algorithm. */ diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 1d329df0cfb..6374c1951bd 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -729,7 +729,11 @@ http.auth.digest.disabledAlgorithms = MD5, SHA-1 # This is in addition to the jdk.certpath.disabledAlgorithms property above. # # See the specification of "jdk.certpath.disabledAlgorithms" for the -# syntax of the disabled algorithm string. +# syntax of the disabled algorithm string. Additionally, TLS cipher suites +# can be disabled with this property using one or more "*" wildcard characters. +# For example, "TLS_RSA_*" disables all cipher suites that start with +# "TLS_RSA_". Only cipher suites starting with "TLS_" are allowed to have +# wildcard characters. # # Note: The algorithm restrictions do not apply to trust anchors or # self-signed certificates. @@ -739,7 +743,7 @@ http.auth.digest.disabledAlgorithms = MD5, SHA-1 # # Example: # jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048, \ -# rsa_pkcs1_sha1, secp224r1 +# rsa_pkcs1_sha1, secp224r1, TLS_RSA_* jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ MD5withRSA, DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ ECDH diff --git a/test/jdk/sun/security/ssl/CipherSuite/AbstractDisableCipherSuites.java b/test/jdk/sun/security/ssl/CipherSuite/AbstractDisableCipherSuites.java new file mode 100644 index 00000000000..3f428e458ca --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/AbstractDisableCipherSuites.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2018, 2024, 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 java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLSession; + +/** + * This is not a test. Actual tests are implemented by concrete subclasses. + * The abstract class AbstractDisableCipherSuites provides a base framework + * for testing cipher suite disablement. + */ +public abstract class AbstractDisableCipherSuites { + + private static final byte RECTYPE_HS = 0x16; + private static final byte HSMSG_CLIHELLO = 0x01; + private static final ByteBuffer CLIOUTBUF = + ByteBuffer.wrap("Client Side".getBytes()); + + /** + * Create an engine with the default set of cipher suites enabled and make + * sure none of the disabled suites are present in the client hello. + * + * @param disabledSuiteIds the {@code List} of disabled cipher suite IDs + * to be checked for. + * + * @return true if the test passed (No disabled suites), false otherwise + */ + protected boolean testDefaultCase(List disabledSuiteIds) + throws Exception { + System.err.println("\nTest: Default SSLEngine suite set"); + SSLEngine ssle = makeEngine(); + if (getDebug()) { + listCiphers("Suite set upon creation", ssle); + } + SSLEngineResult clientResult; + ByteBuffer cTOs = makeClientBuf(ssle); + clientResult = ssle.wrap(CLIOUTBUF, cTOs); + if (getDebug()) { + dumpResult("ClientHello: ", clientResult); + } + cTOs.flip(); + boolean foundSuite = areSuitesPresentCH(cTOs, disabledSuiteIds); + if (foundSuite) { + System.err.println("FAIL: Found disabled suites!"); + return false; + } else { + System.err.println("PASS: No disabled suites found."); + return true; + } + } + + /** + * Create an engine and set only disabled cipher suites. + * The engine should not create the client hello message since the only + * available suites to assert in the client hello are disabled ones. + * + * @param disabledSuiteNames an array of cipher suite names that + * should be disabled cipher suites. + * + * @return true if the engine throws SSLHandshakeException during client + * hello creation, false otherwise. + */ + protected boolean testEngOnlyDisabled(String[] disabledSuiteNames) + throws Exception { + System.err.println( + "\nTest: SSLEngine configured with only disabled suites"); + try { + SSLEngine ssle = makeEngine(); + ssle.setEnabledCipherSuites(disabledSuiteNames); + if (getDebug()) { + listCiphers("Suite set upon creation", ssle); + } + SSLEngineResult clientResult; + ByteBuffer cTOs = makeClientBuf(ssle); + clientResult = ssle.wrap(CLIOUTBUF, cTOs); + if (getDebug()) { + dumpResult("ClientHello: ", clientResult); + } + cTOs.flip(); + } catch (SSLHandshakeException shse) { + System.err.println("PASS: Caught expected exception: " + shse); + return true; + } + System.err.println("FAIL: Expected SSLHandshakeException not thrown"); + return false; + } + + /** + * Create an engine and add some disabled suites to the default + * set of cipher suites. Make sure none of the disabled suites show up + * in the client hello even though they were explicitly added. + * + * @param disabledNames an array of cipher suite names that + * should be disabled cipher suites. + * @param disabledIds the {@code List} of disabled cipher suite IDs + * to be checked for. + * + * @return true if the test passed (No disabled suites), false otherwise + */ + protected boolean testEngAddDisabled(String[] disabledNames, + List disabledIds) throws Exception { + System.err.println("\nTest: SSLEngine with disabled suites added"); + SSLEngine ssle = makeEngine(); + + // Add disabled suites to the existing engine's set of enabled suites + String[] initialSuites = ssle.getEnabledCipherSuites(); + String[] plusDisSuites = Arrays.copyOf(initialSuites, + initialSuites.length + disabledNames.length); + System.arraycopy(disabledNames, 0, plusDisSuites, + initialSuites.length, disabledNames.length); + ssle.setEnabledCipherSuites(plusDisSuites); + + if (getDebug()) { + listCiphers("Suite set upon creation", ssle); + } + SSLEngineResult clientResult; + ByteBuffer cTOs = makeClientBuf(ssle); + clientResult = ssle.wrap(CLIOUTBUF, cTOs); + if (getDebug()) { + dumpResult("ClientHello: ", clientResult); + } + cTOs.flip(); + boolean foundDisabled = areSuitesPresentCH(cTOs, disabledIds); + if (foundDisabled) { + System.err.println("FAIL: Found disabled suites!"); + return false; + } else { + System.err.println("PASS: No disabled suites found."); + return true; + } + } + + protected String getProtocol() { + return "TLSv1.2"; + } + + private SSLEngine makeEngine() throws GeneralSecurityException { + SSLContext ctx = SSLContext.getInstance(getProtocol()); + ctx.init(null, null, null); + return ctx.createSSLEngine(); + } + + private static ByteBuffer makeClientBuf(SSLEngine ssle) { + ssle.setUseClientMode(true); + ssle.setNeedClientAuth(false); + SSLSession sess = ssle.getSession(); + ByteBuffer cTOs = ByteBuffer.allocateDirect(sess.getPacketBufferSize()); + return cTOs; + } + + private static void listCiphers(String prefix, SSLEngine ssle) { + System.err.println(prefix + "\n---------------"); + String[] suites = ssle.getEnabledCipherSuites(); + for (String suite : suites) { + System.err.println(suite); + } + System.err.println("---------------"); + } + + /** + * Walk a TLS 1.2 or earlier ClientHello looking for any of the suites + * in the suiteIdList. + * + * @param clientHello a ByteBuffer containing the ClientHello message as + * a complete TLS record. The position of the buffer should be + * at the first byte of the TLS record header. + * @param suiteIdList a List of integer values corresponding to + * TLS cipher suite identifiers. + * + * @return true if at least one of the suites in {@code suiteIdList} + * is found in the ClientHello's cipher suite list + * + * @throws IOException if the data in the {@code clientHello} + * buffer is not a TLS handshake message or is not a client hello. + */ + private boolean areSuitesPresentCH(ByteBuffer clientHello, + List suiteIdList) throws IOException { + byte val; + + // Process the TLS Record + val = clientHello.get(); + if (val != RECTYPE_HS) { + throw new IOException( + "Not a handshake record, type = " + val); + } + + // Just skip over the version and length + clientHello.position(clientHello.position() + 4); + + // Check the handshake message type + val = clientHello.get(); + if (val != HSMSG_CLIHELLO) { + throw new IOException( + "Not a ClientHello handshake message, type = " + val); + } + + // Skip over the length + clientHello.position(clientHello.position() + 3); + + // Skip over the protocol version (2) and random (32); + clientHello.position(clientHello.position() + 34); + + // Skip past the session ID (variable length <= 32) + int len = Byte.toUnsignedInt(clientHello.get()); + if (len > 32) { + throw new IOException("Session ID is too large, len = " + len); + } + clientHello.position(clientHello.position() + len); + + // Finally, we are at the cipher suites. Walk the list and place them + // into a List. + int csLen = Short.toUnsignedInt(clientHello.getShort()); + if (csLen % 2 != 0) { + throw new IOException("CipherSuite length is invalid, len = " + + csLen); + } + int csCount = csLen / 2; + List csSuiteList = new ArrayList<>(csCount); + log("Found following suite IDs in hello:"); + for (int i = 0; i < csCount; i++) { + int curSuite = Short.toUnsignedInt(clientHello.getShort()); + log(String.format("Suite ID: 0x%04x", curSuite)); + csSuiteList.add(curSuite); + } + + // Now check to see if any of the suites passed in match what is in + // the suite list. + boolean foundMatch = false; + for (Integer cs : suiteIdList) { + if (csSuiteList.contains(cs)) { + System.err.format("Found match for suite ID 0x%04x\n", cs); + foundMatch = true; + break; + } + } + + // We don't care about the rest of the ClientHello message. + // Rewind and return whether we found a match or not. + clientHello.rewind(); + return foundMatch; + } + + private static void dumpResult(String str, SSLEngineResult result) { + System.err.println("The format of the SSLEngineResult is: \n" + + "\t\"getStatus() / getHandshakeStatus()\" +\n" + + "\t\"bytesConsumed() / bytesProduced()\"\n"); + HandshakeStatus hsStatus = result.getHandshakeStatus(); + System.err.println(str + result.getStatus() + "/" + hsStatus + ", " + + result.bytesConsumed() + "/" + result.bytesProduced() + " bytes"); + if (hsStatus == HandshakeStatus.FINISHED) { + System.err.println("\t...ready for application data"); + } + } + + private void log(String str) { + if (getDebug()) { + System.err.println(str); + } + } + + protected boolean getDebug() { + return false; + } +} diff --git a/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4DesEdeCiphSuite.java b/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4DesEdeCiphSuite.java index c9861324237..6b86dacc640 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4DesEdeCiphSuite.java +++ b/test/jdk/sun/security/ssl/CipherSuite/NoDesRC4DesEdeCiphSuite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, 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 @@ -34,21 +34,11 @@ */ import java.security.Security; -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; -import java.util.List; -import java.util.ArrayList; import java.util.Arrays; +import java.util.List; -public class NoDesRC4DesEdeCiphSuite { +public class NoDesRC4DesEdeCiphSuite extends AbstractDisableCipherSuites { - private static final boolean DEBUG = false; - - private static final byte RECTYPE_HS = 0x16; - private static final byte HSMSG_CLIHELLO = 0x01; // These are some groups of Cipher Suites by names and IDs private static final List DES_CS_LIST = Arrays.asList( @@ -93,28 +83,27 @@ public class NoDesRC4DesEdeCiphSuite { "SSL_RSA_WITH_3DES_EDE_CBC_SHA" }; - private static final ByteBuffer CLIOUTBUF = - ByteBuffer.wrap("Client Side".getBytes()); public static void main(String[] args) throws Exception { boolean allGood = true; String disAlg = Security.getProperty("jdk.tls.disabledAlgorithms"); System.err.println("Disabled Algs: " + disAlg); + NoDesRC4DesEdeCiphSuite test = new NoDesRC4DesEdeCiphSuite(); // Disabled DES tests - allGood &= testDefaultCase(DES_CS_LIST); - allGood &= testEngAddDisabled(DES_CS_LIST_NAMES, DES_CS_LIST); - allGood &= testEngOnlyDisabled(DES_CS_LIST_NAMES); + allGood &= test.testDefaultCase(DES_CS_LIST); + allGood &= test.testEngAddDisabled(DES_CS_LIST_NAMES, DES_CS_LIST); + allGood &= test.testEngOnlyDisabled(DES_CS_LIST_NAMES); // Disabled RC4 tests - allGood &= testDefaultCase(RC4_CS_LIST); - allGood &= testEngAddDisabled(RC4_CS_LIST_NAMES, RC4_CS_LIST); - allGood &= testEngOnlyDisabled(RC4_CS_LIST_NAMES); + allGood &= test.testDefaultCase(RC4_CS_LIST); + allGood &= test.testEngAddDisabled(RC4_CS_LIST_NAMES, RC4_CS_LIST); + allGood &= test.testEngOnlyDisabled(RC4_CS_LIST_NAMES); // Disabled 3DES tests - allGood &= testDefaultCase(DESEDE_CS_LIST); - allGood &= testEngAddDisabled(DESEDE_CS_LIST_NAMES, DESEDE_CS_LIST); - allGood &= testEngOnlyDisabled(DESEDE_CS_LIST_NAMES); + allGood &= test.testDefaultCase(DESEDE_CS_LIST); + allGood &= test.testEngAddDisabled(DESEDE_CS_LIST_NAMES, DESEDE_CS_LIST); + allGood &= test.testEngOnlyDisabled(DESEDE_CS_LIST_NAMES); if (allGood) { System.err.println("All tests passed"); @@ -122,242 +111,4 @@ public class NoDesRC4DesEdeCiphSuite { throw new RuntimeException("One or more tests failed"); } } - - /** - * Create an engine with the default set of cipher suites enabled and make - * sure none of the disabled suites are present in the client hello. - * - * @param disabledSuiteIds the {@code List} of disabled cipher suite IDs - * to be checked for. - * - * @return true if the test passed (No disabled suites), false otherwise - */ - private static boolean testDefaultCase(List disabledSuiteIds) - throws Exception { - System.err.println("\nTest: Default SSLEngine suite set"); - SSLEngine ssle = makeEngine(); - if (DEBUG) { - listCiphers("Suite set upon creation", ssle); - } - SSLEngineResult clientResult; - ByteBuffer cTOs = makeClientBuf(ssle); - clientResult = ssle.wrap(CLIOUTBUF, cTOs); - if (DEBUG) { - dumpResult("ClientHello: ", clientResult); - } - cTOs.flip(); - boolean foundSuite = areSuitesPresentCH(cTOs, disabledSuiteIds); - if (foundSuite) { - System.err.println("FAIL: Found disabled suites!"); - return false; - } else { - System.err.println("PASS: No disabled suites found."); - return true; - } - } - - /** - * Create an engine and set only disabled cipher suites. - * The engine should not create the client hello message since the only - * available suites to assert in the client hello are disabled ones. - * - * @param disabledSuiteNames an array of cipher suite names that - * should be disabled cipher suites. - * - * @return true if the engine throws SSLHandshakeException during client - * hello creation, false otherwise. - */ - private static boolean testEngOnlyDisabled(String[] disabledSuiteNames) - throws Exception { - System.err.println( - "\nTest: SSLEngine configured with only disabled suites"); - try { - SSLEngine ssle = makeEngine(); - ssle.setEnabledCipherSuites(disabledSuiteNames); - if (DEBUG) { - listCiphers("Suite set upon creation", ssle); - } - SSLEngineResult clientResult; - ByteBuffer cTOs = makeClientBuf(ssle); - clientResult = ssle.wrap(CLIOUTBUF, cTOs); - if (DEBUG) { - dumpResult("ClientHello: ", clientResult); - } - cTOs.flip(); - } catch (SSLHandshakeException shse) { - System.err.println("PASS: Caught expected exception: " + shse); - return true; - } - System.err.println("FAIL: Expected SSLHandshakeException not thrown"); - return false; - } - - /** - * Create an engine and add some disabled suites to the default - * set of cipher suites. Make sure none of the disabled suites show up - * in the client hello even though they were explicitly added. - * - * @param disabledSuiteNames an array of cipher suite names that - * should be disabled cipher suites. - * @param disabledIds the {@code List} of disabled cipher suite IDs - * to be checked for. - * - * @return true if the test passed (No disabled suites), false otherwise - */ - private static boolean testEngAddDisabled(String[] disabledNames, - List disabledIds) throws Exception { - System.err.println("\nTest: SSLEngine with disabled suites added"); - SSLEngine ssle = makeEngine(); - - // Add disabled suites to the existing engine's set of enabled suites - String[] initialSuites = ssle.getEnabledCipherSuites(); - String[] plusDisSuites = Arrays.copyOf(initialSuites, - initialSuites.length + disabledNames.length); - System.arraycopy(disabledNames, 0, plusDisSuites, - initialSuites.length, disabledNames.length); - ssle.setEnabledCipherSuites(plusDisSuites); - - if (DEBUG) { - listCiphers("Suite set upon creation", ssle); - } - SSLEngineResult clientResult; - ByteBuffer cTOs = makeClientBuf(ssle); - clientResult = ssle.wrap(CLIOUTBUF, cTOs); - if (DEBUG) { - dumpResult("ClientHello: ", clientResult); - } - cTOs.flip(); - boolean foundDisabled = areSuitesPresentCH(cTOs, disabledIds); - if (foundDisabled) { - System.err.println("FAIL: Found disabled suites!"); - return false; - } else { - System.err.println("PASS: No disabled suites found."); - return true; - } - } - - private static SSLEngine makeEngine() throws GeneralSecurityException { - SSLContext ctx = SSLContext.getInstance("TLSv1.2"); - ctx.init(null, null, null); - return ctx.createSSLEngine(); - } - - private static ByteBuffer makeClientBuf(SSLEngine ssle) { - ssle.setUseClientMode(true); - ssle.setNeedClientAuth(false); - SSLSession sess = ssle.getSession(); - ByteBuffer cTOs = ByteBuffer.allocateDirect(sess.getPacketBufferSize()); - return cTOs; - } - - private static void listCiphers(String prefix, SSLEngine ssle) { - System.err.println(prefix + "\n---------------"); - String[] suites = ssle.getEnabledCipherSuites(); - for (String suite : suites) { - System.err.println(suite); - } - System.err.println("---------------"); - } - - /** - * Walk a TLS 1.2 or earlier ClientHello looking for any of the suites - * in the suiteIdList. - * - * @param clientHello a ByteBuffer containing the ClientHello message as - * a complete TLS record. The position of the buffer should be - * at the first byte of the TLS record header. - * @param suiteIdList a List of integer values corresponding to - * TLS cipher suite identifiers. - * - * @return true if at least one of the suites in {@code suiteIdList} - * is found in the ClientHello's cipher suite list - * - * @throws IOException if the data in the {@code clientHello} - * buffer is not a TLS handshake message or is not a client hello. - */ - private static boolean areSuitesPresentCH(ByteBuffer clientHello, - List suiteIdList) throws IOException { - byte val; - - // Process the TLS Record - val = clientHello.get(); - if (val != RECTYPE_HS) { - throw new IOException( - "Not a handshake record, type = " + val); - } - - // Just skip over the version and length - clientHello.position(clientHello.position() + 4); - - // Check the handshake message type - val = clientHello.get(); - if (val != HSMSG_CLIHELLO) { - throw new IOException( - "Not a ClientHello handshake message, type = " + val); - } - - // Skip over the length - clientHello.position(clientHello.position() + 3); - - // Skip over the protocol version (2) and random (32); - clientHello.position(clientHello.position() + 34); - - // Skip past the session ID (variable length <= 32) - int len = Byte.toUnsignedInt(clientHello.get()); - if (len > 32) { - throw new IOException("Session ID is too large, len = " + len); - } - clientHello.position(clientHello.position() + len); - - // Finally, we are at the cipher suites. Walk the list and place them - // into a List. - int csLen = Short.toUnsignedInt(clientHello.getShort()); - if (csLen % 2 != 0) { - throw new IOException("CipherSuite length is invalid, len = " + - csLen); - } - int csCount = csLen / 2; - List csSuiteList = new ArrayList<>(csCount); - log("Found following suite IDs in hello:"); - for (int i = 0; i < csCount; i++) { - int curSuite = Short.toUnsignedInt(clientHello.getShort()); - log(String.format("Suite ID: 0x%04x", curSuite)); - csSuiteList.add(curSuite); - } - - // Now check to see if any of the suites passed in match what is in - // the suite list. - boolean foundMatch = false; - for (Integer cs : suiteIdList) { - if (csSuiteList.contains(cs)) { - System.err.format("Found match for suite ID 0x%04x\n", cs); - foundMatch = true; - break; - } - } - - // We don't care about the rest of the ClientHello message. - // Rewind and return whether we found a match or not. - clientHello.rewind(); - return foundMatch; - } - - private static void dumpResult(String str, SSLEngineResult result) { - System.err.println("The format of the SSLEngineResult is: \n" + - "\t\"getStatus() / getHandshakeStatus()\" +\n" + - "\t\"bytesConsumed() / bytesProduced()\"\n"); - HandshakeStatus hsStatus = result.getHandshakeStatus(); - System.err.println(str + result.getStatus() + "/" + hsStatus + ", " + - result.bytesConsumed() + "/" + result.bytesProduced() + " bytes"); - if (hsStatus == HandshakeStatus.FINISHED) { - System.err.println("\t...ready for application data"); - } - } - - private static void log(String str) { - if (DEBUG) { - System.err.println(str); - } - } } diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite.java new file mode 100644 index 00000000000..57919da9a5a --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8341964 + * @summary Add mechanism to disable different parts of TLS cipher suite + * @run testng/othervm TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite + */ + +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 TLSCipherSuiteWildCardMatchingDisablePartsOfCipherSuite extends + AbstractDisableCipherSuites { + + private static final String SECURITY_PROPERTY = "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = + "TLS_RSA_*," + + " TLS_ECDH*WITH_AES_256_GCM_*," + + " TLS_*_anon_WITH_AES_*_SHA," + + " TLS_.*"; // This pattern should not disable anything + private static final String[] CIPHER_SUITES = new String[] { + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_DH_anon_WITH_AES_128_CBC_SHA", + "TLS_ECDH_anon_WITH_AES_256_CBC_SHA" + }; + static final List CIPHER_SUITES_IDS = List.of( + 0x009D, + 0x009C, + 0x003D, + 0xC02E, + 0xC02C, + 0x0034, + 0xC018 + ); + + @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)); + } +} diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingIllegalArgument.java b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingIllegalArgument.java new file mode 100644 index 00000000000..d9894868337 --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLSCipherSuiteWildCardMatchingIllegalArgument.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8341964 + * @summary Add mechanism to disable different parts of TLS cipher suite + * @run testng/othervm TLSCipherSuiteWildCardMatchingIllegalArgument + */ + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.fail; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.security.Security; + +import javax.net.ssl.SSLContext; + +/** + * SSLContext loads "jdk.tls.disabledAlgorithms" system property statically + * when it's being loaded into memory, so we can't call + * Security.setProperty("jdk.tls.disabledAlgorithms") more than once per test + * class. Thus, we need a separate test class each time we need to modify + * "jdk.tls.disabledAlgorithms" config value for testing. + */ +public class TLSCipherSuiteWildCardMatchingIllegalArgument { + + private static final String SECURITY_PROPERTY = + "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = "ECDHE_*_WITH_AES_256_GCM_*"; + + @BeforeTest + void setUp() throws Exception { + Security.setProperty(SECURITY_PROPERTY, TEST_ALGORITHMS); + } + + @Test + public void testChainedBefore() throws Exception { + try { + SSLContext.getInstance("TLS"); + fail("No IllegalArgumentException was thrown"); + } catch (ExceptionInInitializerError e) { + assertEquals(IllegalArgumentException.class, + e.getCause().getClass()); + assertEquals("Wildcard pattern must start with \"TLS_\"", + e.getCause().getMessage()); + } + } +}