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

commit 2109ab3c4973b542e346834c976224c0eacf1caf
Author: remm <[email protected]>
AuthorDate: Thu Sep 11 20:55:46 2025 +0200

    Add certificate selection code specific to TLS 1.3
    
    The signature algorithms preferences sent in the client hello should be
    enough to select the appropriate certificate (groups can be used as
    well, but it seems redundant and more complex).
    BZ69800: Add groups configuration on SSLHostConfig and pass that to JSSE
    (unsure about the actual usefulness though, I don't see any reason to
    disable a group at this point).
    Remove the OpenSSL specific code extracted from mod_ssl since it is
    supposed to be doing the same thing.
---
 .../org/apache/tomcat/util/compat/Jre20Compat.java | 66 +++++++++++++++++
 .../org/apache/tomcat/util/compat/Jre21Compat.java |  2 +-
 java/org/apache/tomcat/util/compat/JreCompat.java  | 30 ++++++++
 .../tomcat/util/net/AbstractJsseEndpoint.java      | 84 ++++++++++++----------
 java/org/apache/tomcat/util/net/SSLHostConfig.java | 38 ++++++++++
 .../tomcat/util/net/SSLHostConfigCertificate.java  |  8 ++-
 .../apache/tomcat/util/net/SecureNioChannel.java   | 17 ++++-
 .../util/net/openssl/ciphers/Authentication.java   |  1 +
 webapps/docs/changelog.xml                         | 13 ++--
 webapps/docs/config/http.xml                       |  9 +++
 10 files changed, 224 insertions(+), 44 deletions(-)

diff --git a/java/org/apache/tomcat/util/compat/Jre20Compat.java 
b/java/org/apache/tomcat/util/compat/Jre20Compat.java
new file mode 100644
index 0000000000..79c7aa9329
--- /dev/null
+++ b/java/org/apache/tomcat/util/compat/Jre20Compat.java
@@ -0,0 +1,66 @@
+/*
+ *  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.compat;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+public class Jre20Compat extends Jre19Compat {
+
+    private static final Log log = LogFactory.getLog(Jre20Compat.class);
+    private static final StringManager sm = 
StringManager.getManager(Jre20Compat.class);
+
+    private static final boolean supported;
+    private static final Method setNamedGroupsMethod;
+
+
+    static {
+        Class<?> c1 = null;
+        Method m1 = null;
+
+        try {
+            c1 = Class.forName("javax.net.ssl.SSLParameters");
+            m1 = c1.getMethod("setNamedGroups", String[].class);
+        } catch (ClassNotFoundException e) {
+            // Must be pre-Java 20
+            log.debug(sm.getString("jre20Compat.javaPre20"), e);
+        } catch (ReflectiveOperationException e) {
+            // Should never happen
+            log.error(sm.getString("jre20Compat.unexpected"), e);
+        }
+        supported = (c1 != null);
+        setNamedGroupsMethod = m1;
+    }
+
+    static boolean isSupported() {
+        return supported;
+    }
+
+    @Override
+    public void setNamedGroupsMethod(Object sslParameters, String[] names) {
+        try {
+            setNamedGroupsMethod.invoke(sslParameters, (Object[]) names);
+        } catch (IllegalAccessException | IllegalArgumentException | 
InvocationTargetException e) {
+            throw new UnsupportedOperationException(e);
+        }
+    }
+
+}
diff --git a/java/org/apache/tomcat/util/compat/Jre21Compat.java 
b/java/org/apache/tomcat/util/compat/Jre21Compat.java
index 23ebd27feb..6fc13c3b69 100644
--- a/java/org/apache/tomcat/util/compat/Jre21Compat.java
+++ b/java/org/apache/tomcat/util/compat/Jre21Compat.java
@@ -27,7 +27,7 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.res.StringManager;
 
-public class Jre21Compat extends Jre19Compat {
+public class Jre21Compat extends Jre20Compat {
 
     private static final Log log = LogFactory.getLog(Jre21Compat.class);
     private static final StringManager sm = 
StringManager.getManager(Jre21Compat.class);
diff --git a/java/org/apache/tomcat/util/compat/JreCompat.java 
b/java/org/apache/tomcat/util/compat/JreCompat.java
index a1fc8f00c7..be14721d77 100644
--- a/java/org/apache/tomcat/util/compat/JreCompat.java
+++ b/java/org/apache/tomcat/util/compat/JreCompat.java
@@ -48,6 +48,7 @@ public class JreCompat {
     private static final boolean jre12Available;
     private static final boolean jre16Available;
     private static final boolean jre19Available;
+    private static final boolean jre20Available;
     private static final boolean jre21Available;
     private static final boolean jre22Available;
 
@@ -75,6 +76,7 @@ public class JreCompat {
             instance = new Jre22Compat();
             jre22Available = true;
             jre21Available = true;
+            jre20Available = true;
             jre19Available = true;
             jre16Available = true;
             jre12Available = true;
@@ -82,6 +84,15 @@ public class JreCompat {
             instance = new Jre21Compat();
             jre22Available = false;
             jre21Available = true;
+            jre20Available = true;
+            jre19Available = true;
+            jre16Available = true;
+            jre12Available = true;
+        } else if (Jre20Compat.isSupported()) {
+            instance = new Jre20Compat();
+            jre22Available = false;
+            jre21Available = false;
+            jre20Available = true;
             jre19Available = true;
             jre16Available = true;
             jre12Available = true;
@@ -89,6 +100,7 @@ public class JreCompat {
             instance = new Jre19Compat();
             jre22Available = false;
             jre21Available = false;
+            jre20Available = false;
             jre19Available = true;
             jre16Available = true;
             jre12Available = true;
@@ -96,6 +108,7 @@ public class JreCompat {
             instance = new Jre16Compat();
             jre22Available = false;
             jre21Available = false;
+            jre20Available = false;
             jre19Available = false;
             jre16Available = true;
             jre12Available = true;
@@ -103,6 +116,7 @@ public class JreCompat {
             instance = new Jre12Compat();
             jre22Available = false;
             jre21Available = false;
+            jre20Available = false;
             jre19Available = false;
             jre16Available = false;
             jre12Available = true;
@@ -110,6 +124,7 @@ public class JreCompat {
             instance = new JreCompat();
             jre22Available = false;
             jre21Available = false;
+            jre20Available = false;
             jre19Available = false;
             jre16Available = false;
             jre12Available = false;
@@ -142,6 +157,11 @@ public class JreCompat {
     }
 
 
+    public static boolean isJre20Available() {
+        return jre20Available;
+    }
+
+
     public static boolean isJre21Available() {
         return jre21Available;
     }
@@ -402,4 +422,14 @@ public class JreCompat {
             }
         }
     }
+
+    /**
+     * TLS groups configuration from JSSE API in Java 20.
+     * @param sslParameters the parameters object
+     * @param names the names of the groups to enable
+     */
+    public void setNamedGroupsMethod(Object sslParameters, String[] names) {
+        throw new 
UnsupportedOperationException(sm.getString("jreCompat.noNamedGroups"));
+    }
+
 }
diff --git a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java 
b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
index 9384e48268..d6d3f98713 100644
--- a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
@@ -28,11 +28,17 @@ 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.compat.JreCompat;
 import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
+import org.apache.tomcat.util.net.openssl.ciphers.Group;
+import org.apache.tomcat.util.net.openssl.ciphers.SignatureAlgorithm;
 
 public abstract class AbstractJsseEndpoint<S, U> extends AbstractEndpoint<S,U> 
{
 
+    static final ThreadLocal<List<String>> clientRequestedProtocolsThreadLocal 
= new ThreadLocal<>();
+    static final ThreadLocal<List<Group>> clientSupportedGroupsThreadLocal = 
new ThreadLocal<>();
+    static final ThreadLocal<List<SignatureAlgorithm>> 
clientSignatureAlgorithmsThreadLocal = new ThreadLocal<>();
+
     private String sslImplementationName = null;
     private int sniParseLimit = 64 * 1024;
 
@@ -84,24 +90,6 @@ 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);
@@ -129,18 +117,6 @@ public abstract class AbstractJsseEndpoint<S, U> extends 
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);
         }
 
@@ -149,9 +125,14 @@ public abstract class AbstractJsseEndpoint<S, U> extends 
AbstractEndpoint<S,U> {
 
     protected SSLEngine createSSLEngine(String sniHostName, List<Cipher> 
clientRequestedCiphers,
             List<String> clientRequestedApplicationProtocols) {
+        List<String> clientRequestedProtocols = 
clientRequestedProtocolsThreadLocal.get();
+        List<Group> clientSupportedGroups = 
clientSupportedGroupsThreadLocal.get();
+        List<SignatureAlgorithm> clientSignatureAlgorithms = 
clientSignatureAlgorithmsThreadLocal.get();
+
         SSLHostConfig sslHostConfig = getSSLHostConfig(sniHostName);
 
-        SSLHostConfigCertificate certificate = 
selectCertificate(sslHostConfig, clientRequestedCiphers);
+        SSLHostConfigCertificate certificate = 
selectCertificate(sslHostConfig, clientRequestedCiphers,
+                clientRequestedProtocols, clientSignatureAlgorithms);
 
         SSLContext sslContext = certificate.getSslContext();
         if (sslContext == null) {
@@ -165,19 +146,35 @@ public abstract class AbstractJsseEndpoint<S, U> extends 
AbstractEndpoint<S,U> {
 
         SSLParameters sslParameters = engine.getSSLParameters();
         
sslParameters.setUseCipherSuitesOrder(sslHostConfig.getHonorCipherOrder());
-        if (clientRequestedApplicationProtocols != null && 
clientRequestedApplicationProtocols.size() > 0 &&
-                negotiableProtocols.size() > 0) {
+        if (clientRequestedApplicationProtocols != null && 
!clientRequestedApplicationProtocols.isEmpty() &&
+                !negotiableProtocols.isEmpty()) {
             // Only try to negotiate if both client and server have at least
             // one protocol in common
             // Note: Tomcat does not explicitly negotiate http/1.1
-            // TODO: Is this correct? Should it change?
             List<String> commonProtocols = new 
ArrayList<>(negotiableProtocols);
             commonProtocols.retainAll(clientRequestedApplicationProtocols);
-            if (commonProtocols.size() > 0) {
+            if (!commonProtocols.isEmpty()) {
                 String[] commonProtocolsArray = commonProtocols.toArray(new 
String[0]);
                 sslParameters.setApplicationProtocols(commonProtocolsArray);
             }
         }
+        // Merge server groups with the client groups
+        if (JreCompat.isJre20Available()) {
+            List<String> supportedGroups = new ArrayList<>();
+            LinkedHashSet<Group> serverSupportedGroups = 
sslHostConfig.getGroupList();
+            if (serverSupportedGroups != null) {
+                for (Group group : clientSupportedGroups) {
+                    if (serverSupportedGroups.contains(group)) {
+                        supportedGroups.add(group.toString());
+                    }
+                }
+            } else {
+                for (Group group : clientSupportedGroups) {
+                    supportedGroups.add(group.toString());
+                }
+            }
+            JreCompat.getInstance().setNamedGroupsMethod(sslParameters, 
supportedGroups.toArray(new String[0]));
+        }
         switch (sslHostConfig.getCertificateVerification()) {
             case NONE:
                 sslParameters.setNeedClientAuth(false);
@@ -198,13 +195,26 @@ public abstract class AbstractJsseEndpoint<S, U> extends 
AbstractEndpoint<S,U> {
     }
 
 
-    private SSLHostConfigCertificate selectCertificate(SSLHostConfig 
sslHostConfig, List<Cipher> clientCiphers) {
+    private SSLHostConfigCertificate selectCertificate(SSLHostConfig 
sslHostConfig, List<Cipher> clientCiphers,
+            List<String> clientRequestedProtocols, List<SignatureAlgorithm> 
clientSignatureAlgorithms) {
 
         Set<SSLHostConfigCertificate> certificates = 
sslHostConfig.getCertificates(true);
         if (certificates.size() == 1) {
             return certificates.iterator().next();
         }
 
+        // Use signature algorithm for cipher matching with TLS 1.3
+        if ((clientRequestedProtocols.contains(Constants.SSL_PROTO_TLSv1_3)) &&
+                
sslHostConfig.getProtocols().contains(Constants.SSL_PROTO_TLSv1_3)) {
+            for (SignatureAlgorithm signatureAlgorithm : 
clientSignatureAlgorithms) {
+                for (SSLHostConfigCertificate certificate : certificates) {
+                    if 
(certificate.getType().isCompatibleWith(signatureAlgorithm)) {
+                        return certificate;
+                    }
+                }
+            }
+        }
+
         LinkedHashSet<Cipher> serverCiphers = sslHostConfig.getCipherList();
 
         List<Cipher> candidateCiphers = new ArrayList<>();
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java 
b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index b83b3ed6cd..aa2f2ebc15 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -38,6 +38,7 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.net.openssl.OpenSSLConf;
 import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
+import org.apache.tomcat.util.net.openssl.ciphers.Group;
 import 
org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser;
 import org.apache.tomcat.util.res.StringManager;
 
@@ -108,6 +109,8 @@ public class SSLHostConfig implements Serializable {
     // Values <0 mean use the implementation default
     private int sessionCacheSize = -1;
     private int sessionTimeout = 86400;
+    private String groups = null;
+    private LinkedHashSet<Group> groupList = null;
     // JSSE
     private String keyManagerAlgorithm = 
KeyManagerFactory.getDefaultAlgorithm();
     private boolean revocationEnabled = false;
@@ -522,8 +525,43 @@ public class SSLHostConfig implements Serializable {
     }
 
 
+    /**
+     * @return the configured named groups
+     */
+    public String getGroups() {
+        return groups;
+    }
+
+
+    /**
+     * Set the enabled named groups.
+     * @param groupsString the case sensitive comma separated list of groups
+     */
+    public void setGroups(String groupsString) {
+        if (groupsString != null) {
+            LinkedHashSet<Group> groupList = new LinkedHashSet<>();
+            String[] groupNames = groupsString.split(",");
+            for (String groupName : groupNames) {
+                Group group = Group.valueOf(groupName.trim());
+                groupList.add(group);
+            }
+            this.groups = groupsString;
+            this.groupList = groupList;
+        }
+    }
+
+
+    /**
+     * @return the groupList
+     */
+    public LinkedHashSet<Group> getGroupList() {
+        return this.groupList;
+    }
+
+
     // ---------------------------------- JSSE specific configuration 
properties
 
+
     public void setKeyManagerAlgorithm(String keyManagerAlgorithm) {
         setProperty("keyManagerAlgorithm", Type.JSSE);
         this.keyManagerAlgorithm = keyManagerAlgorithm;
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java 
b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
index e7f8d628e4..b7606dd89b 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java
@@ -30,6 +30,7 @@ import javax.net.ssl.X509KeyManager;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.net.openssl.ciphers.Authentication;
+import org.apache.tomcat.util.net.openssl.ciphers.SignatureAlgorithm;
 import org.apache.tomcat.util.res.StringManager;
 
 public class SSLHostConfigCertificate implements Serializable {
@@ -318,7 +319,7 @@ public class SSLHostConfigCertificate implements 
Serializable {
         RSA(Authentication.RSA),
         DSA(Authentication.DSS),
         EC(Authentication.ECDH, Authentication.ECDSA),
-        MLDSA;
+        MLDSA(Authentication.MLDSA);
 
         private final Set<Authentication> compatibleAuthentications;
 
@@ -332,6 +333,11 @@ public class SSLHostConfigCertificate implements 
Serializable {
         public boolean isCompatibleWith(Authentication au) {
             return compatibleAuthentications.contains(au);
         }
+
+        public boolean isCompatibleWith(SignatureAlgorithm al) {
+            return al.toString().toUpperCase().startsWith(toString());
+        }
+
     }
 
     enum StoreType {
diff --git a/java/org/apache/tomcat/util/net/SecureNioChannel.java 
b/java/org/apache/tomcat/util/net/SecureNioChannel.java
index d866c815e9..4d87cb61e5 100644
--- a/java/org/apache/tomcat/util/net/SecureNioChannel.java
+++ b/java/org/apache/tomcat/util/net/SecureNioChannel.java
@@ -41,6 +41,8 @@ import org.apache.tomcat.util.buf.ByteBufferUtils;
 import org.apache.tomcat.util.net.NioEndpoint.NioSocketWrapper;
 import org.apache.tomcat.util.net.TLSClientHelloExtractor.ExtractorResult;
 import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
+import org.apache.tomcat.util.net.openssl.ciphers.Group;
+import org.apache.tomcat.util.net.openssl.ciphers.SignatureAlgorithm;
 import org.apache.tomcat.util.res.StringManager;
 
 /**
@@ -272,6 +274,8 @@ public class SecureNioChannel extends NioChannel {
         String hostName = null;
         List<Cipher> clientRequestedCiphers = null;
         List<String> clientRequestedApplicationProtocols = null;
+        List<Group> clientSupportedGroups = null;
+        List<SignatureAlgorithm> clientSignatureAlgorithms = null;
         switch (extractor.getResult()) {
             case COMPLETE:
                 hostName = extractor.getSNIValue();
@@ -279,6 +283,8 @@ public class SecureNioChannel extends NioChannel {
                 //$FALL-THROUGH$ to set the client requested ciphers
             case NOT_PRESENT:
                 clientRequestedCiphers = extractor.getClientRequestedCiphers();
+                clientSupportedGroups = extractor.getClientSupportedGroups();
+                clientSignatureAlgorithms = 
extractor.getClientSignatureAlgorithms();
                 break;
             case NEED_READ:
                 return SelectionKey.OP_READ;
@@ -302,7 +308,16 @@ public class SecureNioChannel extends NioChannel {
             log.trace(sm.getString("channel.nio.ssl.sniHostName", sc, 
hostName));
         }
 
-        sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers, 
clientRequestedApplicationProtocols);
+        try {
+            
AbstractJsseEndpoint.clientRequestedProtocolsThreadLocal.set(extractor.getClientRequestedProtocols());
+            
AbstractJsseEndpoint.clientSupportedGroupsThreadLocal.set(clientSupportedGroups);
+            
AbstractJsseEndpoint.clientSignatureAlgorithmsThreadLocal.set(clientSignatureAlgorithms);
+            sslEngine = endpoint.createSSLEngine(hostName, 
clientRequestedCiphers, clientRequestedApplicationProtocols);
+        } finally {
+            AbstractJsseEndpoint.clientRequestedProtocolsThreadLocal.set(null);
+            AbstractJsseEndpoint.clientSupportedGroupsThreadLocal.set(null);
+            
AbstractJsseEndpoint.clientSignatureAlgorithmsThreadLocal.set(null);
+        }
 
         // Populate additional TLS attributes obtained from the handshake that
         // aren't available from the session
diff --git 
a/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java 
b/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java
index ec192db6fb..052808a36b 100644
--- a/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java
@@ -29,5 +29,6 @@ public enum Authentication {
     GOST01 /* GOST R 34.10-2001 */,
     FZA /* Fortezza */,
     SRP /* Secure Remote Password */,
+    MLDSA /* ML-DSA */,
     ANY /* TLS 1.3 */
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 02e3094384..803ffb14cd 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -135,10 +135,15 @@
   <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)
+        Add specific certificate selection code for TLS 1.3 supporting post
+        quantum cryptography. Certificates defined with type
+        <code>MLDSA</code> will be selected depending on the TLS client hello.
+        (remm)
+      </update>
+      <update>
+        Add <code>groups</code> attribute on <code>SSLHostConfig</code>
+        allowing to restrict which groups can be enabled on the SSL engine.
+        (remm)
       </update>
       <add>
         Optimize the conversion of HTTP method from byte form to String form.
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 2b16a505e1..04291eb9b5 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1381,6 +1381,15 @@
       not the full chain.</p>
     </attribute>
 
+    <attribute name="groups" required="false">
+      <p>JSSE only.</p>
+      <p>Allows only allowing certain named groups. The value should be a case
+      sensitive comma separated list of the names of the groups.</p>
+      <p>. If not specified, the default named groups of the provider will be
+      used, and any named groups specified by the client will be passed to it.
+      </p>
+    </attribute>
+
     <attribute name="honorCipherOrder" required="false">
       <p>Set to <code>true</code> to enforce the server's cipher order
       (from the <code>ciphers</code> setting) instead of allowing


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

Reply via email to