Search code examples
javassl

java.security.cert.CertificateException: No subject alternative name found matching IP address even when using a FQDN


I am trying to send an HTTPS request to precision.epayworldwide.com from my Java application.

This is the code I am using:

URL url = new URL("https://precision.epayworldwide.com/up-interface");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String responseLine = reader.readLine();

while (responseLine != null)
{
    System.out.printf("%s\n", responseLine);
    responseLine = reader.readLine();
}

and this is the exception I get:

Exception in thread "main" org.bouncycastle.tls.TlsFatalAlert: certificate_unknown(46)
    at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.checkServerTrusted(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvTlsClient$1.notifyServerCertificate(Unknown Source)
    at org.bouncycastle.tls.TlsUtils.processServerCertificate(Unknown Source)
    at org.bouncycastle.tls.TlsClientProtocol.handleServerCertificate(Unknown Source)
    at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)
    at org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.blockForHandshake(Unknown Source)
    at org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:523)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1296)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at Main.main(Main.java:17)
Caused by: java.security.cert.CertificateException: No subject alternative name found matching IP address 195.145.98.203
    at org.bouncycastle.jsse.provider.HostnameUtil.checkHostname(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkEndpointID(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkEndpointID(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkExtendedTrust(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkTrusted(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvX509TrustManager.checkServerTrusted(Unknown Source)
    ... 18 more

I have correctly imported the TLS certificate of that hostname:

Owner: C=GB,L=Billericay,O=Epay Limited,CN=*.epayworldwide.com
Issuer: C=US,O=DigiCert Inc,CN=DigiCert TLS RSA SHA256 2020 CA1
Serial number: 97cc7ab5601ca15b1e65530f3a3e8a3
Valid from: Mon Oct 10 02:00:00 CEST 2022 until: Sun Nov 05 00:59:59 CET 2023
Certificate fingerprints:
         MD5:  AF:C4:FA:F6:A2:99:D8:C3:C3:9A:1D:B7:A4:6D:D7:21
         SHA1: 4D:55:B9:2B:69:67:2E:AA:2A:B4:3B:B6:D1:BC:35:77:B0:FD:50:A0
         SHA256: 96:AF:91:70:80:F6:F1:9E:30:18:CC:97:53:10:B4:7E:B2:CC:37:31:77:CB:C1:E1:1C:14:BC:CF:19:08:04:3B
         Signature algorithm name: SHA256WITHRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

I don't understand why the code is throwing that exception. I have searched here on StackOverflow and I have found similar questions (1, 2, 3), but these people used an IP address to connect to the server, instead of an FQDN.

I do not control the server so I can't change the certificate in any way.

Is there something I can do client-side without disabling the hostname verification?


Solution

  • Since I didn't want to change the code, I digged in the BouncyCastle's Githb issue tracker and I found this JVM property:

    From v1.70 if you want to use option 1 please prefer -Dorg.bouncycastle.jsse.client.assumeOriginalHostName=true instead of -Djdk.tls.trustNameService=true

    (see issue 460 and linked issues for more references)

    So I have added this property to my JVM arg list and now the TLS handshake to precision.epayworldwide.com works.