I am using the spring webflux webclient tool to call the API. The API server address is HTTPS, and it is an IP address without a domain name. I need to disable the hostname validation in webclient. The exception now is as follows
Caused by: java.security.cert.CertificateException: No subject alternative names matching IP address 180.101.147.89 found
at sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:168) ~[na:1.8.0_211]
at sun.security.util.HostnameChecker.match(HostnameChecker.java:94) ~[na:1.8.0_211]
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:461) ~[na:1.8.0_211]
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:442) ~[na:1.8.0_211]
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:260) ~[na:1.8.0_211]
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:144) ~[na:1.8.0_211]
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1626) ~[na:1.8.0_211]
... 28 common frames omitted
@Bean
public WebClient telcomWebclient(WebClient.Builder webClientBuilder,
@Value("${telcom.api.host}") String telcomApiHost,
@Value("${telcom.api.certificate-name}") String telcomApiCertificateName,
@Value("${telcom.api.certificate-store-pass}") String telcomApiCertificateStorePass) {
try {
KeyStore selfCert = KeyStore.getInstance("pkcs12");
selfCert.load(getClass().getResourceAsStream("/cert/outgoing.CertwithKey.pkcs12"), "IoM@1234".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(selfCert, "IoM@1234".toCharArray());
KeyStore caCert = KeyStore.getInstance("jks");
caCert.load(getClass().getResourceAsStream("/cert/" + telcomApiCertificateName), telcomApiCertificateStorePass.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
tmf.init(caCert);
SslContext sslContext = SslContextBuilder.forClient()
.keyManager(kmf)
.trustManager(tmf)
.build();
HttpClient httpClient = HttpClient.create().create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
ClientHttpConnector clientHttpConnector = new ReactorClientHttpConnector(httpClient);
return webClientBuilder.clientConnector(clientHttpConnector).baseUrl(telcomApiHost).build();
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException e) {
log.error("Config webclient,error occurs", e);
System.exit(-1);
}
return null;
}
Click the name of the server for which you want to disable host name verification. Select Configuration > SSL , and click Advanced at the bottom of the page. Set the Hostname Verification field to None.
Simply put, WebClient is an interface representing the main entry point for performing web requests. It was created as part of the Spring Web Reactive module and will be replacing the classic RestTemplate in these scenarios.
Aside from disabling SSL verification entirely, (WHICH I DON'T RECOMMEND) by passing in InsecureTrustManagerFactory.INSTANCE
like this:
SslContext sslContext = SslContextBuilder.forClient()
.keyManager(kmf)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
You can configure the HttpClient to essentially override the hostname verification by configuring a custom SNIMatcher as below:
HttpClient.create().create().secure(sslContextSpec -> sslContextSpec
.sslContext(sslContext)
.handlerConfigurator(sslHandler ->
SSLEngine engine = handler.engine();
//engine.setNeedClientAuth(true);
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);
);
I have tested this and verified it worked. I hope this helps!
This was inspired by the answer here: Configure HostnameVerifier with reactor netty for spring-webflux WebClient
Actually SNIMatcher is for server side only. This can be verified by putting
a break point on the return of the matches() method. Execution never stops at that break point.
The actual reason the host name verification is skipped because the newly params.identificationAlgorithm value is null instead of "HTTPS" when the SSLParameters is overriden.
Here is a simpler code which works as well as above.
HttpClient.create().secure(sslContextSpec -> sslContextSpec
.sslContext(sslContext)
.handlerConfigurator(sslHandler -> {
SSLEngine engine = handler.engine();
SSLParameters params = new SSLParameters(engine.getSSLParameters().getCipherSuites(), engine.getSSLParameters().getProtocols());
// With java update null value is no longer sufficient to skip host name verification
params.setEndpointIdentificationAlgorithm("");
engine.setSSLParameters(params);
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With