HTTPS加密过程详解
对称加密
也就是说,通信双方拥有同样的密钥,那么在通信开始前必须要先让双方知晓该密钥。在密钥传输过程中密钥本身就有被盗的风险。但如果浏览器内部本来就预存了网站A的密钥,但这样的话由于世界上的网站太多了,不现实。
非对称加密
简单而言,就是通信双方一人一把钥匙。分别叫做公钥和私钥。公钥、私钥加密的内容必须要私钥、公钥才能打开。
第一种思路
服务器先以明文方式把公钥传给浏览器,之后浏览器使用该公钥加密传输的内容给服务器,这样就只有服务器能够解开。但是,服务器传给浏览器的安全怎么保障?比如公钥从一开始就被窃取了,那么除了浏览器外也能有别的中间人解开了。
第二种思路(似乎可以,但没有被采用)
使用两组公私密钥。例如:
- 某网站服务器拥有公钥A与对应的私钥A’;浏览器拥有公钥B与对应的私钥B’
- 浏览器把公钥B明文传输给服务器。
- 服务器把公钥A明文给传输浏览器。
- 之后浏览器向服务器传输的内容都用公钥A加密,服务器收到后用私钥A’解密。由于只有服务器拥有私钥A’,所以能保证这条数据的安全。
- 同理,服务器向浏览器传输的内容都用公钥B加密,浏览器收到后用私钥B’解密。同上也可以保证这条数据的安全。
这里仍然有漏洞。下文提及。
第三种思路——非对称+对称加密
- 某网站拥有用于非对称加密的公钥A、私钥A’。
- 浏览器向网站服务器请求,服务器把公钥A明文给传输浏览器。
- 浏览器随机生成一个用于对称加密的密钥X,用公钥A加密后传给服务器。
- 服务器拿到后用私钥A’解密得到密钥X。
- 这样双方就都拥有密钥X了,且别人无法知道它。之后双方所有数据都通过密钥X加密解密即可
这就基本是HTTPS采用的方案。
中间人攻击
劫持过程如图。
问题根源在于浏览器无法确认收到的公钥是不是网站自己的。
但又不可能对公钥本身进行加密,这就变成了鸡生蛋,蛋生鸡的问题了。
如何证明浏览器收到的一定是目标网站的公钥?
有一个CA机构,专门颁发“身份证”,也就是数字证书。
数字证书
网站在使用HTTPS前,先要向CA申请一份数字证书,数字证书里含有证书持有者信息、公钥信息。服务器把证书传给浏览器,浏览器只需要从证书里获取公钥。那么这里又有一个问题,如何防止证书本身被篡改?其实就是用了一些证书防伪技术。
如何防止数字证书被篡改
数字签名
CA机构方面:
其拥有非对称加密的公钥和私钥,CA对证书的明文数据T通过散列函数得到散列值,用私钥对散列值加密,就得到了数字签名。
网站方面:
收下CA给的带数字签名的证书,发给浏览器。
浏览器方面:
对网站给的证书的明文数据T通过散列函数得到散列值,另一方面对数字签名用公钥解密,看看是否和前面得到的散列值一致,一致则说明证书通过了防伪验证。
中间人有可能篡改该证书吗
显然不可能,因为中间人不拥有CA的私钥,因此无法得到能通过浏览器验证的数字签名。
中间人有可能掉包证书吗
假设有另一个网站B也拿到了CA机构认证的证书,它想劫持网站A的信息。于是它成为中间人拦截到了A传给浏览器的证书,然后替换成自己的证书,传给浏览器,之后浏览器就会错误地拿到B的证书里的公钥了,这确实会导致上文“中间人攻击”那里提到的漏洞?
其实这并不会发生,因为证书里包含了网站A的信息,包括域名,浏览器把证书里的域名与自己请求的域名比对一下就知道有没有被掉包了。
也就是说,虽然通过这种方式能够让浏览器成功验证数字签名,但是证书本身含有的是网站B的信息,一对比就知道不是自己请求的网站A
为什么要用一次hash函数
最显然的是性能问题,非对称加密效率较差,证书信息一般较长,比较耗时。而hash后得到的是固定长度的信息(比如用md5算法hash后可以得到固定的128位的值),这样加解密就快很多。
怎么证明CA机构的公钥是可信的?
让我们回想一下数字证书到底是干啥的?没错,为了证明某公钥是可信的,即“该公钥是否对应该网站”,那CA机构的公钥是否也可以用数字证书来证明?没错,操作系统、浏览器本身会预装一些它们信任的根证书,如果其中会有CA机构的根证书,这样就可以拿到它对应的可信公钥了。
不知你们是否遇到过网站访问不了、提示需安装证书的情况?这里安装的就是根证书。说明浏览器不认给这个网站颁发证书的机构,那么你就得手动下载安装该机构的根证书(风险自己承担XD)。安装后,你就有了它的公钥,就可以用它验证服务器发来的证书是否可信了。
每次进行HTTPS请求时都必须在SSL/TLS层进行握手传输密钥吗?
服务器会为每个浏览器(或客户端软件)维护一个session ID,在TLS握手阶段传给浏览器,浏览器生成好密钥(也就是后面对称加密时的密钥X)传给服务器后,服务器会把该密钥存到相应的session ID下,之后浏览器每次请求都会携带session ID,服务器会根据session ID找到相应的密钥并进行解密加密操作,这样就不必要每次重新制作、传输密钥了!