Files
Knowledge/sunyard/7 信安/openssl中如何获得会话密钥?.md

15 KiB
Raw Permalink Blame History

published, created, tags
published created tags
2025-10-18
clippings

背景

OpenSSL 有2大核心:一个是实现 TLS 协议,一个是加密解密库。

接下来,不得不开始讲解极为复杂的加密解密了。

这里有许多数学知识,本人也不懂,只是作为调包侠,知道输入输出而已。

今天,先讲一个大体框架。


引子

现在主流浏览器和大多数 Web 服务器,都采用 TLS1.3 。

以下是一个典型的通讯过程(TLS1.3):

第1个包:客户向服务器发送 Client Hello。向服务器打招呼,我要和你建立 TLS 连接。

第2个包:服务器回应 Server Hello。服务器回应:好吧,现在开始建立TLS 连接。

第2个包,一共有6个记录层 Record Layer。

前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 方法: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

密钥

什么是密钥?

其实就是一长串数字,长度可能是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 评论 文章

这个项目,用来学习 TLS协议和 Open SSL。

解密https:用MITM 技术,两边欺骗,用自己的私钥

现在是学习阶段,只要解析抓到的https:破解需要 ECDHE 私钥,从哪里取得私钥?

这里先卖个关子,以后再说。

现在假设已经取得本次https 通讯的ECDHE 私钥,需要破解本次https,怎么做?

就需要计算上面提到的所有密钥,然后用密钥解密。

下一节,先讲 ECDHE:计算预备主密钥 PMS。

再下一节,讲如何从 PMS 生成 Early 密钥,再生成握手密钥,再生成主密钥;

最后,讲最终密钥的诞生。

都是用 c 语言实现,调用OpenSSL 库,实际项目展示。