This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch task-41 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 92b67655ab26db34c573d80892143ff0269ba6d3 Author: Guillaume Nodet <[email protected]> AuthorDate: Mon Mar 9 12:07:01 2026 +0100 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 --- .../support/jsse/BaseSSLContextParameters.java | 98 ++++++++----- ...bstractBaseSSLContextParametersFactoryBean.java | 31 ++++ .../util/jsse/NamedGroupsParametersDefinition.java | 46 ++++++ .../support/jsse/SSLContextParametersTest.java | 159 +++++++++++++++++++++ .../ROOT/pages/camel-configuration-utilities.adoc | 35 +++++ 5 files changed, 331 insertions(+), 38 deletions(-) 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 0d00bbaa3010..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 @@ -449,8 +449,22 @@ public abstract class BaseSSLContextParameters extends JsseParameters { engine.setEnabledProtocols( filteredSecureSocketProtocols.toArray(new String[0])); - configureNamedGroups(engine.getSSLParameters(), enabledNamedGroups, enabledNamedGroupsPatterns, - engine, SSL_ENGINE_NAMED_GROUP_LOG_MSG); + 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; } @@ -633,8 +647,22 @@ public abstract class BaseSSLContextParameters extends JsseParameters { socket.setEnabledProtocols( filteredSecureSocketProtocols.toArray(new String[0])); - configureNamedGroups(socket.getSSLParameters(), enabledNamedGroups, enabledNamedGroupsPatterns, - socket, SSL_SOCKET_NAMED_GROUP_LOG_MSG); + 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; } @@ -742,8 +770,22 @@ public abstract class BaseSSLContextParameters extends JsseParameters { socket.setEnabledProtocols( filteredSecureSocketProtocols.toArray(new String[0])); - configureNamedGroups(socket.getSSLParameters(), enabledNamedGroups, enabledNamedGroupsPatterns, - socket, SSL_SERVER_SOCKET_NAMED_GROUP_LOG_MSG); + 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; } @@ -887,25 +929,23 @@ public abstract class BaseSSLContextParameters extends JsseParameters { } /** - * Configures named groups on an {@link SSLParameters} instance. If explicit named groups are provided, those are - * used directly. Otherwise, if a named groups filter is configured, the available named groups from the current - * SSLParameters are filtered according to the include/exclude patterns. + * 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 * - * @param sslParameters the current SSL parameters to read available named groups from - * @param enabledNamedGroups the optional explicit named groups list - * @param enabledNamedGroupsPatterns the optional filter patterns - * @param target the SSL object (engine, socket, or server socket) to configure - * @param logMessage the log message template + * @return the filtered named groups array, or {@code null} if no configuration is needed */ - private void configureNamedGroups( - SSLParameters sslParameters, List<String> enabledNamedGroups, - Patterns enabledNamedGroupsPatterns, Object target, String logMessage) { + private String[] resolveNamedGroups( + String[] currentNamedGroups, List<String> enabledNamedGroups, + Patterns enabledNamedGroupsPatterns) { if (enabledNamedGroups == null && enabledNamedGroupsPatterns == null) { - return; + return null; } - String[] currentNamedGroups = sslParameters.getNamedGroups(); if (currentNamedGroups == null) { currentNamedGroups = new String[0]; } @@ -919,25 +959,7 @@ public abstract class BaseSSLContextParameters extends JsseParameters { enabledNamedGroupsPatterns.getIncludes(), enabledNamedGroupsPatterns.getExcludes()); } - if (LOG.isDebugEnabled()) { - LOG.debug(logMessage, - target, - enabledNamedGroups, - enabledNamedGroupsPatterns, - currentNamedGroups, - filteredNamedGroups); - } - - SSLParameters updatedParams = sslParameters; - updatedParams.setNamedGroups(filteredNamedGroups.toArray(new String[0])); - - if (target instanceof SSLEngine engine) { - engine.setSSLParameters(updatedParams); - } else if (target instanceof SSLSocket socket) { - socket.setSSLParameters(updatedParams); - } else if (target instanceof SSLServerSocket serverSocket) { - serverSocket.setSSLParameters(updatedParams); - } + return filteredNamedGroups.toArray(new String[0]); } /** 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
