369 lines
15 KiB
Markdown
369 lines
15 KiB
Markdown
---
|
||
published:
|
||
created: 2025-10-18
|
||
tags:
|
||
- clippings
|
||
---
|
||
|
||
## 背景
|
||
|
||
[OpenSSL](https://zhida.zhihu.com/search?content_id=751981217&content_type=Answer&match_order=1&q=OpenSSL&zhida_source=entity) 有2大核心:一个是实现 TLS 协议,一个是加密解密库。
|
||
|
||
接下来,不得不开始讲解极为复杂的加密解密了。
|
||
|
||
这里有许多数学知识,本人也不懂,只是作为调包侠,知道输入输出而已。
|
||
|
||
今天,先讲一个大体框架。
|
||
|
||
---
|
||
|
||
## 引子
|
||
|
||
现在主流浏览器和大多数 Web 服务器,都采用 [TLS1.3](https://zhida.zhihu.com/search?content_id=751981217&content_type=Answer&match_order=1&q=TLS1.3&zhida_source=entity) 。
|
||
|
||
以下是一个典型的通讯过程(TLS1.3):
|
||
|
||

|
||
|
||
第1个包:客户向服务器发送 [Client Hello](https://zhida.zhihu.com/search?content_id=751981217&content_type=Answer&match_order=1&q=Client+Hello&zhida_source=entity)。向服务器打招呼,我要和你建立 TLS 连接。
|
||
|
||
第2个包:服务器回应 [Server Hello](https://zhida.zhihu.com/search?content_id=751981217&content_type=Answer&match_order=1&q=Server+Hello&zhida_source=entity)。服务器回应:好吧,现在开始建立TLS 连接。
|
||
|
||
第2个包,一共有6个记录层 [Record Layer](https://zhida.zhihu.com/search?content_id=751981217&content_type=Answer&match_order=1&q=Record+Layer&zhida_source=entity)。
|
||
|
||
前2个记录层,明文传输:分别是握手协议;Change Cipher(开始加密的信号)
|
||
|
||
后4个记录层,都是加密的,需要客户端解密。
|
||
|
||
以下是客户端和服务器在前2个包中沟通的内容。
|
||
|
||
```
|
||
packet 1: length is 1759
|
||
IP header length is 20
|
||
TCP header length is 20
|
||
Source Port: 11128
|
||
Dest Port: 443
|
||
|
||
TLS: client to server
|
||
TLS part: len is 1719
|
||
record layer: handshake
|
||
handleshake layer: ClientHello
|
||
Random: 3df8b4e7ae0d778d09a45721d5dfbc064e6dc6b368a1e7c1767ae849eb996824
|
||
Session ID: 56c1d0ef0363be4943c0fa9014229a150d18a8ee7e00e271cb18a37ec449cb99
|
||
total 16 cipher suites
|
||
Extensions length: 1605
|
||
extension_type=UNKNOWN(27242), length=0
|
||
extension_type=server_name(0), length=14
|
||
server_name = localhost
|
||
extension_type=supported_versions(43), length=7
|
||
extension_type=session_ticket(35), length=0
|
||
extension_type=UNKNOWN(65037), length=186
|
||
extension_type=key_share(51), length=1263
|
||
group: UNKNOWN(27242), len = 1
|
||
group: X25519MLKEM768(4588), len = 1216
|
||
group: ecdh_x25519(29), len = 32
|
||
|
||
extension_type=signed_certificate_timestamps(18), length=0
|
||
extension_type=supported_groups(10), length=12
|
||
extension_type=application_layer_protocol_negotiation(16), length=14
|
||
extension_type=UNKNOWN(17613), length=5
|
||
extension_type=extended_master_secret(23), length=0
|
||
extension_type=psk_key_exchange_modes(45), length=2
|
||
extension_type=status_request(5), length=5
|
||
extension_type=signature_algorithms(13), length=18
|
||
extension_type=ec_point_formats(11), length=2
|
||
extension_type=compress_certificate(27), length=3
|
||
extension_type=renegotiate(65281), length=1
|
||
extension_type=UNKNOWN(39578), length=1
|
||
|
||
========================
|
||
packet 2: length is 1474
|
||
IP header length is 20
|
||
TCP header length is 20
|
||
Source Port: 443
|
||
Dest Port: 11128
|
||
|
||
TLS: server to client
|
||
TLS part: len is 1434
|
||
record layer: handshake
|
||
handleshake layer: ServerHello
|
||
Random: a80ae047daa2f5d800b479277f5543408c7127061af40f7cbe8296218632aa81
|
||
Session ID: 56c1d0ef0363be4943c0fa9014229a150d18a8ee7e00e271cb18a37ec449cb99
|
||
Cipher Suite: TLS_AES_256_GCM_SHA384(0x1302)
|
||
Extensions length: 46
|
||
extension_type=supported_versions(43), length=2
|
||
extension_type=key_share(51), length=36
|
||
group: ecdh_x25519(29), len = 32
|
||
|
||
record layer: change cipher spec
|
||
record layer: app data
|
||
record layer: app data
|
||
record layer: app data
|
||
record layer: app data
|
||
```
|
||
|
||
经过以上沟通,双方获取以下参数。
|
||
|
||
### 已知参数
|
||
|
||
加密套件:Cipher Suite:TLS\_AES\_256\_GCM\_SHA384
|
||
|
||
[ECDHE](https://zhida.zhihu.com/search?content_id=751981217&content_type=Answer&match_order=1&q=ECDHE&zhida_source=entity) 方法:X25519
|
||
|
||
| 名称 | 长度 | 来源 |
|
||
| --- | --- | --- |
|
||
| 客户端随机数 | 32字节 | TLS 通讯: client hello |
|
||
| 服务器随机数 | 32字节 | TLS 通讯: server hello |
|
||
| 客户端ECDHE公钥 | 32字节 | TLS 通讯: client hello, key share, x25519, key exchange |
|
||
| 服务器ECDHE公钥 | 32字节 | TLS 通讯: Server hello, key share, x25519, key exchange |
|
||
| TLS 版本 V1.3 | NA | TLS 通讯: server hello,supported version |
|
||
| 加密算法 | NA | TLS 通讯: server hello, cipher suite: AES\_256\_GCM |
|
||
| 散列算法 | NA | TLS 通讯: server hello, cipher suite: SHA384 |
|
||
| ECDHE 密钥交换协议 | NA | TLS 通讯: server hello,key share, group: x25519 |
|
||
|
||
### TLS\_AES\_256\_GCM\_SHA384
|
||
|
||
TLS\_AES\_256\_GCM\_SHA384 :就是服务器和客户端沟通后,决定采取的加密方法。
|
||
|
||
简单解释一下。
|
||
|
||
**`TLS_AES_256_GCM_SHA384` = 协议 + 密钥加密算法 + 消息认证算法**
|
||
|
||
- **密码套件**: `TLS_AES_256_GCM_SHA384`
|
||
- **加密算法**: `AES_256_GCM` (256位密钥的AES-GCM模式)
|
||
- **哈希算法**: `SHA-384`
|
||
|
||
### `AES_256_GCM`
|
||
|
||
- **含义**:**用于批量数据加密的对称加密算法**。
|
||
- **拆解**:
|
||
- **`AES`**:**高级加密标准**。这是目前全球最流行、最安全的对称加密算法之一。它被美国政府选为保护机密信息的标准。
|
||
- **`256`**:**密钥长度**。表示使用 256 位(32字节)的密钥。在 AES 算法中,密钥越长,理论上越难被暴力破解
|
||
- **`GCM`**:**伽罗瓦/计数器模式**。这是一种加密模式,它同时提供了**保密性**(加密)和**完整性认证**(确保数据在传输过程中未被篡改)。
|
||
|
||
**简单来说,`AES_256_GCM` 负责对实际传输的网页内容、数据等所有应用层信息进行快速、高强度的加密和解密。**
|
||
|
||
|
||
|
||
### SHA384
|
||
|
||
- **含义**:**用于“握手阶段”认证的散列算法**。
|
||
- **拆解**:
|
||
- **`SHA`**:**安全散列算法**。这是一种密码学散列函数,能将任意长度的数据映射为固定长度的、唯一的“指纹”(散列值)。
|
||
- **`384`**:**输出长度**。表示产生 384 位的散列值。
|
||
|
||
|
||
|
||
**在 TLS 1.3 中,`SHA384` 的作用与 TLS 1.2 不同:**
|
||
|
||
- **在 TLS 1.2 中**,SHA 系列算法可能用于计算消息认证码(如 HMAC-SHA256),以确保每条记录的完整性。
|
||
- **在 TLS 1.3 中**,数据完整性主要由上面的 `GCM` 模式负责。`SHA384` 在这里主要用于 **握手过程的认证**。具体来说,它被用来生成“握手消息”的摘要,用于创建**伪随机函数** 和计算 **Finished** 消息的验证码,以确保整个 TLS 握手过程没有被篡改。
|
||
|
||
|
||
|
||
### ECDHE
|
||
|
||
Group: x25519 (29)
|
||
|
||
`x25519` 是一种**密钥交换算法**,更具体地说,它是**椭圆曲线迪菲-赫尔曼** 密钥交换协议的一种高效、安全的实现。
|
||
|
||
密钥交换的目的是:**在不安全的网络(如互联网)上,让通信的双方(例如你的浏览器和网站服务器)协商出一个只有他们俩才知道的共享秘密。** 这个共享秘密后续会被用作对称加密(如上面提到的 AES\_256\_GCM)的密钥。
|
||
|
||
`x25519` 就是用来安全地完成这个协商过程的现代方法。
|
||
|
||
- **`X`**: 代表“Montgomery X”,指的是它在椭圆曲线上使用的特定坐标系统和计算公式(蒙哥马利曲线)。
|
||
- **`25519`**: 指的是它所基于的椭圆曲线,即 **Curve25519**。这条曲线由密码学家 Daniel J. Bernstein 设计,其核心域是一个由素数 2255−192255−19 定义的有限域,故名 Curve25519。
|
||
|
||
所以,`x25519` 就是 **在 Curve25519 椭圆曲线上执行迪菲-赫尔曼密钥交换的函数**。
|
||
|
||
|
||
|
||
ECDHE过程简述
|
||
|
||
在 TLS 握手过程中:
|
||
|
||
1. 客户端说:“我支持 `x25519`。”
|
||
2. 服务器选择 `x25519`,并生成一个临时的密钥对(私钥 + 公钥),将其公钥发送给客户端。
|
||
3. 客户端也生成一个临时的 `x25519` 密钥对,并计算与服务器公钥的共享秘密,然后将自己的公钥发送给服务器。
|
||
4. 服务器用自己的私钥和客户端的公钥计算,得到**同一个共享秘密**。Pre-Master secret,预备主密钥
|
||
5. 双方随后使用这个共享秘密派生出用于加密数据的会话密钥。
|
||
|
||
---
|
||
|
||
## TLS1.3密钥
|
||
|
||
经过上面的步骤,可以计算出PMS(预备主密钥, Pre-Master Secret),然后用这个密钥加密,解密。
|
||
|
||
打完收工,文章结束。
|
||
|
||
想多了。
|
||
|
||
TLS 要是这么简单,也不至于让我学习了好多天,才搞清楚大概。
|
||
|
||

|
||
|
||
TLS 的密钥种类,大约有十几种。
|
||
|
||
上图是密钥是如何一步一步生成的,看的头大吧,头大是正常的。
|
||
|
||
具体请看:RFC8446 TLS1.3 协议。
|
||
|
||
[RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3www.rfc-editor.org/rfc/rfc8446](https://link.zhihu.com/?target=https%3A//www.rfc-editor.org/rfc/rfc8446)
|
||
|
||
### 密钥
|
||
|
||
什么是密钥?
|
||
|
||
其实就是一长串数字,长度可能是32字节,48字节,或64字节。
|
||
|
||
这个数字是随机产生的,用于加密和解密。
|
||
|
||
### 密钥分类
|
||
|
||
在 TLS 1.3 中,按用途划分,密钥主要分为以下四个层次,每一层都有其特定用途:
|
||
|
||
1. **早期数据传输密钥**:用于 0-RTT 数据加密。只有启用 0-RTT 模式时才会生成。
|
||
2. **握手数据传输密钥**:用于加密握手消息(如 Certificate, CertificateVerify, Finished)。保护握手过程本身不被窥探。
|
||
3. **应用数据传输密钥**:用于加密和解密上层(如 HTTP)传输的应用数据。这是我们最常说的“通信密钥”。
|
||
4. **更新主密钥**:用于在连接过程中主动更新后续的应用密钥,以提供更强的后向安全性。
|
||
|
||
|
||
|
||
| 类别 | 方向 | 密钥名称 | 数量 |
|
||
| --- | --- | --- | --- |
|
||
| 握手流量 | 客户端 → 服务器 | client\_handshake\_key, client\_handshake\_iv | 2 |
|
||
| | 服务器 → 客户端 | server\_handshake\_key, server\_handshake\_iv | 2 |
|
||
| 应用流量 | 客户端 → 服务器 | client\_application\_key, client\_application\_iv | 2 |
|
||
| | 服务器 → 客户端 | server\_application\_key, server\_application\_iv | 2 |
|
||
| 0-RTT流量 | 客户端 → 服务器 | client\_early\_traffic\_key, client\_early\_traffic\_iv | 2 |
|
||
| Finished验证 | 双向 | client\_finished\_key, server\_finished\_key | 2 |
|
||
| PSK | 双向 | resumption\_master\_secret pre-shared key | 2 |
|
||
| 总计 | | | 14 |
|
||
|
||
客户和服务器通讯,读和写用不同的密钥,增加安全性。
|
||
|
||
那么,刚才协商的 PMS,(预备主密钥, Pre-Master Secret),在哪里?
|
||
|
||
这个表格指的是最终使用的密钥。
|
||
|
||
为了产生这些密钥,需要产生一些中间密钥。
|
||
|
||
```
|
||
Early Secret = HKDF-Extract(salt, IKM) = HKDF-Extract(0, PSK) = HKDF-Extract(0, 0)
|
||
Handshake Secret = HKDF-Extract(salt, IKM) = HKDF-Extract(Derive-Secret(Early Secret, "derived", ""), (EC)DHE)
|
||
Master Secret = HKDF-Extract(salt, IKM) = HKDF-Extract(Derive-Secret(Handshake Secret, "derived", ""), 0)
|
||
```
|
||
|
||
**IKM** 是 **Input Keying Material(输入密钥材料)** 的缩写,这是密钥派生函数的原始输入材料。
|
||
|
||
这里的IKM,就是指 PMS,预备主密钥, Pre-Master Secret。
|
||
|
||
就是从 PMS 产生 Early 密钥,
|
||
|
||
Early 密钥产生握手密钥(Handshake Secret);
|
||
|
||
再从握手密钥产生主密钥(Master Secret)。
|
||
|
||
握手密钥产生握手期间的密钥对;
|
||
|
||
主密钥产生数据传输期间的密钥对。
|
||
|
||
HKDF-Extract 指的是什么?
|
||
|
||
Salt:指的是什么?
|
||
|
||
这里不做说明,请自行查看RFC。
|
||
|
||
### 先做减法
|
||
|
||
先去除一些不常用的密钥。
|
||
|
||
resumption\_master\_secret:用来生成 PSK,可以不用
|
||
|
||
client\_early\_traffic\_key, client\_early\_traffic\_iv:0-RTT,可以不用
|
||
|
||
|
||
|
||
### 必用密钥
|
||
|
||
以下密钥是一定会用到的密钥。
|
||
|
||
一切密钥的起点:ECDHE 私钥。
|
||
|
||
ECDHE 私钥相当于种子,种子确定,后面的密钥都确定了。
|
||
|
||
当然,后面这些密钥还需要一些材料:比如TLS 协议中的随机数,TLS 数据包本身产生的哈希值等。
|
||
|
||
| 密钥名称 | 作用 | 谁派生的 |
|
||
| --- | --- | --- |
|
||
| 临时密钥 | | |
|
||
| PMS,预备主密钥 | 一切密钥的基础 | ECDHE私钥和对方的公钥 |
|
||
| Early, 早期密钥 | 密钥的基础 | PMS 预备主密钥派生 |
|
||
| Handshake,握手密钥 | 产生握手阶段的密钥 | Early 早期密钥派生 |
|
||
| Master,主密钥 | 产生数据传输阶段的密钥 | Handshake 握手密钥派生 |
|
||
| 中间密钥 | | |
|
||
| CLIENT\_HANDSHAKE\_TRAFFIC\_SECRET | 用于产生最终密钥 | Handshake 握手密钥派生 |
|
||
| SERVER\_HANDSHAKE\_TRAFFIC\_SECRET | 用于产生最终密钥 | Handshake 握手密钥派生 |
|
||
| CLIENT\_TRAFFIC\_SECRET\_0 | 用于产生最终密钥 | Master 主密钥派生 |
|
||
| SERVER\_TRAFFIC\_SECRET\_0 | 用于产生最终密钥 | Master 主密钥派生 |
|
||
|
||
最终密钥的类型:TLS\_AES\_256\_GCM\_SHA384 为例
|
||
|
||
```
|
||
- \`客户端写密钥\` - 用于客户端加密/服务器解密。48字节
|
||
- \`服务器写密钥\` - 用于服务器加密/客户端解密。48字节
|
||
- \`客户端写 IV\` - 初始化向量。12字节
|
||
- \`服务器写 IV\` - 初始化向量。12字节
|
||
```
|
||
|
||
IV(Initialization Vector):初始向量,还有一个名字:nonce,也是一个随机数。
|
||
|
||
真正做加密解密的,其实是写密钥和初始向量IV。
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
啰啰嗦嗦这么多,写得我头大,看的人估计也蒙了。
|
||
|
||
没办法,TLS 就是这么复杂。
|
||
|
||
为了写 MITM 代理,不仅要看懂协议,还要在 OpenSSL 开源代码中找到相应的函数,
|
||
|
||
实现所有的计算。
|
||
|
||
从最初的 ECDHE 私钥开始,计算十几个密钥,容易吗。
|
||
|
||
还好,我已经建立了一个项目,专门学习这件事。
|
||
|
||
如果密钥计算错误,后面的包一定解析错误,没法继续。
|
||
|
||
破解 https 流量包,真的很难。
|
||
|
||
谁不难呢?
|
||
|
||
学习OpenSSL,调用 OpenSSL 库都这么难,写 OpenSSL 库就更难了。
|
||
|
||
## 后记
|
||
|
||
[openssl-5-立项openssl-dec1 赞同 · 0 评论](https://zhuanlan.zhihu.com/p/1960580134147958148) 文章
|
||
|
||
这个项目,用来学习 TLS协议和 Open SSL。
|
||
|
||
解密https:用MITM 技术,两边欺骗,用自己的私钥
|
||
|
||
现在是学习阶段,只要解析抓到的https:破解需要 ECDHE 私钥,从哪里取得私钥?
|
||
|
||
这里先卖个关子,以后再说。
|
||
|
||
现在假设已经取得本次https 通讯的ECDHE 私钥,需要破解本次https,怎么做?
|
||
|
||
就需要计算上面提到的所有密钥,然后用密钥解密。
|
||
|
||
下一节,先讲 ECDHE:计算预备主密钥 PMS。
|
||
|
||
再下一节,讲如何从 PMS 生成 Early 密钥,再生成握手密钥,再生成主密钥;
|
||
|
||
最后,讲最终密钥的诞生。
|
||
|
||
都是用 c 语言实现,调用OpenSSL 库,实际项目展示。 |