Java HTTPS 请求 : hostname in certificate doesn't match

标签 java ssl https ssl-certificate apache-httpclient-4.x

我正在尝试从 Java 8 客户端(使用 Apache HttpClient 4.3)向公共(public)服务器(不属于我)发出 HTTPS 请求。我通常对请求没有任何问题,但目标服务器似乎进行了一些更改并且请求开始失败。

最初,错误是 ValidatorException: PKIX Path Building Failed但我通过将服务器证书添加到我的 Java 信任库来解决这个问题。我现在得到的错误是 hostname in certificate didn't match: <target.server.com> != <*.something.else.com> OR <something.else.com> .

something.else.com主机是某种托管站点等。无论如何我很满意没有任何可疑的事情发生。我为 target.server.com 提取了证书通过运行以下命令:

openssl s_client -connect target.server.com:443 -showcerts

我没有看到任何对 target.server.com 的引用在证书信息中,仅适用于 something.else.com .我访问 target.server.com 没有任何问题在网络浏览器 (Chrome) 中,没有关于 SSL 证书的投诉。我使用以下命令将服务器证书添加到我的信任库:

sudo keytool -import -file /tmp/target.cer -keystore /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts -alias targetserv

有什么方法可以在不更改 Java 代码的情况下解决这个问题,例如在证书导入命令等中添加一些内容,如果没有,我还能如何让这些请求再次工作。


这是我的 HTTP 请求代码(使用 HttpClient 4.3):

CloseableHttpClient httpClient = HttpClients.custom().setUserAgent(HTTP_USER_AGENT).build();
HttpGet request = new HttpGet(url);
HttpResponse response = httpClient.execute(request);

...抛出 java.io.IOException: hostname in certificate didn't match

此外,这是我试图到达的实际目标服务器:https://www.isi.gov.ie


我已经尝试使用 HttpClient v4.5,但仍然遇到与 4.3 相同的错误。我尝试使用 Java 内置的 HTTP 请求类发出请求,请求成功(返回 302):

URL obj = new URL("https://www.isi.gov.ie");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();

con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", USER_AGENT);

int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + url);
System.out.println("Response Code : " + responseCode);

BufferedReader in = new BufferedReader(
        new InputStreamReader(con.getInputStream()));

String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
    response.append(inputLine);
}
in.close();

//print result
System.out.println(response.toString());

有什么方法可以解决 HttpClient 的这些问题吗?


事实证明,在我的应用程序的其他地方,我一直在使用 Java 系统属性 jsse.enableSNIExtension 禁用 SNI我已经忘记了。我这样做的原因是因为这是我能找到的唯一修复方法,可以防止所有使用 HttpClient 的 HTTP 请求因错误而失败:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

如何在不出现这些错误的情况下重新启用 SNI?
我正在使用以下版本的 Java:

openjdk version "1.8.0_212"
OpenJDK Runtime Environment (build 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03)
OpenJDK 64-Bit Server VM (build 25.212-b03, mixed mode)

最佳答案

TLDR:它可能是 SNI(和链)

显然,与今天的许多服务器一样,该服务器使用 TLS 扩展支持具有不同证书的多个“虚拟”主机 'Server Name Indication'又名 SNI。如果使用 www.isi.gov.ie 的 SNI 进行联系,它会提供对该域和 isi.gov.ie 有效的证书,但如果不使用 SNI 进行联系,它为域 *.rdccremote.ierdccremote.ie 提供一个过期证书;我认为那些就是您所说的“托管站点”,尽管我没有看到任何迹象;我最好的谷歌搜索结果是 this parliamentary question这似乎权威地说它在政府部门的“共享服务”上——尽管它使用我可以轻松访问的 DNS 解析到相同的地址 (146.0.55.149)。

此外,这两种证书均由 DigiCert 颁发,但在这两种情况下,服务器都不会按照 TLS 规范要求发送验证所需的链/中间证书。浏览器通常可以“填充”缺失的链证书,但 Java JSSE(和大多数其他软件工具)不能;这可能导致您的路径构建错误,但不会影响主机名不匹配。

openssl s_client 在 1.1.0 以下的版本 默认情况下不发送 SNI,并且您的编辑命令并不表示您为其指定了选项。 (-showcerts 在服务器上比不发送链证书更没用)。还要注意 s_client 仅以解码形式显示证书的主题字段(和 Issuer,分别为 s:i:),但是不是 SubjectAlt[ernative]Name aka SAN 扩展名,它自本世纪初以来就用于 SSL/TLS 服务器证书的主机名。要查看 SAN,请通过 openssl x509 -text -noout 或其他解码工具(如 keytool -printcert -file $pemfile)从 s_client 提供 PEM block > 或 Windows 上的证书查看器。

Java 8 通常会发送 SNI,尽管这可能会受到调用代码(应用程序或中间件)的影响。 AHC 4.3 已经很老了,我不确定,但是 4.5 适用于我 Java 7 和 8(Oracle,但 AFAIK JSSE 在 Oracle 和 OpenJDK 之间是相同的)。您(或任何人)能否通过 sysprop jsse.enableSNIExtension=false 配置您的 JVM 以阻止 SNI?您能否使用 sysprop javax.net.debug=ssl 运行并捕获或记录(大)输出,或者使用 wireshark、tcpdump 或类似工具捕获并检查线路数据,以验证您的 Java实际上是在发送 ClientHello 吗?

关于Java HTTPS 请求 : hostname in certificate doesn't match,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56774500/

相关文章:

java - 使用 2 个线程从 gsm 调制解调器获取短信

java - 在android中解析带有字符和数字的字符串

node.js - expressJS 4.x + 虚拟主机 + SSL/HTTPS

java - 从数组中删除一些元素后如何减小数组的大小

Java volatile 引用与 AtomicReference

ruby-on-rails - Heroku:连接中出现未知的 SSL 协议(protocol)错误

security - 您的连接仅在我的网站中不是私有(private)的(隐私错误)- 页面卡在安全页面中

apache - 多个 SSL 证书破坏 Apache

svn - 502 Bad Gateway with nginx + apache + subversion + ssl (SVN COPY)

ssl - WinSCP .NET 程序集是否支持带有 FTPS 客户端证书身份验证的隐式 TLS/SSL?