changeset 805:2fee4b71baac monetdbs

Set ALPN protocol if the runtime supports it (Use introspection because Java 8 can't do it)
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Mon, 11 Dec 2023 15:47:19 +0100 (16 months ago)
parents 361441253305
children f478317138d7
files src/main/java/org/monetdb/mcl/net/SecureSocket.java tests/TLSTester.java
diffstat 2 files changed, 35 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/monetdb/mcl/net/SecureSocket.java
+++ b/src/main/java/org/monetdb/mcl/net/SecureSocket.java
@@ -3,17 +3,18 @@ package org.monetdb.mcl.net;
 import javax.net.ssl.*;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.net.Socket;
 import java.security.*;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
-import java.util.List;
 
 public class SecureSocket {
     private static final String[] ENABLED_PROTOCOLS = {"TLSv1.3"};
-    final String[] APPLICATION_PROTOCOLS = {"mapi/9"};
+    private static final String[] APPLICATION_PROTOCOLS = {"mapi/9"};
 
     public static Socket wrap(Target.Validated validated, Socket inner) throws IOException {
         Target.Verify verify = validated.connectVerify();
@@ -43,17 +44,26 @@ public class SecureSocket {
 
     private static SSLSocket wrapSocket(Socket inner, Target.Validated validated, SSLSocketFactory socketFactory, boolean checkName) throws IOException {
         SSLSocket sock = (SSLSocket) socketFactory.createSocket(inner, validated.connectTcp(), validated.connectPort(), true);
+        sock.setUseClientMode(true);
+        SSLParameters parameters = sock.getSSLParameters();
 
-        sock.setUseClientMode(true);
-        sock.setEnabledProtocols(ENABLED_PROTOCOLS);
+        parameters.setProtocols(ENABLED_PROTOCOLS);
+
+        parameters.setServerNames(Collections.singletonList(new SNIHostName(validated.connectTcp())));
 
         if (checkName) {
-            SSLParameters parameters = sock.getSSLParameters();
-            SNIServerName serverName = new SNIHostName(validated.connectTcp());
-            parameters.setServerNames(Collections.singletonList(serverName));
             parameters.setEndpointIdentificationAlgorithm("HTTPS");
-            sock.setSSLParameters(parameters);
         }
+
+        // Unfortunately, SSLParameters.setApplicationProtocols is only available
+        // since language level 9 and currently we're on 8.
+        // Still call it if it happens to be available.
+        try {
+            Method setApplicationProtocols = SSLParameters.class.getMethod("setApplicationProtocols", String[].class);
+            setApplicationProtocols.invoke(parameters, (Object) APPLICATION_PROTOCOLS);
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ignored) {}
+
+        sock.setSSLParameters(parameters);
         sock.startHandshake();
         return sock;
     }
--- a/tests/TLSTester.java
+++ b/tests/TLSTester.java
@@ -9,7 +9,9 @@ import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Properties;
+import java.util.stream.Collectors;
 
 public class TLSTester {
     int verbose = 0;
@@ -19,6 +21,7 @@ public class TLSTester {
     boolean enableTrusted = false;
     File tempDir = null;
     final HashMap<String, File> fileCache = new HashMap<>();
+    private HashSet<String> preparedButNotRun = new HashSet<>();
 
     public TLSTester(String[] args) {
         for (int i = 0; i < args.length; i++) {
@@ -133,9 +136,15 @@ public class TLSTester {
         test_fail_tls_to_plain();
         test_fail_plain_to_tls();
         test_connect_server_name();
-//        test_connect_alpn_mapi9();
+        test_connect_alpn_mapi9();
         test_connect_trusted();
         test_refuse_trusted_wrong_host();
+
+        // did we forget to call expectSucceed and expectFailure somewhere?
+        if (!preparedButNotRun.isEmpty()) {
+            String names = String.join(", ", preparedButNotRun);
+            throw new RuntimeException("Not all tests called expectSuccess/expectFailure: " + names);
+        }
     }
 
     private void test_connect_plain() throws IOException, SQLException {
@@ -202,24 +211,27 @@ public class TLSTester {
     }
 
     private void test_connect_alpn_mapi9() throws IOException, SQLException {
-        attempt("connect_alpn_mapi9", "");
+        attempt("connect_alpn_mapi9", "alpn_mapi9")
+                .withFile(Parameter.CERT, "/ca1.crt")
+                .expectSuccess();
     }
 
     private void test_connect_trusted() throws IOException, SQLException {
-        attempt("connect_trusted", "alpn_mapi9")
+        attempt("connect_trusted", null)
                 .with(Parameter.HOST, "monetdb.ergates.nl")
                 .with(Parameter.PORT, 50000)
                 .expectSuccess();
     }
 
     private void test_refuse_trusted_wrong_host() throws IOException, SQLException {
-        attempt("connect_trusted", null)
+        attempt("test_refuse_trusted_wrong_host", null)
                 .with(Parameter.HOST, "monetdbxyz.ergates.nl")
                 .with(Parameter.PORT, 50000)
                 .expectFailure("No subject alternative DNS name");
     }
 
     private Attempt attempt(String testName, String portName) throws IOException {
+        preparedButNotRun.add(testName);
         return new Attempt(testName, portName);
     }
 
@@ -268,6 +280,7 @@ public class TLSTester {
         }
 
         public void expectSuccess() throws SQLException {
+            preparedButNotRun.remove(testName);
             if (disabled)
                 return;
             try {