From 5daf0666e9c0fc239d1ba8b63ecb494e367b06f2 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier <goetz@openjdk.org> Date: Fri, 25 Oct 2024 07:37:34 +0000 Subject: [PATCH] 8301379: Verify TLS_ECDH_* cipher suites cannot be negotiated Reviewed-by: lucy Backport-of: 9f64a64376c677dbe1358807329b42737ac78ad9 --- .../ssl/ciphersuites/DisabledAlgorithms.java | 216 ++++++++---------- .../TLSWontNegotiateDisabledCipherAlgos.java | 146 ++++++++++++ 2 files changed, 245 insertions(+), 117 deletions(-) create mode 100644 test/jdk/javax/net/ssl/ciphersuites/TLSWontNegotiateDisabledCipherAlgos.java diff --git a/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java index 855e34b57f0..1dd8be84785 100644 --- a/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java +++ b/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -25,6 +25,7 @@ * @test * @bug 8076221 8211883 8279164 * @summary Check if weak cipher suites are disabled + * @library /javax/net/ssl/templates * @modules jdk.crypto.ec * @run main/othervm DisabledAlgorithms default * @run main/othervm DisabledAlgorithms empty @@ -35,7 +36,6 @@ import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.security.NoSuchAlgorithmException; import java.security.Security; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; @@ -45,68 +45,72 @@ import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; +/* + * This test verifies that setEnabledProtocols() does not override the + * jdk.tls.disabledAlgorithms property. Both the client and server throw + * an exception when creating a handshake context. + * + * In the TLSWontNegotiateDisabledCipherAlgoos test, one side of the connection + * disables the cipher suites and the other side enables them and verifies + * that the handshake cannot complete successfully. + */ public class DisabledAlgorithms { - private static final String pathToStores = "../etc"; - private static final String keyStoreFile = "keystore"; - private static final String trustStoreFile = "truststore"; - private static final String passwd = "passphrase"; - - private static final String keyFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + keyStoreFile; - - private static final String trustFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + trustStoreFile; + public static final SSLContextTemplate.Cert[] CERTIFICATES = { + SSLContextTemplate.Cert.EE_DSA_SHA1_1024, + SSLContextTemplate.Cert.EE_DSA_SHA224_1024, + SSLContextTemplate.Cert.EE_DSA_SHA256_1024, + SSLContextTemplate.Cert.CA_ECDSA_SECP256R1, + SSLContextTemplate.Cert.CA_RSA_2048 + }; // disabled RC4, NULL, anon, and ECDH cipher suites - private static final String[] disabled_ciphersuites - = new String[] { - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", - "TLS_ECDHE_RSA_WITH_RC4_128_SHA", - "SSL_RSA_WITH_RC4_128_SHA", - "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", - "TLS_ECDH_RSA_WITH_RC4_128_SHA", - "SSL_RSA_WITH_RC4_128_MD5", - "TLS_ECDH_anon_WITH_RC4_128_SHA", - "SSL_DH_anon_WITH_RC4_128_MD5", - "SSL_RSA_WITH_NULL_MD5", - "SSL_RSA_WITH_NULL_SHA", - "TLS_RSA_WITH_NULL_SHA256", - "TLS_ECDH_ECDSA_WITH_NULL_SHA", - "TLS_ECDHE_ECDSA_WITH_NULL_SHA", - "TLS_ECDH_RSA_WITH_NULL_SHA", - "TLS_ECDHE_RSA_WITH_NULL_SHA", - "TLS_ECDH_anon_WITH_NULL_SHA", - "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", - "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", - "SSL_DH_anon_WITH_DES_CBC_SHA", - "SSL_DH_anon_WITH_RC4_128_MD5", - "TLS_DH_anon_WITH_AES_128_CBC_SHA", - "TLS_DH_anon_WITH_AES_128_CBC_SHA256", - "TLS_DH_anon_WITH_AES_128_GCM_SHA256", - "TLS_DH_anon_WITH_AES_256_CBC_SHA", - "TLS_DH_anon_WITH_AES_256_CBC_SHA256", - "TLS_DH_anon_WITH_AES_256_GCM_SHA384", - "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", - "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", - "TLS_ECDH_anon_WITH_NULL_SHA", - "TLS_ECDH_anon_WITH_RC4_128_SHA", - "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA" + public static final String[] DISABLED_CIPHERSUITES + = new String[]{ + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + "SSL_RSA_WITH_RC4_128_SHA", + "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", + "TLS_ECDH_RSA_WITH_RC4_128_SHA", + "SSL_RSA_WITH_RC4_128_MD5", + "TLS_ECDH_anon_WITH_RC4_128_SHA", + "SSL_DH_anon_WITH_RC4_128_MD5", + "SSL_RSA_WITH_NULL_MD5", + "SSL_RSA_WITH_NULL_SHA", + "TLS_RSA_WITH_NULL_SHA256", + "TLS_ECDH_ECDSA_WITH_NULL_SHA", + "TLS_ECDHE_ECDSA_WITH_NULL_SHA", + "TLS_ECDH_RSA_WITH_NULL_SHA", + "TLS_ECDHE_RSA_WITH_NULL_SHA", + "TLS_ECDH_anon_WITH_NULL_SHA", + "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", + "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", + "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", + "SSL_DH_anon_WITH_DES_CBC_SHA", + "SSL_DH_anon_WITH_RC4_128_MD5", + "TLS_DH_anon_WITH_AES_128_CBC_SHA", + "TLS_DH_anon_WITH_AES_128_CBC_SHA256", + "TLS_DH_anon_WITH_AES_128_GCM_SHA256", + "TLS_DH_anon_WITH_AES_256_CBC_SHA", + "TLS_DH_anon_WITH_AES_256_CBC_SHA256", + "TLS_DH_anon_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", + "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", + "TLS_ECDH_anon_WITH_NULL_SHA", + "TLS_ECDH_anon_WITH_RC4_128_SHA", + "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA" }; public static void main(String[] args) throws Exception { @@ -114,11 +118,6 @@ public class DisabledAlgorithms { throw new RuntimeException("No parameters specified"); } - System.setProperty("javax.net.ssl.keyStore", keyFilename); - System.setProperty("javax.net.ssl.keyStorePassword", passwd); - System.setProperty("javax.net.ssl.trustStore", trustFilename); - System.setProperty("javax.net.ssl.trustStorePassword", passwd); - switch (args[0]) { case "default": // use default jdk.tls.disabledAlgorithms @@ -126,7 +125,7 @@ public class DisabledAlgorithms { + Security.getProperty("jdk.tls.disabledAlgorithms")); // check that disabled cipher suites can't be used by default - checkFailure(disabled_ciphersuites); + checkFailure(DISABLED_CIPHERSUITES); break; case "empty": // reset jdk.tls.disabledAlgorithms @@ -136,7 +135,7 @@ public class DisabledAlgorithms { // check that disabled cipher suites can be used if // jdk.{tls,certpath}.disabledAlgorithms is empty - checkSuccess(disabled_ciphersuites); + checkSuccess(DISABLED_CIPHERSUITES); break; default: throw new RuntimeException("Wrong parameter: " + args[0]); @@ -149,7 +148,7 @@ public class DisabledAlgorithms { * Checks if that specified cipher suites cannot be used. */ private static void checkFailure(String[] ciphersuites) throws Exception { - try (SSLServer server = SSLServer.init(ciphersuites)) { + try (SSLServer server = new SSLServer(ciphersuites)) { startNewThread(server); while (!server.isRunning()) { sleep(); @@ -157,7 +156,7 @@ public class DisabledAlgorithms { int port = server.getPort(); for (String ciphersuite : ciphersuites) { - try (SSLClient client = SSLClient.init(port, ciphersuite)) { + try (SSLClient client = new SSLClient(port, ciphersuite)) { client.connect(); throw new RuntimeException("Expected SSLHandshakeException " + "not thrown"); @@ -184,7 +183,7 @@ public class DisabledAlgorithms { * Checks if specified cipher suites can be used. */ private static void checkSuccess(String[] ciphersuites) throws Exception { - try (SSLServer server = SSLServer.init(ciphersuites)) { + try (SSLServer server = new SSLServer(ciphersuites)) { startNewThread(server); while (!server.isRunning()) { sleep(); @@ -192,7 +191,7 @@ public class DisabledAlgorithms { int port = server.getPort(); for (String ciphersuite : ciphersuites) { - try (SSLClient client = SSLClient.init(port, ciphersuite)) { + try (SSLClient client = new SSLClient(port, ciphersuite)) { client.connect(); String negotiated = client.getNegotiatedCipherSuite(); System.out.println("Negotiated cipher suite: " @@ -231,7 +230,8 @@ public class DisabledAlgorithms { } } - static class SSLServer implements Runnable, AutoCloseable { + static class SSLServer extends SSLContextTemplate implements Runnable, AutoCloseable { + private final SSLServerSocket ssocket; private volatile boolean stopped = false; @@ -239,7 +239,19 @@ public class DisabledAlgorithms { private volatile boolean sslError = false; private volatile boolean otherError = false; - private SSLServer(SSLServerSocket ssocket) { + private SSLServer(String[] ciphersuites) throws Exception { + SSLContext context = createSSLContext(null, + DisabledAlgorithms.CERTIFICATES, getServerContextParameters()); + SSLServerSocketFactory ssf = context.getServerSocketFactory(); + SSLServerSocket ssocket = (SSLServerSocket) + ssf.createServerSocket(0); + + if (ciphersuites != null) { + System.out.println("Server: enable cipher suites: " + + java.util.Arrays.toString(ciphersuites)); + ssocket.setEnabledCipherSuites(ciphersuites); + } + this.ssocket = ssocket; } @@ -273,8 +285,8 @@ public class DisabledAlgorithms { } else { System.out.println("Server: run: " + e); System.out.println("The exception above occurred " - + "because socket was closed, " - + "please ignore it"); + + "because socket was closed, " + + "please ignore it"); } } } @@ -319,29 +331,23 @@ public class DisabledAlgorithms { public void close() { stop(); } - - static SSLServer init(String[] ciphersuites) - throws IOException { - SSLServerSocketFactory ssf = (SSLServerSocketFactory) - SSLServerSocketFactory.getDefault(); - SSLServerSocket ssocket = (SSLServerSocket) - ssf.createServerSocket(0); - - if (ciphersuites != null) { - System.out.println("Server: enable cipher suites: " - + java.util.Arrays.toString(ciphersuites)); - ssocket.setEnabledCipherSuites(ciphersuites); - } - - return new SSLServer(ssocket); - } } - static class SSLClient implements AutoCloseable { + static class SSLClient extends SSLContextTemplate implements AutoCloseable { private final SSLSocket socket; - private SSLClient(SSLSocket socket) { + private SSLClient(int port, String ciphersuite) throws Exception { + SSLContext context = createSSLContext(DisabledAlgorithms.CERTIFICATES, + null, getClientContextParameters()); + SSLSocketFactory ssf = context.getSocketFactory(); + SSLSocket socket = (SSLSocket) ssf.createSocket("localhost", port); + + if (ciphersuite != null) { + System.out.println("Client: enable cipher suite: " + + ciphersuite); + socket.setEnabledCipherSuites(new String[]{ciphersuite}); + } this.socket = socket; } @@ -381,29 +387,5 @@ public class DisabledAlgorithms { } } } - - static SSLClient init(int port) - throws NoSuchAlgorithmException, IOException { - return init(port, null); - } - - static SSLClient init(int port, String ciphersuite) - throws NoSuchAlgorithmException, IOException { - SSLContext context = SSLContext.getDefault(); - SSLSocketFactory ssf = (SSLSocketFactory) - context.getSocketFactory(); - SSLSocket socket = (SSLSocket) ssf.createSocket("localhost", port); - - if (ciphersuite != null) { - System.out.println("Client: enable cipher suite: " - + ciphersuite); - socket.setEnabledCipherSuites(new String[] { ciphersuite }); - } - - return new SSLClient(socket); - } - } - - } diff --git a/test/jdk/javax/net/ssl/ciphersuites/TLSWontNegotiateDisabledCipherAlgos.java b/test/jdk/javax/net/ssl/ciphersuites/TLSWontNegotiateDisabledCipherAlgos.java new file mode 100644 index 00000000000..b120f33da94 --- /dev/null +++ b/test/jdk/javax/net/ssl/ciphersuites/TLSWontNegotiateDisabledCipherAlgos.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2023, 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 javax.net.ssl.*; +import java.io.IOException; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.security.Security; +import java.util.List; + +/* + * @test id=Server + * @bug 8301379 + * @summary Verify that Java will not negotiate disabled cipher suites when the + * other side of the connection requests them. + * + * @library /javax/net/ssl/templates + * @run main/othervm TLSWontNegotiateDisabledCipherAlgos server true + */ + +/* + * @test id=Client + * @bug 8301379 + * @summary Verify that Java will not negotiate disabled cipher suites when the + * other side of the connection requests them. + * + * @library /javax/net/ssl/templates + * @run main/othervm TLSWontNegotiateDisabledCipherAlgos server false + */ + + +public class TLSWontNegotiateDisabledCipherAlgos { + + public static void main(String [] args) throws Exception { + boolean useDisabledAlgo = Boolean.parseBoolean(args[1]); + if (useDisabledAlgo) { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + if (args[0].equals("server")) { + try (TLSServer server = new TLSServer(useDisabledAlgo)) { + List<String> command = List.of( + Path.of(System.getProperty("java.home"), "bin", "java").toString(), + "TLSWontNegotiateDisabledCipherAlgos", + "client", + Boolean.toString(!useDisabledAlgo), + Integer.toString(server.getListeningPort()) + ); + ProcessBuilder builder = new ProcessBuilder(command); + Process p = builder.inheritIO().start(); + server.run(); + p.destroy(); + } + } else if (args[0].equals("client")) { + try (TLSClient client = new TLSClient(Integer.parseInt(args[2]), useDisabledAlgo)) { + client.run(); + } + } + } + + private static class TLSClient extends SSLContextTemplate implements AutoCloseable { + private final SSLSocket socket; + + public TLSClient(int portNumber, boolean useDisableAlgo) throws Exception { + SSLContext context = createClientSSLContext(); + socket = (SSLSocket)context.getSocketFactory().createSocket("localhost", portNumber); + if (useDisableAlgo) { + socket.setEnabledCipherSuites(DisabledAlgorithms.DISABLED_CIPHERSUITES); + } + } + + public void run() throws IOException { + try { + socket.getOutputStream().write("SECRET MESSAGE".getBytes(StandardCharsets.UTF_8)); + throw new RuntimeException("SSL handshake completed successfully."); + } catch (SSLHandshakeException exc) { + if (!exc.getMessage().equals("Received fatal alert: handshake_failure")) { + throw new RuntimeException("Expected handshake_failure message. Got: " + + "\"" + exc.getMessage() + "\" message.", exc); + } + } + } + + @Override + public void close() throws Exception { + socket.close(); + } + } + + private static class TLSServer extends SSLContextTemplate implements AutoCloseable { + private SSLServerSocket serverSocket; + + public TLSServer(boolean useDisableAlgo) throws Exception { + SSLContext ctx = createServerSSLContext(); + serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(0); + if (useDisableAlgo) { + serverSocket.setEnabledCipherSuites(DisabledAlgorithms.DISABLED_CIPHERSUITES); + } + } + + @Override + public void close() throws Exception { + serverSocket.close(); + } + + public int getListeningPort() { + return serverSocket.getLocalPort(); + } + + public void run() throws IOException { + try (Socket clientSocket = serverSocket.accept()) { + try { + byte[] bytes = clientSocket.getInputStream().readAllBytes(); + throw new RuntimeException("The expected SSLHandshakeException was not thrown."); + } catch (SSLHandshakeException exc) { + if (!exc.getMessage().contains("no cipher suites in common")) { + throw exc; + } else { + System.out.println("Success: The connection could not be negotiated (as expected.)"); + } + } + } + } + } +} -- GitLab