hostname in certificate didn't match
source link: https://wakzz.cn/2020/07/18/java/hostname%20in%20certificate%20didn't%20match/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
hostname in certificate didn't match
某服务商对接公司的在阿里云配置的API网关时,对方开发沟通说我们公司的HTTPS有问题,请求接口后报如下错误:
javax.net.ssl.SSLException: hostname in certificate didn't match: <马赛克.com> != <*.alicloudapi.com> OR <*.alicloudapi.com> OR <alicloudapi.com>
看到错误信息,第一反应是HTTPS证书有问题,所以HTTPS握手失败而报错,但是该API网关有多个服务商对结过,均未发生类似的情况。另外,报错信息中的域名alicloudapi.com
显然不是我们公司的域名,而是阿里云的域名。
抱着证书有问题的怀疑,在证书检查平台myssl.com做了个证书检测,结果见下图。
检查报告显示HTTPS的证书一切正常,但是有一点引起了我的注意:检查报告显示该域名有两个证书信息,一个证书是当前域名的,另个域名是似乎是阿里云的域名,该阿里云域名正是报错信息里的域名。
与对方开发进一步沟通后,得知他们使用的JDK版本是1.6版本,于是我也将本地环境切换成JDK1.6后,请求了自己在阿里云配置的多域名证书的HTTPS链接。
import java.net.URL;
import java.net.URLConnection;
public class Application {
public static void main(String[] args) throws Exception {
URL link = new URL("https://img.wakzz.cn/202004/20200407152546.jpg");
URLConnection connection = link.openConnection();
connection.connect();
System.out.println(connection.getContentLength());
}
}
报错信息如下,果然当使用低版本的JDK1.6请求多证书的HTTPS链接后,HTTPS握手失败,但报错信息与接入商的报错信息并不相同。
Exception in thread "main" javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching img.wakzz.cn found.
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1836)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:287)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:281)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1339)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:203)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:848)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:784)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1012)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1320)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1347)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1331)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:432)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
at Application.main(Application.java:8)
Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching img.wakzz.cn found.
at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:208)
at sun.security.util.HostnameChecker.match(HostnameChecker.java:94)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:285)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:271)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1318)
... 11 more
于是进一步,连同HttpClient
依赖的版本也使用了低版本4.2,请求该HTTPS链接后,报错信息终于与接入商的报错信息相同了。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2</version>
</dependency>
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
public class Application {
public static void main(String[] args) throws Exception {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("https://img.wakzz.cn/202004/20200407152546.jpg");
HttpResponse response = httpClient.execute(httpGet);
System.out.println(response.getEntity().getContentLength());
}
}
Exception in thread "main" javax.net.ssl.SSLException: hostname in certificate didn't match: <img.wakzz.cn> != <img.ucdl.pp.uc.cn> OR <img.ucdl.pp.uc.cn> OR <iscsi.ucdl.pp.uc.cn> OR <slient.ucdl.pp.uc.cn> OR <alissl.ucdl.pp.uc.cn> OR <cdn.osupdateservice.yunos.com> OR <oss.ucdl.pp.uc.cn>
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:228)
at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java:54)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:149)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:130)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:641)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:480)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784)
at Application.main(Application.java:18)
SNI扩展
一个主机可以绑定多个Web服务,比如某个主机的Nginx配置了a.com
、b.com
、c.com
等多个域名的代理。当HTTP请求到达该主机的Nginx时,Nginx可以直接解析Http报文中的Host
值从而将请求转发给特定的Web服务主机。
但是当HTTPS请求到达该主机的Nginx时就出问题了,每个域名绑定一个证书,当HTTPS握手请求到Nginx时,此时HTTPS还没建立完成,请求报文中并没有Host
值,Nginx也就无从得知该给客户端响应哪个域名的证书。
于是在RFC 6066定义了TLS/SSL的SNI扩展server_name
,类似于HTTP请求中的Host
,客户端请求HTTPS握手时的Client Hello
消息中增加一个server_name
字段,值为请求主机的域名。当服务端收到该请求时,解析出server_name
的值从而给客户端响应对应的域名证书。
JDK支持
Enhancements in Java SE 7中显示,SNI扩展是JDK1.7版本才开始支持,因此在上面复现过程中使用JDK1.6时,HTTPS握手失败。
通过抓包HTTPS握手过程,jdk1.8在发送Client Hello
请求时带上了server_name
扩展;
而在jdk1.6在发送Client Hello
请求时并没有server_name
扩展;
而即使使用了高版本的JDK,当使用低版本的HttpClient
请求时,依然会Https握手失败,因为HttpClient
是在4.3.2版本才开始支持SNI扩展功能,见Server Name Indication (SNI) Support。
通过抓包httpclient4.2的Client Hello
请求,显示报文中并没有server_name
扩展,因此当请求多域名证书的域名时,会Https握手失败。
- 该服务器主机下只配置一个HTTPS的域名证书;
- 客户端JDK版本升级到1.7版本或更高版本;
- 客户端
HttpClient
版本升级到4.3.2版本或更高版本;
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK