ssl java.security.cert.CertificateException:即使使用FQDN,也找不到与IP地址匹配的主题备用名称

46scxncf  于 2023-06-23  发布在  Java
关注(0)|答案(3)|浏览(189)

我试图从Java应用程序向precision.epayworldwide.com发送HTTPS请求。
这是我使用的代码:

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();
}

这是我得到的例外:

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

我已正确导入该主机名的TLS证书:

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

我不明白为什么代码会抛出这个异常。我在StackOverflow上搜索过,发现了类似的问题(123),但这些人使用IP地址连接到服务器,而不是FQDN。
我不控制服务器,所以我不能以任何方式更改证书。
有没有什么我可以做的客户端,而不禁用主机名验证?

vjhs03f7

vjhs03f71#

基于this bug report HttpURLConnection将只使用SNI与SunJSSE作为JSSE提供程序,但不使用其他如BouncyCastle -您正在使用的。这就是为什么它会尝试根据IP地址而不是FQDN检查证书的原因:
原因:java.security.cert.CertificateException:未找到与IP地址匹配的主题替代名称195.145.98.203

ryoqjall

ryoqjall2#

这是HttpURLConnection在第三方JSSE实现中不能正常工作的一个潜在问题。
由于您直接打开URL(而不是通过一些难以修改的框架),最简单的解决方案可能是使用BCJSSE实用程序类org.bouncycastle.jsse.util.URLConnectionUtil

new URLConnectionUtil().openConnection(url)

如果BCJSSE未配置为默认提供程序,则还可以将特定的SSLSocketFactory传递给URLConnectionUtil构造函数。
有关基本问题的背景信息,请参阅各种问题:

2nbm6dog

2nbm6dog3#

由于我不想更改代码,我在BouncyCastle的Githb问题跟踪器中挖掘了一下,发现了这个JVM属性:
从v1.70开始,如果您想使用选项1,请首选**-Dorg.bouncycastle.jsse.client.assumeOriginalHostName=true**,而不是-Djdk.tls.trustNameService=true
(see issue 460和链接问题以获取更多参考)
因此,我将此属性添加到JVM参数列表中,现在precision.epayworldwide.com的TLS握手工作正常。

相关问题