对称加密
1.第一次传输对称密钥为明文传输,不安全。
2.密钥存储问题。
对称密钥的问题主要在于:在第一次传输密钥时,这个密钥怎么让传输的双方知晓,同时不被别人知道。
如果由服务器生成一个密钥并传输给浏览器,那这个传输过程中密钥被别人劫持弄到手了怎么办?之后他就能用密钥解开双方传输的任何内容了,所以这么做当然不行。
非对称加密
有两把密钥,通常一把叫做公钥、一把叫做私钥,用公钥加密的内容必须用私钥才能解开,同样,私钥加密的内容只有公钥能解开。
服务器先把公钥直接明文传输给浏览器,之后浏览器向服务器传数据前都先用这个公钥加密好再传。
而服务器则使用私钥加密传输,客户端通过公钥解密。
问题:第一次明文传输公钥时,中间人可以拿到这个公钥,虽然此公钥不能解开由客户端加密传输的数据,但是服务端加密传输的数据,是不安全的。
非对称加密只能保证由浏览器向服务器传输数据时的安全性。
非对称加密传输对称密钥
非对称加密+对称加密结合可以吗?而且得尽量减少非对称加密的次数。当然是可以的,而且非对称加密、解密各只需用一次即可。
请看一下这个过程:
- 某网站拥有用于非对称加密的公钥key1、key1’。
- 浏览器像网站服务器请求,服务器把公钥key1明文给传输浏览器。
- 浏览器随机生成一个用于对称加密的密钥key2,用公钥key1加密后传给服务器。
- 服务器拿到后用非对称私钥key’解密得到密钥key2。
- 这样双方就都拥有密钥key2了,且别人无法知道它。之后双方所有数据都用密钥key2加密解密。
问题:
中间人在第一次明文传输非对称公钥的时候,劫持此公钥,然后中间人自己也创建一份公钥与私钥,公钥发给客户端,私钥自己留着,客户端用中间人的公钥加密了对称密钥key2发给中间人,中间人用自己私钥拿到key2,我们继续再看,中间人用最开始劫持的公钥加密key2,然后发给服务端,服务端用自己私钥拿到key2,以后服务端与客户端还是用key2通信,但是key2已经被中间人拿到手了。
中间人利用自己创建的公钥拿到了客户端的对称密钥,利用服务端的公钥加密传输对称密钥。
解决问题的关键点:公钥有没有被劫持并不重要,重要的是怎么证明客户端收到的公钥一定是服务端的公钥,而不是中间人的公钥。
如何证明浏览器收到的公钥一定是该网站的公钥?
现实生活中,如果想证明某身份证号一定是小明的,怎么办?看身份证。这里政府机构起到了“公信”的作用,身份证是由它颁发的,它本身的权威可以对一个人的身份信息作出证明。互联网中能不能搞这么个公信机构呢?给网站颁发一个“身份证”?
数字证书
网站在使用HTTPS前,需要向“CA机构”申请颁发一份数字证书,数字证书里有证书持有者、证书持有者的公钥等信息,服务器把证书传输给浏览器,浏览器从证书里取公钥就行了,证书就如身份证一样,可以证明“该公钥对应该网站”。然而这里又有一个显而易见的问题了,证书本身的传输过程中,如何防止被篡改?即如何证明证书本身的真实性?身份证有一些防伪技术,数字证书怎么防伪呢?解决这个问题我们就基本接近胜利了!
如何放防止数字证书被篡改?
我们把证书内容生成一份“签名”,比对证书内容中的签名和和生成的签名是否一致就能察觉是否被篡改。这种技术就叫数字签名。
数字签名
这部分内容建议看下图并结合后面的文字理解,图中左侧是数字签名的制作过程,右侧是验证过程(原图出处找不到了,可以看出来这图已经被转载了无数次了。。。)
数字签名的制作过程:
- CA拥有非对称加密的私钥和公钥。
- CA对证书明文信息进行hash。
- 对hash后的值用私钥加密,得到数字签名。
明文和数字签名共同组成了数字证书,这样一份数字证书就可以颁发给网站了。
那浏览器拿到服务器传来的数字证书后,如何验证它是不是真的?(有没有被篡改、掉包)
浏览器验证过程:
- 拿到证书,得到明文T,数字签名S。
- 用CA机构的公钥对S解密(由于是浏览器信任的机构,所以浏览器保有它的公钥。详情见下文),得到S’。
- 用证书里说明的hash算法对明文T进行hash得到T’。
- 比较S’是否等于T’,等于则表明证书可信。
为什么这样可以证明证书可信呢?我们来仔细想一下。
中间人有可能篡改该证书吗?
假设中间人篡改了证书的原文,由于他没有CA机构的私钥,所以无法得到此时加密后签名,无法相应地篡改签名。浏览器收到该证书后会发现原文和签名解密后的值不一致,则说明证书已被篡改,证书不可信,从而终止向服务器传输信息,防止信息泄露给中间人。
既然不可能篡改,那整个证书被掉包呢?
中间人有可能把证书掉包吗?
假设有另一个网站B也拿到了CA机构认证的证书,它想搞垮网站A,想劫持网站A的信息。于是它成为中间人拦截到了A传给浏览器的证书,然后替换成自己的证书,传给浏览器,之后浏览器就会错误地拿到B的证书里的公钥了,会导致上文提到的漏洞。
其实这并不会发生,因为证书里包含了网站A的信息,包括域名,浏览器把证书里的域名与自己请求的域名比对一下就知道有没有被掉包了。
为什么制作数字签名时需要hash一次?
我初学HTTPS的时候就有这个问题,似乎以上过程中hash有点多余,把hash过程去掉也能保证证书没有被篡改。
最显然的是性能问题,前面我们已经说了非对称加密效率较差,证书信息一般较长,比较耗时。而hash后得到的是固定长度的信息(比如用md5算法hash后可以得到固定的128位的值),这样加密解密就会快很多。
当然还有安全上的原因,这部分内容相对深一些,感兴趣的可以看这篇解答:crypto.stackexchange.com/a/12780
怎么证明CA机构的公钥是可信的?
你们可能会发现上文中说到CA机构的公钥,我几乎一笔带过,“浏览器保有它的公钥”,这是个什么保有法?怎么证明这个公钥是否可信?
让我们回想一下数字证书到底是干啥的?没错,为了证明某公钥是可信的,即“该公钥是否对应该网站/机构等”,那这个CA机构的公钥是不是也可以用数字证书来证明?没错,操作系统、浏览器本身会预装一些它们信任的根证书,如果其中有该CA机构的根证书,那就可以拿到它对应的可信公钥了。
实际上证书之间的认证也可以不止一层,可以A信任B,B信任C,以此类推,我们把它叫做信任链
或数字证书链
,也就是一连串的数字证书,由根证书为起点,透过层层信任,使终端实体证书的持有者可以获得转授的信任,以证明身份。
另外,不知你们是否遇到过网站访问不了、提示要安装证书的情况?这里安装的就是跟证书。说明浏览器不认给这个网站颁发证书的机构,那么没有该机构的根证书,你就得手动下载安装(风险自己承担XD)。安装该机构的根证书后,你就有了它的公钥,就可以用它验证服务器发来的证书是否可信了。
HTTPS必须在每次请求中都要先在SSL/TLS层进行握手传输密钥吗?
这也是我当时的困惑之一,显然每次请求都经历一次密钥传输过程非常耗时,那怎么达到只传输一次呢?用session就行。
服务器会为每个浏览器(或客户端软件)维护一个session ID,在TSL握手阶段传给浏览器,浏览器生成好密钥传给服务器后,服务器会把该密钥存到相应的session ID下,之后浏览器每次请求都会携带session ID,服务器会根据session ID找到相应的密钥并进行解密加密操作,这样就不必要每次重新制作、传输密钥了!
总结
可以看下这张图,梳理一下整个流程(SSL、TSL握手有一些区别,不同版本间也有区别,不过大致过程就是这样):
(出处:http://www.extremetech.com)
至此,我们已自下而上地打通了HTTPS加密的整个脉络以及核心知识点,不知你是否真正搞懂了HTTPS呢?
找几个时间,多看、多想、多理解几次就会越来越清晰的!
那么,下面的问题你是否已经可以解答了呢?
- 为什么要用对称加密+非对称加密?
- 为什么不能只用非对称加密?
- 为什么需要数字证书?
- 为什么需要数字签名?