I need to query a third-party API that has a certificate/DNS name mismatch. The left-most label of the hostname specified in the certificate is not known in advance (i.e. CN=some-random-hash.example.com) and therefore I want to configure a HTTP client with a custom hostname verifier.
When using the default Reactor Netty client obtained via HttpClient.create()
, a HostnameChecker
class fails certificate validation and causes an exception to be thrown:
java.security.cert.CertificateException: No subject alternative DNS name matching XXX found.
at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:214)
at sun.security.util.HostnameChecker.match(HostnameChecker.java:96)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:436)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:252)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:136)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1626)
... 30 more
This related answer suggests implementing an SNIMatcher as follows:
HttpClient.create()
.secure(sslContextSpec -> sslContextSpec
.sslContext(sslContext)
.handlerConfigurator((handler) -> {
SSLEngine engine = handler.engine();
SSLParameters params = new SSLParameters();
List<SNIMatcher> matchers = new LinkedList<>();
SNIMatcher matcher = new SNIMatcher(0) {
@Override
public boolean matches(SNIServerName serverName) {
return true;
}
};
matchers.add(matcher);
params.setSNIMatchers(matchers);
engine.setSSLParameters(params);
}));
However, according to the Javadocs of setSNIMatchers:
This method is only useful to SSLSockets or SSLEngines operating in server mode.
The matcher is consequently being ignored and even worse, the above code creates an empty set of SSLParameters
, which seems to disable the SSL verification entirely.
How would one properly implement a custom hostname verifier with Reactor Netty?
Note that when using the Apache HTTPClient library, this can be trivially achieved with the following:
HttpClients.custom().setSSLHostnameVerifier((hostname, session) -> ...).build();