--- 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): ![](https://pic3.zhimg.com/v2-837ffc4578f86fee62efb0065d420fa8_1440w.jpg) 第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 要是这么简单,也不至于让我学习了好多天,才搞清楚大概。 ![](https://pica.zhimg.com/v2-390b61289c1c4a364ac3b39baaab69aa_1440w.jpg) 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 库就更难了。 ## 后记 [![](https://picx.zhimg.com/v2-a1b5f8ff4e7d54f863be51081b631633.jpg?source=7e7ef6e2&needBackground=1)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 库,实际项目展示。