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

davsclaus pushed a commit to branch fix/CAMEL-25102
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 255315ab62f5252dec53928bdf41ed04b582b2fd
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 28 17:08:08 2026 +0200

    CAMEL-25102: camel-core - JSSE utility and camel.ssl configuration: fix 
bugs found in a deep review
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../support/jsse/BaseSSLContextParameters.java     |  33 ++++++-
 .../support/jsse/SSLContextClientParameters.java   |  37 +++++---
 .../camel/support/jsse/SSLContextParameters.java   |  28 +++---
 .../support/jsse/SSLContextServerParameters.java   |   7 +-
 .../camel/support/jsse/JsseEdgeCasesTest.java      | 103 +++++++++++++++++++++
 .../support/jsse/SSLContextParametersTest.java     |  32 ++++---
 .../org/apache/camel/main/BaseMainSupport.java     |  21 ++++-
 .../apache/camel/main/MainSSLTrustStoreTest.java   |  77 +++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  16 ++++
 .../ROOT/pages/camel-configuration-utilities.adoc  |  19 ++--
 10 files changed, 321 insertions(+), 52 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 2d3e0a5ad909..aec871e93f50 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
@@ -47,6 +47,7 @@ import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 
+import org.apache.camel.CamelContext;
 import org.apache.camel.support.jsse.FilterParameters.Patterns;
 import org.jspecify.annotations.Nullable;
 import org.slf4j.Logger;
@@ -58,6 +59,8 @@ import org.slf4j.LoggerFactory;
  */
 public abstract class BaseSSLContextParameters extends JsseParameters {
 
+    private volatile boolean signatureSchemesFilterWarned;
+
     protected static final List<String> DEFAULT_CIPHER_SUITES_FILTER_INCLUDE
             = List.of(".*");
 
@@ -242,6 +245,19 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
         this.cipherSuites = cipherSuites;
     }
 
+    @Override
+    public void setCamelContext(CamelContext context) {
+        super.setCamelContext(context);
+        // the filter patterns may use property placeholders
+        for (FilterParameters filter : new FilterParameters[] {
+                getCipherSuitesFilter(), getSecureSocketProtocolsFilter(), 
getNamedGroupsFilter(),
+                getSignatureSchemesFilter() }) {
+            if (filter != null) {
+                filter.setCamelContext(context);
+            }
+        }
+    }
+
     /**
      * Returns the optional cipher suite 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
@@ -1133,8 +1149,10 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
         } else {
             returnValues = new LinkedList<>();
 
+            // a filter with only exclude patterns includes all the other 
values
+            boolean includeAll = includePatterns.isEmpty() && 
!excludePatterns.isEmpty();
             for (String value : availableValues) {
-                if (this.matchesOneOf(value, includePatterns)
+                if ((includeAll || this.matchesOneOf(value, includePatterns))
                         && !this.matchesOneOf(value, excludePatterns)) {
                     returnValues.add(value);
                 }
@@ -1221,6 +1239,16 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
             return null;
         }
 
+        if (currentSignatureSchemes == null && enabledSignatureSchemes == 
null) {
+            // the JVM does not tell its default signature schemes (null), so 
there is nothing to filter, and
+            // configuring an empty list would fail every handshake
+            if (!signatureSchemesFilterWarned) {
+                signatureSchemesFilterWarned = true;
+                LOG.warn("The signature schemes filter cannot be applied as 
the JVM does not provide its default"
+                         + " signature schemes. Configure the signature 
schemes explicitly instead of using a filter.");
+            }
+            return null;
+        }
         if (currentSignatureSchemes == null) {
             currentSignatureSchemes = new String[0];
         }
@@ -1310,8 +1338,7 @@ public abstract class BaseSSLContextParameters extends 
JsseParameters {
         protected SSLEngine engineCreateSSLEngine() {
             SSLEngine engine = this.context.createSSLEngine();
             LOG.debug("SSLEngine [{}] created from SSLContext [{}].", engine, 
context);
-            this.configureSSLEngine(engine);
-            return engine;
+            return this.configureSSLEngine(engine);
         }
 
         @Override
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextClientParameters.java
 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextClientParameters.java
index f6603d6dd099..c1dd4a72d3f2 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextClientParameters.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextClientParameters.java
@@ -26,6 +26,7 @@ import javax.net.ssl.SNIHostName;
 import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLServerSocketFactory;
 
 import org.slf4j.Logger;
@@ -41,23 +42,26 @@ public class SSLContextClientParameters extends 
BaseSSLContextParameters {
     /**
      * SNI hostnames to be used for SSL (Server Name Indicator)
      */
-    private final List<SNIServerName> sniHostNames = new ArrayList<>();
+    private final List<String> sniHostNames = new ArrayList<>();
 
     public void addAllSniHostNames(List<String> sniHostNames) {
         Objects.requireNonNull(sniHostNames, "sniHostNames");
-        for (String sniHostName : sniHostNames) {
-            this.sniHostNames.add(new SNIHostName(sniHostName));
-        }
+        this.sniHostNames.addAll(sniHostNames);
     }
 
     public void setSniHostName(String sniHostName) {
         Objects.requireNonNull(sniHostName, "sniHostName");
-        this.sniHostNames.add(new SNIHostName(sniHostName));
+        this.sniHostNames.add(sniHostName);
     }
 
     @Override
     protected List<SNIServerName> getSNIHostNames() {
-        return sniHostNames;
+        // the host names may use property placeholders
+        List<SNIServerName> answer = new ArrayList<>(sniHostNames.size());
+        for (String name : sniHostNames) {
+            answer.add(new SNIHostName(parsePropertyValue(name)));
+        }
+        return answer;
     }
 
     @Override
@@ -77,15 +81,24 @@ public class SSLContextClientParameters extends 
BaseSSLContextParameters {
     }
 
     /**
-     * This implementation returns the empty list as the enabled cipher suites 
and protocols are not client and server
-     * side specific in an {@code SSLEngine}. Consequently, overriding them 
here would be a bit odd as the client side
-     * specific configuration shouldn't really override a shared client/server 
configuration option.
+     * This implementation only configures the SNI host names, as the enabled 
cipher suites and protocols are not client
+     * and server side specific in an {@code SSLEngine}. Consequently, 
overriding them here would be a bit odd as the
+     * client side specific configuration shouldn't really override a shared 
client/server configuration option.
      */
     @Override
     protected List<Configurer<SSLEngine>> getSSLEngineConfigurers(SSLContext 
context) {
-        // NOTE: if the super class gets additional shared configuration 
options beyond
-        // cipher suites and protocols, this method needs to address that.
-        return Collections.emptyList();
+        final List<SNIServerName> names = getSNIHostNames();
+        if (names.isEmpty()) {
+            return Collections.emptyList();
+        }
+        // the server names are only used by an engine in client mode
+        Configurer<SSLEngine> sniConfigurer = engine -> {
+            SSLParameters params = engine.getSSLParameters();
+            params.setServerNames(names);
+            engine.setSSLParameters(params);
+            return engine;
+        };
+        return Collections.singletonList(sniConfigurer);
     }
 
     /**
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
index ca7ff156a0b3..c03190c78060 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextParameters.java
@@ -329,19 +329,21 @@ public class SSLContextParameters extends 
BaseSSLContextParameters {
             autoConfiguredPqc = applyPqcNamedGroupDefaults(context);
         }
 
-        this.configureSSLContext(context);
-
-        // Decorate the context.
-        context = new SSLContextDecorator(
-                new SSLContextSpiDecorator(
-                        context,
-                        this.getSSLEngineConfigurers(context),
-                        this.getSSLSocketFactoryConfigurers(context),
-                        this.getSSLServerSocketFactoryConfigurers(context)));
-
-        // Reset auto-configured PQC named groups so they don't persist on 
this instance
-        if (autoConfiguredPqc) {
-            this.setNamedGroups(null);
+        try {
+            this.configureSSLContext(context);
+
+            // Decorate the context.
+            context = new SSLContextDecorator(
+                    new SSLContextSpiDecorator(
+                            context,
+                            this.getSSLEngineConfigurers(context),
+                            this.getSSLSocketFactoryConfigurers(context),
+                            
this.getSSLServerSocketFactoryConfigurers(context)));
+        } finally {
+            // Reset auto-configured PQC named groups so they don't persist on 
this instance (also on failure)
+            if (autoConfiguredPqc) {
+                this.setNamedGroups(null);
+            }
         }
 
         return context;
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextServerParameters.java
 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextServerParameters.java
index 848970756311..d0b017f42cad 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextServerParameters.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/support/jsse/SSLContextServerParameters.java
@@ -20,6 +20,7 @@ import java.security.GeneralSecurityException;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
@@ -87,7 +88,8 @@ public class SSLContextServerParameters extends 
BaseSSLContextParameters {
             if (clientAuthStr == null) {
                 clientAuthStr = this.getClientAuthentication();
             }
-            final ClientAuthentication clientAuthValue = 
ClientAuthentication.valueOf(clientAuthStr);
+            final ClientAuthentication clientAuthValue
+                    = 
ClientAuthentication.valueOf(clientAuthStr.toUpperCase(Locale.ENGLISH));
 
             Configurer<SSLEngine> sslEngineConfigurer = new Configurer<>() {
                 @Override
@@ -129,7 +131,8 @@ public class SSLContextServerParameters extends 
BaseSSLContextParameters {
             if (clientAuthStr == null) {
                 clientAuthStr = this.getClientAuthentication();
             }
-            final ClientAuthentication clientAuthValue = 
ClientAuthentication.valueOf(clientAuthStr);
+            final ClientAuthentication clientAuthValue
+                    = 
ClientAuthentication.valueOf(clientAuthStr.toUpperCase(Locale.ENGLISH));
 
             Configurer<SSLServerSocket> sslServerSocketConfigurer = new 
Configurer<>() {
                 @Override
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/support/jsse/JsseEdgeCasesTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/support/jsse/JsseEdgeCasesTest.java
new file mode 100644
index 000000000000..caf3506e7d0b
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/support/jsse/JsseEdgeCasesTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.List;
+import java.util.Properties;
+
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class JsseEdgeCasesTest {
+
+    private static CamelContext createContext(String key, String value) {
+        CamelContext context = new DefaultCamelContext();
+        Properties prop = new Properties();
+        prop.setProperty(key, value);
+        context.getPropertiesComponent().setInitialProperties(prop);
+        return context;
+    }
+
+    @Test
+    public void testFilterWithOnlyExcludes() throws Exception {
+        FilterParameters filter = new FilterParameters();
+        filter.getExclude().add(".*_CBC_.*");
+        SSLContextParameters scp = new SSLContextParameters();
+        scp.setCipherSuitesFilter(filter);
+
+        SSLEngine engine = scp.createSSLContext(null).createSSLEngine();
+        String[] suites = engine.getEnabledCipherSuites();
+        assertTrue(suites.length > 0);
+        for (String suite : suites) {
+            assertFalse(suite.contains("_CBC_"), suite);
+        }
+    }
+
+    @Test
+    public void testFilterPatternWithPlaceholder() throws Exception {
+        FilterParameters filter = new FilterParameters();
+        filter.getInclude().add("{{inc}}");
+        SSLContextParameters scp = new SSLContextParameters();
+        scp.setCipherSuitesFilter(filter);
+
+        SSLEngine engine = scp.createSSLContext(createContext("inc", 
"TLS_AES_.*")).createSSLEngine();
+        String[] suites = engine.getEnabledCipherSuites();
+        assertTrue(suites.length > 0);
+        for (String suite : suites) {
+            assertTrue(suite.startsWith("TLS_AES_"), suite);
+        }
+    }
+
+    @Test
+    public void testSniOnSSLEngine() throws Exception {
+        SSLContextClientParameters client = new SSLContextClientParameters();
+        client.setSniHostName("{{sni}}");
+        SSLContextParameters scp = new SSLContextParameters();
+        scp.setClientParameters(client);
+
+        SSLContext context = scp.createSSLContext(createContext("sni", 
"example.com"));
+        SSLEngine engine = context.createSSLEngine();
+        engine.setUseClientMode(true);
+        List<SNIServerName> names = engine.getSSLParameters().getServerNames();
+        assertEquals(List.of(new SNIHostName("example.com")), names);
+
+        engine = context.createSSLEngine("localhost", 8443);
+        assertEquals(List.of(new SNIHostName("example.com")), 
engine.getSSLParameters().getServerNames());
+    }
+
+    @Test
+    public void testClientAuthenticationIgnoreCase() throws Exception {
+        SSLContextServerParameters server = new SSLContextServerParameters();
+        server.setClientAuthentication("want");
+        SSLContextParameters scp = new SSLContextParameters();
+        scp.setServerParameters(server);
+
+        SSLEngine engine = scp.createSSLContext(null).createSSLEngine();
+        engine.setUseClientMode(false);
+        assertTrue(engine.getWantClientAuth());
+    }
+}
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 d23cf5f71550..80053adead2f 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
@@ -1003,17 +1003,20 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         assertNull(getSignatureSchemes(socket.getSSLParameters()));
         assertNull(getSignatureSchemes(serverSocket.getSSLParameters()));
 
-        // empty filter - no includes means no schemes match (empty array)
+        // a filter cannot be applied as there are no default schemes to 
filter, so the defaults are kept (null)
+        // instead of an empty list (which would fail every handshake)
         FilterParameters filter = new FilterParameters();
+        filter.getInclude().add(".*");
         scp.setSignatureSchemesFilter(filter);
         context = scp.createSSLContext(null);
         engine = context.createSSLEngine();
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(0, getSignatureSchemes(engine.getSSLParameters()).length);
-        assertEquals(0, getSignatureSchemes(socket.getSSLParameters()).length);
-        assertEquals(0, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
+        assertNull(getSignatureSchemes(engine.getSSLParameters()));
+        assertNull(getSignatureSchemes(socket.getSSLParameters()));
+        assertNull(getSignatureSchemes(serverSocket.getSSLParameters()));
+        filter.getInclude().clear();
 
         // explicit schemes override filter - filter ignored when schemes are 
set
         SignatureSchemesParameters ssp = new SignatureSchemesParameters();
@@ -1032,7 +1035,7 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         // explicit schemes take precedence over filter
         assertEquals(4, getSignatureSchemes(engine.getSSLParameters()).length);
 
-        // clear explicit schemes, keep filter - now filter applies to empty 
JDK defaults
+        // clear explicit schemes, keep filter
         scp.setSignatureSchemes(null);
         filter.getInclude().clear();
         filter.getInclude().add(".*");
@@ -1041,10 +1044,10 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        // JDK defaults are null → filtering null gives empty array
-        assertEquals(0, getSignatureSchemes(engine.getSSLParameters()).length);
-        assertEquals(0, getSignatureSchemes(socket.getSSLParameters()).length);
-        assertEquals(0, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
+        // JDK defaults are null → the filter cannot be applied and the 
defaults are kept (not an empty array)
+        assertNull(getSignatureSchemes(engine.getSSLParameters()));
+        assertNull(getSignatureSchemes(socket.getSSLParameters()));
+        assertNull(getSignatureSchemes(serverSocket.getSSLParameters()));
     }
 
     @Test
@@ -1066,17 +1069,20 @@ public class SSLContextParametersTest extends 
AbstractJsseParametersTest {
 
         int defaultSignatureSchemeNumber = 
getSignatureSchemes(engine.getSSLParameters()).length;
 
-        // empty filter - no includes means no schemes match (empty array)
+        // a filter cannot be applied as there are no default schemes to 
filter, so the defaults are kept (null)
+        // instead of an empty list (which would fail every handshake)
         FilterParameters filter = new FilterParameters();
+        filter.getInclude().add(".*");
         scp.setSignatureSchemesFilter(filter);
         context = scp.createSSLContext(null);
         engine = context.createSSLEngine();
         socket = (SSLSocket) context.getSocketFactory().createSocket();
         serverSocket = (SSLServerSocket) 
context.getServerSocketFactory().createServerSocket();
 
-        assertEquals(0, getSignatureSchemes(engine.getSSLParameters()).length);
-        assertEquals(0, getSignatureSchemes(socket.getSSLParameters()).length);
-        assertEquals(0, 
getSignatureSchemes(serverSocket.getSSLParameters()).length);
+        assertNull(getSignatureSchemes(engine.getSSLParameters()));
+        assertNull(getSignatureSchemes(socket.getSSLParameters()));
+        assertNull(getSignatureSchemes(serverSocket.getSSLParameters()));
+        filter.getInclude().clear();
 
         // explicit schemes override filter - filter ignored when schemes are 
set
         SignatureSchemesParameters ssp = new SignatureSchemesParameters();
diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java 
b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 92dd2ff00a3e..9fbe06aded61 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -2341,8 +2341,24 @@ public abstract class BaseMainSupport extends 
BaseService {
             throws Exception {
 
         SSLConfigurationProperties sslConfig = 
mainConfigurationProperties.sslConfig();
+
+        // a trust store that refers to a KeyStore bean (#bean:name) must be 
kept as-is (and not be converted to a String
+        // when binding the properties) so the bean can be looked up when 
creating the trust managers
+        String trustStoreBean = null;
+        for (String key : properties.stringPropertyNames()) {
+            String value = properties.getProperty(key);
+            if (key.equalsIgnoreCase("trustStore") && value != null && 
value.startsWith("#bean:")) {
+                autoConfiguredProperties.put(properties.getLocation(key), 
PREFIX_SSL + "trustStore", value);
+                properties.remove(key);
+                trustStoreBean = value;
+            }
+        }
+
         setPropertiesOnTarget(camelContext, sslConfig, properties, PREFIX_SSL,
                 failIfNotSet, true, autoConfiguredProperties);
+        if (trustStoreBean != null) {
+            sslConfig.setTrustStore(trustStoreBean);
+        }
 
         if (!sslConfig.isEnabled()) {
             return;
@@ -2381,8 +2397,11 @@ public abstract class BaseMainSupport extends 
BaseService {
             kmp.setCamelContext(camelContext);
             kmp.setKeyPassword(password);
             kmp.setKeyStore(ksp);
+        } else if (sslConfig.getTrustStore() != null || 
sslConfig.isTrustAllCertificates()) {
+            // client side only (no key store), such as trusting a private 
certificate authority
+            kmp = null;
         } else {
-            LOG.warn("SSL is enabled but no keystore is configured."
+            LOG.warn("SSL is enabled but no keystore or truststore is 
configured."
                      + " Set camel.ssl.keyStore or camel.ssl.selfSigned=true 
for development.");
             return;
         }
diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/MainSSLTrustStoreTest.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/MainSSLTrustStoreTest.java
new file mode 100644
index 000000000000..01d2bdf410d9
--- /dev/null
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/MainSSLTrustStoreTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.main;
+
+import java.security.KeyStore;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+public class MainSSLTrustStoreTest {
+
+    private static KeyStore emptyTrustStore() throws Exception {
+        KeyStore ks = KeyStore.getInstance("PKCS12");
+        ks.load(null, null);
+        return ks;
+    }
+
+    @Test
+    public void testTrustStoreBean() throws Exception {
+        KeyStore trust = emptyTrustStore();
+        Main main = new Main();
+        main.bind("myTrust", trust);
+        main.addInitialProperty("camel.ssl.enabled", "true");
+        main.addInitialProperty("camel.ssl.selfSigned", "true");
+        main.addInitialProperty("camel.ssl.trustStore", "#bean:myTrust");
+        main.start();
+        try {
+            CamelContext context = main.getCamelContext();
+            SSLContextParameters scp = context.getSSLContextParameters();
+            assertNotNull(scp);
+            assertSame(trust, 
scp.getTrustManagers().getKeyStore().createKeyStore());
+            assertDoesNotThrow(() -> scp.createSSLContext(context));
+        } finally {
+            main.stop();
+        }
+    }
+
+    @Test
+    public void testOnlyTrustStore() throws Exception {
+        Main main = new Main();
+        main.bind("myTrust", emptyTrustStore());
+        main.addInitialProperty("camel.ssl.enabled", "true");
+        main.addInitialProperty("camel.ssl.trustStore", "#bean:myTrust");
+        main.start();
+        try {
+            CamelContext context = main.getCamelContext();
+            SSLContextParameters scp = context.getSSLContextParameters();
+            // client side only, so there are no key managers
+            assertNotNull(scp);
+            assertNull(scp.getKeyManagers());
+            assertNotNull(scp.getTrustManagers());
+            assertDoesNotThrow(() -> scp.createSSLContext(context));
+        } finally {
+            main.stop();
+        }
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index d5a6dcc6fb92..02bdbbe28f0e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -2999,6 +2999,22 @@ The fluent builder 
`CircuitBreakerDefinition.inheritErrorHandler(boolean)` and t
 added for placeholders. Routes written in XML, YAML or the Java DSL do not 
need any change; the `inheritErrorHandler`
 attribute in the XML schema is now `xs:string` so a placeholder validates.
 
+=== camel-core - JSSE utility (SSLContextParameters)
+
+A filter (such as `cipherSuitesFilter` or `namedGroupsFilter`, or 
`camel.ssl.cipherSuitesExclude` and
+`camel.ssl.namedGroupsExclude`) with only exclude patterns now includes all 
the other available values. Previously it
+included nothing, which made every TLS handshake fail (for example 
`camel.ssl.namedGroupsExclude = X25519MLKEM768`,
+which is the documented way to disable the post-quantum named groups). A 
filter with no patterns at all still
+includes nothing.
+
+On JVMs that do not provide their default signature schemes (such as JDK 25 
and older) a `signatureSchemesFilter`
+(or `camel.ssl.signatureSchemesInclude`/`signatureSchemesExclude`) is no 
longer applied (a WARN is logged) and the
+defaults of the JVM are used. Previously an empty list of signature schemes 
was configured, which made every TLS
+handshake fail. Configure the signature schemes explicitly instead.
+
+The SNI host names of the client parameters are now also set on the 
`SSLEngine` (used by components such as
+camel-netty). Previously they were only set on an `SSLSocket`.
+
 === camel-weaviate - removed the unused vector field name header
 
 The `CamelweaviateVectorFieldName` header 
(`WeaviateVectorDbHeaders.VECTOR_FIELD_NAME`) has been removed.
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 5342bcc3ecf5..1b446d28d1be 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-configuration-utilities.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-configuration-utilities.adoc
@@ -59,7 +59,7 @@ constructing the SSLContext. If omitted, the standard 
provider look-up
 mechanism is used to resolve the provider.
 secureSocketProtocol::
 The optional secure socket protocol. See 
https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html[Java
 Security Standard Algorithm Names]
-for information about standard protocol names. If omitted, TLS is used by 
default.
+for information about standard protocol names. If omitted, TLSv1.3 is used by 
default.
 Note that this property is related to but distinctly different from the
 secureSocketProtocols and secureSocketProtocolsFilter properties.
 certAlias::
@@ -82,7 +82,8 @@ This optional property represents a collection of include and 
exclude
 patterns for cipher suites to enable on both the client and server side
 as well as in the SSLEngine. The patterns are applied over only the
 available cipher suites. The excludes patterns have precedence over the
-includes patterns. If no cipherSuites and no cipherSuitesFilter are
+includes patterns. A filter with only excludes patterns includes all the
+other available values (this applies to all the filters). If no cipherSuites 
and no cipherSuitesFilter are
 present, the default patterns applied are:
 
 [source,text]
@@ -376,7 +377,9 @@ patterns for signature schemes to enable on both the client 
and server
 side as well as in the SSLEngine. The patterns are applied over only the
 available signature schemes. The excludes patterns have precedence over
 the includes patterns. No default filtering is applied to signature
-schemes.
+schemes. Some JVMs (such as JDK 25 and older) do not provide their default
+signature schemes, and then the filter cannot be applied (a WARN is logged)
+and the defaults of the JVM are used; configure signatureSchemes explicitly 
instead.
 
 === Configuring SSLContextParameters in XML and YAML DSL
 
@@ -493,8 +496,8 @@ camel.ssl.enabled = true
 # Explicit named groups ordering (PQC-first)
 camel.ssl.namedGroups = X25519MLKEM768,x25519,secp256r1,secp384r1
 
-# Explicit signature schemes (if your JDK supports PQC signatures)
-camel.ssl.signatureSchemes = ML-DSA,ECDSA,RSA
+# Explicit signature schemes (using the JSSE names of the signature schemes)
+camel.ssl.signatureSchemes = ecdsa_secp256r1_sha256,rsa_pss_rsae_sha256,ed25519
 ----
 
 You can also use include/exclude filters instead of explicit lists:
@@ -533,7 +536,7 @@ SSLContextParameters scp = new SSLContextParameters();
 scp.setKeyManagers(kmp);
 scp.setNamedGroups(ngp);
 
-SSLContext context = scp.createSSLContext();
+SSLContext context = scp.createSSLContext(camelContext);
 ----
 
 === Disabling PQC Auto-Configuration
@@ -633,7 +636,7 @@ SSLContextParameters scp = new SSLContextParameters();
 scp.setServerParameters(scsp);
 scp.setKeyManagers(kmp);
 
-SSLContext context = scp.createSSLContext();
+SSLContext context = scp.createSSLContext(camelContext);
 SSLEngine engine = scp.createSSLEngine();
 -------------------------------------------------------------------
 
@@ -670,7 +673,7 @@ SSLContextParameters scp = new SSLContextParameters();
 scp.setClientParameters(sccp);
 scp.setKeyManagers(kmp);
 
-SSLContext context = scp.createSSLContext();
+SSLContext context = scp.createSSLContext(camelContext);
 SSLEngine engine = scp.createSSLEngine();
 -------------------------------------------------------------------
 

Reply via email to