> 文章列表 > 【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

SSL/TLS层的位置

SSL/TLS层在网络模型的位置,它属于应用层协议。接管应用层的数据加解密,并通过网络层发送给对方。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

SSL/TLS协议分握手协议和记录协议,握手协议用来协商会话参数(比如会话密钥、应用层协议等等),记录协议主要用来传输应用层数据和握手协议消息数据,以及做加解密处理。我们应用层的的消息数据在SSL记录协议会给分成很多段,然后再对这个片段进行加密,最后在加上记录头后就发送出去。

SSL/TLS 握手协议又细分为四个子协议,分别是握手协议、密码规格变更协议、警告协议和应用数据协议。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

握手流程(HTTPS中TLS/SSL的握手过程)

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

首先是TCP握手,TCP三次完成之后才进入SSL握手,SSL握手总是以ClientHello消息开始,就跟TCP握手总是以SYN包开始一样。

SSL/TLS握手时的私钥用途(RSA、ECDHE)

两种使用方式分别是:使用RSA来做密钥交换和使用ECDHE来做密钥交换。对于RSA来说,客户端生成预主密钥,然后用公钥加密再发给服务器,服务器用私钥来解密得到预主密钥,然后由预主密钥生成主密钥,再由主密钥生会话密钥,最后用会话密钥来通信。

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

对于ECDHE来说,客户端和服务器双方是交换椭圆曲线参数,私钥只是用来签名,这是为了保证这个消息是持有私钥的人给我发的,而不是冒充的。双方交换完参数之后生成预主密钥,再生成主密钥和会话密钥。这就跟刚才RSA后面的流程一样了。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

可以看出RSA和椭圆曲线密钥交换算法的私钥用途是不一样的,RSA密钥交换时是用来做加解密的,椭圆曲线密钥交换时是用来做签名的。

SSL/TLS中的预主密钥、主密钥和会话密钥

主密钥是由预主密钥、客户端随机数和服务器随机数通过PRF函数来生成;会话密钥是由主密钥、客户端随机数和服务器随机数通过PRF函数来生成,会话密钥里面包含对称加密密钥、消息认证和CBC模式的初始化向量,但对于非CBC模式的加密算法来说,就没有用到这个初始化向量。

session 缓存和session ticket里面保存的是主密钥,而不是会话密钥,这是为了保证每次会话都是独立的,这样才安全,即使一个主密钥泄漏了也不影响其他会话。

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程


基于 RSA 密钥协商算法的首次握手

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

握手开始,Client 先发送 ClientHello ,在这条消息中,Client 会上报它支持的所有“能力”。client_version 中标识了 Client 能支持的最高 TLS 版本号;random 中标识了 Client 生成的随机数,用于预备主密钥和主密钥以及密钥块的生成,总长度是 32 字节,其中前 4 个字节是时间戳,后 28 个字节是随机数;cipher_suites 标识了 Client 能够支持的密码套件。extensions 中标识了 Client 能够支持的所有扩展。

Server 在收到 ClientHello 之后,如果能够继续协商,就会发送 ServerHello,否则发送 Hello Request 重新协商。在 ServerHello 中,Server 会结合 Client 的能力,选择出双方都支持的协议版本以及密码套件进行下一步的握手流程。server_version 中标识了经过协商以后,Server 选出了双方都支持的协议版本。random 中标识了 Server 生成的随机数,用于预备主密钥和主密钥以及密钥块的生成,总长度是 32 字节,其中前 4 个字节是时间戳,后 28 个字节是随机数;cipher_suites 标识了经过协商以后,Server 选出了双方都支持的密码套件。extensions 中标识了 Server 处理 Client 的 extensions 之后的结果。

当协商出了双方都能满足的密钥套件,根据需要 Server 会发送 Certificate 消息。Certificate 消息会带上 Server 的证书链。Certificate 消息的目的一是为了验证 Server 身份,二是为了让 Client 根据协商出来的密码套件从证书中获取 Server 的公钥。Client 拿到 Server 的公钥和 server 的 random 会生成预备主密钥。

由于密钥协商算法是 RSA,需要 Server 在发送完 Certificate 消息以后就直接发送 ServerHelloDone 消息了。

Client 收到 ServerHelloDone 消息以后,会开始计算预备主密钥,计算出来的预备主密钥会经过 RSA/ECDSA 算法加密,并通过 ClientKeyExchange 消息发送给 Server。RSA 密码套件的预备主密钥是 48 字节。前 2 个字节是 client_version,后 46 字节是随机数。Server 收到 ClientKeyExchange 消息以后就会开始计算主密钥和密钥块了。同时 Client 也会在自己本地算好主密钥和密钥块。

有些人会说“主密钥和会话密钥”,这里的会话密钥和密钥块是相同的意思。主密钥是由预主密钥、客户端随机数和服务器随机数通过 PRF 函数来生成;会话密钥是由主密钥、客户端随机数和服务器随机数通过 PRF 函数来生成。
会话密钥 = key_block = 密钥块,这三者的意思是一样的,只是翻译不同罢了。

Client 发送完 ClientKeyExchange 消息紧接着还会继续发送 ChangeCipherSpec 消息和 Finished 消息。Server 也会回应 ChangeCipherSpec 消息和 Finished 消息。如果 Finished 消息校验完成以后,代表握手最终成功。

再来看看基于 DH 密钥协商算法的首次握手:
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

基于 DH 密钥协商算法和基于 RSA 密码协商的区别在 Server 和 Client 协商 DH 参数上面。这里只说明一下 DH 密钥协商过程比 RSA 多的几步,其他的流程和 RSA 的流程基本一致。

在 Server 发送完 Certificate 消息以后,还会继续发送 ServerKeyExchange 消息,在这条消息里面传递 DH 参数。

另外一个不同点在于 ClientKeyExchange 消息中传递给 Server 预备主密钥长度不是 48 字节。基于 DH/ECDH 算法的协商密钥长度取决于 DH/ECDH 算法的公钥。

为了让读者能更加直观的理解 TLS 1.2 的流程,笔者用 Wireshark 抓取了 Chrome 和笔者博客 TLS 握手中的网络包。结合 Wireshark 的抓包分析,让我们更加深入的理解 TLS 1.2 握手吧。下面的例子以 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 密码套件为例:
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

上面这是一次从 TLS 握手开始到 TCP 4 次挥手完整的过程。这里我们重要关注 protocol = TLS 1.2 的所有消息。整体流程和上面分析的基于 DH 密钥交换算法的是一致的。在 TCP 4 次挥手之前,TLS 层会先收到 Close Notify 的 Alert 消息。

为什么经过 TLS 加密以后的上层数据会以明文展示在抓包中?HTTPS 不安全?这里需要解释一下,因为我利用 export SSLKEYLOGFILE=/Users/XXXX/sslkeylog.log把 ECDHE 协商的密钥保存成 log 文件了,解析上层 HTTP/2 的加密包的时候就可以利用 log 中的 TLS key 进行解密。上图中绿色的部分,就是通过解密出来得到的 HTTP/2 的解密内容。具体这块内容笔者会在 HTTP/2 相关的文章里面会提一提,这里就认为是笔者利用某些手段解析出了 HTTPS 加密的内容即可。

接下来一条条的看看网络是如何传输数据包的。从 ClientHello 开始。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

从 TLS 1.2 Record Layer 的 Length 字段中我们可以看到 TLS 记录层的这条 TLS 握手消息长度是 512 字节,其中 ClientHello 消息中占 508 字节。ClientHello 中标识了 Client 最高支持的 TLS 版本是 TLS 1.2 (0x0303)。Client 支持的密码套件有 17 种,优先支持的是 TLS_AES_128_GCM_SHA256 。Session ID 的长度是 32 字节,这里不为空。压缩算法是 null。signature_algorithms 中标识了 Client 支持 9 对数字签名算法。

默认情况下 TLS 压缩都是关闭的,因为 CRIME 攻击会利用 TLS 压缩恢复加密认证 cookie,实现会话劫持,而且一般配置gzip 等内容压缩后再压缩 TLS 分片效益不大又额外占用资源,所以一般都关闭 TLS 压缩。

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

ClientHello 中发送了 status_request 扩展,查询 OCSP 封套消信息。发送了 signed_certificate_timestamp 扩展,查询 SCT 信息。发送了 application_layer_protocol_negotiation (ALPN)扩展,询问服务端是否支持 HTTP/2 协议。supported_group 扩展里面标识了 Client 中支持的椭圆曲线。SessionTicket TLS 扩展表明了 Client 支持基于 Session Ticket 的会话恢复。

再看看 ServerHello 消息。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

从 TLS 1.2 Record Layer 的 Length 字段中我们可以看到 TLS 记录层的这条 TLS 握手协议消息长度是 82 字节,其中 ServerHello 消息中占 78 字节。Server 选择用 TLS 1.2 版本与 Client 进行接下来的握手流程。Server 与 Client 协商出来的密码套件是 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384。

Server 支持 HTTP/2,在 ALPN 扩展中进行了回复。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

从 TLS 1.2 Record Layer 的 Length 字段中我们可以看到 TLS 记录层的这条 TLS 握手消息长度是 2544 字节,其中 Certificate 消息中占 2540 字节,Certificates 证书链包含 2 张证书,Server 实体证书 1357 字节,中间证书 1174 字节。中间证书是 Let’s Encrypt 签发的。两张证书都是用 sha256WithRSAEncryption 签名算法进行签名的。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

从 TLS 1.2 Record Layer 的 Length 字段中我们可以看到 TLS 记录层的这条 TLS 握手协议消息长度是 535 字节,其中 Certificate Status 消息中占 531 字节。Server 将 OCSP 的 response 发送给 Client。

从 TLS 1.2 Record Layer 的 Length 字段中我们可以看到 TLS 记录层的这条 TLS 握手协议消息长度是 116 字节,其中 ServerKeyExchange 消息中占 112 字节。由于协商出来的是 ECDHE 密钥协商算法,所以 Server 需要把 ECDH 的参数和公钥通过 ServerKeyExchange 消息发给 Client。这里 ECDHE 使用的 ECC 命名曲线是 x25519 。Server 的公钥是 (62761b5……),签名算法是 ECDSA_secp256r1_SHA256 。签名值是 (3046022……)。

ServerHelloDone 消息结构很简单,见上图。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

由于是 ECDHE 协商算法,所以 Client 需要发送 ECC DH 公钥,对应的公钥值是 (1e58cf……)。公钥长度 32 字节。

ChangeCipherSpec 消息结构很简单,发送这条消息是为了告诉 Server ,Client 可以使用 TLS 记录层协议进行密码学保护了。第一条进行密码学保护的消息是 Finished 消息。

Finished 消息结构很简单,见上图。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

Server 如果只是 SessionTicket,那么会生成新的 NewSessionTicket 返给 Client,然后同样返回 ChangeCipherSpec 消息和 Finished 消息。
【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

当页面关闭的时候,Server 会给 Client 发送 TLS Alert 消息,这条消息里面的描述就是 Close Notify。同时 Server 会发送 FIN 包开始 4 次挥手。

TLS 1.2 会话恢复

Client 和 Server 只要一关闭连接,短时间内再次访问 HTTPS 网站的时候又需要重新连接。新的连接会造成网络延迟,并消耗双方的计算能力。有没有办法能复用之前的 TLS 连接呢?办法是有的,这就涉及到了 TLS 会话复用机制。

当 Client 和 Server 决定继续一个以前的会话或复制一个现存的会话(取代协商新的安全参数)时,消息流如下:

Client 使用需要恢复的当前会话的 ID 发送一个 ClientHello。Server 检查它的会话缓存以进行匹配。如果匹配成功,并且 Server 愿意在指定的会话状态下重建连接,它将会发送一个带有相同会话 ID 值的 ServerHello 消息。这时,Client 和 Server 必须都发送 ChangeCipherSpec 消息并且直接发送 Finished 消息。一旦重建立完成,Client 和 Server 可以开始交换应用层数据(见下面的流程图)。如果一个会话 ID 不匹配,Server 会产生一个新的会话 ID,然后 TLS Client 和 Server 需要进行一次完整的握手。

 Client Server ClientHello --------> ServerHello [ChangeCipherSpec]  Application Data  Application Data 

Session ID 由服务器端支持,协议中的标准字段,因此基本所有服务器都支持,服务器端保存会话 ID 以及协商的通信信息,占用服务器资源较多。
1. 基于 Session ID 的会话恢复

当 Client 通过一次完整的握手,与 Server 建立了一次完整的 Session,Server 会记录这次 Session 的信息,以备恢复会话的时候使用:

  1. 会话标识符(session identifier):
  2. 每个会话的唯一标识符
  3. 对端的证书(peer certificate):
  4. 对端的证书,一般为空
  5. 压缩算法(compression method):
  6. 一般不启用
  7. 密码套件(cipher spec):
  8. Client 和 Server 协商共同协商出来的密码套件
  9. 主密钥(master secret):
  10. 每个会话都会保存一份主密钥,注意不是预备主密钥
  11. 会话可恢复标识(is resumable):
  12. 标识会话是否可恢复

当 Server 保存了以上的信息,可以再次计算出 TLS 记录层需要的 security parameters 加密参数,从而加密应用数据。

基于 Session ID 会话恢复的流程如下:

Client Server ClientHello --------> ServerHello [ChangeCipherSpec]  Application Data  Application Data 

【RSA】HTTPS中SSL/TLS握手时RSA前后端加密流程

Client 发现请求的网站是之前请求过的,即内存中存在 Session ID,那么再次建立连接的时候会在 ClientHello 中附带与网站对应的 Session ID。

Server 的内存中会保存一份 Session Cache 的字典,key 是 Session ID,value 是会话信息。Server 收到 ClientHello 以后,根据传过来的 Session ID 查看是否有相关的会话信息,如果有,就会允许 Client 进行会话恢复,直接发送 ChangeCipherSpec 和 Finished 消息。如果没有相关的会话信息,就会开始一次完整的握手,并在 ServerHello 中生成新的 Session ID 返回给 Client。

Client 收到 Server 发来的 ChangeCipherSpec 和 Finished 消息,代表会话恢复成功,也发送 ChangeCipherSpec 和 Finished 消息作为回应。