This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 0f67ae74860c CAMEL-23154 - Post-Quantum Cryptography (PQC) readiness:
Add namedGro… (#21864)
0f67ae74860c is described below
commit 0f67ae74860cb0f729f716f7bfe4f77723c519cf
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Mar 9 13:39:22 2026 +0100
CAMEL-23154 - Post-Quantum Cryptography (PQC) readiness: Add namedGro…
(#21864)
* CAMEL-23154 - Post-Quantum Cryptography (PQC) readiness: Add namedGroups
support to BaseSSLContextParameters
Add namedGroups and namedGroupsFilter fields to BaseSSLContextParameters,
following the existing cipherSuites/cipherSuitesFilter pattern. This allows
users to configure TLS named groups (key exchange algorithms) including
post-quantum hybrid groups such as X25519MLKEM768.
Changes:
- Add NamedGroupsParameters class mirroring CipherSuitesParameters
- Add namedGroups (NamedGroupsParameters) and namedGroupsFilter
(FilterParameters) fields with getters/setters
- Update SSLEngine, SSLSocket, and SSLServerSocket configurers to call
SSLParameters.setNamedGroups() with configured or filtered values
- Add configureNamedGroups() helper shared by all three configurers
- Add debug log messages for named groups configuration
Signed-off-by: Andrea Cosentino <[email protected]>
* CAMEL-23154: Fix named groups implementation - add tests, docs, and XML
support
- Refactor configureNamedGroups to resolveNamedGroups returning String[]
instead of Object target + instanceof dispatch, avoiding potential
setSSLParameters overriding cipher suites and protocols
- Add NamedGroupsParametersDefinition for Spring XML configuration support
- Wire namedGroups/namedGroupsFilter in
AbstractBaseSSLContextParametersFactoryBean
- Add testNamedGroups, testNamedGroupsFilter, and
testNamedGroupsDoNotAffectCipherSuitesOrProtocols tests
- Document namedGroups and namedGroupsFilter in JSSE configuration docs
* CAMEL-23154: Regenerate JAXB index for NamedGroupsParametersDefinition
---------
Signed-off-by: Andrea Cosentino <[email protected]>
Co-authored-by: Guillaume Nodet <[email protected]>
---
.../apache/camel/catalog/schemas/camel-spring.xsd | 11 ++
.../support/jsse/BaseSSLContextParameters.java | 202 +++++++++++++++++++++
.../camel/support/jsse/NamedGroupsParameters.java | 66 +++++++
.../org/apache/camel/core/xml/util/jsse/jaxb.index | 1 +
...bstractBaseSSLContextParametersFactoryBean.java | 31 ++++
.../util/jsse/NamedGroupsParametersDefinition.java | 46 +++++
.../support/jsse/SSLContextParametersTest.java | 159 ++++++++++++++++
.../ROOT/pages/camel-configuration-utilities.adoc | 35 ++++
8 files changed, 551 insertions(+)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index 9dda3d0ef604..235f087ea21e 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -3477,6 +3477,11 @@ configuration.
<xs:element maxOccurs="unbounded" minOccurs="0" name="exclude"
nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="namedGroupsParameters">
+ <xs:sequence>
+ <xs:element maxOccurs="unbounded" minOccurs="0" name="namedGroup"
nillable="true" type="xs:string"/>
+ </xs:sequence>
+ </xs:complexType>
<xs:complexType name="sniHostNames">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="sniHostName"
type="xs:string"/>
@@ -18046,6 +18051,8 @@ converter. The default logging level is DEBUG. Default
value: DEBUG
<xs:element minOccurs="0" name="cipherSuitesFilter"
type="tns:filterParameters"/>
<xs:element minOccurs="0" name="secureSocketProtocols"
type="tns:secureSocketProtocolsParameters"/>
<xs:element minOccurs="0" name="secureSocketProtocolsFilter"
type="tns:filterParameters"/>
+ <xs:element minOccurs="0" name="namedGroups"
type="tns:namedGroupsParameters"/>
+ <xs:element minOccurs="0" name="namedGroupsFilter"
type="tns:filterParameters"/>
<xs:element minOccurs="0" name="sniHostNames"
type="tns:sniHostNames"/>
</xs:all>
<xs:attribute name="sessionTimeout" type="xs:string"/>
@@ -18060,6 +18067,8 @@ converter. The default logging level is DEBUG. Default
value: DEBUG
<xs:element minOccurs="0" name="cipherSuitesFilter"
type="tns:filterParameters"/>
<xs:element minOccurs="0" name="secureSocketProtocols"
type="tns:secureSocketProtocolsParameters"/>
<xs:element minOccurs="0" name="secureSocketProtocolsFilter"
type="tns:filterParameters"/>
+ <xs:element minOccurs="0" name="namedGroups"
type="tns:namedGroupsParameters"/>
+ <xs:element minOccurs="0" name="namedGroupsFilter"
type="tns:filterParameters"/>
<xs:element minOccurs="0" name="keyManagers"
type="tns:keyManagersParametersFactoryBean"/>
<xs:element minOccurs="0" name="trustManagers"
type="tns:trustManagersParametersFactoryBean"/>
<xs:element minOccurs="0" name="secureRandom"
type="tns:secureRandomParametersFactoryBean"/>
@@ -18130,6 +18139,8 @@ An optional certificate alias to use. This is useful
when the keystore has multi
<xs:element minOccurs="0" name="cipherSuitesFilter"
type="tns:filterParameters"/>
<xs:element minOccurs="0" name="secureSocketProtocols"
type="tns:secureSocketProtocolsParameters"/>
<xs:element minOccurs="0" name="secureSocketProtocolsFilter"
type="tns:filterParameters"/>
+ <xs:element minOccurs="0" name="namedGroups"
type="tns:namedGroupsParameters"/>
+ <xs:element minOccurs="0" name="namedGroupsFilter"
type="tns:filterParameters"/>
</xs:all>
<xs:attribute name="sessionTimeout" type="xs:string"/>
<xs:attribute name="clientAuthentication" type="xs:string"/>
diff --git
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
index c7fe6d8ce233..3c1a464f7d04 100644
---
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
+++
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/BaseSSLContextParameters.java
@@ -83,6 +83,12 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
private static final String SSL_SERVER_SOCKET_PROTOCOL_LOG_MSG =
createProtocolLogMessage("SSLServerSocket");
+ private static final String SSL_ENGINE_NAMED_GROUP_LOG_MSG =
createNamedGroupLogMessage("SSLEngine");
+
+ private static final String SSL_SOCKET_NAMED_GROUP_LOG_MSG =
createNamedGroupLogMessage("SSLSocket");
+
+ private static final String SSL_SERVER_SOCKET_NAMED_GROUP_LOG_MSG =
createNamedGroupLogMessage("SSLServerSocket");
+
/**
* The optional explicitly configured cipher suites for this configuration.
*/
@@ -103,6 +109,18 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
*/
private FilterParameters secureSocketProtocolsFilter;
+ /**
+ * The optional explicitly configured named groups (key exchange groups)
for this configuration. Named groups
+ * control which key exchange algorithms are available during the TLS
handshake, including post-quantum hybrid
+ * groups such as X25519MLKEM768.
+ */
+ private NamedGroupsParameters namedGroups;
+
+ /**
+ * The optional named groups filter configuration for this configuration.
+ */
+ private FilterParameters namedGroupsFilter;
+
/**
* The optional {@link SSLSessionContext} timeout time for {@link
javax.net.ssl.SSLSession}s in seconds.
*/
@@ -210,6 +228,62 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
this.secureSocketProtocolsFilter = secureSocketProtocolsFilter;
}
+ /**
+ * Returns the optional explicitly configured named groups for this
configuration. These options are used in the
+ * configuration of {@link SSLEngine}, {@link SSLSocketFactory} and {@link
SSLServerSocketFactory} depending on the
+ * context in which they are applied.
+ * <p/>
+ * Named groups control which key exchange algorithms are available during
the TLS handshake, including post-quantum
+ * hybrid groups such as {@code X25519MLKEM768}.
+ * <p/>
+ * These values override any filters supplied in {@link
#setNamedGroupsFilter(FilterParameters)}
+ */
+ public NamedGroupsParameters getNamedGroups() {
+ return namedGroups;
+ }
+
+ /**
+ * Sets the optional explicitly configured named groups for this
configuration. These options are used in the
+ * configuration of {@link SSLEngine}, {@link SSLSocketFactory} and {@link
SSLServerSocketFactory} depending on the
+ * context in which they are applied.
+ * <p/>
+ * Named groups control which key exchange algorithms are available during
the TLS handshake, including post-quantum
+ * hybrid groups such as {@code X25519MLKEM768}.
+ * <p/>
+ * These values override any filters supplied in {@link
#setNamedGroupsFilter(FilterParameters)}
+ *
+ * @param namedGroups the named groups configuration
+ */
+ public void setNamedGroups(NamedGroupsParameters namedGroups) {
+ this.namedGroups = namedGroups;
+ }
+
+ /**
+ * Returns the optional named groups filter for this configuration. These
options are used in the configuration of
+ * {@link SSLEngine}, {@link SSLSocketFactory} and {@link
SSLServerSocketFactory} depending on the context in which
+ * they are applied.
+ * <p/>
+ * These values are ignored if {@link
#setNamedGroups(NamedGroupsParameters)} is called with a non {@code null}
+ * argument.
+ */
+ public FilterParameters getNamedGroupsFilter() {
+ return namedGroupsFilter;
+ }
+
+ /**
+ * Sets the optional named groups filter for this JSSE configuration.
These options are used in the configuration of
+ * {@link SSLEngine}, {@link SSLSocketFactory} and {@link
SSLServerSocketFactory} depending on the context in which
+ * they are applied.
+ * <p/>
+ * These values are ignored if {@link
#setNamedGroups(NamedGroupsParameters)} is called with a non {@code null}
+ * argument.
+ *
+ * @param namedGroupsFilter the filter configuration
+ */
+ public void setNamedGroupsFilter(FilterParameters namedGroupsFilter) {
+ this.namedGroupsFilter = namedGroupsFilter;
+ }
+
/**
* Returns the optional {@link SSLSessionContext} timeout time for {@link
javax.net.ssl.SSLSession}s in seconds.
*/
@@ -314,6 +388,17 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
enabledSecureSocketProtocolsPatterns = null;
}
+ final List<String> enabledNamedGroups = this.getNamedGroups() == null
+ ? null :
this.parsePropertyValues(this.getNamedGroups().getNamedGroup());
+
+ final Patterns enabledNamedGroupsPatterns;
+
+ if (this.getNamedGroupsFilter() != null) {
+ enabledNamedGroupsPatterns =
this.getNamedGroupsFilter().getPatterns();
+ } else {
+ enabledNamedGroupsPatterns = null;
+ }
+
//
final boolean allowPassthrough = getAllowPassthrough();
@@ -364,6 +449,23 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
engine.setEnabledProtocols(
filteredSecureSocketProtocols.toArray(new String[0]));
+ String[] namedGroups = resolveNamedGroups(
+ engine.getSSLParameters().getNamedGroups(),
+ enabledNamedGroups, enabledNamedGroupsPatterns);
+ if (namedGroups != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(SSL_ENGINE_NAMED_GROUP_LOG_MSG,
+ engine,
+ enabledNamedGroups,
+ enabledNamedGroupsPatterns,
+ engine.getSSLParameters().getNamedGroups(),
+ namedGroups);
+ }
+ SSLParameters params = engine.getSSLParameters();
+ params.setNamedGroups(namedGroups);
+ engine.setSSLParameters(params);
+ }
+
return engine;
}
};
@@ -479,6 +581,17 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
enabledSecureSocketProtocolsPatterns = null;
}
+ final List<String> enabledNamedGroups = this.getNamedGroups() == null
+ ? null :
this.parsePropertyValues(this.getNamedGroups().getNamedGroup());
+
+ final Patterns enabledNamedGroupsPatterns;
+
+ if (this.getNamedGroupsFilter() != null) {
+ enabledNamedGroupsPatterns =
this.getNamedGroupsFilter().getPatterns();
+ } else {
+ enabledNamedGroupsPatterns = null;
+ }
+
//
final boolean allowPassthrough = getAllowPassthrough();
@@ -533,6 +646,24 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
socket.setEnabledProtocols(
filteredSecureSocketProtocols.toArray(new String[0]));
+
+ String[] namedGroups = resolveNamedGroups(
+ socket.getSSLParameters().getNamedGroups(),
+ enabledNamedGroups, enabledNamedGroupsPatterns);
+ if (namedGroups != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(SSL_SOCKET_NAMED_GROUP_LOG_MSG,
+ socket,
+ enabledNamedGroups,
+ enabledNamedGroupsPatterns,
+ socket.getSSLParameters().getNamedGroups(),
+ namedGroups);
+ }
+ SSLParameters params = socket.getSSLParameters();
+ params.setNamedGroups(namedGroups);
+ socket.setSSLParameters(params);
+ }
+
return socket;
}
};
@@ -578,6 +709,17 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
enabledSecureSocketProtocolsPatterns = null;
}
+ final List<String> enabledNamedGroups = this.getNamedGroups() == null
+ ? null :
this.parsePropertyValues(this.getNamedGroups().getNamedGroup());
+
+ final Patterns enabledNamedGroupsPatterns;
+
+ if (this.getNamedGroupsFilter() != null) {
+ enabledNamedGroupsPatterns =
this.getNamedGroupsFilter().getPatterns();
+ } else {
+ enabledNamedGroupsPatterns = null;
+ }
+
//
final boolean allowPassthrough = getAllowPassthrough();
@@ -627,6 +769,24 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
socket.setEnabledProtocols(
filteredSecureSocketProtocols.toArray(new String[0]));
+
+ String[] namedGroups = resolveNamedGroups(
+ socket.getSSLParameters().getNamedGroups(),
+ enabledNamedGroups, enabledNamedGroupsPatterns);
+ if (namedGroups != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(SSL_SERVER_SOCKET_NAMED_GROUP_LOG_MSG,
+ socket,
+ enabledNamedGroups,
+ enabledNamedGroupsPatterns,
+ socket.getSSLParameters().getNamedGroups(),
+ namedGroups);
+ }
+ SSLParameters params = socket.getSSLParameters();
+ params.setNamedGroups(namedGroups);
+ socket.setSSLParameters(params);
+ }
+
return socket;
}
};
@@ -768,6 +928,40 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
return matches;
}
+ /**
+ * Resolves the named groups to configure based on explicit values or
filter patterns. Returns {@code null} if no
+ * named groups configuration is needed (both parameters are {@code null}).
+ *
+ * @param currentNamedGroups the currently available named groups
from the SSL object
+ * @param enabledNamedGroups the optional explicit named groups
list
+ * @param enabledNamedGroupsPatterns the optional filter patterns
+ *
+ * @return the filtered named groups array, or
{@code null} if no configuration is needed
+ */
+ private String[] resolveNamedGroups(
+ String[] currentNamedGroups, List<String> enabledNamedGroups,
+ Patterns enabledNamedGroupsPatterns) {
+
+ if (enabledNamedGroups == null && enabledNamedGroupsPatterns == null) {
+ return null;
+ }
+
+ if (currentNamedGroups == null) {
+ currentNamedGroups = new String[0];
+ }
+
+ Collection<String> filteredNamedGroups;
+ if (enabledNamedGroups != null) {
+ filteredNamedGroups = new ArrayList<>(enabledNamedGroups);
+ } else {
+ filteredNamedGroups = this.filter(
+ null, Arrays.asList(currentNamedGroups),
+ enabledNamedGroupsPatterns.getIncludes(),
enabledNamedGroupsPatterns.getExcludes());
+ }
+
+ return filteredNamedGroups.toArray(new String[0]);
+ }
+
/**
* Configures a {@code T} based on the related configuration options.
*/
@@ -1106,4 +1300,12 @@ public abstract class BaseSSLContextParameters extends
JsseParameters {
+ "\t and default protocol patterns [{}]." + LS
+ "\t Resulting enabled protocols are [{}].";
}
+
+ private static String createNamedGroupLogMessage(String entityName) {
+ return "Configuring " + entityName + " [{}] with " + LS
+ + "\t explicitly set named groups [{}]," + LS
+ + "\t named group patterns [{}]," + LS
+ + "\t available named groups [{}]," + LS
+ + "\t Resulting enabled named groups are [{}].";
+ }
}
diff --git
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/NamedGroupsParameters.java
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/NamedGroupsParameters.java
new file mode 100644
index 000000000000..760778618d72
--- /dev/null
+++
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/NamedGroupsParameters.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.camel.support.jsse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a list of TLS/SSL named groups (also known as elliptic curves or
key exchange groups) for use in TLS
+ * handshakes. Named groups control which key exchange algorithms are
available during the TLS handshake, including
+ * post-quantum hybrid groups such as {@code X25519MLKEM768}.
+ */
+public class NamedGroupsParameters {
+ private List<String> namedGroup;
+
+ /**
+ * Returns a live reference to the list of named group names.
+ *
+ * @return a reference to the list, never {@code null}
+ */
+ public List<String> getNamedGroup() {
+ if (this.namedGroup == null) {
+ this.namedGroup = new ArrayList<>();
+ }
+ return this.namedGroup;
+ }
+
+ public void addNamedGroup(String group) {
+ if (this.namedGroup == null) {
+ this.namedGroup = new ArrayList<>();
+ }
+ this.namedGroup.add(group.trim());
+ }
+
+ /**
+ * Sets the named groups. It creates a copy of the given list.
+ *
+ * @param namedGroup named groups
+ */
+ public void setNamedGroup(List<String> namedGroup) {
+ this.namedGroup = namedGroup == null ? null : new
ArrayList<>(namedGroup);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("NamedGroupsParameters[namedGroup=");
+ builder.append(getNamedGroup());
+ builder.append("]");
+ return builder.toString();
+ }
+}
diff --git
a/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/util/jsse/jaxb.index
b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/util/jsse/jaxb.index
index 8d84387108de..ecefb19403b1 100644
---
a/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/util/jsse/jaxb.index
+++
b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/util/jsse/jaxb.index
@@ -1,5 +1,6 @@
# Generated by camel build tools - do NOT edit this file!
CipherSuitesParametersDefinition
FilterParametersDefinition
+NamedGroupsParametersDefinition
SNIHostNamesDefinition
SecureSocketProtocolsParametersDefinition
diff --git
a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/util/jsse/AbstractBaseSSLContextParametersFactoryBean.java
b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/util/jsse/AbstractBaseSSLContextParametersFactoryBean.java
index baf2463dd1f9..ff0d4d944dc9 100644
---
a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/util/jsse/AbstractBaseSSLContextParametersFactoryBean.java
+++
b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/util/jsse/AbstractBaseSSLContextParametersFactoryBean.java
@@ -23,6 +23,7 @@ import org.apache.camel.spi.Metadata;
import org.apache.camel.support.jsse.BaseSSLContextParameters;
import org.apache.camel.support.jsse.CipherSuitesParameters;
import org.apache.camel.support.jsse.FilterParameters;
+import org.apache.camel.support.jsse.NamedGroupsParameters;
import org.apache.camel.support.jsse.SecureSocketProtocolsParameters;
@XmlTransient
@@ -37,6 +38,10 @@ public abstract class
AbstractBaseSSLContextParametersFactoryBean<T extends Base
private FilterParametersDefinition secureSocketProtocolsFilter;
+ private NamedGroupsParametersDefinition namedGroups;
+
+ private FilterParametersDefinition namedGroupsFilter;
+
@XmlAttribute
@Metadata(description = "The optional SSLSessionContext timeout time for
javax.net.ssl.SSLSession in seconds.")
private String sessionTimeout;
@@ -83,6 +88,16 @@ public abstract class
AbstractBaseSSLContextParametersFactoryBean<T extends Base
newInstance.setSecureSocketProtocolsFilter(createFilterParameters(secureSocketProtocolsFilter));
}
+ if (namedGroups != null) {
+ NamedGroupsParameters namedGroupsInstance = new
NamedGroupsParameters();
+ namedGroupsInstance.setNamedGroup(namedGroups.getNamedGroup());
+ newInstance.setNamedGroups(namedGroupsInstance);
+ }
+
+ if (namedGroupsFilter != null) {
+
newInstance.setNamedGroupsFilter(createFilterParameters(namedGroupsFilter));
+ }
+
if (sessionTimeout != null) {
newInstance.setSessionTimeout(sessionTimeout);
}
@@ -131,6 +146,22 @@ public abstract class
AbstractBaseSSLContextParametersFactoryBean<T extends Base
this.secureSocketProtocolsFilter = secureSocketProtocolsFilter;
}
+ public NamedGroupsParametersDefinition getNamedGroups() {
+ return namedGroups;
+ }
+
+ public void setNamedGroups(NamedGroupsParametersDefinition namedGroups) {
+ this.namedGroups = namedGroups;
+ }
+
+ public FilterParametersDefinition getNamedGroupsFilter() {
+ return namedGroupsFilter;
+ }
+
+ public void setNamedGroupsFilter(FilterParametersDefinition
namedGroupsFilter) {
+ this.namedGroupsFilter = namedGroupsFilter;
+ }
+
public String getSessionTimeout() {
return sessionTimeout;
}
diff --git
a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/util/jsse/NamedGroupsParametersDefinition.java
b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/util/jsse/NamedGroupsParametersDefinition.java
new file mode 100644
index 000000000000..eecfd6ffe275
--- /dev/null
+++
b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/util/jsse/NamedGroupsParametersDefinition.java
@@ -0,0 +1,46 @@
+/*
+ * 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.camel.core.xml.util.jsse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlType;
+
+import org.apache.camel.spi.Metadata;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "namedGroupsParameters", propOrder = { "namedGroup" })
+public class NamedGroupsParametersDefinition {
+
+ @Metadata(description = "List of TLS/SSL named groups (key exchange
groups)")
+ private List<String> namedGroup;
+
+ /**
+ * Returns a live reference to the list of named group names.
+ *
+ * @return a reference to the list, never {@code null}
+ */
+ public List<String> getNamedGroup() {
+ if (this.namedGroup == null) {
+ this.namedGroup = new ArrayList<>();
+ }
+ return this.namedGroup;
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
b/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
index 0df49fd146f3..0ad671cf15a3 100644
---
a/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/support/jsse/SSLContextParametersTest.java
@@ -741,6 +741,165 @@ public class SSLContextParametersTest extends
AbstractJsseParametersTest {
}
}
+ @Test
+ public void testNamedGroups() throws Exception {
+ SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
+ controlContext.init(null, null, null);
+ SSLEngine controlEngine = controlContext.createSSLEngine();
+ String[] controlNamedGroups =
controlEngine.getSSLParameters().getNamedGroups();
+
+ // default - no named groups configured, should keep defaults
+ SSLContextParameters scp = new SSLContextParameters();
+ SSLContext context = scp.createSSLContext(null);
+
+ SSLEngine engine = context.createSSLEngine();
+ SSLSocket socket = (SSLSocket)
context.getSocketFactory().createSocket();
+ SSLServerSocket serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertArrayEquals(controlNamedGroups,
engine.getSSLParameters().getNamedGroups());
+ assertArrayEquals(controlNamedGroups,
socket.getSSLParameters().getNamedGroups());
+ assertArrayEquals(controlNamedGroups,
serverSocket.getSSLParameters().getNamedGroups());
+
+ // empty ngp - sets empty list
+ NamedGroupsParameters ngp = new NamedGroupsParameters();
+ scp.setNamedGroups(ngp);
+ context = scp.createSSLContext(null);
+ engine = context.createSSLEngine();
+ socket = (SSLSocket) context.getSocketFactory().createSocket();
+ serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertEquals(0, engine.getSSLParameters().getNamedGroups().length);
+ assertEquals(0, socket.getSSLParameters().getNamedGroups().length);
+ assertEquals(0,
serverSocket.getSSLParameters().getNamedGroups().length);
+
+ // explicit named group
+ ngp.setNamedGroup(Collections.singletonList(controlNamedGroups[0]));
+ context = scp.createSSLContext(null);
+ engine = context.createSSLEngine();
+ socket = (SSLSocket) context.getSocketFactory().createSocket();
+ serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertEquals(1, engine.getSSLParameters().getNamedGroups().length);
+ assertEquals(controlNamedGroups[0],
engine.getSSLParameters().getNamedGroups()[0]);
+ assertEquals(1, socket.getSSLParameters().getNamedGroups().length);
+ assertEquals(controlNamedGroups[0],
socket.getSSLParameters().getNamedGroups()[0]);
+ assertEquals(1,
serverSocket.getSSLParameters().getNamedGroups().length);
+ assertEquals(controlNamedGroups[0],
serverSocket.getSSLParameters().getNamedGroups()[0]);
+
+ // explicit named groups override filter
+ FilterParameters filter = new FilterParameters();
+ filter.getInclude().add(".*");
+ scp.setNamedGroupsFilter(filter);
+ context = scp.createSSLContext(null);
+ engine = context.createSSLEngine();
+ socket = (SSLSocket) context.getSocketFactory().createSocket();
+ serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertEquals(1, engine.getSSLParameters().getNamedGroups().length);
+ assertEquals(controlNamedGroups[0],
engine.getSSLParameters().getNamedGroups()[0]);
+ assertEquals(1, socket.getSSLParameters().getNamedGroups().length);
+ assertEquals(controlNamedGroups[0],
socket.getSSLParameters().getNamedGroups()[0]);
+ assertEquals(1,
serverSocket.getSSLParameters().getNamedGroups().length);
+ assertEquals(controlNamedGroups[0],
serverSocket.getSSLParameters().getNamedGroups()[0]);
+ }
+
+ @Test
+ public void testNamedGroupsFilter() throws Exception {
+ SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
+ controlContext.init(null, null, null);
+ SSLEngine controlEngine = controlContext.createSSLEngine();
+ String[] controlNamedGroups =
controlEngine.getSSLParameters().getNamedGroups();
+
+ // default - no filter, keeps defaults
+ SSLContextParameters scp = new SSLContextParameters();
+ SSLContext context = scp.createSSLContext(null);
+
+ SSLEngine engine = context.createSSLEngine();
+ SSLSocket socket = (SSLSocket)
context.getSocketFactory().createSocket();
+ SSLServerSocket serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertArrayEquals(controlNamedGroups,
engine.getSSLParameters().getNamedGroups());
+ assertArrayEquals(controlNamedGroups,
socket.getSSLParameters().getNamedGroups());
+ assertArrayEquals(controlNamedGroups,
serverSocket.getSSLParameters().getNamedGroups());
+
+ // empty filter - no includes means no groups match
+ FilterParameters filter = new FilterParameters();
+ scp.setNamedGroupsFilter(filter);
+ context = scp.createSSLContext(null);
+ engine = context.createSSLEngine();
+ socket = (SSLSocket) context.getSocketFactory().createSocket();
+ serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertEquals(0, engine.getSSLParameters().getNamedGroups().length);
+ assertEquals(0, socket.getSSLParameters().getNamedGroups().length);
+ assertEquals(0,
serverSocket.getSSLParameters().getNamedGroups().length);
+
+ // include all
+ filter.getInclude().add(".*");
+ context = scp.createSSLContext(null);
+ engine = context.createSSLEngine();
+ socket = (SSLSocket) context.getSocketFactory().createSocket();
+ serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertArrayEquals(controlNamedGroups,
engine.getSSLParameters().getNamedGroups());
+ assertArrayEquals(controlNamedGroups,
socket.getSSLParameters().getNamedGroups());
+ assertArrayEquals(controlNamedGroups,
serverSocket.getSSLParameters().getNamedGroups());
+
+ // include all but exclude all (excludes win)
+ filter.getExclude().add(".*");
+ context = scp.createSSLContext(null);
+ engine = context.createSSLEngine();
+ socket = (SSLSocket) context.getSocketFactory().createSocket();
+ serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertEquals(0, engine.getSSLParameters().getNamedGroups().length);
+ assertEquals(0, socket.getSSLParameters().getNamedGroups().length);
+ assertEquals(0,
serverSocket.getSSLParameters().getNamedGroups().length);
+
+ // include only x* groups (e.g. x25519, x448)
+ filter.getInclude().clear();
+ filter.getExclude().clear();
+ filter.getInclude().add("x.*");
+ context = scp.createSSLContext(null);
+ engine = context.createSSLEngine();
+ socket = (SSLSocket) context.getSocketFactory().createSocket();
+ serverSocket = (SSLServerSocket)
context.getServerSocketFactory().createServerSocket();
+
+ assertTrue(engine.getSSLParameters().getNamedGroups().length >= 1);
+ for (String group : engine.getSSLParameters().getNamedGroups()) {
+ assertTrue(group.startsWith("x"), "Expected group starting with
'x' but got: " + group);
+ }
+ assertTrue(socket.getSSLParameters().getNamedGroups().length >= 1);
+ for (String group : socket.getSSLParameters().getNamedGroups()) {
+ assertTrue(group.startsWith("x"), "Expected group starting with
'x' but got: " + group);
+ }
+ assertTrue(serverSocket.getSSLParameters().getNamedGroups().length >=
1);
+ for (String group : serverSocket.getSSLParameters().getNamedGroups()) {
+ assertTrue(group.startsWith("x"), "Expected group starting with
'x' but got: " + group);
+ }
+ }
+
+ @Test
+ public void testNamedGroupsDoNotAffectCipherSuitesOrProtocols() throws
Exception {
+ SSLContext controlContext = SSLContext.getInstance("TLSv1.3");
+ controlContext.init(null, null, null);
+ SSLEngine controlEngine = controlContext.createSSLEngine();
+
+ // setting named groups should not change cipher suites or protocols
+ SSLContextParameters scp = new SSLContextParameters();
+ NamedGroupsParameters ngp = new NamedGroupsParameters();
+ ngp.setNamedGroup(Collections.singletonList("x25519"));
+ scp.setNamedGroups(ngp);
+
+ SSLContext context = scp.createSSLContext(null);
+ SSLEngine engine = context.createSSLEngine();
+
+ assertArrayEquals(controlEngine.getEnabledCipherSuites(),
engine.getEnabledCipherSuites());
+ assertArrayEquals(controlEngine.getEnabledProtocols(),
engine.getEnabledProtocols());
+ assertEquals(1, engine.getSSLParameters().getNamedGroups().length);
+ assertEquals("x25519", engine.getSSLParameters().getNamedGroups()[0]);
+ }
+
@Test
public void testSessionTimeout() throws Exception {
SSLContextParameters scp = new SSLContextParameters();
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-configuration-utilities.adoc
b/docs/user-manual/modules/ROOT/pages/camel-configuration-utilities.adoc
index 295ad0a67378..2db76abada0a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-configuration-utilities.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-configuration-utilities.adoc
@@ -46,6 +46,8 @@ contain the following elements:
* cipherSuitesFilter (element)
* secureSocketProtocols (element)
* secureSocketProtocolsFilter (element)
+* namedGroups (element)
+* namedGroupsFilter (element)
* keyManagers (element)
* trustManagers (element)
* secureRandom (element)
@@ -113,6 +115,23 @@ are:
Includes .*
----
+namedGroups::
+This optional property represents a collection of explicitly named
+TLS named groups (key exchange algorithms) to enable on both the client
+and server side as well as in the SSLEngine. Named groups control which
+key exchange algorithms are available during the TLS handshake, including
+post-quantum hybrid groups such as `X25519MLKEM768`. These values take
+precedence over filters supplied in namedGroupsFilter. The utility
+attempts to enable the listed named groups regardless of whether the
+JSSE provider actually supports them or not. No default filtering is
+applied to named groups.
+namedGroupsFilter::
+This optional property represents a collection of include and exclude
+patterns for named groups to enable on both the client and server side
+as well as in the SSLEngine. The patterns are applied over only the
+available named groups. The excludes patterns have precedence over the
+includes patterns. No default filtering is applied to named groups.
+
keyManagers::
This optional property configures the source of key material for
providing identity of client and server side connections as well as in
@@ -271,6 +290,8 @@ SSLContextServerParameters contain the following elements:
* cipherSuitesFilter (element)
* secureSocketProtocols (element)
* secureSocketProtocolsFilter (element)
+* namedGroups (element)
+* namedGroupsFilter (element)
clientAuthentication::
This optional property indicates if the server side does not request,
@@ -300,6 +321,14 @@ secureSocketProtocolsFilter::
This optional property overrides the value of this
setting in the SSLContextParameters. This option has no effect on the
SSLEngine configuration.
+namedGroups::
+This optional property overrides the value of this
+setting in the SSLContextParameters. This option has no effect on the
+SSLEngine configuration.
+namedGroupsFilter::
+This optional property overrides the value of this
+setting in the SSLContextParameters. This option has no effect on the
+SSLEngine configuration.
=== SSLContextClientParameters
@@ -311,6 +340,8 @@ SSLContextClientParameters contains the following elements:
* cipherSuitesFilter (element)
* secureSocketProtocols (element)
* secureSocketProtocolsFilter (element)
+* namedGroups (element)
+* namedGroupsFilter (element)
sniHostNames::
Contains a list of sniHostName elements which provides a list
@@ -325,6 +356,10 @@ secureSocketProtocols::
See above
secureSocketProtocolsFilter::
See above
+namedGroups::
+See above
+namedGroupsFilter::
+See above
== Examples