This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch 4.0.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit c552341ded4c870b9fb342c60ed3056ebbad19e2 Author: Andriy Redko <[email protected]> AuthorDate: Sat Jan 24 08:13:00 2026 -0500 CXF-9194: Support SNI in CXF client with Apache HttpComponents HttpClient 4 / HttpClient 5 (#2822) (cherry picked from commit 9b224a7de6cba1c8edb5c5ddc43a1fc9b4755da8) (cherry picked from commit 43877d6d1315d4f8c6ce3d82d3f4c985174f7455) # Conflicts: # rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java --- .../configuration/jsse/TLSClientParameters.java | 24 +++++ .../jsse/TLSClientParametersConfig.java | 3 + .../resources/schemas/configuration/security.xsd | 28 +++++- .../http/asyncclient/AsyncHTTPConduit.java | 10 ++ .../http/asyncclient/hc5/AsyncHTTPConduit.java | 10 ++ .../spring/HttpConduitBeanDefinitionParser.java | 3 + .../cxf/systest/hc5/https/sni/ClientSniServer.java | 47 +++++++++ .../cxf/systest/hc5/https/sni/ClientSniTest.java | 104 +++++++++++++++++++ .../systest/hc5/https/sni/client-sni-server.xml | 56 +++++++++++ .../cxf/systest/hc5/https/sni/client-sni.xml | 44 +++++++++ .../cxf/systest/https/sni/ClientSniServer.java | 47 +++++++++ .../cxf/systest/https/sni/ClientSniTest.java | 110 +++++++++++++++++++++ .../cxf/systest/https/sni/client-sni-server.xml | 56 +++++++++++ .../apache/cxf/systest/https/sni/client-sni.xml | 44 +++++++++ 14 files changed, 581 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java b/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java index ad1992823c..c7cebf0665 100644 --- a/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java +++ b/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParameters.java @@ -58,6 +58,7 @@ public class TLSClientParameters extends TLSParameterBase { private boolean useHttpsURLConnectionDefaultHostnameVerifier; private HostnameVerifier hostnameVerifier; private SSLContext sslContext; + private List<String> serverNames; /** * Set custom HostnameVerifier @@ -165,6 +166,21 @@ public class TLSClientParameters extends TLSParameterBase { this.useHttpsURLConnectionDefaultHostnameVerifier = useHttpsURLConnectionDefaultHostnameVerifier; } + /** + * Sets SNI server names + * @param serverNames SNI server names + */ + public void setServerNames(List<String> serverNames) { + this.serverNames = serverNames; + } + + /** + * Returns SNI server names + */ + public List<String> getServerNames() { + return serverNames; + } + public int hashCode() { int hash = disableCNCheck ? 37 : 17; if (sslSocketFactory != null) { @@ -193,6 +209,9 @@ public class TLSClientParameters extends TLSParameterBase { hash = hash(hash, certConstraints.getIssuerDNConstraints()); hash = hash(hash, certConstraints.getSubjectDNConstraints()); } + if (serverNames != null) { + hash = hash(hash, serverNames); + } return hash; } private int hash(int i, Object o) { @@ -253,6 +272,11 @@ public class TLSClientParameters extends TLSParameterBase { } else { eq &= that.certConstraints == null; } + if (serverNames != null) { + eq &= equals(serverNames, that.serverNames); + } else { + eq &= that.serverNames == null; + } return eq; } return false; diff --git a/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParametersConfig.java b/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParametersConfig.java index 781abcc15f..7eb060074f 100644 --- a/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParametersConfig.java +++ b/core/src/main/java/org/apache/cxf/configuration/jsse/TLSClientParametersConfig.java @@ -137,6 +137,9 @@ public final class TLSClientParametersConfig { if (iparams != null && iparams.isSetTrustManagersRef() && !usingDefaults) { ret.setTrustManagers(iparams.getTrustManagersRef()); } + if (iparams != null && iparams.isSetServerNames()) { + ret.setServerNames(iparams.getServerNames().getServerName()); + } return ret; } diff --git a/core/src/main/resources/schemas/configuration/security.xsd b/core/src/main/resources/schemas/configuration/security.xsd index 73f5e1767f..fbc28a30e7 100644 --- a/core/src/main/resources/schemas/configuration/security.xsd +++ b/core/src/main/resources/schemas/configuration/security.xsd @@ -436,7 +436,18 @@ <xs:enumeration value="ALL"/> </xs:restriction> </xs:simpleType> - + + <xs:complexType name="ServerNames"> + <xs:annotation> + <xs:documentation> + This structure holds the list of SNI server names. + </xs:documentation> + </xs:annotation> + <xs:sequence> + <xs:element name="serverName" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + <!-- Although there are common elements of TLSClientParametersType ** and TLSServerParametersType they are listed separate so we ** can use the xs:all element. @@ -460,7 +471,7 @@ <xs:element name="cipherSuites" type="tns:CipherSuites" minOccurs="0"> <xs:annotation> <xs:documentation> - This element contains the the CipherSuites that will be supported. + This element contains the CipherSuites that will be supported. </xs:documentation> </xs:annotation> </xs:element> @@ -494,6 +505,13 @@ </xs:documentation> </xs:annotation> </xs:element> + <xs:element name="serverNames" type="tns:ServerNames" minOccurs="0"> + <xs:annotation> + <xs:documentation> + This element contains the SNI server names. + </xs:documentation> + </xs:annotation> + </xs:element> </xs:all> <xs:attribute name="useHttpsURLConnectionDefaultSslSocketFactory" type="pt:ParameterizedBoolean" default="false"> <xs:annotation> @@ -577,7 +595,7 @@ <xs:element name="cipherSuites" type="tns:CipherSuites" minOccurs="0"> <xs:annotation> <xs:documentation> - This element contains the the CipherSuites that will be supported. + This element contains the CipherSuites that will be supported. </xs:documentation> </xs:annotation> </xs:element> @@ -592,14 +610,14 @@ <xs:element name="excludeProtocols" type="tns:ExcludeProtocols" minOccurs="0"> <xs:annotation> <xs:documentation> - This element contains the the Protocols that will be excluded + This element contains the Protocols that will be excluded </xs:documentation> </xs:annotation> </xs:element> <xs:element name="includeProtocols" type="tns:IncludeProtocols" minOccurs="0"> <xs:annotation> <xs:documentation> - This element contains the the Protocols that will be included + This element contains the Protocols that will be included </xs:documentation> </xs:annotation> </xs:element> diff --git a/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java b/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java index edf8b8cfb9..f7e4867d7e 100755 --- a/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java +++ b/rt/transports/http-hc/src/main/java/org/apache/cxf/transport/http/asyncclient/AsyncHTTPConduit.java @@ -39,12 +39,15 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Future; import java.util.logging.Level; +import java.util.stream.Collectors; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; +import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; @@ -977,6 +980,13 @@ public class AsyncHTTPConduit extends HttpClientHTTPConduit { if (p != null) { sslengine.setEnabledProtocols(p); } + + final List<String> serverNames = tlsClientParameters.getServerNames(); + if (serverNames != null && !serverNames.isEmpty()) { + final SSLParameters params = new SSLParameters(); + params.setServerNames(serverNames.stream().map(SNIHostName::new).collect(Collectors.toList())); + sslengine.setSSLParameters(params); + } } private String[] findProtocols(String p, String[] options) { diff --git a/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java b/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java index b0f38474d2..16830da689 100644 --- a/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java +++ b/rt/transports/http-hc5/src/main/java/org/apache/cxf/transport/http/asyncclient/hc5/AsyncHTTPConduit.java @@ -39,12 +39,15 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Future; import java.util.logging.Level; +import java.util.stream.Collectors; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManager; +import javax.net.ssl.SNIHostName; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; @@ -993,6 +996,13 @@ public class AsyncHTTPConduit extends HttpClientHTTPConduit { if (p != null) { sslengine.setEnabledProtocols(p); } + + final List<String> serverNames = tlsClientParameters.getServerNames(); + if (serverNames != null && !serverNames.isEmpty()) { + final SSLParameters params = new SSLParameters(); + params.setServerNames(serverNames.stream().map(SNIHostName::new).collect(Collectors.toList())); + sslengine.setSSLParameters(params); + } } @Override diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/spring/HttpConduitBeanDefinitionParser.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/spring/HttpConduitBeanDefinitionParser.java index f751847826..994b29a343 100644 --- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/spring/HttpConduitBeanDefinitionParser.java +++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/spring/HttpConduitBeanDefinitionParser.java @@ -36,6 +36,7 @@ import org.apache.cxf.configuration.security.FiltersType; import org.apache.cxf.configuration.security.KeyManagersType; import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy; import org.apache.cxf.configuration.security.SecureRandomParameters; +import org.apache.cxf.configuration.security.ServerNames; import org.apache.cxf.configuration.security.TrustManagersType; import org.apache.cxf.configuration.spring.AbstractBeanDefinitionParser; import org.apache.cxf.transport.http.HTTPConduit; @@ -171,6 +172,8 @@ public class HttpConduitBeanDefinitionParser CertificateConstraintsType.class); } else if ("certAlias".equals(ename)) { paramsbean.addPropertyValue(ename, n.getTextContent()); + } else if ("serverNames".equals(ename)) { + mapElementToJaxbProperty((Element)n, paramsbean, ename, ServerNames.class); } n = n.getNextSibling(); } diff --git a/systests/transport-hc5/src/test/java/org/apache/cxf/systest/hc5/https/sni/ClientSniServer.java b/systests/transport-hc5/src/test/java/org/apache/cxf/systest/hc5/https/sni/ClientSniServer.java new file mode 100644 index 0000000000..901629fae9 --- /dev/null +++ b/systests/transport-hc5/src/test/java/org/apache/cxf/systest/hc5/https/sni/ClientSniServer.java @@ -0,0 +1,47 @@ +/** + * 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.cxf.systest.hc5.https.sni; + +import java.net.URL; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.testutil.common.AbstractBusTestServerBase; + +public class ClientSniServer extends AbstractBusTestServerBase { + + public ClientSniServer() { + + } + + protected void run() { + URL busFile = ClientSniServer.class.getResource("client-sni-server.xml"); + Bus busLocal = new SpringBusFactory().createBus(busFile); + BusFactory.setDefaultBus(busLocal); + setBus(busLocal); + + try { + new ClientSniServer(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/systests/transport-hc5/src/test/java/org/apache/cxf/systest/hc5/https/sni/ClientSniTest.java b/systests/transport-hc5/src/test/java/org/apache/cxf/systest/hc5/https/sni/ClientSniTest.java new file mode 100644 index 0000000000..8a390bd46c --- /dev/null +++ b/systests/transport-hc5/src/test/java/org/apache/cxf/systest/hc5/https/sni/ClientSniTest.java @@ -0,0 +1,104 @@ +/** + * 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.cxf.systest.hc5.https.sni; + +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; + +import jakarta.xml.ws.BindingProvider; +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; +import org.apache.hello_world.Greeter; +import org.apache.hello_world.services.SOAPService; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized.Parameters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * A set of tests for TLS SNI + */ +@RunWith(value = org.junit.runners.Parameterized.class) +public class ClientSniTest extends AbstractBusClientServerTestBase { + static final String PORT = allocatePort(ClientSniServer.class); + + final Boolean async; + + public ClientSniTest(Boolean async) { + this.async = async; + } + + @Parameters(name = "{0}") + public static Collection<Boolean> data() { + return Arrays.asList(new Boolean[] {Boolean.FALSE, Boolean.TRUE}); + } + + @BeforeClass + public static void startServers() throws Exception { + assertTrue( + "Server failed to launch", + // run the server in the same process + // set this to false to fork + launchServer(ClientSniServer.class, true) + ); + } + + @AfterClass + public static void cleanup() throws Exception { + stopAllServers(); + } + + // Server directly trusts the client cert + @org.junit.Test + public void testSniServerNames() throws Exception { + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = ClientSniTest.class.getResource("client-sni.xml"); + + Bus bus = bf.createBus(busFile.toString()); + BusFactory.setDefaultBus(bus); + BusFactory.setThreadDefaultBus(bus); + + URL url = SOAPService.WSDL_LOCATION; + SOAPService service = new SOAPService(url, SOAPService.SERVICE); + assertNotNull("Service is null", service); + final Greeter port = service.getHttpsPort(); + assertNotNull("Port is null", port); + + updateAddressPort(port, PORT); + + // Enable Async + if (async) { + ((BindingProvider)port).getRequestContext().put("use.async.http.conduit", true); + } + + assertEquals(port.greetMe("Kitty"), "Hello Kitty"); + + ((java.io.Closeable)port).close(); + bus.shutdown(true); + } +} diff --git a/systests/transport-hc5/src/test/resources/org/apache/cxf/systest/hc5/https/sni/client-sni-server.xml b/systests/transport-hc5/src/test/resources/org/apache/cxf/systest/hc5/https/sni/client-sni-server.xml new file mode 100644 index 0000000000..378aa31a7f --- /dev/null +++ b/systests/transport-hc5/src/test/resources/org/apache/cxf/systest/hc5/https/sni/client-sni-server.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:jaxws="http://cxf.apache.org/jaxws" + xmlns:http="http://cxf.apache.org/transports/http/configuration" + xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration" + xmlns:sec="http://cxf.apache.org/configuration/security" + xmlns:cxf="http://cxf.apache.org/core" + xmlns:p="http://cxf.apache.org/policy" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache.org/policy http://cxf.apache.org/schemas/policy.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/transports/http-jetty/confi [...] + <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/> + <cxf:bus> + <cxf:features> + <bean class="org.apache.cxf.ext.logging.LoggingFeature"/> + </cxf:features> + </cxf:bus> + + <httpj:engine-factory id="tls-settings"> + <httpj:engine port="${testutil.ports.ClientSniServer}"> + <httpj:tlsServerParameters> + <sec:keyManagers keyPassword="password"> + <sec:keyStore type="jks" password="password" resource="keys/Bethal.jks"/> + </sec:keyManagers> + <sec:trustManagers> + <sec:keyStore type="jks" password="password" resource="keys/Truststore.jks"/> + </sec:trustManagers> + </httpj:tlsServerParameters> + </httpj:engine> + </httpj:engine-factory> + + <jaxws:endpoint xmlns:e="http://apache.org/hello_world/services" + xmlns:s="http://apache.org/hello_world/services" + id="DirectTrustServer" + implementor="org.apache.cxf.systest.hc5.GreeterImpl" + address="https://localhost:${testutil.ports.ClientSniServer}/SoapContext/HttpsPort" + serviceName="s:SOAPService" + endpointName="e:HttpsPort" depends-on="tls-settings"/> +</beans> \ No newline at end of file diff --git a/systests/transport-hc5/src/test/resources/org/apache/cxf/systest/hc5/https/sni/client-sni.xml b/systests/transport-hc5/src/test/resources/org/apache/cxf/systest/hc5/https/sni/client-sni.xml new file mode 100644 index 0000000000..cca43394e7 --- /dev/null +++ b/systests/transport-hc5/src/test/resources/org/apache/cxf/systest/hc5/https/sni/client-sni.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:http="http://cxf.apache.org/transports/http/configuration" + xmlns:jaxws="http://cxf.apache.org/jaxws" + xmlns:cxf="http://cxf.apache.org/core" + xmlns:p="http://cxf.apache.org/policy" + xmlns:sec="http://cxf.apache.org/configuration/security" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache [...] + + <cxf:bus> + <cxf:features> + <bean class="org.apache.cxf.ext.logging.LoggingFeature"/> + </cxf:features> + </cxf:bus> + <http:conduit name="https://localhost:.*"> + <http:tlsClientParameters disableCNCheck="true"> + <sec:trustManagers> + <sec:keyStore type="jks" password="password" resource="keys/Truststore.jks"/> + </sec:trustManagers> + <sec:serverNames> + <sec:serverName>bethal</sec:serverName> + </sec:serverNames> + </http:tlsClientParameters> + </http:conduit> +</beans> \ No newline at end of file diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/https/sni/ClientSniServer.java b/systests/transports/src/test/java/org/apache/cxf/systest/https/sni/ClientSniServer.java new file mode 100644 index 0000000000..b453e225fc --- /dev/null +++ b/systests/transports/src/test/java/org/apache/cxf/systest/https/sni/ClientSniServer.java @@ -0,0 +1,47 @@ +/** + * 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.cxf.systest.https.sni; + +import java.net.URL; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.testutil.common.AbstractBusTestServerBase; + +public class ClientSniServer extends AbstractBusTestServerBase { + + public ClientSniServer() { + + } + + protected void run() { + URL busFile = ClientSniServer.class.getResource("client-sni-server.xml"); + Bus busLocal = new SpringBusFactory().createBus(busFile); + BusFactory.setDefaultBus(busLocal); + setBus(busLocal); + + try { + new ClientSniServer(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/https/sni/ClientSniTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/https/sni/ClientSniTest.java new file mode 100644 index 0000000000..f6d1cfb180 --- /dev/null +++ b/systests/transports/src/test/java/org/apache/cxf/systest/https/sni/ClientSniTest.java @@ -0,0 +1,110 @@ +/** + * 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.cxf.systest.https.sni; + +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; + +import jakarta.xml.ws.BindingProvider; +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; +import org.apache.hello_world.Greeter; +import org.apache.hello_world.services.SOAPService; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized.Parameters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * A set of tests for TLS SNI + */ +@RunWith(value = org.junit.runners.Parameterized.class) +public class ClientSniTest extends AbstractBusClientServerTestBase { + static final String PORT = allocatePort(ClientSniServer.class); + + final String clientKey; + final Object clientVal; + + public ClientSniTest(String ck, Object value) { + this.clientKey = ck; + this.clientVal = value; + } + + @Parameters(name = "{0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { + {"force.urlconnection.http.conduit", true}, + {"use.async.http.conduit", true}, + {"defaultConduit", true}, + {"org.apache.cxf.transport.http.forceVersion", "1.1"}, + {"org.apache.cxf.transport.http.forceVersion", "2"} + }); + } + + @BeforeClass + public static void startServers() throws Exception { + assertTrue( + "Server failed to launch", + // run the server in the same process + // set this to false to fork + launchServer(ClientSniServer.class, true) + ); + } + + @AfterClass + public static void cleanup() throws Exception { + stopAllServers(); + } + + // Server directly trusts the client cert + @org.junit.Test + public void testSniServerNames() throws Exception { + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = ClientSniTest.class.getResource("client-sni.xml"); + + Bus bus = bf.createBus(busFile.toString()); + BusFactory.setDefaultBus(bus); + BusFactory.setThreadDefaultBus(bus); + + URL url = SOAPService.WSDL_LOCATION; + SOAPService service = new SOAPService(url, SOAPService.SERVICE); + assertNotNull("Service is null", service); + final Greeter port = service.getHttpsPort(); + assertNotNull("Port is null", port); + + updateAddressPort(port, PORT); + + ((BindingProvider)port).getRequestContext().put(clientKey, clientVal); + + + assertEquals(port.greetMe("Kitty"), "Hello Kitty"); + + ((java.io.Closeable)port).close(); + bus.shutdown(true); + } +} diff --git a/systests/transports/src/test/resources/org/apache/cxf/systest/https/sni/client-sni-server.xml b/systests/transports/src/test/resources/org/apache/cxf/systest/https/sni/client-sni-server.xml new file mode 100644 index 0000000000..da1ed372fb --- /dev/null +++ b/systests/transports/src/test/resources/org/apache/cxf/systest/https/sni/client-sni-server.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:jaxws="http://cxf.apache.org/jaxws" + xmlns:http="http://cxf.apache.org/transports/http/configuration" + xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration" + xmlns:sec="http://cxf.apache.org/configuration/security" + xmlns:cxf="http://cxf.apache.org/core" + xmlns:p="http://cxf.apache.org/policy" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache.org/policy http://cxf.apache.org/schemas/policy.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/transports/http-jetty/confi [...] + <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/> + <cxf:bus> + <cxf:features> + <bean class="org.apache.cxf.ext.logging.LoggingFeature"/> + </cxf:features> + </cxf:bus> + + <httpj:engine-factory id="tls-settings"> + <httpj:engine port="${testutil.ports.ClientSniServer}"> + <httpj:tlsServerParameters> + <sec:keyManagers keyPassword="password"> + <sec:keyStore type="jks" password="password" resource="keys/Bethal.jks"/> + </sec:keyManagers> + <sec:trustManagers> + <sec:keyStore type="jks" password="password" resource="keys/Truststore.jks"/> + </sec:trustManagers> + </httpj:tlsServerParameters> + </httpj:engine> + </httpj:engine-factory> + + <jaxws:endpoint xmlns:e="http://apache.org/hello_world/services" + xmlns:s="http://apache.org/hello_world/services" + id="DirectTrustServer" + implementor="org.apache.cxf.systest.http.GreeterImpl" + address="https://localhost:${testutil.ports.ClientSniServer}/SoapContext/HttpsPort" + serviceName="s:SOAPService" + endpointName="e:HttpsPort" depends-on="tls-settings"/> +</beans> \ No newline at end of file diff --git a/systests/transports/src/test/resources/org/apache/cxf/systest/https/sni/client-sni.xml b/systests/transports/src/test/resources/org/apache/cxf/systest/https/sni/client-sni.xml new file mode 100644 index 0000000000..cca43394e7 --- /dev/null +++ b/systests/transports/src/test/resources/org/apache/cxf/systest/https/sni/client-sni.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:http="http://cxf.apache.org/transports/http/configuration" + xmlns:jaxws="http://cxf.apache.org/jaxws" + xmlns:cxf="http://cxf.apache.org/core" + xmlns:p="http://cxf.apache.org/policy" + xmlns:sec="http://cxf.apache.org/configuration/security" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache [...] + + <cxf:bus> + <cxf:features> + <bean class="org.apache.cxf.ext.logging.LoggingFeature"/> + </cxf:features> + </cxf:bus> + <http:conduit name="https://localhost:.*"> + <http:tlsClientParameters disableCNCheck="true"> + <sec:trustManagers> + <sec:keyStore type="jks" password="password" resource="keys/Truststore.jks"/> + </sec:trustManagers> + <sec:serverNames> + <sec:serverName>bethal</sec:serverName> + </sec:serverNames> + </http:tlsClientParameters> + </http:conduit> +</beans> \ No newline at end of file
