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");
+                            }
+                        });
+            }
+        };
+    }
+}

Reply via email to