javasslkubernetes-ingresssnissl-handshake

Why with apache-http-client at some point our Https calls are treated as calls without DNS name, but with IP address?


We have a problem with doing a handshake for the certificate like

javax.net.ssl.SSLPeerUnverifiedException: Certificate for <g.i.t.n.c.com> doesn't match any of the subject alternative names: [*.t.n.c.com]

Our ingress is configured to provide a certificate with alternative name g.i.t.n.c.com if we access it through DNS name and *.t.n.c.com if we access through the IP.

But in our code we just have a Spring WS client using Apache Http Client under the hood.

Caused by: javax.net.ssl.SSLPeerUnverifiedException: Certificate for <g.i.t.n.c.com> doesn't match any of the subject alternative names: [*.t.n.c.com]
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSocketFactory.java:507)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:437)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384)
    at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:374)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
    at org.springframework.ws.transport.http.HttpComponentsConnection.onSendAfterWrite(HttpComponentsConnection.java:121)
    at org.springframework.ws.transport.AbstractWebServiceConnection.send(AbstractWebServiceConnection.java:48)
    at org.springframework.ws.client.core.WebServiceTemplate.sendRequest(WebServiceTemplate.java:658)
    at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:606)
    at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)

And for unknown reason that started happening at some time, and at some time it was ok and SslSocket was established retrieving the right certificate (like it was accessing our Ingress via DNS hostname with SNI hostname data) and at some point (causing the above exception) the call for establishing connection was using IP without DNS hostname and no SNI data, and we got the broken certificate that appears only if our ingress is accessed without DNS name in SNI.

To get SNI details (ClientHello message) you may run Java app with

-Djavax.net.debug=ssl:handshake:verbose:keymanager:trustmanager

And you will have extra loggin to std.out like

javax.net.ssl|DEBUG|02 14|XNIO-1 task-9|2023-09-04 12:48:06.626 MSK|Finished.java:860|Consuming server Finished handshake message (

How to force it to use only one way to connect, using the DNS name at any time?

We use Java 11.


Solution

  • https://bugs.openjdk.org/browse/JDK-8220723 this was the root cause of the problem

    To work it around we forced the TLS1.2 to work to avoid using TLS1.3 through

    adding

    -Djdk.tls.client.protocols=TLSv1.2
    

    or

    -Dhttps.protocols=TLSv1.2