关于https:一张证书引发的噱案

61次阅读

共计 3977 个字符,预计需要花费 10 分钟才能阅读完成。

– 引 –

我也没想到在神策数据这大半年能遇到好几次和证书相干的问题。

– 起 –

2021 年 9 月 3 号,一个新客户接入到咱们的 SaaS 零碎。在某个环节,咱们会给客户发个 HTTPS 申请,没想到居然遇到了个 SSLHandshakeException:

Caused by: javax.net.ssl.SSLHandshakeException: … unable to find valid certification path to requested target

在服务器上用 curl 试一把,也报错:

$ curl -v https://some.domain/
CAfile: /etc/pki/tls/certs/ca-bundle.crt
...
curl: (60) Peer's Certificate issuer is not recognized.

但用浏览器关上这个 URL,却是没问题的,这阐明问题应该出在咱们的服务器端。

– 析 –

咱们晓得,HTTPS 是靠证书保障通信安全的;但客户端如何保障服务端给的证书是可信的呢?

因为证书总是由某个证书颁发机构(Certificate issuer,或 Certificate Authority,简写成 CA)签发的,如果咱们当时将一批可信的证书颁发机构存储在本地,就能够在发动申请的时候判断证书是否可信了。

有时状况会更简单一些:某些机构不在咱们的列表里,但他的证书是由咱们信赖的某个机构颁发的,咱们也认为他是可信的,因而他颁发的证书也是可信的。

于是这就形成了一个信赖链,链的末端是「根证书颁发机构」(Root CA),这些机构通常是国内上公认牢靠的大型机构,或者国家权威机关背书的机构。

了解了这点,就能够揣测,该当是咱们服务器上的机构列表没有及时更新;只有把该客户证书的颁发机构退出本地的列表就应该能解决该问题。

– 解 –

再细看下面 curl 命令的输入,有一行 CAfile: /etc/pki/tls/certs/ca-bundle.crt,这就是 curl 应用到的证书颁发机构列表。

www.baidu.com 为例,咱们能够通过如下命令获取客户证书的信赖链:

$ openssl s_client -showcerts -servername server -connect www.baidu.com:443 > cacert.pem

在失去的 cacert.pem 中,咱们能够看到如下内容(略作简化 ):

Certificate chain
0 s:/CN=baidu.com
i:/CN=GlobalSign Organization Validation CA - SHA256 - G2
-----BEGIN CERTIFICATE-----
MIIKQDCCCSigAwIBAgIMEZhyT2Z0o9Yhv76iMA0GCSqGSIb3DQEBCwUAMGYxCzAJ
...(略)...
n3XcFtwQLBY9Iuqh8Mn7vtiv5k2azdGsYhZcFBCBAeUoRhDC
-----END CERTIFICATE-----
1 s:/CN=GlobalSign Organization Validation CA - SHA256 - G2
i:/OU=Root CA/CN=GlobalSign Root CA
-----BEGIN CERTIFICATE-----
MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG
...(略)...
K1pp74P1S8SqtCr4fKGxhZSM9AyHDPSsQPhZSZg=
-----END CERTIFICATE-----
...(略)...

能够看到外面有两段用 --BEGIN CERTIFICATE----END CERTIFICATE-- 包起来的 base64 编码字符串,这就是被编码为 PEM 格局(Privacy Enhanced Mail)的证书了(有时也会用 .crt 作为扩展名)。

在 BEGIN 后面有一些摘要,能够帮忙咱们理解证书的内容,比方 s:/CN=baidu.com 示意这个证书的主体(s 即 subject)是 baidu.com(CN 即 common name),i:/CN=GlobalSign 示意它的颁发机构(i 即 issuer)是 GlobalSign。

因而能够看到,这个 cacert.pem 实际上蕴含了两个证书,一个是百度应用的证书,另一个是颁发该证书的 GlobalSign 这个机构(CA)本人的证书。

通过 curl --cacert cacert.pem https://www.baidu.com 咱们能够确认,这个信赖链能用来验证 www.baidu.com 的证书(实际上咱们只须要外面第二个证书,将第一个证书删除,不影响 curl 的执行)。

回到该客户的状况,咱们用雷同的办法获得客户证书颁发机构的证书,将它放到 /etc/pki/ca-trust/source/anchors/ 目录,执行 update-ca-trust 将其退出到证书列表中,就能够失常应用 curl 命令来申请了。

– 然 –

没有「然而」的文章不是好文章。

curl 失常了,然而咱们的 Java 代码仍然报错,这阐明 java 和 curl 应用了不同的 CA 列表。

问题倒是好解决,简略搜寻一下,就理解到 jre 的证书是寄存在 $JAVA_HOME/jre/lib/security/cacerts 这个文件里,须要应用专门的 keytool 工具来更新它:

$ keytool -import -trustcacerts -file cacert.pem -alias 证书颁发机构的名称 -keystore $JAVA_HOME/jre/lib/security/cacerts
Enter keystore password: changeit(这是 jre 自带的默认明码)Certificate was added to keystore

再次验证,Java 代码就能够失常运行了。

注:如果想要独自验证某个证书,能够这样

  1. 先创立一个空的 keyStore(明码为 storePassword):

    $ keytool -genkeypair -alias boguscert -storepass storePassword -keypass secretPassword -keystore keystore -dname "CN=Developer"
    $ keytool -delete -alias boguscert -storepass storePassword -keystore emptyStore.keystore
  2. 增加证书到该 keyStore:

    $ keytool -import -trustcacerts -file cacert.pem -alias 机构名称 -keystore keystore
  3. 指定 keyStore 启动 java 程序:

    $ java -Djavax.net.ssl.trustStore=keystore -Djavax.net.ssl.trustStorePassword=storePassword -cp $CLASS_PATH CLASS_NAME

– 劫 –

不巧的是,这周又遇到了一个证书信赖的问题,这次是客户的环境向咱们的服务器发动申请,报了雷同的谬误。

有了前事不忘; 后事之师,下面这些命令执行起来堪称得心应手,然而这次却不灵了。

排查过程比拟琐碎,也因为陷入思维定势而走了一些弯路,但其实起因很简略,这里就不卖关子了。

这家客户是一家泛金融类的企业,其生产环境的网络安全级别十分高,不仅有严格的外网拜访限度,而且针对所有 https 申请都会默认劫持,用一个自签名证书返回错误信息。

通过与客户沟通,将神策数据的域名增加到白名单后,问题得以解决。

– 故事 –

讲完了事变,再讲讲故事。

非对称加密、证书、信赖链这一系列创造,形成了当初 web 通信安全的基石,很难设想如果没有这些基础设施,当初互联网还能做些什么。

然而这里暗藏了一个大 bug: 咱们凭什么置信本地这些证书颁发机构是可信的?

至多有三种状况会突破这个假如:

  1. 本地 CA 列表被净化

可能你的电脑 / 手机被病毒导入了 CA 证书;或者你本人可能就做过这个事件,比方公司网管要求增加公司的自签名证书,又或者你为了能应用 Charles 来抓 https 申请,导入了它自签名的 Root CA 证书。

  1. 机构的私钥透露

我没有在公开渠道查到相干的事变(倒是有一个代理商把客户证书的私钥给透露了);如果某个机构的私钥透露,这家机构应该离开张也不远了。

  1. 看起来正经的机构也可能不正经

各国政府管制的 CA 机构大略都干过些「不洁净」的事件(至多有这种激动),有一些被发现了,有一些还没有。出于本文的平安思考,这里就不开展细节了。此外,「不被政府管制」的那些机构,就肯定洁净么?说到底,机构总是被所在国管辖的,当遇到政府行政命令的时候,不肯定有镇压的能力。

综上,实践上并不存在 100% 牢靠的通信安全计划。

如果你的利用对通信安全要求十分严格,连本地的 CA 列表都不置信,能够思考退出更多的伎俩来进步通信的安全等级。

简略一点的场景(例如 app 不想被抓包破解协定),能够本人校验服务器的证书(证书指纹,或者本人指定证书颁发机构列表);要求更高的场景(例如须要拜访外部控制系统),能够给客户端颁发证书,浏览器会在申请时提供证书用于校验,感兴趣的话能够参考 这个不太欠缺的我的项目。

– 收 –

结尾照例做一个小结:

  1. HTTPS 是基于证书链来保障通信安全的;
  2. 信赖的基石是本地的证书颁发机构(CA)列表;
  3. 能够通过向本地列表增加 CA 证书的形式来解决须要信赖的证书;
  4. 本地的 CA 不肯定都是可信的;
  5. 能够通过更严格的校验,或者客户端证书来增强通信的安全等级。

最初,神策在北京、上海、成都、武汉、深圳等多地均在招聘开发、产品、QA 等岗位,感兴趣的小伙伴欢送私信勾结;也能够点击我的 内推链接 或扫码查看 JD 并投递简历:

欢送关注

正文完
 0