最近这几天互联网似乎不太平,先是证书私钥泄露,后是包括 PHP在内的多种语言爆出漏洞。趁着这次私钥泄露事件,复习一下专业课的相关知识,再了解一下 HTTPS 背后的密码学原理。
私钥泄露
根据公开记录,10月17日,Mozilla 收到报告称,eMudhra 的 8 张中间 CA 证书对应的私钥已失守。随后,Mozilla 确认已有多人独立恢复出了这些私钥,说明相关密钥的生成过程存在严重问题。
部署网站时,经常需要配置 HTTPS 证书和私钥。但究竟什么是证书,什么又是公私钥?为什么 CA 自己的私钥出了问题会影响到其他网站?这篇文章就从这些问题开始,把 HTTPS 背后的过程捋一遍。
公钥和私钥
先从对称加密开始,加密密钥和解密密钥是同一个,这就是对称加密,常见算法有 AES。它的效率高,适合加密大量数据。但有一个问题:两个人一开始怎么安全地拿到同一个密钥?万一密钥被别人截获,岂不是可以直接解密消息?
非对称加密是另一种思路,加解密可以用不同的密钥。基于数学原理生成一对密钥,公钥可以公开,私钥自己保管。在算法、参数和密钥生成过程安全的前提下,可以从私钥计算出公钥,但根据公钥反推私钥,在现实计算资源下不可行。常见算法有 RSA。
举个例子,小明向小红发送信息,小明用小红的公钥加密信息,然后小红再用自己的私钥解密。这样,小红只需要公开公钥。不过,小明怎么知道拿到的公钥真的是小红的?如果中间人把它换成自己的公钥,消息确实加密了,却加密给了别人。这个问题怎么解决?
数字证书和 CA
为了解决公钥被冒充的问题,需要有人为“这个公钥属于谁”作证明,这就引出了数字证书和 CA。
CA,全称 Certificate Authority,证书颁发机构。以网站常见的域名验证型证书为例,申请者需要先证明自己能控制这个域名,比如按要求设置一条 DNS 记录。验证通过后,CA 才会签发证书,一张网站证书通常包含这些信息:
- 适用的域名。
- 网站的公钥。
- 签发者、有效期及用途。
- 签发者的数字签名。
不过,证书也是一份数据,攻击者为什么不能修改里面的公钥,或者自己编一张证书?这就要说到数字签名。
数字签名
加密是为了让别人看不懂消息,签名则是为了验证消息的来源,以及内容有没有被修改。
具体原理如下:CA 使用自己的私钥对证书内容(比如上面提到的网站证书)签名,浏览器用 CA 的公钥进行验证,如果攻击者修改了证书里的域名或网站的公钥,原来的签名就无法通过验证。如果攻击者自己编一张证书,没有对应的 CA 私钥,生成的签名也无法被 CA 公钥验证。
所以网站公钥由 CA 证明,那 CA 的公钥由谁证明?事实上存在一条证书链:
根 CA
↓ 签发
中间 CA
↓ 签发
网站证书浏览器或操作系统维护着可信根列表。浏览器用中间 CA 的公钥验证网站证书,然后用上一级的 CA 的公钥验证中间 CA 证书,最后找到信任的根。
HTTPS 通信过程
前面提到对称加密适合处理大量数据,但需要安全地建立双方共享的秘密。这些工作主要在 TLS 握手中完成,HTTPS 通过 TLS 保护 HTTP 通信,不过不同版本的密钥交换过程并不完全相同。
TLS 1.2 中的 RSA 密钥交换
- 双方打招呼。浏览器发送
ClientHello,携带支持的 TLS 版本、算法和客户端随机数。服务器返回ServerHello,选定参数、提供服务器随机数,随后发送网站证书。 - 浏览器验证证书,检查域名是否匹配、证书是否过期、证书链是否可信等。验证通过后就可以信任证书里的网站公钥。
- 浏览器生成秘密。浏览器生成一个预主密钥,用网站的 RSA 公钥加密后发送。服务器用私钥解密。
- 计算通信密钥。双方结合预主密钥、客户端/服务器随机数等信息分别计算通信使用的密钥。
- 确认握手并传输数据。双方利用本次握手建立的秘密和握手记录计算校验值。接收方独立计算预期结果,与收到的
Finished内容比较,验证通过后再使用对称加密保护 HTTP 请求和响应。
TLS 1.3
在上述 RSA 密钥交换方式中,如果攻击者保存了当年的完整握手和加密业务数据,后来又拿到网站的 RSA 私钥,就可能恢复当时的通信密钥,解密 HTTP 请求和响应。
TLS 1.2 也支持 ECDHE,TLS 1.3 则移除了 RSA 密钥交换,但仍支持 RSA 签名。
以 TLS 1.3 使用 ECDHE 密钥协商的情况为例。双方各自生成临时密钥对,交换临时公钥,结合自己的临时私钥,分别计算出相同的共享秘密 S,再通过密钥派生得到通信所需的密钥。
RSA:
客户端生成 S → 加密发送 → 服务器解密得到 S
ECDHE:
双方各留临时私钥 → 交换临时公钥 → 各自算出相同的 S
之后:
S → 密钥派生 → 得到各种用途的密钥协商出秘密后还不能证明对方身份可信。服务器发送证书,并用网站私钥对本次握手记录签名。浏览器先验证证书的域名、有效期和信任链,再用证书中的网站公钥验证握手签名,从而确认对方持有这张可信证书对应的私钥。这里有两种签名:CA 签的是网站证书,服务器签的是本次握手记录,后者不属于自签名证书。最后双方通过 Finished 校验握手。
与前面的 RSA 方式相比,网站私钥在这里负责身份认证,建立共享秘密则由临时密钥完成。共享秘密依赖本次连接的临时私钥,在临时秘密妥善销毁的前提下,事后拿到网站的长期认证私钥,也不能直接恢复历史通信密钥,这叫前向保密。
回到 CA 私钥泄露
现在可以理解 CA 私钥为什么重要了。攻击者拿到了中间 CA 私钥,就可以在证书允许的范围内,为自己控制的公钥签发冒充正规网站的证书。私钥失守后,不仅需要更换密钥,还需要让客户端停止信任问题证书链。
这也不意味着所有历史 HTTPS 流量都能被解密,正确使用临时密钥协商并妥善销毁临时秘密的连接,仍有前向保密提供保护。
这篇文章就到这里。