diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index 8706c7cfa70..4e1ccf385c7 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -123,13 +123,13 @@ enum Alert { } if (cause instanceof IOException) { - return new SSLException(reason, cause); + return new SSLException("(" + description + ") " + reason, cause); } else if ((this == UNEXPECTED_MESSAGE)) { - return new SSLProtocolException(reason, cause); + return new SSLProtocolException("(" + description + ") " + reason, cause); } else if (handshakeOnly) { - return new SSLHandshakeException(reason, cause); + return new SSLHandshakeException("(" + description + ") " + reason, cause); } else { - return new SSLException(reason, cause); + return new SSLException("(" + description + ") " + reason, cause); } } diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index b43ea3835af..cdb65bd329a 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -385,7 +385,7 @@ final class CertificateMessage { if (shc.sslConfig.clientAuthType != ClientAuthType.CLIENT_AUTH_REQUESTED) { // unexpected or require client authentication - throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, + throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Empty client certificate chain"); } else { return; @@ -1162,7 +1162,7 @@ final class CertificateMessage { shc.handshakeConsumers.remove( SSLHandshake.CERTIFICATE_VERIFY.id); if (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED) { - throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, + throw shc.conContext.fatal(Alert.CERTIFICATE_REQUIRED, "Empty client certificate chain"); } else { // optional client authentication @@ -1186,7 +1186,7 @@ final class CertificateMessage { T13CertificateMessage certificateMessage )throws IOException { if (certificateMessage.certEntries == null || certificateMessage.certEntries.isEmpty()) { - throw chc.conContext.fatal(Alert.BAD_CERTIFICATE, + throw chc.conContext.fatal(Alert.DECODE_ERROR, "Empty server certificate chain"); } diff --git a/test/jdk/javax/net/ssl/SSLSession/CertMsgCheck.java b/test/jdk/javax/net/ssl/SSLSession/CertMsgCheck.java new file mode 100644 index 00000000000..37c252bc6f0 --- /dev/null +++ b/test/jdk/javax/net/ssl/SSLSession/CertMsgCheck.java @@ -0,0 +1,59 @@ +/* + * 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 + * @library /javax/net/ssl/templates + * @bug 8311644 + * @summary Verify CertificateMessage alerts are correct to the TLS specs + * @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 CertMsgCheck handshake_failure + * @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 CertMsgCheck certificate_required + * + */ + +public class CertMsgCheck { + + public static void main(String[] args) throws Exception { + // Start server + TLSBase.Server server = new TLSBase.ServerBuilder().setClientAuth(true). + build(); + + // Initial client session + TLSBase.Client client1 = new TLSBase.Client(true, false); + if (server.getSession(client1).getSessionContext() == null) { + for (Exception e : server.getExceptionList()) { + System.out.println("Looking at " + e.getClass() + " " + + e.getMessage()); + if (e.getMessage().contains(args[0])) { + System.out.println("Found correct exception: " + args[0] + + " in " + e.getMessage()); + return; + } else { + System.out.println("No \"" + args[0] + "\" found."); + } + } + + throw new Exception("Failed to find expected alert: " + args[0]); + } + } +} diff --git a/test/jdk/javax/net/ssl/SSLSession/CheckSessionContext.java b/test/jdk/javax/net/ssl/SSLSession/CheckSessionContext.java index 98c7afb2738..183354ed9db 100644 --- a/test/jdk/javax/net/ssl/SSLSession/CheckSessionContext.java +++ b/test/jdk/javax/net/ssl/SSLSession/CheckSessionContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -36,15 +36,11 @@ */ import javax.net.ssl.SSLSession; +import java.util.HexFormat; public class CheckSessionContext { - static void toHex(byte[] id) { - for (byte b : id) { - System.out.printf("%02X ", b); - } - System.out.println(); - } + static HexFormat hex = HexFormat.of(); public static void main(String[] args) throws Exception { TLSBase.Server server = new TLSBase.Server(); @@ -52,20 +48,18 @@ public class CheckSessionContext { // Initial client session TLSBase.Client client1 = new TLSBase.Client(); if (server.getSession(client1).getSessionContext() == null) { - throw new Exception("Context was null"); + throw new Exception("Context was null. Handshake failure."); } else { System.out.println("Context was found"); } SSLSession ss = server.getSession(client1); System.out.println(ss); byte[] id = ss.getId(); - System.out.print("id = "); - toHex(id); + System.out.println("id = " + hex.formatHex(id)); System.out.println("ss.getSessionContext().getSession(id) = " + ss.getSessionContext().getSession(id)); if (ss.getSessionContext().getSession(id) != null) { id = ss.getSessionContext().getSession(id).getId(); - System.out.print("id = "); - toHex(id); + System.out.println("id = " + hex.formatHex(id)); } server.close(client1); client1.close(); diff --git a/test/jdk/javax/net/ssl/templates/TLSBase.java b/test/jdk/javax/net/ssl/templates/TLSBase.java index bcddb1147c8..414e7e106b3 100644 --- a/test/jdk/javax/net/ssl/templates/TLSBase.java +++ b/test/jdk/javax/net/ssl/templates/TLSBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -21,18 +21,15 @@ * questions. */ -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; +import javax.net.ssl.*; +import java.io.*; import java.net.InetSocketAddress; +import java.security.KeyStore; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** @@ -67,11 +64,11 @@ abstract public class TLSBase { TLSBase() { String keyFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + keyStoreFile; + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile; String trustFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + trustStoreFile; + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + trustStoreFile; System.setProperty("javax.net.ssl.keyStore", keyFilename); System.setProperty("javax.net.ssl.keyStorePassword", passwd); System.setProperty("javax.net.ssl.trustStore", trustFilename); @@ -79,30 +76,65 @@ abstract public class TLSBase { } // Base read operation - byte[] read(SSLSocket sock) { - try { - BufferedReader reader = new BufferedReader( - new InputStreamReader(sock.getInputStream())); - String s = reader.readLine(); - System.err.println("(read) " + name + ": " + s); - return s.getBytes(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; + byte[] read(SSLSocket sock) throws Exception { + BufferedReader reader = new BufferedReader( + new InputStreamReader(sock.getInputStream())); + String s = reader.readLine(); + System.err.println("(read) " + name + ": " + s); + return s.getBytes(); } // Base write operation - public void write(SSLSocket sock, byte[] data) { - try { - PrintWriter out = new PrintWriter( - new OutputStreamWriter(sock.getOutputStream())); - out.println(new String(data)); - out.flush(); - System.err.println("(write)" + name + ": " + new String(data)); - } catch (Exception e) { - e.printStackTrace(); + public void write(SSLSocket sock, byte[] data) throws Exception { + PrintWriter out = new PrintWriter( + new OutputStreamWriter(sock.getOutputStream())); + out.println(new String(data)); + out.flush(); + System.err.println("(write)" + name + ": " + new String(data)); + } + + private static KeyManager[] getKeyManager(boolean empty) throws Exception { + FileInputStream fis = null; + if (!empty) { + fis = new FileInputStream(System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile); } + // Load the keystore + char[] pwd = passwd.toCharArray(); + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(fis, pwd); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); + kmf.init(ks, pwd); + return kmf.getKeyManagers(); + } + + private static TrustManager[] getTrustManager(boolean empty) throws Exception { + FileInputStream fis = null; + if (!empty) { + fis = new FileInputStream(System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + trustStoreFile); + } + // Load the keystore + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(fis, passwd.toCharArray()); + + PKIXBuilderParameters pkixParams = + new PKIXBuilderParameters(ks, new X509CertSelector()); + + // Explicitly set revocation based on the command-line + // parameters, default false + pkixParams.setRevocationEnabled(false); + + // Register the PKIXParameters with the trust manager factory + ManagerFactoryParameters trustParams = + new CertPathTrustManagerParameters(pkixParams); + + // Create the Trust Manager Factory using the PKIX variant + // and initialize it with the parameters configured above + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(trustParams); + return tmf.getTrustManagers(); } /** @@ -117,14 +149,17 @@ abstract public class TLSBase { new ConcurrentHashMap<>(); boolean exit = false; Thread t; + List exceptionList = new ArrayList<>(); - Server() { + Server(ServerBuilder builder) { super(); name = "server"; try { - sslContext = SSLContext.getDefault(); + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(TLSBase.getKeyManager(builder.km), TLSBase.getTrustManager(builder.tm), null); fac = sslContext.getServerSocketFactory(); ssock = (SSLServerSocket) fac.createServerSocket(0); + ssock.setNeedClientAuth(builder.clientauth); serverPort = ssock.getLocalPort(); } catch (Exception e) { System.err.println(e.getMessage()); @@ -136,13 +171,14 @@ abstract public class TLSBase { try { while (true) { System.err.println("Server ready on port " + - serverPort); + serverPort); SSLSocket c = (SSLSocket)ssock.accept(); clientMap.put(c.getPort(), c); try { write(c, read(c)); } catch (Exception e) { e.printStackTrace(); + exceptionList.add(e); } } } catch (Exception ex) { @@ -153,6 +189,51 @@ abstract public class TLSBase { t.start(); } + Server() { + this(new ServerBuilder()); + } + + /** + * @param km - true for an empty key manager + * @param tm - true for an empty trust manager + */ + Server(boolean km, boolean tm) { + super(); + name = "server"; + try { + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(TLSBase.getKeyManager(km), TLSBase.getTrustManager(tm), null); + fac = sslContext.getServerSocketFactory(); + ssock = (SSLServerSocket) fac.createServerSocket(0); + ssock.setNeedClientAuth(true); + serverPort = ssock.getLocalPort(); + } catch (Exception e) { + System.err.println(e.getMessage()); + e.printStackTrace(); + } + + // Thread to allow multiple clients to connect + t = new Thread(() -> { + try { + while (true) { + System.err.println("Server ready on port " + + serverPort); + SSLSocket c = (SSLSocket)ssock.accept(); + clientMap.put(c.getPort(), c); + try { + write(c, read(c)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (Exception ex) { + System.err.println("Server Down"); + ex.printStackTrace(); + } + }); + t.start(); + } + // Exit test to quit the test. This must be called at the end of the // test or the test will never end. void done() { @@ -166,7 +247,7 @@ abstract public class TLSBase { } // Read from the client - byte[] read(Client client) { + byte[] read(Client client) throws Exception { SSLSocket s = clientMap.get(Integer.valueOf(client.getPort())); if (s == null) { System.err.println("No socket found, port " + client.getPort()); @@ -175,13 +256,13 @@ abstract public class TLSBase { } // Write to the client - void write(Client client, byte[] data) { + void write(Client client, byte[] data) throws Exception { write(clientMap.get(client.getPort()), data); } // Server writes to the client, then reads from the client. // Return true if the read & write data match, false if not. - boolean writeRead(Client client, String s) { + boolean writeRead(Client client, String s) throws Exception{ write(client, s.getBytes()); return (Arrays.compare(s.getBytes(), client.read()) == 0); } @@ -197,30 +278,61 @@ abstract public class TLSBase { SSLSocket s = clientMap.get(Integer.valueOf(c.getPort())); s.close(); } + + List getExceptionList() { + return exceptionList; + } } + static class ServerBuilder { + boolean km = false, tm = false, clientauth = false; + + ServerBuilder setKM(boolean b) { + km = b; + return this; + } + + ServerBuilder setTM(boolean b) { + tm = b; + return this; + } + + ServerBuilder setClientAuth(boolean b) { + clientauth = b; + return this; + } + + Server build() { + return new Server(this); + } + } /** * Client side will establish a connection from the constructor and wait. * It must be run after the Server constructor is called. */ static class Client extends TLSBase { SSLSocket sock; - + boolean km, tm; Client() { + this(false, false); + } + + /** + * @param km - true sets an empty key manager + * @param tm - true sets an empty trust manager + */ + Client(boolean km, boolean tm) { super(); - try { - sslContext = SSLContext.getDefault(); - } catch (Exception e) { - System.err.println(e.getMessage()); - e.printStackTrace(); - } + this.km = km; + this.tm = tm; connect(); } // Connect to server. Maybe runnable in the future public SSLSocket connect() { try { - sslContext = SSLContext.getDefault(); + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(TLSBase.getKeyManager(km), TLSBase.getTrustManager(tm), null); sock = (SSLSocket)sslContext.getSocketFactory().createSocket(); sock.connect(new InetSocketAddress("localhost", serverPort)); System.err.println("Client connected using port " + @@ -235,21 +347,21 @@ abstract public class TLSBase { } // Read from the client socket - byte[] read() { + byte[] read() throws Exception { return read(sock); } // Write to the client socket - void write(byte[] data) { + void write(byte[] data) throws Exception { write(sock, data); } - void write(String s) { + void write(String s) throws Exception { write(sock, s.getBytes()); } // Client writes to the server, then reads from the server. // Return true if the read & write data match, false if not. - boolean writeRead(Server server, String s) { + boolean writeRead(Server server, String s) throws Exception { write(s.getBytes()); return (Arrays.compare(s.getBytes(), server.read(this)) == 0); } diff --git a/test/jdk/sun/security/ssl/DHKeyExchange/LegacyDHEKeyExchange.java b/test/jdk/sun/security/ssl/DHKeyExchange/LegacyDHEKeyExchange.java index 206761025c4..cac7c832402 100644 --- a/test/jdk/sun/security/ssl/DHKeyExchange/LegacyDHEKeyExchange.java +++ b/test/jdk/sun/security/ssl/DHKeyExchange/LegacyDHEKeyExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -51,7 +51,7 @@ public class LegacyDHEKeyExchange extends SSLSocketTemplate{ throw new Exception("Legacy DH keys (< 1024) should be restricted"); } catch (SSLHandshakeException she) { String expectedExMsg = "Received fatal alert: insufficient_security"; - if (!expectedExMsg.equals(she.getMessage())) { + if (!she.getMessage().endsWith(expectedExMsg)) { throw she; } System.out.println("Expected exception thrown in server"); @@ -77,7 +77,7 @@ public class LegacyDHEKeyExchange extends SSLSocketTemplate{ } catch (SSLHandshakeException she) { String expectedExMsg = "DH ServerKeyExchange does not comply to" + " algorithm constraints"; - if (!expectedExMsg.equals(she.getMessage())) { + if (!she.getMessage().endsWith(expectedExMsg)) { throw she; } System.out.println("Expected exception thrown in client"); diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java index 30a01552b30..598c0fe62af 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (C) 2021, 2024 THL A29 Limited, a Tencent company. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -122,7 +123,7 @@ public class SigAlgosExtTestWithTLS12 extends SSLEngineTemplate { "Expected SSLHandshakeException wasn't thrown"); } } catch (SSLHandshakeException e) { - if (EXPECT_FAIL && e.getMessage().equals( + if (EXPECT_FAIL && e.getMessage().endsWith( "No supported signature algorithm")) { System.out.println("Expected SSLHandshakeException"); } else { diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java index 49e8da8af78..2ad48f59e83 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -78,7 +79,7 @@ public class SigAlgosExtTestWithTLS13 extends SSLSocketTemplate { "Expected SSLHandshakeException wasn't thrown"); } } catch (SSLHandshakeException e) { - if (expectFail && e.getMessage().equals( + if (expectFail && e.getMessage().endsWith( "No supported signature algorithm")) { System.out.println("Expected SSLHandshakeException"); } else {