3

hostname in certificate didn't match

 2 years ago
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.
neoserver,ios ssh client

hostname in certificate didn't match

祈雨的博客
2020-07-18

某服务商对接公司的在阿里云配置的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做了个证书检测,结果见下图。

image

image

检查报告显示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.comb.comc.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扩展;

image

而在jdk1.6在发送Client Hello请求时并没有server_name扩展;

image

而即使使用了高版本的JDK,当使用低版本的HttpClient请求时,依然会Https握手失败,因为HttpClient是在4.3.2版本才开始支持SNI扩展功能,见Server Name Indication (SNI) Support

通过抓包httpclient4.2的Client Hello请求,显示报文中并没有server_name扩展,因此当请求多域名证书的域名时,会Https握手失败。

image

  1. 该服务器主机下只配置一个HTTPS的域名证书;
  2. 客户端JDK版本升级到1.7版本或更高版本;
  3. 客户端HttpClient版本升级到4.3.2版本或更高版本;

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK