This is an automated email from the ASF dual-hosted git repository. remm pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push: new 535dc655cb Add hybrid PQC support for OpenSSL 3.5 535dc655cb is described below commit 535dc655cb55f4ce4e6be82f67fb1906f8bf009c Author: remm <r...@apache.org> AuthorDate: Thu Sep 4 16:18:46 2025 +0200 Add hybrid PQC support for OpenSSL 3.5 This adds any of the PQC certificates to the other SSL contexts. Each certificate still gets its own SSL context. The PQC certificate will never be selected by itself for a handshake. This technique is simple but will likely remain OpenSSL specific. The code is clearly separate and could be refactored out if a more general solution is implemented for JSSE. Based on PR#888. Based itself on the current mod_ssl code for doing hybrid PQC handshakes. --- .../apache/tomcat/util/net/AbstractEndpoint.java | 32 ++++++++++++++++++++++ .../tomcat/util/net/SSLHostConfigCertificate.java | 3 +- java/org/apache/tomcat/util/net/SSLUtil.java | 10 +++++++ .../util/net/openssl/LocalStrings.properties | 1 + .../tomcat/util/net/openssl/OpenSSLUtil.java | 10 +++++++ .../net/openssl/panama/LocalStrings.properties | 1 + .../util/net/openssl/panama/OpenSSLContext.java | 2 +- .../util/net/openssl/panama/OpenSSLUtil.java | 18 ++++++++++++ webapps/docs/changelog.xml | 10 +++++++ 9 files changed, 85 insertions(+), 2 deletions(-) diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java index a5f1393f3e..868c253f6b 100644 --- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java +++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java @@ -55,6 +55,7 @@ import org.apache.tomcat.util.collections.SynchronizedStack; import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.net.Acceptor.AcceptorState; import org.apache.tomcat.util.net.SSLHostConfigCertificate.StoreType; +import org.apache.tomcat.util.net.openssl.OpenSSLStatus; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.threads.LimitLatch; @@ -396,6 +397,24 @@ public abstract class AbstractEndpoint<S, U> { */ protected void createSSLContext(SSLHostConfig sslHostConfig) throws IllegalArgumentException { + boolean useHybridSslContext = false; + if (sslHostConfig.getProtocols().contains(Constants.SSL_PROTO_TLSv1_3) && OpenSSLStatus.isAvailable()) { + // If TLS 1.3 is enabled, check if a hybrid scheme using a single SSL context + // should be attempted + boolean nonMldsaFound = false; + boolean mldsaFound = false; + for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) { + if (certificate.getType().equals(SSLHostConfigCertificate.Type.MLDSA)) { + mldsaFound = true; + } else { + nonMldsaFound = true; + } + } + if (mldsaFound && nonMldsaFound) { + useHybridSslContext = true; + } + } + boolean firstCertificate = true; for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) { SSLUtil sslUtil = sslImplementation.getSSLUtil(certificate); @@ -423,8 +442,21 @@ public abstract class AbstractEndpoint<S, U> { certificate.setSslContextGenerated(sslContext); } + // If using a hybrid scheme, add any MLDSA certificates to all other SSL contexts + if (useHybridSslContext && !certificate.getType().equals(SSLHostConfigCertificate.Type.MLDSA)) { + for (SSLHostConfigCertificate certificateToAdd : sslHostConfig.getCertificates(true)) { + // Add additional certificate to all non MLDSA contexts + if (certificateToAdd.getType().equals(SSLHostConfigCertificate.Type.MLDSA)) { + if (!sslUtil.addSecondCertificate(sslContext, certificateToAdd)) { + throw new IllegalArgumentException(sm.getString("endpoint.errorCreatingSSLContext")); + } + } + } + } + logCertificate(certificate); } + } diff --git a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java index 6a708c9cf3..e3c27522d6 100644 --- a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java +++ b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java @@ -319,7 +319,8 @@ public class SSLHostConfigCertificate implements Serializable { UNDEFINED, RSA(Authentication.RSA), DSA(Authentication.DSS), - EC(Authentication.ECDH, Authentication.ECDSA); + EC(Authentication.ECDH, Authentication.ECDSA), + MLDSA; private final Set<Authentication> compatibleAuthentications; diff --git a/java/org/apache/tomcat/util/net/SSLUtil.java b/java/org/apache/tomcat/util/net/SSLUtil.java index da89dddb20..7a9c32dc4a 100644 --- a/java/org/apache/tomcat/util/net/SSLUtil.java +++ b/java/org/apache/tomcat/util/net/SSLUtil.java @@ -90,4 +90,14 @@ public interface SSLUtil { */ String getNegotiatedProtocol(); } + + /** + * Add a second certificate to an existing context, to enable hybrid TLS 1.3 handshakes. + * @param context the existing context + * @param certificate the second certificate to add + * @return true if supported by the context + */ + default boolean addSecondCertificate(SSLContext context, SSLHostConfigCertificate certificate) { + return false; + } } diff --git a/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties index c67bca7ec5..f827e486ec 100644 --- a/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties +++ b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties @@ -56,6 +56,7 @@ openssl.keyManagerMissing.warn=No key manager found. TLS will work but the certi openssl.makeConf=Creating OpenSSLConf context openssl.nonJsseCertificate=The certificate [{0}] or its private key [{1}] could not be processed using a JSSE key manager and will be given directly to OpenSSL openssl.nonJsseChain=The certificate chain [{0}] was not specified or was not valid and JSSE requires a valid certificate chain so attempting to use OpenSSL directly +openssl.secondCertificateError=Error adding second certificate to OpenSSL context openssl.trustManagerMissing=No trust manager found opensslconf.applyCommand=OpenSSLConf applying command (name [{0}], value [{1}]) diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java index dc792281a2..5f3ec28eda 100644 --- a/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java +++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java @@ -131,4 +131,14 @@ public class OpenSSLUtil extends SSLUtilBase { } } + @Override + public boolean addSecondCertificate(SSLContext context, SSLHostConfigCertificate certificate) { + try { + ((OpenSSLContext) context).addCertificate(certificate); + return true; + } catch (Exception e) { + throw new IllegalArgumentException(sm.getString("openssl.secondCertificateError"), e); + } + } + } diff --git a/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties b/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties index e1c58ce12e..5e4f66af2a 100644 --- a/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties +++ b/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties @@ -70,6 +70,7 @@ openssl.noCACerts=No CA certificates were configured openssl.nonJsseCertificate=The certificate [{0}] or its private key [{1}] could not be processed using a JSSE key manager and will be given directly to OpenSSL openssl.nonJsseChain=The certificate chain [{0}] was not specified or was not valid and JSSE requires a valid certificate chain so attempting to use OpenSSL directly openssl.passwordTooLong=The certificate password is too long +openssl.secondCertificateError=Error adding second certificate to OpenSSL context openssl.setCustomDHParameters=Setting custom DH parameters ([{0}] bits) for the key [{1}] openssl.setECDHCurve=Setting ECDH curve ([{0}]) for the key [{1}] openssl.trustManagerMissing=No trust manager found diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java index bb2f916567..2d7d654357 100644 --- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java +++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java @@ -874,7 +874,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } - private boolean addCertificate(SSLHostConfigCertificate certificate, Arena localArena) throws Exception { + public boolean addCertificate(SSLHostConfigCertificate certificate, Arena localArena) throws Exception { int index = getCertificateIndex(certificate); // Load Server key and certificate if (certificate.getCertificateFile() != null) { diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java index 3475190e5a..955d0aba0d 100644 --- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java +++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java @@ -17,6 +17,7 @@ package org.apache.tomcat.util.net.openssl.panama; import java.io.IOException; +import java.lang.foreign.Arena; import java.security.KeyException; import java.security.KeyStoreException; import java.util.List; @@ -106,4 +107,21 @@ public class OpenSSLUtil extends SSLUtilBase { } } + + @Override + public boolean addSecondCertificate(SSLContext context, SSLHostConfigCertificate certificate) { + try (var localArena = Arena.ofConfined()) { + try { + if (((OpenSSLContext) context).addCertificate(certificate, localArena)) { + return true; + } else { + log.warn(sm.getString("openssl.secondCertificateError")); + return false; + } + } catch (Exception e) { + throw new IllegalArgumentException(sm.getString("openssl.secondCertificateError"), e); + } + } + } + } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 0dcdf5f7fe..0e28a19314 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -105,6 +105,16 @@ issues do not "pop up" wrt. others). --> <section name="Tomcat 11.0.12 (markt)" rtext="in development"> + <subsection name="Coyote"> + <changelog> + <update> + Add hybrid PQC support to OpenSSL, based on code from + <code>mod_ssl</code>. Using this OpenSSL specific code path, + additional PQC certificates defined with type <code>MLDSA</code> are + added to contexts which use classic certificates. (jfclere/remm) + </update> + </changelog> + </subsection> </section> <section name="Tomcat 11.0.11 (markt)" rtext="release in progress"> <subsection name="Catalina"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org