This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch camel-3.x in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-3.x by this push: new 2ddb366db85 CAMEL-19502 Netty4-http SNI configuration using SSLContextParameters (#10516) 2ddb366db85 is described below commit 2ddb366db85562aaad9ecdce158b51ecb7934ca3 Author: Michael Hughes <mikee.hug...@gmail.com> AuthorDate: Tue Jun 27 19:51:57 2023 +0100 CAMEL-19502 Netty4-http SNI configuration using SSLContextParameters (#10516) * CAMEL-19502 Netty4-http SNI configuration using SSLContextParameters * CAMEL-19502 fix build by committing changed file --- .../netty/http/HttpClientInitializerFactory.java | 9 +- .../component/netty/http/NettyHttpSSLSNITest.java | 135 +++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java index 7ee0bc61211..2a0bc816f49 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/HttpClientInitializerFactory.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; 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; @@ -54,6 +55,7 @@ public class HttpClientInitializerFactory extends ClientInitializerFactory { protected NettyHttpConfiguration configuration; private NettyHttpProducer producer; private SSLContext sslContext; + private List<SNIServerName> sniServerNames; public HttpClientInitializerFactory() { // default constructor needed @@ -143,6 +145,10 @@ public class HttpClientInitializerFactory extends ClientInitializerFactory { // create ssl context once if (configuration.getSslContextParameters() != null) { answer = configuration.getSslContextParameters().createSSLContext(producer.getContext()); + if (answer.getSupportedSSLParameters().getServerNames() != null + && !answer.getSupportedSSLParameters().getServerNames().isEmpty()) { + sniServerNames = answer.getSupportedSSLParameters().getServerNames(); + } } else { if (configuration.getKeyStoreFile() == null && configuration.getKeyStoreResource() == null) { LOG.debug("keystorefile is null"); @@ -192,7 +198,8 @@ public class HttpClientInitializerFactory extends ClientInitializerFactory { SSLEngine engine = sslContext.createSSLEngine(uri.getHost(), uri.getPort()); engine.setUseClientMode(true); SSLParameters sslParameters = engine.getSSLParameters(); - sslParameters.setServerNames(Arrays.asList(new SNIHostName(uri.getHost()))); + sslParameters + .setServerNames(sniServerNames != null ? sniServerNames : Arrays.asList(new SNIHostName(uri.getHost()))); engine.setSSLParameters(sslParameters); if (producer.getConfiguration().getSslContextParameters() == null) { // just set the enabledProtocols if the SslContextParameter doesn't set diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSSLSNITest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSSLSNITest.java new file mode 100644 index 00000000000..6a473bb317f --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpSSLSNITest.java @@ -0,0 +1,135 @@ +/* + * 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 java.util.List; + +import javax.net.ssl.ExtendedSSLSession; +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIServerName; + +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.component.netty.NettyConstants; +import org.apache.camel.support.jsse.KeyStoreParameters; +import org.apache.camel.support.jsse.SSLContextClientParameters; +import org.apache.camel.support.jsse.SSLContextParameters; +import org.apache.camel.support.jsse.TrustManagersParameters; +import org.junit.jupiter.api.Test; + +import static org.apache.camel.test.junit5.TestSupport.isJavaVendor; +import static org.junit.jupiter.api.Assumptions.assumeFalse; + +public class NettyHttpSSLSNITest extends BaseNettyTest { + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Test + public void testSSLAddsDefaultServerNameIndication() throws Exception { + // ibm jdks dont have sun security algorithms + assumeFalse(isJavaVendor("ibm")); + + getMockEndpoint("mock:output").expectedBodiesReceived("localhost"); + + context.getRegistry().bind("customSSLContextParameters", createSSLContextParameters(null)); + context.addRoutes(nettyServerThatRespondsWithSNI()); + context.addRoutes(new RouteBuilder() { + public void configure() { + from("direct:in") + .setHeader(Exchange.HTTP_METHOD, constant("GET")) + .to("netty-http:https://localhost:{{port}}?sslContextParameters=#customSSLContextParameters") + .to("mock:output"); + } + }); + context.start(); + template.sendBody("direct:in", null); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testSSLAddsCustomServerNameIndication() throws Exception { + // ibm jdks dont have sun security algorithms + assumeFalse(isJavaVendor("ibm")); + + String customSNI = "custom.host.name"; + + getMockEndpoint("mock:output").expectedBodiesReceived(customSNI); + + SSLContextClientParameters customClientParameters = new SSLContextClientParameters(); + customClientParameters.setSniHostName(customSNI); + + context.getRegistry().bind("customSSLContextParameters", createSSLContextParameters(customClientParameters)); + context.addRoutes(nettyServerThatRespondsWithSNI()); + context.addRoutes(new RouteBuilder() { + public void configure() { + from("direct:in") + .setHeader(Exchange.HTTP_METHOD, constant("GET")) + .to("netty-http:https://localhost:{{port}}?sslContextParameters=#customSSLContextParameters") + .to("mock:output"); + } + }); + context.start(); + template.sendBody("direct:in", null); + + MockEndpoint.assertIsSatisfied(context); + } + + private SSLContextParameters createSSLContextParameters(SSLContextClientParameters clientParameters) { + SSLContextParameters sslContextParameters = new SSLContextParameters(); + + sslContextParameters.setClientParameters(clientParameters); + + KeyStoreParameters trustStoreParameters = new KeyStoreParameters(); + trustStoreParameters.setResource("jsse/localhost.p12"); + trustStoreParameters.setPassword("changeit"); + + TrustManagersParameters trustManagersParameters = new TrustManagersParameters(); + trustManagersParameters.setKeyStore(trustStoreParameters); + + sslContextParameters.setTrustManagers(trustManagersParameters); + + return sslContextParameters; + } + + private RouteBuilder nettyServerThatRespondsWithSNI() { + return new RouteBuilder() { + @Override + public void configure() { + from("netty-http:https://localhost:{{port}}?ssl=true&passphrase=changeit&keyStoreResource=jsse/localhost.p12&trustStoreResource=jsse/localhost.p12") + .process(exchange -> { + ExtendedSSLSession extendedSSLSession + = exchange.getIn().getHeader(NettyConstants.NETTY_SSL_SESSION, ExtendedSSLSession.class); + if (extendedSSLSession != null) { + List<SNIServerName> serverNames = extendedSSLSession.getRequestedServerNames(); + if (serverNames.size() == 1) { + exchange.getMessage().setBody(((SNIHostName) serverNames.get(0)).getAsciiName()); + } else { + exchange.getMessage().setBody("SNI is missing or incorrect"); + } + } else { + exchange.getMessage().setBody("Cannot determine success without ExtendedSSLSession"); + } + }); + } + }; + } +}