On Fri, May 15, 2026 at 6:03 PM Mark Thomas <[email protected]> wrote: > > On 15/05/2026 16:50, Dimitris Soumis wrote: > > https://github.com/apache/tomcat/blob/main/java/org/apache/tomcat/util/openssl/openssl_h.java#L82 > > > > Maybe LibreSSL is loaded which doesn't support PQC? > > Almost. It was the usual multiple inter-related errors making debugging > more difficult than usual. > > The sytem OpenSSL 3.0.x was being picked up. That was adding a lot of > noise. Then OpenSSL master needed slightly different settings to OpenSSL > 3.x. The following fixed the OpenSSL version issues (running in the IDE > - equivalent settings would be required in build.properties) > > -Dtomcat.test.openssl.path=/home/mark/repos/openssl/build-master/bin/openssl > -Djava.library.path=/home/mark/libs/tomcat-native-2.0.14-openssl-master/lib:/home/mark/repos/openssl/build-master/lib64 > -Dorg.apache.tomcat.util.openssl.USE_SYSTEM_LOAD_LIBRARY=true > > And with those in place I could switch between Native 2.0.14 and 2.0.x > and OpenSSL 3.5.6 and master and get consistent errors. The errors look > like an OpenSSL + Native + PQC bug that the tests are now exposing. That > isn't fixed yet but I feel I am making progress.
This happened to me before (unfortunately ...). Here's the build.properties I use to test the various impls with the testsuite: #runtests.librarypath=-Djava.library.path=/home/remm/Work/tomcat/openssl #runtests.librarypath=-Djava.library.path=/home/remm/Work/libressl-3.5.0/ssl/.libs:/home/remm/Work/libressl-3.5.0/crypto/.libs #runtests.librarypath=-Djava.library.path=/home/remm/Work/libressl-3.3.6/ssl/.libs:/home/remm/Work/libressl-3.3.6/crypto/.libs #runtests.librarypath=-Djava.library.path=/home/remm/Work/libressl-4.0.0/ssl/.libs:/home/remm/Work/libressl-4.0.0/crypto/.libs #runtests.librarypath=-Djava.library.path=/home/remm/Work/boringssl/build #openssl.ffm.1=-Dorg.apache.tomcat.util.openssl.USE_SYSTEM_LOAD_LIBRARY=true I also have: #openssl.ffm.2=-Dorg.apache.tomcat.util.openssl.CRYPTO_LIBRARY_NAME=crypto but I don't remember why. Rémy > Mark > > > > > > Dimitris > > > > On Fri, May 15, 2026 at 5:35 PM Mark Thomas <[email protected]> wrote: > > > >> On 15/05/2026 15:12, Dimitris Soumis wrote: > >>> PQC should be enabled by default. > >> > >> Agreed. And it appears to be. > >> > >>> dsoumis@192:~$ openssl list -signature-algorithms 2>&1 | grep -i mldsa > >>> { 2.16.840.1.101.3.4.3.17, id-ml-dsa-44, ML-DSA-44, MLDSA44 } @ > >> default > >>> { 2.16.840.1.101.3.4.3.18, id-ml-dsa-65, ML-DSA-65, MLDSA65 } @ > >> default > >>> { 2.16.840.1.101.3.4.3.19, id-ml-dsa-87, ML-DSA-87, MLDSA87 } @ > >> default > >> > >> That is what I see. > >> > >>> We should add an extra check for ML-DSA availability if that's the > >>> issue instead of just checking the version number. > >>> I will add this if you agree. > >> > >> I don't think that is the issue given I am seeing the same results as you. > >> > >> I am seeing slightly different behaviour with OpenSSL master. > >> > >> It is looking more like an environmental issue. I need to keep digging > >> to figure out what is going wrong. > >> > >> Until I figure out what is going wrong, I don't think it is worth adding > >> additional checks to the tests. > >> > >> Mark > >> > >> > >>> > >>> Dimitris > >>> > >>> > >>> On Fri, May 15, 2026 at 4:55 PM Mark Thomas <[email protected]> wrote: > >>> > >>>> I'm seeing lots of failures with 3.5.5 > >>>> > >>>> The root cause appears to be: > >>>> > >>>> 15-May-2026 12:59:58.147 SEVERE [main] > >>>> org.apache.tomcat.util.net.openssl.panama.OpenSSLContext.logLastError > >>>> Error loading certificate: [error:0A0000F7:SSL routines::unknown > >>>> certificate type] > >>>> > >>>> and similar variations. > >>>> > >>>> Does PQC need to be explicitly enabled in the OpenSSL build? > >>>> > >>>> Mark > >>>> > >>>> > >>>> On 15/05/2026 14:41, Dimitris Soumis wrote: > >>>>> OpenSSL-FFM tests did pass for me though with Openssl 3.5.4. Could you > >>>>> provide the failure logs if there are any pending or what version you > >> are > >>>>> using that triggers those failures? > >>>>> > >>>>> On Fri, May 15, 2026 at 4:16 PM Dimitris Soumis <[email protected]> > >>>> wrote: > >>>>> > >>>>>> Apologies for the noise. Indeed, it wasn't tested properly. Thanks for > >>>>>> fixing it. > >>>>>> > >>>>>> Dimitris > >>>>>> > >>>>>> On Fri, May 15, 2026 at 3:02 PM Mark Thomas <[email protected]> wrote: > >>>>>> > >>>>>>> On 13/05/2026 12:44, [email protected] wrote: > >>>>>>>> This is an automated email from the ASF dual-hosted git repository. > >>>>>>>> > >>>>>>>> dsoumis pushed a commit to branch main > >>>>>>>> in repository https://gitbox.apache.org/repos/asf/tomcat.git > >>>>>>>> > >>>>>>>> > >>>>>>>> The following commit(s) were added to refs/heads/main by this push: > >>>>>>>> new 7ff10fab8e Add unit tests for PQC features > >>>>>>>> 7ff10fab8e is described below > >>>>>>>> > >>>>>>>> commit 7ff10fab8ede061fe61524ef96b463fef637429f > >>>>>>>> Author: Dimitrios Soumis <[email protected]> > >>>>>>>> AuthorDate: Wed May 13 13:44:42 2026 +0200 > >>>>>>>> > >>>>>>>> Add unit tests for PQC features > >>>>>>> > >>>>>>> How well tested is this patch? And with which OpenSSL versions? > >>>>>>> > >>>>>>> The OpenSSL tests can never run because the version check is looking > >> at > >>>>>>> the OpenSSLStatus rather than AprStatus. > >>>>>>> > >>>>>>> With the above fixed, the OpenSSL tests still won't run because the > >>>>>>> OpenSSL version isn't set until after the version check. > >>>>>>> > >>>>>>> With that fixed, most of the OpenSSL tests result in errors or > >>>> failures. > >>>>>>> The OpenSSL-FFM tests have a similar failure rate. > >>>>>>> > >>>>>>> Mark > >>>>>>> > >>>>>>> > >>>>>>>> --- > >>>>>>>> test/org/apache/tomcat/util/net/TestPQC.java | 331 > >>>>>>> +++++++++++++++++++++ > >>>>>>>> .../tomcat/util/net/TesterKeystoreGenerator.java | 65 ++++ > >>>>>>>> 2 files changed, 396 insertions(+) > >>>>>>>> > >>>>>>>> diff --git a/test/org/apache/tomcat/util/net/TestPQC.java > >>>>>>> b/test/org/apache/tomcat/util/net/TestPQC.java > >>>>>>>> new file mode 100644 > >>>>>>>> index 0000000000..db3f53ff60 > >>>>>>>> --- /dev/null > >>>>>>>> +++ b/test/org/apache/tomcat/util/net/TestPQC.java > >>>>>>>> @@ -0,0 +1,331 @@ > >>>>>>>> +/* > >>>>>>>> + * Licensed to the Apache Software Foundation (ASF) under one or > >>>> more > >>>>>>>> + * contributor license agreements. See the NOTICE file > >> distributed > >>>>>>> with > >>>>>>>> + * this work for additional information regarding copyright > >>>> ownership. > >>>>>>>> + * The ASF licenses this file to You under the Apache License, > >>>>>>> Version 2.0 > >>>>>>>> + * (the "License"); you may not use this file except in compliance > >>>>>>> with > >>>>>>>> + * the License. You may obtain a copy of the License at > >>>>>>>> + * > >>>>>>>> + * http://www.apache.org/licenses/LICENSE-2.0 > >>>>>>>> + * > >>>>>>>> + * Unless required by applicable law or agreed to in writing, > >>>> software > >>>>>>>> + * distributed under the License is distributed on an "AS IS" > >> BASIS, > >>>>>>>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or > >>>>>>> implied. > >>>>>>>> + * See the License for the specific language governing permissions > >>>> and > >>>>>>>> + * limitations under the License. > >>>>>>>> + */ > >>>>>>>> +package org.apache.tomcat.util.net; > >>>>>>>> + > >>>>>>>> +import java.io.File; > >>>>>>>> +import java.util.ArrayList; > >>>>>>>> +import java.util.Collection; > >>>>>>>> +import java.util.List; > >>>>>>>> +import java.util.Map; > >>>>>>>> +import java.util.concurrent.TimeUnit; > >>>>>>>> + > >>>>>>>> +import javax.net.ssl.SSLContext; > >>>>>>>> +import javax.net.ssl.SSLHandshakeException; > >>>>>>>> +import javax.net.ssl.TrustManager; > >>>>>>>> + > >>>>>>>> +import org.junit.Assert; > >>>>>>>> +import org.junit.Assume; > >>>>>>>> +import org.junit.Test; > >>>>>>>> +import org.junit.runner.RunWith; > >>>>>>>> +import org.junit.runners.Parameterized; > >>>>>>>> +import org.junit.runners.Parameterized.Parameter; > >>>>>>>> + > >>>>>>>> +import org.apache.catalina.Context; > >>>>>>>> +import org.apache.catalina.connector.Connector; > >>>>>>>> +import org.apache.catalina.startup.TesterServlet; > >>>>>>>> +import org.apache.catalina.startup.Tomcat; > >>>>>>>> +import org.apache.catalina.startup.TomcatBaseTest; > >>>>>>>> +import org.apache.tomcat.util.buf.ByteChunk; > >>>>>>>> +import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; > >>>>>>>> +import org.apache.tomcat.util.net.openssl.OpenSSLStatus; > >>>>>>>> + > >>>>>>>> +@RunWith(Parameterized.class) > >>>>>>>> +public class TestPQC extends TomcatBaseTest { > >>>>>>>> + > >>>>>>>> + @Parameterized.Parameters(name = "{0}") > >>>>>>>> + public static Collection<Object[]> parameters() { > >>>>>>>> + List<Object[]> parameterSets = new ArrayList<>(); > >>>>>>>> + parameterSets.add(new Object[] { > >>>>>>>> + "JSSE", Boolean.FALSE, "org.apache.tomcat.util.net > >>>>>>> .jsse.JSSEImplementation"}); > >>>>>>>> + parameterSets.add(new Object[] { > >>>>>>>> + "OpenSSL", Boolean.TRUE, " > >> org.apache.tomcat.util.net > >>>>>>> .openssl.OpenSSLImplementation"}); > >>>>>>>> + parameterSets.add(new Object[] { > >>>>>>>> + "OpenSSL-FFM", Boolean.TRUE, " > >>>>>>> org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"}); > >>>>>>>> + return parameterSets; > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Parameter(0) > >>>>>>>> + public String connectorName; > >>>>>>>> + > >>>>>>>> + @Parameter(1) > >>>>>>>> + public boolean useOpenSSL; > >>>>>>>> + > >>>>>>>> + @Parameter(2) > >>>>>>>> + public String sslImplementationName; > >>>>>>>> + > >>>>>>>> + @Override > >>>>>>>> + public void setUp() throws Exception { > >>>>>>>> + super.setUp(); > >>>>>>>> + > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + Connector connector = tomcat.getConnector(); > >>>>>>>> + > >>>>>>>> + Assert.assertTrue(connector.setProperty("SSLEnabled", > >>>> "true")); > >>>>>>>> + SSLHostConfig sslHostConfig = new SSLHostConfig(); > >>>>>>>> + sslHostConfig.setProtocols(Constants.SSL_PROTO_TLSv1_3); > >>>>>>>> + connector.addSslHostConfig(sslHostConfig); > >>>>>>>> + > >>>>>>>> + TesterSupport.configureSSLImplementation(tomcat, > >>>>>>> sslImplementationName, useOpenSSL); > >>>>>>>> + > >>>>>>>> + Context ctx = getProgrammaticRootContext(); > >>>>>>>> + Tomcat.addServlet(ctx, "TesterServlet", new > >> TesterServlet()); > >>>>>>>> + ctx.addServletMappingDecoded("/*", "TesterServlet"); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostMLDSA44() throws Exception { > >>>>>>>> + File[] pqcFiles = configureHostMLDSA("ML-DSA-44"); > >>>>>>>> + doTestWithOpenSSLClient(pqcFiles[0].getAbsolutePath(), > >> null, > >>>>>>> null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostMLDSA65() throws Exception { > >>>>>>>> + File[] pqcFiles = configureHostMLDSA("ML-DSA-65"); > >>>>>>>> + doTestWithOpenSSLClient(pqcFiles[0].getAbsolutePath(), > >> null, > >>>>>>> null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostMLDSA87() throws Exception { > >>>>>>>> + File[] pqcFiles = configureHostMLDSA("ML-DSA-87"); > >>>>>>>> + doTestWithOpenSSLClient(pqcFiles[0].getAbsolutePath(), > >> null, > >>>>>>> null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostRSAandMLDSA() throws Exception { > >>>>>>>> + configureHostRSA(); > >>>>>>>> + configureHostMLDSA("ML-DSA-65"); > >>>>>>>> + doTest(); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostECandMLDSA() throws Exception { > >>>>>>>> + configureHostEC(); > >>>>>>>> + configureHostMLDSA("ML-DSA-65"); > >>>>>>>> + doTest(); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostRSAwithX25519MLKEM768() throws Exception { > >>>>>>>> + configureHostRSA(); > >>>>>>>> + configureHostWithGroup("X25519MLKEM768"); > >>>>>>>> + doTestWithOpenSSLClient(new > >>>>>>> File(TesterSupport.CA_CERT_PEM).getAbsolutePath(), > >>>>>>>> + "X25519MLKEM768", null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostRSAwithSecP256r1MLKEM768() throws > >> Exception { > >>>>>>>> + configureHostRSA(); > >>>>>>>> + configureHostWithGroup("SecP256r1MLKEM768"); > >>>>>>>> + doTestWithOpenSSLClient(new > >>>>>>> File(TesterSupport.CA_CERT_PEM).getAbsolutePath(), > >>>>>>>> + "SecP256r1MLKEM768", null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostRSAwithSecP384r1MLKEM1024() throws > >> Exception > >>>> { > >>>>>>>> + configureHostRSA(); > >>>>>>>> + configureHostWithGroup("SecP384r1MLKEM1024"); > >>>>>>>> + doTestWithOpenSSLClient(new > >>>>>>> File(TesterSupport.CA_CERT_PEM).getAbsolutePath(), > >>>>>>>> + "SecP384r1MLKEM1024", null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostMLDSAwithX25519MLKEM768() throws Exception > >> { > >>>>>>>> + File[] pqcFiles = configureHostMLDSA("ML-DSA-65"); > >>>>>>>> + configureHostWithGroup("X25519MLKEM768"); > >>>>>>>> + doTestWithOpenSSLClient(pqcFiles[0].getAbsolutePath(), > >>>>>>> "X25519MLKEM768", null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testHostMLDSAwithSecP256r1MLKEM768() throws > >>>> Exception { > >>>>>>>> + File[] pqcFiles = configureHostMLDSA("ML-DSA-65"); > >>>>>>>> + configureHostWithGroup("SecP256r1MLKEM768"); > >>>>>>>> + doTestWithOpenSSLClient(pqcFiles[0].getAbsolutePath(), > >>>>>>> "SecP256r1MLKEM768", null, null); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testClientMLDSA() throws Exception { > >>>>>>>> + configureHostRSA(); > >>>>>>>> + File[] clientFiles = > >>>>>>> TesterKeystoreGenerator.generatePQCCertificate("testuser", > >> "ML-DSA-65", > >>>>>>>> + null, null); > >>>>>>>> + SSLHostConfig sslHostConfig = > >>>>>>> getTomcatInstance().getConnector().findSslHostConfigs()[0]; > >>>>>>>> + sslHostConfig.setCertificateVerification("required"); > >>>>>>>> + > >>>>>>> sslHostConfig.setCaCertificateFile(clientFiles[0].getAbsolutePath()); > >>>>>>>> + doTestWithOpenSSLClient(new > >>>>>>> File(TesterSupport.CA_CERT_PEM).getAbsolutePath(), null, > >>>>>>>> + clientFiles[0].getAbsolutePath(), > >>>>>>> clientFiles[1].getAbsolutePath()); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test > >>>>>>>> + public void testClientMLDSAwithMLDSAServer() throws Exception { > >>>>>>>> + File[] serverFiles = configureHostMLDSA("ML-DSA-65"); > >>>>>>>> + File[] clientFiles = > >>>>>>> TesterKeystoreGenerator.generatePQCCertificate("testuser", > >> "ML-DSA-65", > >>>>>>>> + null, null); > >>>>>>>> + SSLHostConfig sslHostConfig = > >>>>>>> getTomcatInstance().getConnector().findSslHostConfigs()[0]; > >>>>>>>> + sslHostConfig.setCertificateVerification("required"); > >>>>>>>> + > >>>>>>> sslHostConfig.setCaCertificateFile(clientFiles[0].getAbsolutePath()); > >>>>>>>> + doTestWithOpenSSLClient(serverFiles[0].getAbsolutePath(), > >>>> null, > >>>>>>>> + clientFiles[0].getAbsolutePath(), > >>>>>>> clientFiles[1].getAbsolutePath()); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + @Test(expected = SSLHandshakeException.class) > >>>>>>>> + public void testHostMLDSAHandshakeFailure() throws Exception { > >>>>>>>> + assumePQCSupported(); > >>>>>>>> + configureHostMLDSA("ML-DSA-65"); > >>>>>>>> + > >>>>>>>> + SSLContext sc = > >>>>>>> SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_2); > >>>>>>>> + sc.init(null, new TrustManager[] { new > >>>>>>> TesterSupport.TrustAllCerts() }, null); > >>>>>>>> + TesterSupport.ClientSSLSocketFactory > >> clientSSLSocketFactory = > >>>>>>>> + new > >>>>>>> TesterSupport.ClientSSLSocketFactory(sc.getSocketFactory()); > >>>>>>>> + clientSSLSocketFactory.setProtocols(new String[] { > >>>>>>> Constants.SSL_PROTO_TLSv1_2 }); > >>>>>>>> + > >>>>>>> > >>>> > >> javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(clientSSLSocketFactory); > >>>>>>>> + > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + tomcat.start(); > >>>>>>>> + getUrl("https://localhost:" + getPort() + "/"); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + > >>>>>>>> + private void assumePQCSupported() { > >>>>>>>> + if (!useOpenSSL) { > >>>>>>>> + Assume.assumeTrue("JSSE does not yet support PQC", > >>>> false); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + Assume.assumeTrue("PQC requires OpenSSL 3.5+", > >>>>>>>> + OpenSSLStatus.getMajorVersion() > 3 || > >>>>>>>> + OpenSSLStatus.getMajorVersion() == 3 && > >>>>>>> OpenSSLStatus.getMinorVersion() >= 5); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + private File[] configureHostMLDSA(String algorithm) throws > >>>>>>> Exception { > >>>>>>>> + File[] pqcFiles = > >>>>>>> TesterKeystoreGenerator.generatePQCCertificate("localhost", > >> algorithm, > >>>>>>>> + new String[] { "localhost" }, null); > >>>>>>>> + > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + Connector connector = tomcat.getConnector(); > >>>>>>>> + SSLHostConfig sslHostConfig = > >>>>>>> connector.findSslHostConfigs()[0]; > >>>>>>>> + > >>>>>>>> + SSLHostConfigCertificate cert = new > >>>>>>> SSLHostConfigCertificate(sslHostConfig, Type.MLDSA); > >>>>>>>> + cert.setCertificateFile(pqcFiles[0].getAbsolutePath()); > >>>>>>>> + cert.setCertificateKeyFile(pqcFiles[1].getAbsolutePath()); > >>>>>>>> + sslHostConfig.addCertificate(cert); > >>>>>>>> + > >>>>>>>> + return pqcFiles; > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + private void configureHostRSA() { > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + Connector connector = tomcat.getConnector(); > >>>>>>>> + SSLHostConfig sslHostConfig = > >>>>>>> connector.findSslHostConfigs()[0]; > >>>>>>>> + > >>>>>>>> + SSLHostConfigCertificate cert = new > >>>>>>> SSLHostConfigCertificate(sslHostConfig, Type.RSA); > >>>>>>>> + cert.setCertificateFile(new > >>>>>>> File(TesterSupport.LOCALHOST_RSA_CERT_PEM).getAbsolutePath()); > >>>>>>>> + cert.setCertificateKeyFile(new > >>>>>>> File(TesterSupport.LOCALHOST_RSA_KEY_PEM).getAbsolutePath()); > >>>>>>>> + cert.setCertificateKeyPassword(TesterSupport.JKS_PASS); > >>>>>>>> + sslHostConfig.addCertificate(cert); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + private void configureHostEC() { > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + Connector connector = tomcat.getConnector(); > >>>>>>>> + SSLHostConfig sslHostConfig = > >>>>>>> connector.findSslHostConfigs()[0]; > >>>>>>>> + > >>>>>>>> + SSLHostConfigCertificate cert = new > >>>>>>> SSLHostConfigCertificate(sslHostConfig, Type.EC); > >>>>>>>> + cert.setCertificateFile(new > >>>>>>> File(TesterSupport.LOCALHOST_EC_CERT_PEM).getAbsolutePath()); > >>>>>>>> + cert.setCertificateKeyFile(new > >>>>>>> File(TesterSupport.LOCALHOST_EC_KEY_PEM).getAbsolutePath()); > >>>>>>>> + sslHostConfig.addCertificate(cert); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + private void configureHostWithGroup(String groupName) { > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + Connector connector = tomcat.getConnector(); > >>>>>>>> + SSLHostConfig sslHostConfig = > >>>>>>> connector.findSslHostConfigs()[0]; > >>>>>>>> + sslHostConfig.setGroups(groupName); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + private void doTest() throws Exception { > >>>>>>>> + assumePQCSupported(); > >>>>>>>> + SSLContext sc = > >>>>>>> SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3); > >>>>>>>> + sc.init(null, new TrustManager[] { new > >>>>>>> TesterSupport.TrustAllCerts() }, null); > >>>>>>>> + > >>>>>>> > >>>> > >> javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + tomcat.start(); > >>>>>>>> + ByteChunk res = getUrl("https://localhost:" + getPort() + > >>>>>>> "/"); > >>>>>>>> + Assert.assertEquals("OK", res.toString()); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + private void doTestWithOpenSSLClient(String caFile, String > >>>> groups, > >>>>>>>> + String clientCert, String clientKey) throws Exception { > >>>>>>>> + assumePQCSupported(); > >>>>>>>> + > >>>>>>>> + Tomcat tomcat = getTomcatInstance(); > >>>>>>>> + tomcat.start(); > >>>>>>>> + > >>>>>>>> + String openSSLPath = > >>>>>>> System.getProperty("tomcat.test.openssl.path"); > >>>>>>>> + String openSSLLibPath = null; > >>>>>>>> + if (openSSLPath == null || openSSLPath.length() == 0) { > >>>>>>>> + openSSLPath = "openssl"; > >>>>>>>> + } else { > >>>>>>>> + openSSLLibPath = openSSLPath.substring(0, > >>>>>>> openSSLPath.lastIndexOf('/')); > >>>>>>>> + openSSLLibPath = openSSLLibPath + "/../:" + > >>>> openSSLLibPath > >>>>>>> + "/../lib:" + openSSLLibPath + "/../lib64"; > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + List<String> cmd = new ArrayList<>(); > >>>>>>>> + cmd.add(openSSLPath); > >>>>>>>> + cmd.add("s_client"); > >>>>>>>> + cmd.add("-connect"); > >>>>>>>> + cmd.add("localhost:" + getPort()); > >>>>>>>> + cmd.add("-CAfile"); > >>>>>>>> + cmd.add(caFile); > >>>>>>>> + cmd.add("-tls1_3"); > >>>>>>>> + if (groups != null) { > >>>>>>>> + cmd.add("-groups"); > >>>>>>>> + cmd.add(groups); > >>>>>>>> + } > >>>>>>>> + if (clientCert != null) { > >>>>>>>> + cmd.add("-cert"); > >>>>>>>> + cmd.add(clientCert); > >>>>>>>> + cmd.add("-key"); > >>>>>>>> + cmd.add(clientKey); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + ProcessBuilder pb = new ProcessBuilder(cmd); > >>>>>>>> + > >>>>>>>> + if (openSSLLibPath != null) { > >>>>>>>> + Map<String,String> env = pb.environment(); > >>>>>>>> + String libraryPath = env.get("LD_LIBRARY_PATH"); > >>>>>>>> + if (libraryPath == null) { > >>>>>>>> + libraryPath = openSSLLibPath; > >>>>>>>> + } else { > >>>>>>>> + libraryPath = libraryPath + ":" + openSSLLibPath; > >>>>>>>> + } > >>>>>>>> + env.put("LD_LIBRARY_PATH", libraryPath); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + pb.redirectErrorStream(true); > >>>>>>>> + Process p = pb.start(); > >>>>>>>> + > >>>>>>>> + p.getOutputStream().write("GET / HTTP/1.0\r\nHost: > >>>>>>> localhost\r\n\r\n".getBytes()); > >>>>>>>> + p.getOutputStream().flush(); > >>>>>>>> + > >>>>>>>> + String output = new > >>>> String(p.getInputStream().readAllBytes()); > >>>>>>>> + > >>>>>>>> + Assert.assertTrue("Process did not complete in time", > >>>>>>> p.waitFor(10, TimeUnit.SECONDS)); > >>>>>>>> + Assert.assertTrue("TLS handshake failed:\n" + output, > >>>>>>> output.contains("HTTP/1.")); > >>>>>>>> + Assert.assertTrue("Unexpected response body:\n" + output, > >>>>>>> output.contains("OK")); > >>>>>>>> + } > >>>>>>>> +} > >>>>>>>> diff --git > >>>>>>> a/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java > >>>>>>> b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java > >>>>>>>> index 9fd4affde6..00f1772fcc 100644 > >>>>>>>> --- a/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java > >>>>>>>> +++ b/test/org/apache/tomcat/util/net/TesterKeystoreGenerator.java > >>>>>>>> @@ -19,6 +19,7 @@ package org.apache.tomcat.util.net; > >>>>>>>> > >>>>>>>> import java.io.File; > >>>>>>>> import java.io.FileOutputStream; > >>>>>>>> +import java.io.FileWriter; > >>>>>>>> import java.math.BigInteger; > >>>>>>>> import java.security.KeyPair; > >>>>>>>> import java.security.KeyPairGenerator; > >>>>>>>> @@ -33,6 +34,8 @@ import org.bouncycastle.asn1.x509.GeneralNames; > >>>>>>>> import org.bouncycastle.cert.X509v3CertificateBuilder; > >>>>>>>> import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; > >>>>>>>> import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; > >>>>>>>> +import org.bouncycastle.jce.provider.BouncyCastleProvider; > >>>>>>>> +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; > >>>>>>>> import org.bouncycastle.operator.ContentSigner; > >>>>>>>> import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; > >>>>>>>> > >>>>>>>> @@ -100,4 +103,66 @@ public final class TesterKeystoreGenerator { > >>>>>>>> > >>>>>>>> return keystoreFile; > >>>>>>>> } > >>>>>>>> + > >>>>>>>> + /** > >>>>>>>> + * Generate temporary PEM files containing a self-signed PQC > >>>>>>> certificate and private key. > >>>>>>>> + * > >>>>>>>> + * @param cn the Common Name for the certificate subject > >>>>>>>> + * @param algorithm the PQC algorithm name, e.g. {@code > >>>>>>> "ML-DSA-44"}, {@code "ML-DSA-65"}, > >>>>>>>> + * or {@code "ML-DSA-87"} > >>>>>>>> + * @param sanNames DNS Subject Alternative Names to include, > >> or > >>>>>>> {@code null} for none > >>>>>>>> + * @param customizer callback to add extensions to the > >>>>>>> certificate, or {@code null} for none > >>>>>>>> + * > >>>>>>>> + * @return a two-element array: {@code [0]} is the certificate > >>>> PEM > >>>>>>> file, {@code [1]} is the > >>>>>>>> + * private key PEM file > >>>>>>>> + * > >>>>>>>> + * @throws Exception if certificate generation fails > >>>>>>>> + */ > >>>>>>>> + public static File[] generatePQCCertificate(String cn, String > >>>>>>> algorithm, String[] sanNames, > >>>>>>>> + > >>>>>>> CertificateExtensionsCustomizer customizer) throws Exception { > >>>>>>>> + BouncyCastleProvider bouncyCastleProvider = new > >>>>>>> BouncyCastleProvider(); > >>>>>>>> + > >>>>>>>> + KeyPairGenerator keyPairGenerator = > >>>>>>> KeyPairGenerator.getInstance(algorithm, bouncyCastleProvider); > >>>>>>>> + KeyPair keyPair = keyPairGenerator.generateKeyPair(); > >>>>>>>> + > >>>>>>>> + X500Name subject = new X500Name("CN=" + cn); > >>>>>>>> + BigInteger serial = > >>>>>>> BigInteger.valueOf(System.currentTimeMillis()); > >>>>>>>> + long oneDay = 86400000L; > >>>>>>>> + Date notBefore = new Date(System.currentTimeMillis() - > >>>> oneDay); > >>>>>>>> + Date notAfter = new Date(System.currentTimeMillis() + 365L > >> * > >>>>>>> oneDay); > >>>>>>>> + > >>>>>>>> + X509v3CertificateBuilder certBuilder = new > >>>>>>> JcaX509v3CertificateBuilder(subject, serial, notBefore, > >>>>>>>> + notAfter, subject, keyPair.getPublic()); > >>>>>>>> + > >>>>>>>> + if (sanNames != null && sanNames.length > 0) { > >>>>>>>> + GeneralName[] generalNames = new > >>>>>>> GeneralName[sanNames.length]; > >>>>>>>> + for (int i = 0; i < sanNames.length; i++) { > >>>>>>>> + generalNames[i] = new > >>>> GeneralName(GeneralName.dNSName, > >>>>>>> sanNames[i]); > >>>>>>>> + } > >>>>>>>> + > >>>> certBuilder.addExtension(Extension.subjectAlternativeName, > >>>>>>> false, new GeneralNames(generalNames)); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + if (customizer != null) { > >>>>>>>> + customizer.customize(keyPair, certBuilder); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + ContentSigner signer = new > >>>>>>> JcaContentSignerBuilder(algorithm).setProvider(bouncyCastleProvider) > >>>>>>>> + .build(keyPair.getPrivate()); > >>>>>>>> + X509Certificate certificate = new > >>>>>>> JcaX509CertificateConverter().setProvider(bouncyCastleProvider) > >>>>>>>> + .getCertificate(certBuilder.build(signer)); > >>>>>>>> + > >>>>>>>> + File certFile = File.createTempFile("test-pqc-cert-", > >>>> ".pem"); > >>>>>>>> + certFile.deleteOnExit(); > >>>>>>>> + try (JcaPEMWriter writer = new JcaPEMWriter(new > >>>>>>> FileWriter(certFile))) { > >>>>>>>> + writer.writeObject(certificate); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + File keyFile = File.createTempFile("test-pqc-key-", > >> ".pem"); > >>>>>>>> + keyFile.deleteOnExit(); > >>>>>>>> + try (JcaPEMWriter writer = new JcaPEMWriter(new > >>>>>>> FileWriter(keyFile))) { > >>>>>>>> + writer.writeObject(keyPair.getPrivate()); > >>>>>>>> + } > >>>>>>>> + > >>>>>>>> + return new File[] { certFile, keyFile }; > >>>>>>>> + } > >>>>>>>> } > >>>>>>>> > >>>>>>>> > >>>>>>>> > >> --------------------------------------------------------------------- > >>>>>>>> To unsubscribe, e-mail: [email protected] > >>>>>>>> For additional commands, e-mail: [email protected] > >>>>>>>> > >>>>>>> > >>>>>>> > >>>>>>> --------------------------------------------------------------------- > >>>>>>> To unsubscribe, e-mail: [email protected] > >>>>>>> For additional commands, e-mail: [email protected] > >>>>>>> > >>>>>>> > >>>>> > >>>> > >>>> > >>>> --------------------------------------------------------------------- > >>>> To unsubscribe, e-mail: [email protected] > >>>> For additional commands, e-mail: [email protected] > >>>> > >>>> > >>> > >> > >> > >> --------------------------------------------------------------------- > >> To unsubscribe, e-mail: [email protected] > >> For additional commands, e-mail: [email protected] > >> > >> > > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [email protected] > For additional commands, e-mail: [email protected] > --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
