前言
大家都知道,https俗称为安全的http,何谓安全?在讲https之前,我们先来看一个简单的聊天软件demo
看下面例子,我们要实现A能发一个hello消息给B:
如果我们要实现这个聊天软件,本文只考虑安全性问题,则要实现:A发给B的hello消息包,即使被中间人拦截到了,也无法得知消息的内容
如何解决安全问题?
在讲如何解决安全问题之前,我们先来看2个很重要的概念——对称加密和非对称加密
所谓对称加密,就是加密和解密都通过一个密钥进行,信息通过密钥加密成密文,通过网络传输,到达对端以后,通过密钥把密文解密成明文。对称加密算法的最大优点就是加解密速度,缺点是一旦密钥泄露,密文将非常容易被解密出明文来。
再来看看什么是非对称加密,所谓非对称,意思就是加密和解密用的秘钥是不同的,我们称这一对不同的秘钥为公钥和私钥,其中,公钥是公开的,所有人都能获得,而私钥则是不公开的。在非对称算法里,通过公钥解密的密文只有通过私钥才能解密,同样,通过私钥加密的密文,只有通过公钥才能解密。通过公钥获取私钥是非常困难的,目前只能通过暴力破解,这个涉及到大质数相乘后因数分解的数学问题,大家有兴趣可以自行了解。相对于对称加密,非对称加密算法会更加安全,但是相对应的,耗时也会比较长
使用对称加密算法传输数据
说到这个安全问题,大家脑海里出现的可能就是各种什么加密和验签,什么RSA、MD5、对称加密、非对称加密等等等等。
对,这种做法是最直接的做法,例如,AB通信,A用密钥S加密,加完密之后,发送给B,B接收了信息之后再通过密钥S解密出来,拿到信息,示例如下
通过上面的对称算法和非对称算法的解释,可以知道,这里进行的加解密过程使用的是对称加密算法
上图示例,好像问题已经完美解决了,攻击者就算拿到传输的数据,也拿不到我传输的明文。但是,大家再来看看上文提到对称加密的一个问题——一旦密钥泄露,密文将非常容易被解密出明文来。意思就是说,如果使用对称加密,密钥的管理是最大的问题。我们来看看这个密钥怎么管理,管理方法无非就是两种,一种就是配置在某个远端,A端再发一次请求获取后加密,B也获取到之后再解密;一种就是在传输的过程中传输密钥。
我们假设B为服务器,A为客户端,分析一下这2种方案
先来看看第一种,第一种形式管理密钥,会产生第三方额外的交互,所以我们不采取这种做法
既然不采用第一种方法管理密钥,我们只能采用第二种方案来管理了,我们来看看第二种有什么问题,第二种管理方法是密钥实时生成实时传输,这就产生一个问题了——密钥怎么传输?传输的过程中,万一也出现被盗取的情况,攻击者又将轻易进行信息获取了,这就产生“鸡生蛋,蛋生鸡”的问题了
使用非对称加密算法传输密钥
从上文我们了解到,密钥还是需要传输才能得到,那如何才能安全传输呢?答案是:通过非对称加密算法加密密钥进行传输
我们来看看使用非对称加密算法是如果加密密钥进行传输的,上文提到,非对称加密算法有2个密钥,一个是公钥,一个是私钥。公钥是公开的,而私钥是不公开的,在我们这个场景里,明显客户端存着公钥,服务器存着私钥。传输信息的秘钥在客户端通过公钥加密后,传输到服务器,服务器通过私钥解密出数据,即得到通信所需要的密钥。
看到这里,大家是不是有个疑问,既然可以通过非对称算法可以用来加密密钥,何不直接用非对称加密算法来加密传输的内容?
这个问题很简单,前面提到了:非对称加密相对于对称加密,耗时会比较长,虽然http是短链接,但是由于http的header里有个参数keep-alive,所以是有很大可能在一个链接里进行多次数据传输,这样一来,对称加密算法的优势就立刻体现了
总结上文,现在AB之间的交互是这样的:
PS:这下大家对书本上的一个说法——“HTTPS同时采用对称加密和非对称加密算法进行数据传输” 彻底理解了吧?
公钥的管理方式
细心的人可能已经注意到了,如果使用非对称加密算法,客户端必须要持有公钥,否则无法对信息进行加密处理,那么这个公钥是怎么获取到的呢?
一样的,管理公钥跟密钥都有2种方式,要么通过第三方获取,要么直接双方交互获得公钥。我们还是采用第二种做法——双方交互获得公钥
但是,一旦公钥被调包了怎么办?客户端获取不到正确的公钥,是会产生很大问题的,详见下图
可以看到,这里信息就被篡改了,就算不篡改,通信的私钥就泄露了
通过第三方机构解决公钥被篡改问题
公钥被调包的问题出现,是因为我们的客户端无法分辨返回公钥的人到底是中间人,还是真的服务器。这其实就是密码学中提的身份验证问题。
既然服务器需要将公钥传给客户端,这个过程本身是不安全,那么我们为什么不对这个过程本身再加密一次?可是,你是使用对称加密,还是非对称加密?这下好了,感觉又进了鸡生蛋蛋生鸡问题了。
问题的难点是如果我们选择直接将公钥传递给客户端的方案,我们始终无法解决公钥传递被中间人调包的问题。
所以,我们不能直接将服务器的公钥传递给客户端,而是第三方机构使用它的私钥对我们的公钥进行加密后,再传给客户端。客户端再使用第三方机构的公钥进行解密。
这个时候,我们的获取公钥流程就变成这样了:
PS1:看到这里,你明白颁发CA证书的HTTPS认证机构是用来干吗的了吧?
PS2:客户端是浏览器,浏览器发行的时候,证书机构就已经把公钥内置到浏览器中
PS3:这里涉及到2套公私钥了,一套是用于服务器和客户端交换对称加密密钥的,一套是第三方跟我们交互的公私钥
PS4:服务器和第三方机构的交互过程是服务端和服务端的交互,可以随意加密,这样攻击人就不能随意伪造这次的请求了
数字签名,解决同一机构颁发的不同证书被篡改问题
看到这里,好像问题已经完美解决了,其实,还遗留了一个小问题:我们客户端安装证书的时候,怎样保证证书是对的呢?
要解决这个问题,我们首先要想清楚一个问题,辨别同一机构下不同证书的这个职责,我们应该放在哪?
只能放到客户端了。意思是,客户端在拿到证书后,自己就有能力分辨证书是否被篡改了。如何才能有这个能力呢?
我们从现实中找灵感。比如你是HR,你手上拿到候选人的学历证书,证书上写了持证人,颁发机构,颁发时间等等,同时证书上,还写有一个最重要的:证书编号!我们怎么鉴别这张证书是的真伪呢?只要拿着这个证书编号上相关机构去查,如果证书上的持证人与现实的这个候选人一致,同时证书编号也能对应上,那么就说明这个证书是真实的。
我们的客户端能不能采用这个机制呢?像这样:
可是,这个“第三方机构”到底是在哪呢?是一个远端服务?不可能吧?如果是个远端服务,整个交互都会慢了。所以,这个第三方机构的验证功能只能放在客户端的本地了。
客户端如何验证编号的正确性
客户端本地怎么验证证书呢?答案是证书本身就已经告诉客户端怎么验证证书的真伪。
也就是证书上写着如何根据证书的内容生成证书编号。客户端拿到证书后根据证书上的方法自己生成一个证书编号,如果生成的证书编号与证书上的证书编号相同,那么说明这个证书是真实的。
同时,为避免证书编号本身又被调包,所以使用第三方的公钥进行加密。
PS:前文提到,浏览器发行的时候,证书机构就已经把公钥内置到浏览器中,每个域名在第三方机构的公钥是不一样的
有点抽象,下面我们一步步剖析一下证书编号生成和校验过程:
1.第三方收到服务器拿证书的请求
2.第三方开始生成证书,编号的生成过程为:对证书的其他一些内容做一次MD5,然后使用该站点在第三方机构的秘钥做一次非对称加密,作为证书编号
3.客户端拿到证书后,对证书编号进行解密,再把解密出来的数据跟算法(这里是MD5)计算出来的数据比较是否一致,如果一致,说明是没问题的,如果不一致,说明证书非法
可以参考以下图片:(生成编号为上图,校验编号为下图)
PS:由于攻击者拿不到第三方的私钥,所以要伪造这个编号是非常困难的
但是第三方机构的公钥怎么跑到了客户端的机器中呢?世界上这么多机器。
其实呢,现实中,浏览器和操作系统都会维护一个权威的第三方机构列表(包括它们的公钥)。因为客户端接收到的证书中会写有颁发机构,客户端就根据这个颁发机构的值在本地找相应的公钥。
对称加密的密钥如何生成
其实到这里,https的整个运作机制已经整理得差不多了,但是我们还有个问题要解决一下——前面不断被提到的对称加密的密钥,是怎么生成的。一张图解释
生成一个对称性密钥为什么需要三个随机数?我只用最后一个加密的随机字符串去生成不行吗?
这样更能保证安全性,不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么第三个随机数就有可能被猜出来,那么只使用第三个随机数作为密钥就不合适了,因此必须引入新的随机因素。客户端的两个随机数和服务器上的一个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。
HTTPS的运行机制
其实上文倒过来看,就是整个HTTPS的运作机制了,总结一下,HTTPS的运作机制如下图所示