一次HTTPS请求,TCP做了什么?

2023年 8月 1日 22.5k 0

大家好,在上一篇文章中,我们对 https 协议的发展有了基本了解;知道了如何自己搭建一个简单的支持 https 协议的服务。那么在此基础上,让我们一起来看下,当客户端向服务端发送一个基于 https 协议的请求时,tcp/ip 层都做了什么?还是老套路,先看理论,再来实践。

理论

当我们在浏览器输入 www.baidu.com 后,浏览器便作为客户端,开始向百度服务器发送请求。那如何确保,我的请求能够到达真正的百度服务器呢?因为百度服务器提供的接口是基于 https 协议的,也就是说,服务端和客户端的交互数据都要进行加密,那用何种加解密算法呢?加解密的密钥,又如何安全的传递呢?带着这些疑问,我们一起来看下。

在我们开始了解客户端与服务端的交互过程前,我们先来了解一下“加密/解密”的过程以及相关的概念。其实这方面的文章,网上有很多大佬已经做了很详细的讲解,这里,我只是简单快速的做个总结,对于这部分不熟悉的同学可以先在网上看一看,了解一下。

我们通常说的加解密算法,主要分为对称加密算法和非对称加密算法,与之相关的,还涉及到数字签名,消息摘要,数字证书,CA,证书链等概念,那么这些名词都是什么意思?

  • 对称加密算法:对数据加密、解密使用相同的算法。因此,其效率比非对称加密算法更快。但是通信双方如何安全的获得这个密钥,是一个不容易解决的问题,所以非对称加密算法的方式应运而生。

  • 非对称加密算法:密钥分为公钥和私钥,私钥只有数据发送者自己持有,不能暴露给其它人,而公钥是公开的。任何人如果想要与你进行通信,只需要用你提供的公钥对数据进行加密,然后将加密后的密文发送给你即可。自己用私钥对密文进行解密。同理,当你要发送数据给其他人时,你也要用对方的公钥对数据进行加密,对方也会用自己的私钥对密文进行解密。

  • 数字签名:使用非对称加密算法成功的解决了通信双方无法安全交换密钥的问题,但是另一个问题产生了,如果中间人模拟其中一方行为,将自己的公钥发送给一方,这一方是无法辨别的。

    那如何证明“你妈是你妈”这个问题呢?于是数据的发送方用自己的私钥,对数据进行加密,这一行为,我们习惯上称之为“签名”。然后再用数据接收方的公钥对签名进行加密,将带有签名的密文,发送给数据接收方。

    数据接收方收到数据后,先用自己的私钥对密文进行解密,得到一个带有签名的密文,然后再用数据发送方的公钥对密文进行解密,这个过程也就是我们常说的“验签”。如果验签成功,则说明,该消息确实来自于消息发送方。

  • 消息摘要:有的时候,通信双方的每个数据包的体量非常大,可能是一大段文本,一个文件,一个流媒体等等,那如何提高签名和验签的速度?

    那就是通过消息摘要算法,比如常见的 MD5, SHA-1, SHA-256 等等,对原始数据进行摘要提取,获得一个较短的,且不可逆的摘要信息。然后数据的发送方先用自己的私钥对摘要信息进行签名,紧接着将签名后的摘要信息和原始数据一起,用数据接收方的公钥加密,再发送出去。

    这样,消息的接收方在收到消息后,首先用自己的私钥解密,得到原始数据和带有签名的摘要信息。然后,再用发送方的公钥,对摘要信息进行验签,得到明文的摘要信息。这时候,再用相同的摘要算法对原始数据进行摘要提取,与发送过来的摘要信息进行比对,如果一致,则证明原始数据没有被篡改过。

  • 数字证书 / CA: 在通过消息摘要算法解决了数字签名带来的性能问题后,还有一个关键问题,就是数据发送方的公钥如何确保是安全的,受信的,不是第三方伪造的公钥?

    于是 CA 机构出现了,以官方组织作为背书,任何人都可以信任 CA,并且向 CA 申请签发证书。证书里包含了申请者的信息,比如国家、组织、网址等,以及申请者的公钥信息等。在有了证书后,CA 机构,会用自己的私钥对证书签名。任何人都可以获得 CA 的公钥,而事实上,CA 机构的公钥,都已经默认在我们的系统上安装了。比如:上面的例子中,我们可以用 CA 的公钥,对百度的证书进行验签,证明这个证书确实是 CA 颁发的。

  • 证书链:由于 CA 机构身负重任,如果每个申请域名的公司、组织、个人等都来找 CA 申请证书,那 CA 可能无法及时响应,因此,CA 会授权其它机构,作为子机构,代替 CA 颁发证书和签名。因此,一层层下来,一个域名的证书可能是被某一个子机构的子机构进行颁发的。但是客户端一般信任根 CA 的认证,所以当获得对方提供的证书时,会按照证书链的顺序,找到根 CA,进行验证。

    比如查看掘金的证书颁发机构,可以看到,根 CA 机构是: DigiCert Global Root CA,而真正给掘金签发的机构是其授权的子机构签发的
    image.png

在我们了解了以上概念后,让我们通过下图,一起来看下在我们发送了 https 请求后,客户端和服务端是如何进行交互的?

Untitled Diagram.drawio.png

  • 首先,客户端和服务端依旧是先通过三次握手建立 tcp 连接。对这部分不熟悉的同学,可以先看下我之前的文章:一次HTTP请求,TCP做了什么?

  • tcp 连接建立后,客户端首先向服务端发送一个带有【Client Hello】标识的数据包,其中,包含了一个 TLS 版本号,一个随机字符串(划重点,后面要考),客户端支持的加密算法套件列表,客户端支持的摘要算法列表等等。

  • 服务端收到消息后,会返回一个带有【Server Hello】标识的数据包,其中包括采用的 TLS 版本号,采用的加密算法套件,以及一个服务端生成的随机字符串(划重点,后面要考)。

  • 然后,服务端将自己的证书发送给客户端,供客户端验证。

  • 最后,服务端发送一个带有【Sever Hello Done】标识的数据包,表示自己的信息全部提供给了客户端。

  • 客户端收到服务端发送的证书后,会根据证书链的信息到 root CA 进行验证,用 CA 的公钥对证书进行验签,同时获得了服务端的公钥。此时,客户端会生成第三个随机字符串,并且用服务端的公钥进行加密,然后将这个加密后的字符串放到带有【Client Key Exchange】标识的数据包里,发送给服务端。

  • 此时,客户端有了三个随机字符串,客户端会根据这三个字符串生成一个新的密钥,也就是对称加密算法的密钥,在后续的数据交互中,都将使用这个密钥对数据进行加、解密。密钥生成后,客户端会向服务端发送一个带有【Change Cipher Spec】标识的数据包,告诉服务端,密钥变了。

    然后再发送一个带有【Encrypted Handshake Message】标识的数据包,对之前发送过的数据进行摘要处理,然后用新的密钥对摘要信息进行加密,发送给服务端验证。

  • 服务端在收到了客户端的第三个字符串后,首先,用自己的私钥进行解密,得到一个明文的随机字符串,然后用同样的算法,根据三个字符串生成对称加密算法的密钥,然后发送一个带有【Change Cipher Spec】标识的数据包,告诉客户端,密钥也改变了。

  • 然后把之前发送的数据进行摘要处理,并对摘要信息进行加密,将加密后的信息也放到带有【Encrypted Handshake Message】标识的数据包发送给客户端,让客户端验证。

  • 之后,客户端和服务端就开始用新的对称加密算法的密钥对数据进行加密通信了。

  • 实践

    在有了理论知识后,让我们通过抓包工具 wireshark 来看一下真实的过程:

  • 执行 tcpdump 命令监听网卡上的所有流量,然后使用 curl 命令,访问: www.baidu.com 执行完成后,退出当前 session 模拟客户端断开连接操作
    Untitled Diagram.drawio.png

  • 使用 wiershark 打开 pcap 文件,因为是监听网卡的所有数据包,所以要过滤下客户端和服务端的 ip 地址。对重点的数据包,我也进行了标记。
    Untitled Diagram-Page-2.drawio.png

  • 序号16-18是客户端和服务端的三次握手,具体过程不再赘述。

  • 查看序号20的数据包,我们可以看到此时客户端支持的最高 TLS 版本为1.2,支持的加密算法套件有28种,算法套件的命名规则为【密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法】:
    Untitled Diagram-Page-2.drawio.png

  • 查看序号22的数据包,我们可以看到,本次连接,服务端从客户端提供的列表里,选择的加密算法套件
    Untitled Diagram-Page-2.drawio.png

  • 查看序号24的数据包,这里我们依旧可以看到服务端根据延迟 ACK 的规则,将应该分三次发送的数据包合并成一次发送,而且还多了一个带有【Server Key Exchange】标识的数据包,这个在我们的理论知识部分没有提及,这一点我们稍后再说,我们先看下带有【Certificate】标识的数据包,我们可以看到,baidu服务器提供的证书并不是根 CA 直接签发的,也是存在证书链的
    Untitled Diagram-Page-2.drawio.png

  • 查看序号26的数据包,因为我是在 linux 系统上测试的,所以客户端也是遵循延迟 ACK 的规则,将应该分三次发送的数据包,一次发送。我们观察到,带有【Client Key Exchange】标识里的数据包并没有按照我们之前说的,会生成第三个字符串,而是发送了一个 pubkey,这个我们稍后和【Server Key Exchange】一起来解答。
    Untitled Diagram-Page-2.drawio.png

  • 查看序号29的数据包,为服务端最后一次 TLS 协议交互,此时双方都已经有了新的对称加密的密钥,之后就可以进行安全的数据传输了。

  • 【Server Key Exchange】和【Client Key Exchange】为什么和理论知识不一样?

    因为我们的理论知识是根据目前大多数网站采用的 RSA 对称加密算法进行学习的,而百度采用了更为安全的 ECDHE_RSA 的加密算法。算法的具体内容感兴趣的小伙伴可以自行研究,这方面,不是我的强项。这里贴一张 RSA 算法的网图,证明我们的理论知识没错
    image.png

    根据查询的资料,我的理解是,新算法和传统的 RSA 的区别在于,对于上文中提到的第三个随机数,不再是客户端自己生成,而是客户端和服务端双方都生成一个 pubkey ,然后放到带有【Server Key Exchange】或者【Client Key Exchange】的数据包中发送给对方。此时,双方都已经有了 pubkey,使用这两个 pubkey 根据密钥交换算法生成一个 pre-master 密钥。然后再根据前两个随机数和 pre-master密钥,生成 master sceret,也就是对称加密的密钥。
    Untitled Diagram-Page-2.drawio.png

    总结

    到此,我们已经对整个过程有了了解,总结一句话就是,利用非对称加密的安全性,客户端和服务端先交换对称加密的密钥;再利用对称加密的性能,对之后发送的数据进行加、解密。

    (感谢阅读,如有不正确之处,欢迎指教)

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论