Apache HttpClient 4.5.13切换主机名验证器后证书匹配异常求助
我将Apache HttpClient 4.5.13的SSLConnectionSocketFactory主机名验证器从NoopHostnameVerifier改为DefaultHostnameVerifier,修改前后代码如下:
修改前代码:
final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1.2" }, null, NoopHostnameVerifier.INSTANCE);
修改后代码:
final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1.2" }, null, new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault()));
此后访问https://foo.s3.amazonaws.com/fileName.csv时抛出SSL验证异常,报错信息:
Certificate for <foo.s3.amazonaws.com> doesn't match any of the subject alternative names:[*.s3.amazonaws.com, s3.amazonaws.com]
完整报错堆栈:
javax.net.ssl.SSLPeerUnverifiedException: Certificate for <foo.s3.amazonaws.com> doesn't match any of the subject alternative names: [*.s3.amazonaws.com, s3.amazonaws.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:376) 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)
该问题仅在生产环境出现,本地无法复现,请问可能是什么原因导致的?
公共后缀匹配逻辑差异:
DefaultHostnameVerifier搭配PublicSuffixMatcherLoader.getDefault()后,会依据公共后缀列表验证域名。如果生产环境的公共后缀数据将s3.amazonaws.com识别为公共后缀,那么*.s3.amazonaws.com通配符只能匹配该后缀前的一级域名,无法匹配foo.s3.amazonaws.com——这会触发验证失败。而本地环境的公共后缀列表可能未将s3.amazonaws.com标记为公共后缀,因此验证通过。DNS解析不一致:生产环境的DNS服务器可能返回了异常解析结果,将
foo.s3.amazonaws.com指向了非标准S3节点的IP,该节点的证书虽然包含*.s3.amazonaws.com,但受公共后缀规则影响无法通过验证;本地则解析到正常S3节点,验证无问题。代理/网关的证书拦截:生产环境存在的代理或网关设备可能对HTTPS请求做了证书重签名,生成的新证书SAN仅包含
*.s3.amazonaws.com和s3.amazonaws.com,结合公共后缀的判断逻辑,导致通配符无法匹配目标域名。类加载或版本差异:生产环境可能存在类加载冲突,实际生效的HttpClient类版本与本地的4.5.13不一致,其
DefaultHostnameVerifier的通配符验证逻辑更严格,导致验证失败。
内容的提问来源于stack exchange,提问作者Erez Scholz

