你应该了解的 HTTPS 基本常识
为什么需要 HTTPS
HTTP 协议是明文传输的,而这些被传输的数据会经过中间代理服务器、路由器、WIFI 热点、通信服务运营商等多个物理节点,如果数据在传输过程中被劫持,就会导致信息泄露。所以我们需要对数据进行加密,这催发了HTTPS 协议的诞生,HTTPS 协议密文传输,保障了数据的安全性。
什么是密钥?
密钥可以加密一段信息,也可以对加密后的信息进行解密,可以理解为钥匙可以上锁也可以解锁。
HTTPS 加密方案发展史
对称加密
在对称加密方案中,发送方和接收方各自持有同一把密钥 A。那么如何使得发送方和接收方共同持有 A 呢?
(1)服务端把 A 明文传输给客户端,这样双方就都有 A 了,但是明文传输 A 很有可能被不法分子劫持,然后伪装成客户端的角色,这样数据就处于十分危险的状态。
(2)浏览器预先就存下了 A,但是这样显然是不科学的,因为世界上有无数的网站,浏览器不可能事先存下所有网站的密钥。
由此来看,对称加密方案不可取!
非对称加密
非对称加密方案有两把密钥,称为公钥和私钥,用公钥加密的数据可以用对应的私钥解开,用私钥加密的数据可以用对应的公钥解开。
基本做法:服务器先把公钥以明文方式传输给浏览器,之后浏览器向服务器传数据前都先用这个公钥加密好再传,这样浏览器到服务端的数据就是安全的。但是服务器到浏览器的数据还是不安全的,公钥有可能被劫持,传输的数据也可能泄露。
为了服务端到浏览器端的数据也是安全的,需要对非对称加密方案进行改良:采用了两对公钥和私钥来解决这个问题。
(1)网站拥有自己的公钥 A 和私钥 A1,浏览器拥有自己的公钥 B 和私钥 B1;
(2)服务器把公钥 A 明文传输给浏览器,浏览器把公钥 B 明文传输给服务器;
(3)以后服务端发送给浏览器的信息都用公钥 B 加密,因为浏览器有对应的私钥 B1,所以可以解密,而第三方没有私钥 B1,就算劫持了也无法解密;同理,也能保证另一方向的通信安全。
这样好像的确可以!但是由于该方案每次通信都需要对数据进行加密、解密,效率低下,所以该方案没有被采纳。
混合加密
混合加密的过程大致如下:
(1)服务端拥有用于非对称加密的公钥 A 和私钥 A1。
(2)浏览器向服务端请求,服务器把公钥 A 明文传输给浏览器。
(3)浏览器随机生成一个用于对称加密的密钥 X,用公钥 A 加密后传给服务器。
(4)服务器拿到后用私钥 A1 解密得到密钥 X。
(5)双方都拥有密钥 X 了,且第三方不知。以后都可以使用 X 来对称加密了,效率大大提高!
该方案也是目前 HTTPS 采用的方案的雏形,之所以只是雏形,是因为还是没解决掉明文传输中公钥被劫持并被不法分子冒充身份的问题。
我们需要明确我们的根本痛点,那便是:浏览器不知道自己接收到的公钥是不是可信任的!。如果浏览器能识别公钥是自己请求的网站所属的公钥,那么问题就可以解决了。
CA 机构认证
CA 机构解决了上述问题
使用 HTTPS 协议的网站,需要向CA机构申领数字证书,数字证书里含有证书持有者信息、公钥信息等。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了,证书能够证明"该公钥对应该网站"。
但是,又有一个问题,如何保障数字证书在传输给浏览器的过程中的安全性?
如何防止数字证书被篡改?
我们先要了解数字证书的组成:数字证书由明文数据 T 和数字签名 S 组成。
数字签名 S 的制作流程
(1)CA 机构拥有非对称加密的私钥和公钥。
(2)CA 机构对证书明文数据 T 进行hash。
(3)对 hash 后的值用私钥加密,得到数字签名S。
浏览器验证过程
(1)拿到证书,得到明文 T,签名 S。
(2)浏览器拥有认证机构 CA 的公钥,对 S 解密就可以得到 S1。
(3)用证书里指明的 hash 算法对明文 T 进行 hash 得到 T1。
(4)比较 T1 和 S1,如果 T1 和 S1 相等则是正常的,否则说明明文或者签名被篡改。
假设中间人篡改了证书的原文 ,由于他没有 CA 机构的私钥,此时他拿到的是加密后的签名 S,无法篡改签名 。浏览器收到该证书后会发现原文和签名解密后的值不一致,则说明证书已被篡改,证书不可信,从而终止向服务器传输信息,防止信息泄露给中间人。
如何防止数字证书被掉包?
假设中间人拦截到了 CA 传给浏览器的证书,然后替换成自己的证书,传给浏览器,之后浏览器就会拿到错误的证书。
但其实这并不会发生,因为证书里包含了目标网站的信息,包括域名,浏览器把证书里的域名与自己请求的域名比对一下就知道有没有被掉包了。
补充
为什么 CA 机构要把明文进行 hash ?
最主要的是性能问题,证书信息一般较长,而 hash 后得到的是固定长度的信息(比如用 md5 算法 hash 后可以得到固定的 128 位的值),这样加解密就快很多。
SSL/TSL 握手
(1)客户端向服务器发起握手请求。
(2)服务器发送数字证书给客户端。
(3)客户端验证数字证书的合法性,包括验证证书颁发机构的信任关系、证书过期时间和域名匹配等。
(4)客户端生成一个随机数并使用服务器的公钥对其进行加密,并将其发送给服务器。
(5)服务器使用私钥对该随机数进行解密。
(6)客户端和服务器都使用这个随机数生成两个对称密钥,用于后续的数据加密和解密。
(7)客户端向服务器发送握手结束通知。
(8)服务器也向客户端发送握手结束通知。
注意
每次进行 HTTPS 请求时都必须在 SSL/TLS 层进行握手传输密钥吗?
实际上,服务器会为每个客户端维护一个 session ID,在 TLS 握手阶段传给浏览器,浏览器生成好密钥传给服务器后,服务器会把该密钥存到相应的 session ID 下,之后浏览器每次请求都会携带 session ID,服务器会根据 session ID 找到相应的密钥并进行解密加密操作,这样密钥传输只需要一次即可!