惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
C
Check Point Blog
有赞技术团队
有赞技术团队
H
Help Net Security
V
Vulnerabilities – Threatpost
N
News | PayPal Newsroom
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LINUX DO - 最新话题
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 【当耐特】
爱范儿
爱范儿
I
InfoQ
V
Visual Studio Blog
O
OpenAI News
Google DeepMind News
Google DeepMind News
S
Security Affairs
T
Troy Hunt's Blog
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
Engineering at Meta
Engineering at Meta
雷峰网
雷峰网
N
Netflix TechBlog - Medium
Latest news
Latest news
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Webroot Blog
Webroot Blog
S
Schneier on Security
MongoDB | Blog
MongoDB | Blog
T
Tor Project blog
V2EX - 技术
V2EX - 技术
Security Latest
Security Latest
Cloudbric
Cloudbric
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog RSS Feed
C
CERT Recently Published Vulnerability Notes
T
The Exploit Database - CXSecurity.com
P
Privacy International News Feed
S
Securelist
C
Cisco Blogs
博客园_首页
TaoSecurity Blog
TaoSecurity Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Threat Research - Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
S
Secure Thoughts

祈雨的笔记

安全多方计算MPC spark原理解析 kueue执行源码分析 spark on k8s执行源码分析 spark-operator源码解析 系统压测遇到的缓存击穿问题 我的世界PC与安卓联机 蚂蚁金服流量投放平台的AIG改造 G1大对象致Old区占用率高 日志打印导致接口响应率下跌分析 Groovy加载类导致OOM分析 ERROR日志打印导致CPU满载 记OceanBase死锁超时 应用发版期间服务响应超时 Ark Serverless初探 系统优化复盘一二三 The user specified as a definer does not exist Kong网关初探 API网关选型调研 CPU火焰图常用工具 配置中心选型调研 root操作Nginx导致用户组错误 基于Proxifier使用代理 FastJSON字段智能匹配踩坑 Nacos初探 记一次Nginx服务器CPU满荷载故障 基于券系统分库分表的思考 limit不参与SQL成本计算致索引失效 Linux常用性能监控命令 golang低版本http2偶现400 hostname in certificate didn't match 常见对称加密原理以及应用 tcp_tw_recycle引起的TCP握手失败 记一次mysql执行DDL导致锁表 mysql磁盘占用查看 mysql对text字段update致磁盘增长 elasticsearch报错index read-only TIME_WAIT与Http的Keep-Alive 记一次TIME_WAIT导致连接数报警 记一次生产事故OOM问题排查 redis分布式锁RedissonLock的实现细节 webservice复杂加密签名(2)java调用 webservice复杂加密签名(1)SoapUI mysql延时关联 利用中间人拦截实现APP内H5窜改 MySQL表字符集不同导致关联查询索引失效 通过SSH隧道远程办公 数据落盘方案 BeanDefinitionRegistryPostProcessor扩展 mysql空间索引 HTTPS攻击 spring循环依赖过程解析 elasticsearch性能优化 mysql IS NULL 使用索引 mysql字符集utf8mb4失效踩坑 常用加密算法 xml与javaBean转换 初探InnoDB MVCC源码实现 mysql索引原理 redis之list源码分析 redis之key过期源码分析 redis之string源码分析 redis之hash源码分析 线程池之ThreadPoolExecutor mysql数据页结构 Using temporary与Using filesort mysql回表致索引失效 springboot(28)HTTP连接池 定时任务之ScheduledThreadPoolExecutor elasticsearch常用script聚合 elasticsearch实现like查询 elasticsearch实现乐观锁 elasticsearch准实时原理 springboot(27)自定义缓存读写机制CachingConfigurerSupport optimizer tracing arthas常用命令 HTTP和HTTPS详解 redis集群选举机制 kafka消息重试 一点压力测试的经验 kafka架构概念 explain分析sql语句字段的解释 JVM问题分析处理手册 logstash过滤器(2)date logstash过滤器(3)dissect logstash编码器(1)json logstash编码器(2)multiline logstash表达式 logstash输入(1)通用选项 logstash输入(3)file logstash过滤器(1)通用选项 logstash输入(2)stdin logstash安装 记一次前端vConsole导致JSON序列化错误排查 解决多个异步操作嵌套问题 fastjson反序列化失败autoType is not support RTMP串流服务 POI自动调整列宽错误 Nginx+Lua实现动态黑名单 使用curl命令模拟POST和GET请求
hostname in certificate didn't match
祈雨的笔记 · 2020-07-18 · via 祈雨的笔记

描述

某服务商对接公司的在阿里云配置的API网关时,对方开发沟通说我们公司的HTTPS有问题,请求接口后报如下错误:

1
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链接。

1
2
3
4
5
6
7
8
9
10
11
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握手失败,但报错信息与接入商的报错信息并不相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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链接后,报错信息终于与接入商的报错信息相同了。

1
2
3
4
5
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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());
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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版本或更高版本;