This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch backport/28305-to-camel-4.22.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit c5fa2a678a8c31010c7a3f21fe8242fe9eb12806 Author: Guillaume Nodet <[email protected]> AuthorDate: Fri Oct 23 11:02:42 2026 +0000 [backport camel-4.22.x] CAMEL-25964: camel-vertx - VertxHelper does not apply standalone TrustManager from SSLContextParameters --- components/camel-vertx/camel-vertx-common/pom.xml | 12 + .../camel/component/vertx/common/VertxHelper.java | 375 +++++++++++---------- .../component/vertx/common/VertxHelperTest.java | 132 ++++++++ .../src/test/resources/jsse/truststore.jks | Bin 0 -> 582 bytes .../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 7 + 5 files changed, 339 insertions(+), 187 deletions(-) diff --git a/components/camel-vertx/camel-vertx-common/pom.xml b/components/camel-vertx/camel-vertx-common/pom.xml index 3ef527fbf2e6..e0319f8130df 100644 --- a/components/camel-vertx/camel-vertx-common/pom.xml +++ b/components/camel-vertx/camel-vertx-common/pom.xml @@ -52,5 +52,17 @@ </exclusion> </exclusions> </dependency> + + <!-- testing --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-junit6</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/components/camel-vertx/camel-vertx-common/src/main/java/org/apache/camel/component/vertx/common/VertxHelper.java b/components/camel-vertx/camel-vertx-common/src/main/java/org/apache/camel/component/vertx/common/VertxHelper.java index 20a1387785e3..8141e724645e 100644 --- a/components/camel-vertx/camel-vertx-common/src/main/java/org/apache/camel/component/vertx/common/VertxHelper.java +++ b/components/camel-vertx/camel-vertx-common/src/main/java/org/apache/camel/component/vertx/common/VertxHelper.java @@ -1,187 +1,188 @@ -/* - * 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.component.vertx.common; - -import java.security.KeyStore; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.TrustManagerFactory; - -import io.vertx.core.http.ClientAuth; -import io.vertx.core.net.NetServerOptions; -import io.vertx.core.net.TCPSSLOptions; -import io.vertx.core.net.TrustOptions; -import org.apache.camel.CamelContext; -import org.apache.camel.support.jsse.ClientAuthentication; -import org.apache.camel.support.jsse.KeyManagersParameters; -import org.apache.camel.support.jsse.SSLContextParameters; -import org.apache.camel.support.jsse.TrustAllTrustManager; -import org.apache.camel.support.jsse.TrustManagersParameters; - -public final class VertxHelper { - - private VertxHelper() { - // Utility class - } - - /** - * Configures key store and trust store options, the client authentication (of a server) and the cipher suites and - * protocols for the given TCPSSLOptions from the configuration specified on SSLContextParameters - * - * @param camelContext the CamelContext - * @param sslContextParameters the SSL configuration to use for the KeyManagerFactory & TrustManagerFactory - * @param tcpsslOptions the TCPSSLOptions instance to configure - */ - public static void setupSSLOptions( - CamelContext camelContext, SSLContextParameters sslContextParameters, TCPSSLOptions tcpsslOptions) - throws Exception { - - if (camelContext == null) { - throw new IllegalArgumentException("camelContext cannot be null"); - } - - if (sslContextParameters == null) { - throw new IllegalArgumentException("sslContextParameters cannot be null"); - } - - if (tcpsslOptions == null) { - throw new IllegalArgumentException("tcpsslOptions cannot be null"); - } - - tcpsslOptions.setSsl(true); - - KeyManagerFactory keyManagerFactory = createKeyManagerFactory(camelContext, sslContextParameters); - if (keyManagerFactory != null) { - tcpsslOptions.setKeyCertOptions(new KeyManagerFactoryOptions(keyManagerFactory)); - } - - TrustManagerFactory trustManagerFactory = createTrustManagerFactory(camelContext, sslContextParameters); - if (trustManagerFactory != null) { - tcpsslOptions.setTrustOptions(new TrustManagerFactoryOptions(trustManagerFactory)); - } - - if (sslContextParameters.getTrustManagers() != null && - sslContextParameters.getTrustManagers().getTrustManager() == TrustAllTrustManager.INSTANCE) { - tcpsslOptions.setTrustOptions(TrustOptions.wrap(TrustAllTrustManager.INSTANCE)); - } - - // client authentication of a server - if (tcpsslOptions instanceof NetServerOptions serverOptions - && sslContextParameters.getServerParameters() != null - && sslContextParameters.getServerParameters().getClientAuthentication() != null) { - String value = camelContext.resolvePropertyPlaceholders( - sslContextParameters.getServerParameters().getClientAuthentication()); - ClientAuthentication clientAuthentication = ClientAuthentication.valueOf(value.toUpperCase(Locale.ENGLISH)); - serverOptions.setClientAuth(switch (clientAuthentication) { - case REQUIRE -> ClientAuth.REQUIRED; - case WANT -> ClientAuth.REQUEST; - case NONE -> ClientAuth.NONE; - }); - } - - // the cipher suites and protocols (when configured) as computed by the SSLContextParameters - // (such as from the include and exclude filters) - boolean cipherSuites = sslContextParameters.getCipherSuites() != null - || sslContextParameters.getCipherSuitesFilter() != null; - boolean protocols = sslContextParameters.getSecureSocketProtocols() != null - || sslContextParameters.getSecureSocketProtocolsFilter() != null; - if (cipherSuites || protocols) { - SSLEngine engine = sslContextParameters.createSSLContext(camelContext).createSSLEngine(); - if (cipherSuites) { - for (String suite : engine.getEnabledCipherSuites()) { - tcpsslOptions.addEnabledCipherSuite(suite); - } - } - if (protocols) { - tcpsslOptions.setEnabledSecureTransportProtocols(new LinkedHashSet<>(List.of(engine.getEnabledProtocols()))); - } - } - } - - private static KeyManagerFactory createKeyManagerFactory( - CamelContext camelContext, SSLContextParameters sslContextParameters) - throws Exception { - final KeyManagersParameters keyManagers = sslContextParameters.getKeyManagers(); - if (keyManagers == null) { - return null; - } - keyManagers.setCamelContext(camelContext); - if (keyManagers.getKeyStore() != null) { - keyManagers.getKeyStore().setCamelContext(camelContext); - } - - String kmfAlgorithm = camelContext.resolvePropertyPlaceholders(keyManagers.getAlgorithm()); - if (kmfAlgorithm == null) { - kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); - } - - KeyManagerFactory kmf; - if (keyManagers.getProvider() == null) { - kmf = KeyManagerFactory.getInstance(kmfAlgorithm); - } else { - kmf = KeyManagerFactory.getInstance(kmfAlgorithm, - camelContext.resolvePropertyPlaceholders(keyManagers.getProvider())); - } - - char[] kmfPassword = null; - if (keyManagers.getKeyPassword() != null) { - kmfPassword = camelContext.resolvePropertyPlaceholders(keyManagers.getKeyPassword()).toCharArray(); - } - - KeyStore ks = keyManagers.getKeyStore() == null ? null : keyManagers.getKeyStore().createKeyStore(); - - kmf.init(ks, kmfPassword); - return kmf; - } - - private static TrustManagerFactory createTrustManagerFactory( - CamelContext camelContext, SSLContextParameters sslContextParameters) - throws Exception { - final TrustManagersParameters trustManagers = sslContextParameters.getTrustManagers(); - if (trustManagers == null) { - return null; - } - trustManagers.setCamelContext(camelContext); - if (trustManagers.getKeyStore() != null) { - trustManagers.getKeyStore().setCamelContext(camelContext); - } - - TrustManagerFactory tmf = null; - - if (trustManagers.getKeyStore() != null) { - String tmfAlgorithm = camelContext.resolvePropertyPlaceholders(trustManagers.getAlgorithm()); - if (tmfAlgorithm == null) { - tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); - } - - if (trustManagers.getProvider() == null) { - tmf = TrustManagerFactory.getInstance(tmfAlgorithm); - } else { - tmf = TrustManagerFactory.getInstance(tmfAlgorithm, - camelContext.resolvePropertyPlaceholders(trustManagers.getProvider())); - } - - KeyStore ks = trustManagers.getKeyStore() == null ? null : trustManagers.getKeyStore().createKeyStore(); - tmf.init(ks); - } - return tmf; - } -} +/* + * 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.component.vertx.common; + +import java.security.KeyStore; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import io.vertx.core.http.ClientAuth; +import io.vertx.core.net.NetServerOptions; +import io.vertx.core.net.TCPSSLOptions; +import io.vertx.core.net.TrustOptions; +import org.apache.camel.CamelContext; +import org.apache.camel.support.jsse.ClientAuthentication; +import org.apache.camel.support.jsse.KeyManagersParameters; +import org.apache.camel.support.jsse.SSLContextParameters; +import org.apache.camel.support.jsse.TrustManagersParameters; + +public final class VertxHelper { + + private VertxHelper() { + // Utility class + } + + /** + * Configures key store and trust store options, the client authentication (of a server) and the cipher suites and + * protocols for the given TCPSSLOptions from the configuration specified on SSLContextParameters + * + * @param camelContext the CamelContext + * @param sslContextParameters the SSL configuration to use for the KeyManagerFactory & TrustManagerFactory + * @param tcpsslOptions the TCPSSLOptions instance to configure + */ + public static void setupSSLOptions( + CamelContext camelContext, SSLContextParameters sslContextParameters, TCPSSLOptions tcpsslOptions) + throws Exception { + + if (camelContext == null) { + throw new IllegalArgumentException("camelContext cannot be null"); + } + + if (sslContextParameters == null) { + throw new IllegalArgumentException("sslContextParameters cannot be null"); + } + + if (tcpsslOptions == null) { + throw new IllegalArgumentException("tcpsslOptions cannot be null"); + } + + tcpsslOptions.setSsl(true); + + KeyManagerFactory keyManagerFactory = createKeyManagerFactory(camelContext, sslContextParameters); + if (keyManagerFactory != null) { + tcpsslOptions.setKeyCertOptions(new KeyManagerFactoryOptions(keyManagerFactory)); + } + + TrustManager trustManager = sslContextParameters.getTrustManagers() != null + ? sslContextParameters.getTrustManagers().getTrustManager() : null; + if (trustManager != null) { + tcpsslOptions.setTrustOptions(TrustOptions.wrap(trustManager)); + } else { + TrustManagerFactory trustManagerFactory = createTrustManagerFactory(camelContext, sslContextParameters); + if (trustManagerFactory != null) { + tcpsslOptions.setTrustOptions(new TrustManagerFactoryOptions(trustManagerFactory)); + } + } + + // client authentication of a server + if (tcpsslOptions instanceof NetServerOptions serverOptions + && sslContextParameters.getServerParameters() != null + && sslContextParameters.getServerParameters().getClientAuthentication() != null) { + String value = camelContext.resolvePropertyPlaceholders( + sslContextParameters.getServerParameters().getClientAuthentication()); + ClientAuthentication clientAuthentication = ClientAuthentication.valueOf(value.toUpperCase(Locale.ENGLISH)); + serverOptions.setClientAuth(switch (clientAuthentication) { + case REQUIRE -> ClientAuth.REQUIRED; + case WANT -> ClientAuth.REQUEST; + case NONE -> ClientAuth.NONE; + }); + } + + // the cipher suites and protocols (when configured) as computed by the SSLContextParameters + // (such as from the include and exclude filters) + boolean cipherSuites = sslContextParameters.getCipherSuites() != null + || sslContextParameters.getCipherSuitesFilter() != null; + boolean protocols = sslContextParameters.getSecureSocketProtocols() != null + || sslContextParameters.getSecureSocketProtocolsFilter() != null; + if (cipherSuites || protocols) { + SSLEngine engine = sslContextParameters.createSSLContext(camelContext).createSSLEngine(); + if (cipherSuites) { + for (String suite : engine.getEnabledCipherSuites()) { + tcpsslOptions.addEnabledCipherSuite(suite); + } + } + if (protocols) { + tcpsslOptions.setEnabledSecureTransportProtocols(new LinkedHashSet<>(List.of(engine.getEnabledProtocols()))); + } + } + } + + private static KeyManagerFactory createKeyManagerFactory( + CamelContext camelContext, SSLContextParameters sslContextParameters) + throws Exception { + final KeyManagersParameters keyManagers = sslContextParameters.getKeyManagers(); + if (keyManagers == null) { + return null; + } + keyManagers.setCamelContext(camelContext); + if (keyManagers.getKeyStore() != null) { + keyManagers.getKeyStore().setCamelContext(camelContext); + } + + String kmfAlgorithm = camelContext.resolvePropertyPlaceholders(keyManagers.getAlgorithm()); + if (kmfAlgorithm == null) { + kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + } + + KeyManagerFactory kmf; + if (keyManagers.getProvider() == null) { + kmf = KeyManagerFactory.getInstance(kmfAlgorithm); + } else { + kmf = KeyManagerFactory.getInstance(kmfAlgorithm, + camelContext.resolvePropertyPlaceholders(keyManagers.getProvider())); + } + + char[] kmfPassword = null; + if (keyManagers.getKeyPassword() != null) { + kmfPassword = camelContext.resolvePropertyPlaceholders(keyManagers.getKeyPassword()).toCharArray(); + } + + KeyStore ks = keyManagers.getKeyStore() == null ? null : keyManagers.getKeyStore().createKeyStore(); + + kmf.init(ks, kmfPassword); + return kmf; + } + + private static TrustManagerFactory createTrustManagerFactory( + CamelContext camelContext, SSLContextParameters sslContextParameters) + throws Exception { + final TrustManagersParameters trustManagers = sslContextParameters.getTrustManagers(); + if (trustManagers == null) { + return null; + } + trustManagers.setCamelContext(camelContext); + if (trustManagers.getKeyStore() != null) { + trustManagers.getKeyStore().setCamelContext(camelContext); + } + + TrustManagerFactory tmf = null; + + if (trustManagers.getKeyStore() != null) { + String tmfAlgorithm = camelContext.resolvePropertyPlaceholders(trustManagers.getAlgorithm()); + if (tmfAlgorithm == null) { + tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + } + + if (trustManagers.getProvider() == null) { + tmf = TrustManagerFactory.getInstance(tmfAlgorithm); + } else { + tmf = TrustManagerFactory.getInstance(tmfAlgorithm, + camelContext.resolvePropertyPlaceholders(trustManagers.getProvider())); + } + + KeyStore ks = trustManagers.getKeyStore() == null ? null : trustManagers.getKeyStore().createKeyStore(); + tmf.init(ks); + } + return tmf; + } +} diff --git a/components/camel-vertx/camel-vertx-common/src/test/java/org/apache/camel/component/vertx/common/VertxHelperTest.java b/components/camel-vertx/camel-vertx-common/src/test/java/org/apache/camel/component/vertx/common/VertxHelperTest.java new file mode 100644 index 000000000000..539d09a15318 --- /dev/null +++ b/components/camel-vertx/camel-vertx-common/src/test/java/org/apache/camel/component/vertx/common/VertxHelperTest.java @@ -0,0 +1,132 @@ +/* + * 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.component.vertx.common; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +import io.vertx.core.net.NetClientOptions; +import io.vertx.core.net.TCPSSLOptions; +import org.apache.camel.CamelContext; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.support.jsse.KeyStoreParameters; +import org.apache.camel.support.jsse.SSLContextParameters; +import org.apache.camel.support.jsse.TrustAllTrustManager; +import org.apache.camel.support.jsse.TrustManagersParameters; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class VertxHelperTest { + + private CamelContext context; + + @BeforeEach + public void setUp() { + context = new DefaultCamelContext(); + } + + @AfterEach + public void tearDown() throws Exception { + if (context != null) { + context.stop(); + } + } + + @Test + public void testStandaloneCustomTrustManagerApplied() throws Exception { + SSLContextParameters sslContextParameters = new SSLContextParameters(); + TrustManagersParameters trustManagers = new TrustManagersParameters(); + + X509TrustManager customTrustManager = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + throw new CertificateException("Custom reject"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + throw new CertificateException("Custom reject"); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }; + + trustManagers.setTrustManager(customTrustManager); + sslContextParameters.setTrustManagers(trustManagers); + + TCPSSLOptions options = new NetClientOptions(); + VertxHelper.setupSSLOptions(context, sslContextParameters, options); + + assertThat(options.isSsl()).isTrue(); + assertThat(options.getTrustOptions()).isNotNull(); + assertThat(options.getTrustOptions().getTrustManagerFactory(null).getTrustManagers()) + .containsExactly(customTrustManager); + } + + @Test + public void testTrustAllTrustManagerApplied() throws Exception { + SSLContextParameters sslContextParameters = new SSLContextParameters(); + TrustManagersParameters trustManagers = new TrustManagersParameters(); + trustManagers.setTrustManager(TrustAllTrustManager.INSTANCE); + sslContextParameters.setTrustManagers(trustManagers); + + TCPSSLOptions options = new NetClientOptions(); + VertxHelper.setupSSLOptions(context, sslContextParameters, options); + + assertThat(options.isSsl()).isTrue(); + assertThat(options.getTrustOptions()).isNotNull(); + assertThat(options.getTrustOptions().getTrustManagerFactory(null).getTrustManagers()) + .containsExactly(TrustAllTrustManager.INSTANCE); + } + + @Test + public void testKeyStoreTrustManagerFactoryApplied() throws Exception { + SSLContextParameters sslContextParameters = new SSLContextParameters(); + TrustManagersParameters trustManagers = new TrustManagersParameters(); + KeyStoreParameters keyStore = new KeyStoreParameters(); + keyStore.setResource("jsse/truststore.jks"); + keyStore.setPassword("storepass"); + trustManagers.setKeyStore(keyStore); + sslContextParameters.setTrustManagers(trustManagers); + + TCPSSLOptions options = new NetClientOptions(); + VertxHelper.setupSSLOptions(context, sslContextParameters, options); + + assertThat(options.isSsl()).isTrue(); + assertThat(options.getTrustOptions()).isNotNull(); + assertThat(options.getTrustOptions()).isInstanceOf(TrustManagerFactoryOptions.class); + } + + @Test + public void testNoTrustOptionsWhenNoneConfigured() throws Exception { + SSLContextParameters sslContextParameters = new SSLContextParameters(); + + TCPSSLOptions options = new NetClientOptions(); + VertxHelper.setupSSLOptions(context, sslContextParameters, options); + + assertThat(options.isSsl()).isTrue(); + assertThat(options.getTrustOptions()).isNull(); + } +} diff --git a/components/camel-vertx/camel-vertx-common/src/test/resources/jsse/truststore.jks b/components/camel-vertx/camel-vertx-common/src/test/resources/jsse/truststore.jks new file mode 100644 index 000000000000..44d82a85baa9 Binary files /dev/null and b/components/camel-vertx/camel-vertx-common/src/test/resources/jsse/truststore.jks differ diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc index 815dbc57e3dd..d69bc9153916 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc @@ -2086,3 +2086,10 @@ acknowledged and the failure is left to the Camel error handler. A consumer that option leaves a failed QoS 1 message on a session that the broker keeps unacknowledged, as before; as the consumer does not connect that client, set the `cleanStart` and `sessionExpiryInterval` of the endpoint to match its connection options. + +=== camel-vertx - standalone TrustManager on SSLContextParameters is applied + +When configuring TLS through `SSLContextParameters` with a standalone `TrustManager` instance (via `TrustManagersParameters.setTrustManager(...)`), Vert.x components now wrap and apply the configured `TrustManager`. Previously, only `TrustAllTrustManager.INSTANCE` and KeyStore-backed configurations were applied, and other standalone `TrustManager` instances were not set on the underlying Vert.x options. + +When both a `TrustManager` and a keystore are configured on `TrustManagersParameters`, the `TrustManager` is now used, +as with the other components that use `SSLContextParameters`; before, the keystore-based trust options were used.
