This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/10.1.x by this push:
     new 46be800962 Add hybrid PQC support for OpenSSL 3.5
46be800962 is described below

commit 46be800962a3048aa8b6983ccaa16f810f8fd39b
Author: remm <[email protected]>
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.
---
 .../tomcat/util/net/AbstractJsseEndpoint.java      | 34 +++++++++++++++++++++-
 .../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, 86 insertions(+), 3 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java 
b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
index 63605be3f9..9384e48268 100644
--- a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
@@ -28,6 +28,7 @@ import java.util.Set;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
 
+import org.apache.tomcat.util.net.openssl.OpenSSLStatus;
 import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
 
 public abstract class AbstractJsseEndpoint<S, U> extends AbstractEndpoint<S,U> 
{
@@ -83,6 +84,24 @@ public abstract class AbstractJsseEndpoint<S, U> extends 
AbstractEndpoint<S,U> {
     @Override
     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);
@@ -104,14 +123,27 @@ public abstract class AbstractJsseEndpoint<S, U> extends 
AbstractEndpoint<S,U> {
                 try {
                     sslContext = sslUtil.createSSLContext(negotiableProtocols);
                 } catch (Exception e) {
-                    throw new IllegalArgumentException(e.getMessage(), e);
+                    throw new 
IllegalArgumentException(sm.getString("endpoint.errorCreatingSSLContext"), e);
                 }
 
                 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 7b68844d06..e7f8d628e4 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
@@ -317,7 +317,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 1c31d7e970..2112fa8674 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java
@@ -136,4 +136,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 a5e93aae6b..5dbadcb993 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 10.1.46 (schultz)" 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 10.1.45 (schultz)" rtext="release in progress">
   <subsection name="Catalina">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to