SOAP over TLS 1.2, Server Name Indication (SNI), and CXF

It seems destined to become a deep expert on the vagaries of TLS these days. My most recent challenge was figuring out why the Server Name Indication (SNI) extension simply didn't work in the BC-FIPS implementation I've been talking about in the last few posts.

Background of SNI

Let's talk a little about SNI for a moment. TLS is a lower-layer session protocol on top of TCP that encrypts communications. HTTP and HTTPS are upper layer (application) protocols above TLS. When you connect to an IP address over TCP and then initiate a TLS connection, the application layer has not yet seen the HTTP request, let alone the host header. SNI provides the same functionality as the HTTP host header in TLS. In fact, it works the same way as the HTTP host header.

The Host header in HTTP allows one server to serve multiple websites or DNS endpoints, but unless you use SNI, each endpoint must be served with the same certificate using wildcare or multiple alternative names. SNI allows one host to serve multiple sites using a different certificate for each site.

Integrating SNI with Apache CXF and BCFIPS

“… Unfortunately, when using HttpsURLConnection, SunJSSE uses some magic (reflection and/or internal API) to tell the socket about the “original hostname” used for the connection, and you can't use the same magic that exists inside the JVM. doesn't exist. .

For endpoint validation to work properly, you must use one of the following three workarounds:

We then go on to suggest the following recommended solutions:

3. A third (recommended) alternative is to set up a customized SSLSocketFactory on HttpsURLConnection, then intercept the socket creation call and manually set the SNI hostname on the created socket. We provide a utility class to make this simple, as shown in the example code below.

// main code block

{ SSLContext sslContext = …;

URL server URL = …;

URLConnectionUtil util = new URLConnectionUtil();

HttpsURLConnection connection =

(HttpsURLConnection)util.openConnection(serverURL);

}

It's very simple. what URLConnectionUtil.openConnection Wrap the socket factory provided by conn (see HttpsURLConnection.setSSLSocketFactory) with a method that calls the original createSocket method found on the connection and then calls a method on createSocket that sets the server name extension.

See also  Standard Essential Patents: What the Medical Technology Industry Needs to Know

So if you look at the CXF, it's the HttpURLConnectionFactory class that calls url.openConnection. Depending on your code for that class, you can simply override that class and replace it with a call to util.openConnection. Here is the original:

public HttpURLConnection createConnection(TLSClientParameters tlsClientParameters,
deputy deputyURL URL) throw IOException {
HttpURLConnection connection =
(HttpURLConnection) (deputy != Do not have ? URL.openConnection(deputy) : URL.openConnection());
If the (HTTPS_URL_PROTOCOL_ID.equivalence(URL.getProtocol())) {
If the (tlsClientParameters == Do not have) {
tlsClientParameters = new TLSClientParameters();
}
try hard {
DecorationTLS(tlsClientParameters, connection);
} Catch (Throwable jeon-) {
throw new IOException(“An error occurred initializing the secure socket”, jeon-);
}
}
return connection;
}

And a little tweak to the first two lines:

URLConnectionUtil utility = new URLConnectionUtil(
tlsClientParameters == Do not have ? Do not have : tlsClientParameters.getSSLSocketFactory()
);
HttpURLConnection connection =
(HttpURLConnection) (deputy != Do not have ? utility.openConnection(url, deputy) : utility.openConnection(url));

But for some reason it didn't work.

What I found while debugging this is that the decorWithTLS method also wraps the connection's socket factory, but fails to actually see the server socket factory, which may already be set in the HttpsUrlConnection passed in.

Here's a picture of how to do it:

It goes on for almost 100 lines, and does all kinds of weird circling of low-level code that often requires working across multiple libraries, including reflection and various other oddities.

What's missing here is an initial check to see if the connection is already a HttpsURLConnection, and if so, whether a non-default SSL socket factory is already set up. In these situations it becomes a socket factory (created by URLConnectionUtil) that needs to be rewrapped. As I looked at everything this method does, I realized the following:

  1. I'm not interested in anything other than the JSSE implementation.
  2. When I enter this method my SocketFactory is always set and this is the method that is used.
See also  Elevance, BCBSLA revive $2.5 billion merger

So I replaced the middle if statement in the redefined function with:

If the (HTTPS_URL_PROTOCOL_ID.equivalence(URL.getProtocol())) {
If the (tlsClientParameters == Do not have) {
tlsClientParameters = new TLS client parameters();
}
hostname verifier validator = SSLUtils.getHostnameVerifier(tlsClientParameters);
connection.setHostnameVerifier(validator);
}

All I care about with DecorWithTLS is setting up the hostname resolver, so it simplifies everything a lot.

This is how I enabled SNI with BCFIPS in previous versions of Apache CXF. There is also some other code required because we need to import a subclass that creates a connection to the factory used by Conduit. Its contents are explained below.

public class HTTPConduit extend URLConnectionHTTPConduit {
public revolution class factory It is implemented HTTPConduitFactory {
@dominate
public org.apache.cxf.transport.http.HTTPConduit createConduit(HTTPTransportFactory f, bus b,
EndpointInfo localInfo, EndpointReferenceType target) throw IOException {

HTTPConduit Conduit = new HTTPConduit(b, localInfo, target);
// Perform other conduit configurations here.
return conduit;
}
}
public HTTPConduit(Bus b, EndpointInfo ei, EndpointReferenceType t) throw IOException {
supervisor(b, no, t);
// Override the default ConnectionFactory.
Connected factory = new connectionfactory();
}
}

If you want to create that bean in one of your configuration classes somewhere else in your application, you must include a @Bean declaration.

@Configuration class MyAppConfig {

// …

@Bean HTTPConduitFactory httpConduitFactory() {

Returns a new HTTPConduit.Factory().

}

}

Leave a Reply

Your email address will not be published. Required fields are marked *