This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-25674 in repository https://gitbox.apache.org/repos/asf/camel.git
commit f0b9f12943f1d281a0416946066cc61bec4f3f88 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Nov 2 14:27:40 2026 +0100 CAMEL-25674: camel-netty-http - hostnameVerification no longer breaks client certificates on a consumer HttpServerInitializerFactory set endpointIdentificationAlgorithm=HTTPS on the server engine when hostnameVerification=true. The JDK does not support host name verification on the server side, so every handshake in which the client presented a certificate failed with "Endpoint Identification Algorithm HTTPS is not supported on the server side", and nothing was checked otherwise. The consumer now ignores the option, as camel-netty does since CAMEL-25649 and as HttpServerSharedInitializerFactory always did. The producer still verifies the host. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/catalog/docs/netty-http-component.adoc | 11 ++ .../src/main/docs/netty-http-component.adoc | 11 ++ .../netty/http/HttpServerInitializerFactory.java | 9 +- ...NettyHttpSSLServerHostnameVerificationTest.java | 122 +++++++++++++++++++++ .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 10 ++ 5 files changed, 157 insertions(+), 6 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/netty-http-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/netty-http-component.adoc index 2373c69dce3a..5b098510360b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/netty-http-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/netty-http-component.adoc @@ -175,6 +175,17 @@ invoking the route, unless the endpoint is restricted to the `OPTIONS` method wi `httpMethodRestrict`. All other HTTP methods are authenticated when `oauthProfile` is set. ==== +=== Verifying the host name of the server + +Set `hostnameVerification=true` on a producer that calls an `https` URL to check that the certificate of the server +matches the host it connects to, with the same rules as an HTTPS client: a connection to an IP address needs a +certificate that lists that address as a subject alternative name of its own. A certificate that does not match makes +the call fail with an `SSLHandshakeException`. + +The option applies to the producer only. On a consumer it is ignored, because the JDK does not support host name +verification on the server side of a handshake. To control which clients may connect to a consumer, use client +authentication (`needClientAuth=true`) with a trust store that holds only the certificates you accept. + === Access to Netty types This component uses the diff --git a/components/camel-netty-http/src/main/docs/netty-http-component.adoc b/components/camel-netty-http/src/main/docs/netty-http-component.adoc index 2373c69dce3a..5b098510360b 100644 --- a/components/camel-netty-http/src/main/docs/netty-http-component.adoc +++ b/components/camel-netty-http/src/main/docs/netty-http-component.adoc @@ -175,6 +175,17 @@ invoking the route, unless the endpoint is restricted to the `OPTIONS` method wi `httpMethodRestrict`. All other HTTP methods are authenticated when `oauthProfile` is set. ==== +=== Verifying the host name of the server + +Set `hostnameVerification=true` on a producer that calls an `https` URL to check that the certificate of the server +matches the host it connects to, with the same rules as an HTTPS client: a connection to an IP address needs a +certificate that lists that address as a subject alternative name of its own. A certificate that does not match makes +the call fail with an `SSLHandshakeException`. + +The option applies to the producer only. On a consumer it is ignored, because the JDK does not support host name +verification on the server side of a handshake. To control which clients may connect to a consumer, use client +authentication (`needClientAuth=true`) with a trust store that holds only the certificates you accept. + === Access to Netty types This component uses the diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java index f303bde010ac..e42b50a5cf44 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpServerInitializerFactory.java @@ -20,7 +20,6 @@ import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; @@ -178,11 +177,9 @@ public class HttpServerInitializerFactory extends ServerInitializerFactory { // REQUIRE or WANT) when needClientAuth is not enabled engine.setNeedClientAuth(true); } - if (consumer.getConfiguration().isHostnameVerification()) { - SSLParameters sslParams = engine.getSSLParameters(); - sslParams.setEndpointIdentificationAlgorithm("HTTPS"); - engine.setSSLParameters(sslParams); - } + // hostnameVerification is not applied: the JDK does not support host name verification on the server side + // of a handshake, and rejects every client certificate with "Endpoint Identification Algorithm HTTPS is not + // supported on the server side" when asked to (HttpServerSharedInitializerFactory does not apply it either) if (consumer.getConfiguration().getSslContextParameters() == null) { // just set the enabledProtocols if the SslContextParameter doesn't set engine.setEnabledProtocols(consumer.getConfiguration().getEnabledProtocols().split(",")); diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSSLServerHostnameVerificationTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSSLServerHostnameVerificationTest.java new file mode 100644 index 000000000000..96c3fb18b874 --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSSLServerHostnameVerificationTest.java @@ -0,0 +1,122 @@ +/* + * 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.netty.http; + +import org.apache.camel.BindToRegistry; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.support.jsse.KeyManagersParameters; +import org.apache.camel.support.jsse.KeyStoreParameters; +import org.apache.camel.support.jsse.SSLContextParameters; +import org.apache.camel.support.jsse.TrustManagersParameters; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A consumer that asks for client certificates completes the handshake when {@code hostnameVerification=true}. + * <p/> + * The JDK does not support host name verification on the server side of a handshake: when the option was passed to the + * server engine, it rejected every client certificate with + * {@code Endpoint Identification Algorithm HTTPS is not supported on the server side}. + */ +@DisabledIfSystemProperty(named = "java.vendor", matches = ".*ibm.*") +public class NettyHttpSSLServerHostnameVerificationTest extends BaseNettyTestSupport { + + @BindToRegistry("clientCertParameters") + public SSLContextParameters loadClientCertParameters() { + SSLContextParameters answer = loadTrustOnlyParameters(); + KeyManagersParameters kmp = new KeyManagersParameters(); + kmp.setKeyPassword("changeit"); + kmp.setKeyStore(localhostKeyStore()); + answer.setKeyManagers(kmp); + return answer; + } + + @BindToRegistry("trustOnlyParameters") + public SSLContextParameters loadTrustOnlyParameters() { + TrustManagersParameters tmp = new TrustManagersParameters(); + tmp.setKeyStore(localhostKeyStore()); + + SSLContextParameters answer = new SSLContextParameters(); + answer.setTrustManagers(tmp); + return answer; + } + + private static KeyStoreParameters localhostKeyStore() { + KeyStoreParameters ksp = new KeyStoreParameters(); + ksp.setResource("jsse/localhost.p12"); + ksp.setPassword("changeit"); + return ksp; + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("netty-http:https://localhost:{{port}}/hello?sslContextParameters=#clientCertParameters" + + "&needClientAuth=true&hostnameVerification=true") + .transform().constant("Bye World"); + } + }; + } + + @Test + public void testClientCertificateAccepted() { + String out = template.requestBody( + "netty-http:https://localhost:{{port}}/hello?sslContextParameters=#clientCertParameters", + "Hello World", String.class); + + assertEquals("Bye World", out); + } + + /** + * The consumer still requires a client certificate, so the handshake in the test above did not pass because the + * client authentication was dropped. + */ + @Test + public void testNoClientCertificateRejected() { + assertThrows(CamelExecutionException.class, + () -> template.requestBody( + "netty-http:https://localhost:{{port}}/hello?sslContextParameters=#trustOnlyParameters", + "Hello World", String.class)); + } + + /** + * The option still applies to the producer: localhost.p12 lists only DNS:localhost as subject alternative name, so + * it does not match 127.0.0.1. + */ + @Test + public void testProducerRejectsCertificateNotMatchingTheHost() { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.requestBody( + "netty-http:https://127.0.0.1:{{port}}/hello?sslContextParameters=#clientCertParameters" + + "&hostnameVerification=true", + "Hello World", String.class)); + + Throwable cause = e; + while (cause.getCause() != null) { + cause = cause.getCause(); + } + assertTrue(cause.getMessage() != null && cause.getMessage().contains("127.0.0.1"), + "the host name check should have rejected the certificate, but got: " + cause.getMessage()); + } +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index 9becf9752ee5..65b45954c254 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -2786,6 +2786,16 @@ anything there - the JDK does not support host name verification on the server s client certificate with `Endpoint Identification Algorithm HTTPS is not supported on the server side` - so a consumer that asks for client certificates and had the option set now completes the handshake instead of failing it. +=== camel-netty-http - hostnameVerification is ignored on a consumer + +A `netty-http` consumer no longer passes `hostnameVerification=true` to its TLS engine, as `netty` consumers already +do not (see above). The JDK does not support host name verification on the server side of a handshake: a client that +presented a certificate failed the handshake with +`Endpoint Identification Algorithm HTTPS is not supported on the server side`, and a client that presented none was not +checked at all. A consumer with `needClientAuth=true` and the option set now accepts trusted client certificates +instead of rejecting all of them. Consumers that share a server through `nettySharedHttpServer` never applied the +option, and are unchanged. The option still verifies the host on a `netty-http` producer. + === camel-alibaba-oss - a listed object that cannot be got is skipped When the consumer lists the bucket, a failure to get one of the listed objects (for example access denied, or an
