HTTPS 如何保证安全通信?
HTTP 不安全
HTTP (Hypertext Transfer Protocol,超文本传输协议) 是一种基于 TCP/IP 的应用层协议,用来在服务器和客户端之间传输 HTML、图像等数据。HTTP 是一种明文协议,所传输的数据没有经过加密,任何人都可以查看和篡改,这被称作 中间人攻击
。我们在访问一些使用 HTTP 协议的网站时,浏览器会给出我们警告------"您并未安全地连接至此网站":
图1 访问 HTTP 网站时浏览器警告
对称加密和非对称加密
为了防止中间人攻击,我们需要对传输的数据进行加密,加密方式有两种------对称加密
和非对称加密
。
图2展示了对称加密的过程,对称加密的双方使用同一个密钥对数据进行加密和解密。
图3展示了非对称加密的过程,在非对称加密中双方使用不同的密钥,一个密钥被称为公钥,另一个密钥被称为私钥。非对称加密的特点是公钥和私钥不同,但由公钥加密的数据能且只能由私钥解密,由私钥加密的数据能且只能由公钥解密。
图2 对称加密
图3 非对称加密
在了解两种加密方式后,我们先不去看 HTTPS 是如何工作的,我们自己先设想一下使用对称加密和非对称加密是否可以解决中间人攻击问题。
使用对称加密
如果使用对称加密的方式,服务器和浏览器需要事先协商好所使用的密钥,如果这个密钥不被中间人知道,那么服务器和浏览器之间的通信就是安全的。但是如何保证密钥不被中间人知道呢?如果我们的操作系统或者浏览器在安装的时候就已经预装了世界上所有网站的密钥,而且这些密钥不能被用户从操作系统和浏览器的获取,那么就可以保证对称加密的安全性。然而这种方式并不现实,世界上网站的数量非常庞大,每天有许多网站关闭和出现,操作系统和浏览器难以维护和更新这些密钥。
另一种思路是由浏览器或服务器在通信前生成所要使用的密钥明文发送给对方,然而这个通信过程是可以被中间人监听的,中间人拿到密钥后就可以查看和篡改数据。
图4 中间人窃取密钥
使用非对称加密
使用非对称加密时,服务器生成私钥和公钥,然后将公钥明文发送给浏览器。虽然中间人此时也可以窃取到公钥,但是由公钥加密的数据只能由私钥解密,而私钥只有服务器持有。
浏览器把要发送的数据使用公钥加密后发送给服务器,中间人无法对加密后的数据进行解密。但是当服务器向浏览器发送数据时,数据由服务器持有的私钥加密,中间人窃取的公钥可以对数据进行解密,服务器到浏览器方向的通信并不安全。
实际上浏览器到服务器方向的通信也不安全,中间人可以窃取服务器发送给浏览器的明文公钥,并将自己的公钥发送至浏览器,这一系列动作浏览器毫不知情。当浏览器向服务器发送数据时,使用中间人的公钥加密,中间人可以使用自己的私钥解密,欣赏完你发出的信息后,再用窃取的明文公钥加密它篡改后的数据发送至服务器。
双向非对称加密
那是否可以使用"双向非对称加密"呢?即服务器和浏览器各自生成一对私钥和公钥<A
, PubA
>、<B
, PubB
>,然后将各自的公钥 PubA
和 PubB
发送给对方。当服务器向浏览器发送数据时,使用浏览器生成的公钥 PubB
对数据进行加密,中间人不持有浏览器的私钥 B
,无法对加密后的数据进行解密,而浏览器持有自己的私钥 B
可以对数据进行解密;当浏览器向服务器发送数据时,使用服务器生成的公钥 PubA
对数据进行加密,中间人不持有服务器的私钥 A
,无法对加密后的数据进行解密,而服务器持有自己的私钥 A
可以对数据进行解密。
图5 服务器和浏览器交换公钥,中间人只可能持有双方的公钥
这种方式真的安全吗?我们考虑这样一种场景,中间人也生成了一对公钥和私钥<C
, PubC
>,在服务器和浏览器交换彼此的公钥PubA
和PubB
时,中间人将PubA
和PubB
截获后替换为自己的PubC
,服务器和浏览器收到的公钥都是PubC
,但是浏览器和服务器并不知道收到的公钥是假的。
当服务器向浏览器发送数据时,使用的是中间人生成的公钥PubC
对数据进行加密,中间人持有私钥C
,对加密后的数据进行解密后就可以对数据进行窃取和篡改了。中间人再使用PubB
对篡改后的数据进行加密发送给浏览器,浏览器使用自己的私钥B
解密后的数据就是中间人篡改后的数据,然而这一过程服务器和浏览器都不知道!同样的,浏览器向服务器发送数据也是不安全的。
图6 服务器与浏览器交换公钥时,中间人将公钥替换为自己的公钥
我们思考一下上述单向非对称加密和看似完美的双向非对称加密的致命弱点是什么?致命弱点就是公钥的接收方无法确定自己收到的公钥是否来自于发送方!
HTTPS 的出现就是为了解决这一问题,HTTPS借助 签名
和 证书
等技术保证了浏览器所收到的公钥肯定是服务器的公钥。然后浏览器生成一个用于对称加密的密钥 (这里的生成对称密钥的描述并不准确,实际上对称密钥的产生是由双方协定的,具体的过程可以查找 SSL 的工作流程),使用服务器的公钥进行加密后发送给服务器,服务器使用自己的私钥解密后获取到对称加密的密钥 ,之后双方就可以使用此密钥进行对称加密了。那么 HTTPS 是如何保证浏览器收到的公钥肯定是服务器的公钥的呢,没有被中间人调包或篡改过呢?
CA机构、证书、签名
现实生活中我们如果想证明自己的身份,我们可以使用公安局颁发的身份证。当我们去办身份证时,我们将基本信息告知工作人员,工作人员使用特殊的方法和工艺制作身份证卡片,卡片上的一些特殊图案由我们的基本信息生成,这样我们就获得了一张世界上独一无二的身份证。即便有他人伪造我们的身份证,我们也能将其识别出来,因为伪造者不具备公安局制作身份证卡片的方法和工艺。
和上述流程类似,服务器首先选定一个可信的 CA 机构,将网站的基本信息(域名,公钥等)发送给 CA 机构。每个 CA 机构都有自己的公钥和私钥,当 CA 机构收到网站的基本信息后,首先对网站的基本信息做哈希得到一个哈希值,然后使用自己的私钥对哈希值进行加密,加密后的数据就叫作 签名
,这一过程类似于公安局工作人员使用特殊方法和工艺制作身份证上的特殊图案。最后 CA 机构将签名附在网站基本信息后面,最终得到的东西就叫作 证书
(相当于我们的身份证),CA 机构制作好证书后将证书发送给网站所有者,网站所有者在收到证书后使用 CA 机构的公钥进行验签。
图7 证书签发流程
网站怎么知道CA机构的公钥
上面说到网站使用 CA 机构的公钥对证书进行验签,那么网站是如何知道 CA 机构的公钥的呢?答案很简单,CA 机构的公钥在操作系统或浏览器被安装时就存在了。
网站收到的证书是否可信
网站在向CA机构申请证书时也涉及到网络通信,那么网站是如何确定收到的证书是否可信的呢?网站在接收到证书时,使用CA机构的公钥对签名进行解密得到哈希值 Hash1
,同时使用哈希函数对证书上的基本信息进行哈希得到哈希值Hash2
,比对 Hash1
和 Hash2
,如果两者相等说明证书没有被篡改或掉包过,这一过程叫作验签
。
一定要这么麻烦吗?网站直接比较证书上的基本信息是否是自己的信息不就完了?考虑这么一种情况,当 CA 机构将证书发送给网站时,中间人将证书掉包,保持基本信息不变,只是对签名进行篡改或用自己的私钥重签。那这样网站收到的证书实际上是一个无效的证书,无法在互联网上使用,就好像我们在办身份证的时候,最终拿到的身份证是一个不由公安局颁发的假身份证。
网站有了证书后如何使用
浏览器想和网站服务器发起通信时,网站将证书发送给浏览器,浏览器使用 CA 机构的公钥(CA 机构的公钥在操作系统或浏览器被安装时就存在了)对证书进行验签,如果验签通过说明证书可信,证书基本信息中的公钥就是网站服务器的公钥。然后浏览器生成一个用于对称加密的密钥 A
,使用服务器的公钥进行加密后发送给服务器,服务器使用自己的私钥解密后获取到对称加密的密钥 A
,之后双方就可以使用此密钥 A
进行对称加密了。
图8 证书使用流程
浏览器收到的证书是否可以被中间人篡改
网站将证书发送给浏览器时,即便中间人可以将证书中网站的公钥篡改为自己的公钥,但是中间人无法对修改后信息进行有效签名,因为它不持有 CA 机构的私钥。浏览器在收到证书时,使用CA机构的公钥进行验签,发现公钥解密后的哈希值 Hash1
和对证书中基本信息进行哈希得到哈希值 Hash2
不相同,就知道证书被篡改了。
浏览器收到的证书是否可以被中间人掉包
如果中间人也在同一个 CA 机构对自己的网站申请了证书,那么中间人截获网站发送给浏览器的证书,将其替换为自己网站的证书,这样浏览器使用 CA 机构的公钥验签也能通过,浏览器就会错误的使用中间人的公钥。
然而这种情况不会发生,浏览器会比对证书上的域名和想要通信的网站的域名,域名是独一无二的,中间人自己网站的域名和想要攻击的网站的域名不同。那中间人把自己证书上的域名改成想要攻击的网站的域名可以吗?那这样就无法通过浏览器的验签。
浏览器生成的对称加密的密钥会不会被篡改和调包
浏览器在确定收到的公钥一定是网站服务器的公钥后,会生成一个对称加密所需要的密钥 A
,使用网站服务器的公钥加密后发送给网站服务器。即便中间人将其截获,因为中间人没有网站服务器的私钥,也无法知晓此密钥到底是什么。
进一步,假如中间人也生成一个对称加密所需要的密钥 B
,使用网站服务器的公钥加密后发送给网站服务器,那这样不就又被中间人得逞了吗?实际上,中间人这样做没有任何意义,浏览器使用密钥 A
加密的数据,中间人无法解密,服务器也无法解密,通信无法继续。(描述并不精确,但这里并不想展开 SSL)
看看证书长什么样
图8是在 Edge 浏览器中查看 github.com 的证书,可以看到证书字段中有公钥、证书签名算法、证书签名值等信息。
实际上证书之间的认证也可以不止一层,图中证书层次结构表示 DigiCert Global Root CA
认证 DigiCert TLS Hybird ECC SHA384 2020 CA1
,DigiCert TLS Hybird ECC SHA384 2020 CA1
认证github.com
。我们把这种链式信任叫作 信任链
或 数字证书链
,"信任"从根证书(根CA机构)层层透传:DigiCert Global Root CA
中的公钥可以验签 DigiCert TLS Hybird ECC SHA384 2020 CA1
,DigiCert TLS Hybird ECC SHA384 2020 CA1
中的公钥可以验签 github.com
。
图9 github 的证书
参考
[1] 彻底搞懂HTTPS加密原理
[2] 一起学加密
最后
I do and I understand.