前情提要
上一篇文章简单描述了加密解密的演进历史,如果对这部分不感兴趣的小伙伴其实可以跳过那篇文章,不过在讲之前我还是要先做一些知识点铺垫工作,避免文中遇到这些名词时没头绪。
加密主要是分成对称加密和非对称加密,这里会涉及到这些知识点:
- 钥匙,可以理解为原文密文的对应替换规则;
- 对称加密,指的是加密和解密过程中使用的钥匙都是同一把钥匙,这样虽然加密解密后信息安全传输得到保障,但是存在一个严重问题就是对称加密使用到的钥匙要如何传递给对方;
- 非对称加密,指的是加密和解密过程需要使用到的钥匙是两把不同的钥匙,公开的那把被称为公钥,自己持有那把被称为私钥,所以我只需要将公钥开放出去,别人通过使用我的公钥对信息进行加密,等我收到消息时再用自己的私钥进行解密,这样我就可以看到原文内容了,这个过程既完成了信息安全传输,而且还避免了对称加密那把钥匙传输的问题;
- 签名,非对称加密不仅可以公钥加密私钥解密,还可以私钥加密公钥解密,不过由于这个私钥只有自己持有,所以这个过程也叫签名,而公钥解私钥的过程也被称为验签,为什么叫签名呢,其实是因为私钥只有我自己持有,所以可以利用这个特性对你进行身份认证的,就像是我给你写了张欠条然后签了自己的名字,因为笔迹并不是那么容易伪造的,而不可伪造也就可以用来进行身份验证;
- 效率问题,非对称加密由于涉及到大量数学运算,所以加密解密效率相比对称加密是低的;
HTTPS 通信流程
讲 HTTPS 之前我们先来看看为什么 HTTP 不能满足我们的需求,它都存在哪些问题?当我们了解了它的缺点,我们也就更容易理解,为什么 HTTPS 的通信过程要像现在这样设计了。
HTTP 通信的四类问题
HTTP 通信的四类问题:
- 拦截(机密性),传输信息可以被中间人拦截,从而导致数据泄漏;
- 篡改(完整性),基于拦截,中间人可以对数据进行了修改,从而没办法保证数据完整;
- 伪装(身份验证),两个人通信时,任何一方都可能被中间人冒名顶替;
- 否认(不可否认),由于存在伪装,所以其中一方可以假装没有收到对方消息或对方收到的消息不是自己发的;
那我们能否利用已有知识解决这些问题呢?答案是基本上可以,让我们来看看这些问题具体该如何解决:
- 机密性可以使用对称加密或非对称加密可以解决这个问题,只是还有一些区别:
- 对称加密速度快效率高,非对称加密速度慢(因为涉及到大量数据运算)效率低;
- 对称加密虽然过程是安全的,但是信息加密传输之前的密钥传输是一个很大的问题;
- 完整性可以通过摘要算法来完成;
- 它会把任意长度的数据压缩成固定长度,且生成的是独一无二的摘要信息,就像指纹一样;
- 会存在摘要信息冲突的可能性,所以要看具体摘要算法本身和信息长度;
- 具有单向性(不可逆)和雪崩效应(微小修改都会引起剧烈变化);
- 完整性还是要建立在机密性之上,因为如果摘要信息是明文的还是有可能被修改;
- 身份验证 / 不可否认:可以利用非对称加密的签名特性来同时完成,这里还有知识点就是证书(后面仔细讲),其实当你身份验证完成后其实你就没法对发送的消息进行否认了,因为别人无法伪造;
那既然我们知道了 HTTP 的信息传输过程中存在的这些问题,那我们接下来就进入正题,看看 HTTPS 如何解决掉这些问题。
HTTPS 介绍
了解通信流程之前,还需要先对 HTTPS 简单做个介绍,为什么 HTTPS 可以保障安全,它和 HTTP 到底有什么区别和联系?
其实 HTTPS = HTTP over SSL / TLS,这样看的话,其实它和 HTTP 的区别就是在于多了 SSL / TLS,所以 HTTPS 的密钥协商、传递、加密解密也肯定都是由 SSL / TLS 完成的。至于 SSL 和 TLS 的区别其实是 TLS 是 SSL 的升级版,一开始 HTTPS 刚出来的时候安全层是 SSL,后来协议标准化,就在 SSL 的基础上进行优化修改并且将其改名为 TLS。
原本 HTTP 的数据是会扔给 TCP 进行处理发送的,而 HTTPS 中成了 HTTP 数据扔给 SSL / TLS 加密处理,然后在由 SSL / TLS 扔给 TCP,对方收到后也是 TCP 扔给 SSL / TLS 进行解密,最后扔给 HTTP。现在像是在这两者之间插入了一层,所以我一般也称 SSL / TLS 为安全层。
HTTP -> TCP(HTTP)
HTTP -> TLS -> TCP(HTTPS)
介绍完了什么是 HTTPS 后,如果只用一句话概括它,其实 HTTPS 的本质就是用非对称加密协商出一套对称加密密钥。然后数据的传递都是通过对称加密去做。这样既保障了安全又保障了效率。
保障效率是因为后续真正的数据传输其实是通过对称加密来完成的,相比之下,非对称加密由于涉及到到大量运算,执行起来比对称加密要慢,而保障安全是因为对称加密密钥的传输是通过非对称加密的方式解决掉的。
HTTPS 通信流程
下面就来看看 HTTPS 的通信流程吧。
先来看下面这张图:
TLS 或 HTTP 下层都是 TCP,所以不管怎样我们都需要先通过 TCP 三次握手来建立连接,然后再在这个连接基础上,进行 TLS 的握手。粗略的看话,我觉得 TLS 握手主要包括以下内容:
- 客户端请求建立 TLS 连接;
- 服务器返回信息和发回证书;
- 客户端验证服务器证书;
- 客户端信任服务器后,开始与服务器协商对称加密密钥;
- 验证密钥是否可用和使用对称密钥开始通信;
如果仔细来看的话,我建议看着下面这张图:
那让我们再来详细的展开以下这个 TLS 的握手流程:
- Client Hello,也就是告诉对方我想和你建立 TLS 连接,主要是协商加密方案和发送随机数;
- 附加内容列一下:
- TLS 版本;
- 可选的对称加密算法;
- 可选的非对称加密算法;
- 可选的 Hash 算法(信息完整性计算时使用);
- 客户端随机数(客户端和服务端的随机数都是用来后续对称密钥生成的);
- 附加内容列一下:
- Service Hello,服务端会返回确认后的加密方案和随机数;
- 附加信息也都是确认信息,如下:
- 确认的 TLS 版本;
- 确认的对称加密算法;
- 确认的非对称加密算法;
- 确认的 Hash 算法;
- 服务器随机数;
- 附加信息也都是确认信息,如下:
- 接下来服务器还会把证书发送过来,用于客户端进行验证服务端的身份;
- 意义在于服务器的公钥在证书里,接下来用于让客户端拿验证好公钥发消息给服务端;
- 具体验证细节和证书是什么会后面讲;
- 如果是双向验证客户端也需要把自己证书发给服务端;
- 客户端生成 pre-master 随机数,并用刚才验证过没问题的证书里的服务器公钥进行加密,传输给服务器;
- 两边通过这三个随机数(客户端随机数、服务端随机数以及刚生成的 pre-master)进行对称加密的密钥;
- 虽然客户端随机数、服务端随机数是明文发送的,但是 pre-master 是使用服务端公钥进行传输的,所以就算别人拿到前两个随机数,别人还是不知道实际的对称密钥是什么,因为中间人没有拿到 pre-master,就没法推算出真正的对称加密密钥;
- 如果只是单向证书验证,其实只需要客户端拿到服务器证书里面的公钥进行加密,然后后将加密后消息传递给服务端即可,但双向验证时,就需要客户端不仅用服务端的公钥加密,还需要用自己私钥对加密后的内容签名,从而确保安全和身份验证功能,这样服务端拿到内容先验签再解密了;
- 这里的计算会生成四个东西:
- 客户端加密密钥,客户端发过去的东西用这个加密,然后服务端收到后用它解密;
- 服务端加密密钥,服务端发过去的东西用这个加密,然后客户端收到后用它解密;
- 上面两个为啥这么设计?为了区分是谁发的,也防止消息被扔回去;
- 如果都使用同一把钥匙那中间人虽然看不懂,但是可以扔回去,你也不知道这消息究竟是来自自己这边的还是服务端那边的;
- 客户端 Mac secret;
- 服务端 Mac secret;
- Mac secret,是 HMAC(基于 Hash 消息认证码) secret,用来验证消息是否被篡改过,和签名的作用非常像,可以验证消息的完整性和来源(验证来源是因为两边的 HMAC 不一样);
- 客户端通知:将使用加密通信;
- Finished(并不是真的发 Finished 这个几个字母,是个信息指代);
- 这两步也是为了验证一下,我们刚才商量好的对称密钥行不行,我发给你你能不能解开;
- 把之前所有发送消息做一个摘要,然后加密,发过去让服务器验证;
- 服务端通知:将使用加密通信;
- Finished;
- 这两步只是上面的反过来,这个也验证完就说明两边发的对方都能看懂了,可以开始正式通信了;
这就是我理解的 HTTPS 的通信流程了,不过刚才上面还缺失了重要一环也是证书验证的内容,下面来主要讲讲证书验证。
证书验证
回想一下我们最开始提到的如果爱丽丝想要和鲍勃使用非对称加密进行安全通信的话,那就必须先让爱丽丝拿到鲍勃的公钥才行,有什么办法可以拿到对方的公钥吗?
- 鲍勃把公钥放在网站上别人去拿;
- 爱丽丝和鲍勃进行通信时,鲍勃发给爱丽丝;
如果仔细想想的话这两种方式其实都有问题,前者如果中间人伊芙引导你访问了错误的网站就会导致你拿到错误的公钥,而后者伊芙可能在你们公钥传输的过程中对信息进行拦截和篡改,所以爱丽丝想要拿到正确的鲍勃的公钥并不容易。
那这个问题有解决方案吗?就是通过非对称加密的签名来处理,但是鉴于没法拿到正确的鲍勃的公钥所以没法让鲍勃进行签名发送过来,这时就需要一个第三方权威机构,我们都信任它,而它会用自己的私钥对鲍勃的公钥进行签名,然后我们只需要拿到这个第三方权威机构的公钥进行验签即可。
回到实际场景中来,这个第三方机构,就是证书颁发机构,也被称为 CA(后面也都简写为 CA),如果服务端想让 CA 给它颁发证书(信用背书),就会把自己的公钥和身份信息发给 CA,CA 验证没问题后,生成证书信息,并且 CA 会对证书信息做一个摘要算法,就像提取指纹信息一样,然后会对计算出来的 Hash 值进行签名,最后打包下发。
先来粗略看下证书里面都会包含什么信息:
- 要背书的公钥;
- 证书所有者;
- 证书有效期;
- 证书发布机构;
- 签名算法;
- ...;
那我们该如何验证签名呢?
- 先对服务端发来的证书用指定的摘要算法进行摘要,得到指纹 P1;
- 然后使用这个证书颁发 CA 的公钥对他的数字签名进行验签,得到指纹 P2;
- 最后看看 P1 和 P2 是否相等,相等就说明证书没有被窜改过,如果不相同就说证书被人篡改过,因为 P2 是无法伪造的,CA 的签名时使用的私钥是只有 CA 自己才有;
但你可能会问,这个过程好像只是验证了给证书签名时的 CA 私钥和我们拿到的 CA 公钥是对的上的,那如果 CA 证书下发过程本身就问题怎么办呢? 我怎么能确认给这个服务端背书的 CA 是否可信呢?
要回答这个问题其实并不难,就是让那个 CA 在找别的更大的更可信的 CA 给他做信用背书,直到找到背后那几个全球最大的 CA 机构,这些 CA 机构也被称为 root CA,这些 CA 就已经是我们无条件要相信的了,这就像你可以不相信同事告诉你的公司消息,但是同事告诉你这个消息是组长告诉我的,你去问了组长还是不相信,组长说这个是经理告诉我的,你去问了经理还是不相信,经理说这个是老板刚发的公司消息,最终你去问了老板并确认了消息,这时这条信任链就已经完整构建起来了。
所以给服务端下发证书的 CA(简称 CA L),它的公钥其实是另一个更大 CA(简称 CA H)用自己的私钥给它签名的,也就是 CA H 会根据 CA L 提供行公钥和身份信息给它颁发证书,然后这样递归下去,直到我们刚刚说的 root CA。
所以验证的顺序也像图中这样,可能看图更好理解,我要验证服务端证书就去找给中介 CA 拿它的公钥进行验签(因为服务端证书是中介 CA 下发的),而中介 CA 证书验证又要去找 root CA 拿它的公钥进行验证(同理中介 CA 的证书是 root CA 下发的),那么 root CA 的公钥在哪里呢?一般在操作系统里面都会有各大 root CA 的信息,当然浏览器或者某些软件(比如支付宝)里也会有。这时我拿到了 root CA 的公钥信息,然后对中介 CA 证书进行验证(证书验证过程上面已经讲到了),验证完成后拿到中介 CA 的公钥再对服务端证书进行验证,这样如果都没问题,就说明整条信任链是通的,而我也可以放心拿服务端证书里的公钥和对方进行非对称加密通信了。
补充
- 服务端返回的是一个证书链,然后和你本地的根证书形成一个完整的验证链;
- root CA 的公钥是在操作系统里或者软件里,根证书没有上层机构再为其本身作数字签名,所以都是自签证书;
- 自签名证书就给人一种感觉,我就是我,你爱信不信;
- 证书验证可以是单向的也可以双向的,要看业务需求;
- 为什么 TLS 会有三个随机数?
- TLS 设计者考虑的很周到,不相信“伪随机“,为了保障更加随机,就把三个随机数很合起来了;
- 只有你拥有对应域名,你才能对其申请证书;
- 再附上一张更详细图:
总结
到这里 HTTPS 的通信流程也就说完了。
如果只用一句话总结 HTTPS 就是它的本质是通过非对称加密协商出一套对称密钥,而后续真正数据都是使用对称加密进行通信。
这里面的最难搞懂的点可能就是为什么要有证书和证书验证过程。
说多了都是泪,希望看到这里之后 HTTPS 对你来说就不是什么难理解的事情了。
感谢
以上内容参考了扔物线的 HenCoder Plus、极客时间的《趣谈网络协议》、《透视 HTTP 协议》、《全栈工程师修炼指南》及 HTTPS 相关维基百科。
尤其是那两张很全的 HTTPS 流程图都是出自极客时间专栏,黑白的那种出自《趣谈网络协议》,而最后那张彩色的出自《透视 HTTP 协议》。
最后的最后
如果你觉得我写不错,那就通过点赞,点赞,还 tm 是点赞的方式给我反馈吧,感谢你的支持。