这里结合 RFC8446,MbedTLS 源码, Wireshark 抓包对 TLS 1.3 几个重要特性做深入剖析。
一 特性剖析
1. TLS 1.3 VS 1.2
https://datatracker.ietf.org/doc/html/rfc8446#section-1.2 。
TLS 1.3 对比 TLS 1.2 的主要差异。
项目 | 条目 | TLS 1.2 | TLS 1.3 |
---|---|---|---|
加密与算法 | 对称加密算法 | 支持多种,包括已废弃的 CBC、RC4 | 仅支持 AEAD 算法(如 AES-GCM、ChaCha20-Poly1305) |
密钥派生算法 | 自定义复杂结构 KDF | 使用统一的 HKDF(HMAC-based Extract-and-Expand) | |
密钥交换机制 | 支持 RSA、DH、ECDHE,包括静态密钥 | 移除静态 RSA/DH,统一使用 ECDHE,具备前向安全 | |
签名算法支持 | RSA、DSA、ECDSA | RSA、DSA、ECDSA | |
安全改进 | 前向安全(PFS) | 可选,取决于是否使用 (EC)DHE | 所有公钥交换均具备前向安全 |
报文完整性保护 | 加密和 MAC 分离(MAC-then-encrypt) | 使用 AEAD(集成加密+认证) | |
证书扩展等隐私保护 | 扩展字段如 SNI 以明文发送 | 扩展通过 EncryptedExtensions 加密传输 | |
握手机制 | 握手消息加密 | ServerHello 后部分消息仍明文 | ServerHello 后全部握手消息加密 |
握手流程结构 | 含 ServerHelloDone、ChangeCipherSpec | 精简状态机,移除冗余消息 | |
0-RTT 数据支持 | 不支持 | 支持 0-RTT,允许部分早期数据发送(有安全限制) | |
会话恢复 | 会话恢复机制 | Session ID / Session ticket 两种方式 | 统一为 PSK-based 会话恢复,使用 SessionTicket + Binder |
PSK 模式 | 较少使用 | 明确支持 PSK-only / PSK + ECDHE | |
兼容性机制 | 版本协商 | 通过 ClientHello.version,存在兼容问题 | 使用 extension 提供版本列表,增强兼容性 |
压缩与 DSA | 支持 TLS-level 压缩、支持 DSA | 移除压缩与 DSA,避免压缩攻击 |
重点介绍全新特性前。介绍下 TLS 1.3 的完整握手流程。
https://datatracker.ietf.org/doc/html/rfc8446#section-2
如上完整握手流程,包含密钥交换(Key Exchange)和双向认证(Authentication,可选)流程。
- 对于密钥交换仅支持 ECDHE。
- 对于认证支持证书认证和 PSK 认证,当然也可以不做认证。
如上 KE 和 Auth 流程组合,TLS 1.3 共支持如下5种模式。
握手 | 模式 | 描述 |
---|---|---|
完整握手 | ECDHE | 使用 ECDHE 密钥交换,无认证。 |
PSK ECHDE | 使用预设密钥 PSK 快速认证 + ECDHE 密钥交换 | |
证书 ECHDE | 使用证书进行身份验证 + ECDHE 密钥交换 | |
会话恢复 | PSK ECDHE | 基于 Session Ticket 快速恢复会话,流程与 PSK-ECDHE 类似,区别在于 PSK 来源不同:完整 PSK-ECDHE 使用预共享凭据;会话恢复则使用前次握手后协商出的临时 PSK。 |
PSK Only | 基于 Session Ticket 快速恢复会话,但是不做 ECHDE。需要说明的是,尽管不做 ECHDE,但是还有一个简单 KE,同 TLS 1.2 PSK KE 原理相似。在后面章节会详细介绍。 |
2. 安全增强
2.1 PSK KE VS PSK ECHDE
TLS 1.2 PSK KE 对比 TLS 1.3 PSK ECDHE 一个安全特性就是前向安全。 前向安全确保了长期密钥泄露不会影响历史会话的安全性。
以下是 TLS 1.2 PSK KE 与 TLS 1.3 PSK ECDHE 的握手流程对比。在密钥交换过程中,最大的区别在于是否引入了临时椭圆曲线 Diffie-Hellman 的密钥交换。
注意:如上为简化公式。完整的密钥派生会在后面详细介绍。
在 TLS 1.2 PSK KE 模式下,会话密钥完全依赖预共享密钥(PSK),如果 PSK 泄露,攻击者可以离线解密之前或当前的通信内容。
提示:ClientRandom 和 ServerRandom 为明文交换,所以 PSK 泄漏,MasterSecret 就会泄漏。
在 TLS 1.3 PSK ECDHE 模式下,引入了临时的 ECDHE ClientShare 和 ServerShare 公钥交换,即使 PSK 被泄露,也无法恢复会话密钥,因此提供了前向安全性。
提示:尽管 ClientShare、ServerShare、ClientRandom、ServerRandom 都是明文交换,但是 ECDH 共享密钥计算需要私钥参与。所以 MasterSecret 不会直接泄漏。
2.2 密钥派生
https://datatracker.ietf.org/doc/html/rfc8446#section-7.1
如下流程为 TLS 1.3 完整的密钥派生流程。
HKDF-Extract 为熵压缩器,可以看作是一个安全的“种子生成器” 。需要输入 salt 和 IKM(Input Key Material)。如下框图为从上方获取 Salt 参数,从左侧获取 IKM 参数,输出结果向下,其输出变量名写在右边。
Derive-Secret 为密钥派生器,可以理解为如上“种子”长出多种不同用途的“树”。需要输入:Secret 和 Label 和 Messages。
Derive-Secret(Secret, Label, Messages) = HKDF-Expand-Label(Secret, Label, Transcript-Hash(Messages), Hash.length)
- 如下框图为从上方获取 Secret 参数,Label 和 Messages 已经包含在 Derive-Secret() 函数参数中。其中 ClientHello...ServerHello 表示两条消息的 hash 值。ClientHello...server Finished 表示从 ClientHello 到 Finished 所有消息的 hash 值。
- 0 表示一个长度为 Hash.length 字节的全 0 字节串。
首先需要具体说明每一个密钥的使用场景。
阶段 | 密钥 | 描述 |
---|---|---|
Early Secret 早期密钥 | binder_key | 用于验证 PSK 的合法性(在 ClientHello 中计算 binder HMAC) |
client_early_traffic_secret | 用于 加密 0-RTT 数据(仅客户端发送) | |
early_exporter_master_secret | 用于早期阶段的数据导出(如 token 等) | |
Handshake Secret 握手阶段密钥 | client_handshake_traffic_secret | 加密客户端握手阶段消息,如 Certificate、Finished |
server_handshake_traffic_secret | 加密服务端握手阶段消息,如 Certificate、Finished | |
Master Secret 主密钥 | server_application_traffic_secret_0 | 加密服务端应用层数据 |
exporter_master_secret | 用于导出额外密钥材料(给上层协议或 token 用) | |
resumption_master_secret | 会话恢复时用于生成 Session Ticket 中的 PSK |
如上密钥派生流程,主要输入信息为 PSK 和 ECDHE。上面我们已经讨论了 TLS 1.3 的5种不同模式。接下来我们看看不同模式的 PSK 和 ECDHE 信息。
握手 | 模式 | 描述 | PSK |
---|---|---|---|
完整握手 | ECDHE | 使用 ECDHE 密钥交换,无认证。 | 无 PSK,有 ECHDE |
PSK ECHDE | 使用预设密钥 PSK 快速认证 + ECDHE 密钥交换 | 预设 PSK,有 ECHDE | |
证书 ECHDE | 使用证书进行身份验证 + ECDHE 密钥交换 | 无 PSK,有 ECDHE | |
会话恢复 | PSK ECDHE | 基于 Session Ticket 快速恢复会话,流程与 PSK-ECDHE 类似,区别在于 PSK 来源不同:常规 PSK-ECDHE 使用预共享凭据;会话恢复则使用前次握手后协商出的临时 PSK。 | 有 PSK,PSK = HKDF-Expand-Label(resumption_master_secret, "resumption", ticket_nonce, Hash.length)PSK 由 resumption_master_secret(上次会话派生出) 和 ticket_nonce(上次会话临时临时生成,通过 NewSessionTicket 发送给客户端)。有 ECDHE |
PSK Only | 基于 Session Ticket 快速恢复会话,但是不做 ECHDE。需要说明的是,尽管不做 ECHDE,但是还有一个简单 KE,同时 TLS 1.2 PSK KE 原理一致。在后面章节会详细介绍。 | 有 PSK,PSK 同上。无 ECDHE。 |
3. 2-RTT VS 1-RTT
TLS 1.3 的一个重要特性是优化了握手阶段的消息交互效率,将原本的 2-RTT 优化为 1-RTT,主要通过以下两个机制实现:
- 在 TLS 1.2 中,ClientKeyExchange 和 ServerKeyExchange 是独立消息;而在 TLS 1.3 中,这些密钥交换参数被整合进 ClientHello 和 ServerHello 的 key_share 和 pre_shared_key 扩展中,避免了额外的往返消息。
- 对于身份验证相关的 Finished 消息,TLS 1.2 是由客户端先发送,TLS 1.3 则调整为服务端先发送,这使得 Finished 可以合并在服务端的响应路径中发出。
另外对于 RTT 计算你可能还会有如下疑问。
- TLS 1.3 最后还需要发送 Finished 消息,应该算 1.5 RTT?
对于客户端 Application Data 可以和 Finished 可以一起发送。
- 对于 ServerHello 和 CertificateRequest 以及 Certificate 是单独发送为什么 RTT 算在一起?
在 TLS 1.3 中,服务端从 ServerHello 开始,接下来可以批量发出:
EncryptedExtensions → Certificate → CertificateVerify → Finished,中间不需要等待客户端的响应,因此这整个过程都算作第 1 个 RTT 内的服务端响应部分。 - 如果 TCP RWND = 1 MSS,并且不支持 Trickle ,RTT 如何计算?
在特定网络条件下(RWND = 1 MSS,不支持 Trickle),TLS 1.3 中服务端确实无法一次性发完所有握手消息,需要多个 TCP ACK 往返来推动 TLS 消息发出。
在网络层面上 RTT 被拉长了:虽然 TLS 协议逻辑上是一轮 RTT完成握手,但实际 由于 TCP 的传输能力受限,ServerHello 后的证书链等可能被拆分成多个 TCP 分段发送。这个时候,从“时延体验”上看类似于 2-RTT,但协议本身仍然是 1-RTT 的握手逻辑。
4. 0-RTT Early Data
https://datatracker.ietf.org/doc/html/rfc8446#section-2.3
首先说明应用场景。0-RTT Early Data 只能用以有 PSK 的握手流程。
- 基于 Resumption PSK 会话恢复流程
- 基于 External PSK 的完整握手流程
不能在首次基于证书的完整握手流程中使用。
提示:如图所示绿色握手消息表示 Early Data 特定流程。
如上图所示
ClientHello
客户端发起握手,并尝试发送 Early Data,包含以下关键扩展:
字段 描述 + early_data 告诉服务端:“我打算发送 0-RTT 数据”。仅作为信号,无内容。 + pre_shared_key 包含 PSK identity(用于识别会话)和 binder(对 ClientHello 做 MAC 认证)。用于认证服务器并恢复密钥。 - (Application Data*)
如果客户端在 ClientHello 中附带了
early_data
扩展,它可以立即附加加密的 Early Application Data(比如一个 HTTP 请求或 MQTT CONNECT)。
该数据使用 client_early_traffic_secret 派生出的密钥进行加密。 ServerHello
服务端响应握手,并选择一个 PSK:
字段 描述 + pre_shared_key 告诉客户端:“我接受了你提供的哪个 PSK identity”。这一决定决定了后续密钥派生路径。 {EncryptedExtensions}
这是 TLS 1.3 新增的加密消息,用于传输额外扩展字段:
字段 描述 + early_data* 如果服务端接受了 0-RTT 数据,则必须发送该扩展,告知客户端 early data 被接受;否则不发送,表示拒绝 early data。 - (EndOfEarlyData)
如果客户端确实发送了 Early Data,那么它在收到服务端
EncryptedExtensions
后,必须发送 EndOfEarlyData 消息。
它是握手中的一个标记,告知服务端:早期应用数据(0-RTT)已经发送完毕。
它也意味着后续将切换到 Handshake Secret 加密上下文。 - [Application Data*]
这是服务端在握手完成后发送的正常 Application Data:使用 server_application_traffic_secret 加密;它可能包含服务端响应,例如 HTTP 响应或 MQTT CONNACK。
总结:
消息 | 加密上下文 | 作用说明 |
---|---|---|
ClientHello | 明文 | 携带 PSK、声明 early_data 意图 |
(Application Data*) | 使用 early secret 加密 | 早期发送的业务数据 |
ServerHello | 明文 | 响应 ClientHello,指定使用哪个 PSK |
{EncryptedExtensions} | Handshake Secret | 服务端确认是否接受 early_data |
EndOfEarlyData | Handshake Secret | 客户端结束 early data 发送 |
[Application Data*] | Application Secret | 正式数据传输阶段开始 |
5. 会话恢复
https://datatracker.ietf.org/doc/html/rfc8446#section-2.2
https://datatracker.ietf.org/doc/html/rfc5077
会话恢复是基于完整 1-RTT 握手协议进一步优化的机制,旨在减少连接建立时的延迟开销。TLS 1.2 支持两种会话恢复机制:Session ID(服务端有状态)和 Session Ticket(服务端无状态)。TLS 1.3 延续了 Session Ticket 的会话恢复机制,但不再直接恢复 Master Secret,而是将 Resumption Secret 用作 PSK,重新进行密钥派生。该过程支持可选的 ECDHE 交换(PSK+ECDHE),进一步增强前向安全性。
看看3种完整握手流程的在2种会话恢复下的收益。
完整握手 | 握手模式 | 会话恢复 | 恢复模式 | 收益 |
---|---|---|---|---|
完整握手 | ECDHE | 会话恢复 | PSK ECDHE | 无收益 |
PSK Only | 省略 ECDHE 流程 | |||
PSK ECHDE | 会话恢复 | PSK ECDHE | 无收益,还增加 PSK 解密流程 | |
PSK Only | 省略 ECDHE 流程 | |||
证书 ECHDE | 会话恢复 | PSK ECDHE | 省略证书如流程 | |
PSK Only | 省略证书认证和 ECDHE 流程 |
提示:如图所示绿色握手消息表示 PSK 会话恢复特定流程。
https://datatracker.ietf.org/doc/html/rfc5077#section-4
TLS 1.3 会话恢复的关键路径是:完成完整握手后,服务端通过 NewSessionTicket 向客户端下发一个加密的 Ticket。该 Ticket 由服务端的 Ticket Protection Key 加密,服务端不保存 Ticket 内容,因此这种机制被称为“无状态的会话恢复”。
在后续会话恢复阶段,客户端将该 Ticket 原样通过 ClientHello 的 pre_shared_key 扩展回传,服务端解密后取出其中的 PSK,重新执行密钥派生流程,从而恢复会话。
详细看看 Ticket 数据结构。
https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.1
struct {
uint32 ticket_lifetime; // 客户端可使用该 Ticket 的有效时间(单位:秒)
uint32 ticket_age_add; // 随机扰动值,防止重放攻击,服务端生成
opaque ticket_nonce<0..255>; // 用于导出 PSK 的 nonce 值(参与 HKDF)
opaque ticket<1..2^16-1>; // 实际 Ticket 内容(密文)
Extension extensions<0..2^16-2>; // 可选扩展,例如 early_data 支持等
} NewSessionTicket;
https://datatracker.ietf.org/doc/html/rfc5077#section-4
struct {
opaque key_name[16]; // 标识用于加密该 ticket 的密钥名称
opaque iv[16]; // AEAD 加密使用的初始化向量
opaque encrypted_state<0..2^16-1>; // 加密的 session state(服务端状态)
opaque mac[32]; // AEAD 加密产生的认证标签(MAC)
} ticket;
提示:而对于 PSK 加密的 encrypted_state 数据结构在 RFC 8446 和 RFC 5077 均没有详细定义。需要结合 https://github.com/Mbed-TLS/mbedtls/blob/v3.6.3/library/ssl_tls.c#L3818 代码。
struct {
uint32_t ticket_age_add; // 与 NewSessionTicket 中一致,用于计算实际 age
uint8_t ticket_flags; // 标志位,如是否支持 0-RTT 等
uint8_t resumption_key_len; // PSK 长度(通常是 32)
uint8_t resumption_key[32]; // 客户端用于恢复 early_secret 的 PSK
uint32_t ticket_lifetime; // Ticket 生命周期,服务端控制
uint16_t ticket_len; // ticket ID 长度
uint8_t ticket[ticket_len]; // 唯一标识该 Session Ticket 的 ID
// 以下为可选字段(根据编译选项启用)
uint64_t ticket_creation_time; // Ticket 创建时间(用于服务端过期检查)
uint32_t max_early_data_size; // 客户端支持的最大 Early Data 大小
uint16_t record_size_limit; // 最大记录大小限制
uint16_t hostname_len; // 主机名长度(客户端 SNI)
uint8_t hostname[hostname_len]; // 主机名内容(如有)
uint16_t alpn_len; // ALPN 协议长度
uint8_t alpn[alpn_len]; // ALPN 协议内容
} encrypted_state;
TLS 1.3 的会话恢复不同于 TLS 1.2 的直接恢复 Master Secret。其核心增强在使用 resumption_key 作为 PSK,重新执行一轮完整的密钥派生,从而获得前向安全。
分别看看客户端和服务端如何持有 PSK 。
- 客户端在接收到 NewSessionTicket 后,根据 ticket_nonce 和本地的 resumption_master_secret 派生出会话恢复用的 PSK:
- 对于 Server 端,并不存储 Ticket,而是等待客户端在 ClientHello 的 pre_shared_key 直接将加密 ticket 回传给服务端,使用 Ticket Protection Key 解密后直接取出 encrypted_state 中的 resumption_key 作为 PSK。
对于 Ticket Protection Key 的管理,不再 RFC 讨论中,Mbed TLS 做了双密钥的轮换管理。
/*
* Rotate active session ticket encryption key
*/
int mbedtls_ssl_ticket_rotate(mbedtls_ssl_ticket_context *ctx,
const unsigned char *name, size_t nlength,
const unsigned char *k, size_t klength,
uint32_t lifetime)
二 测试分析
1. 编译使用
这里使用 Mbed TLS v3.6.3 官方仓库 programs/ssl/ssl_client2.c 和 programs/ssl/ssl_server2.c 完成测试。
直接参考工程 README.md 编译 ssl_client2 和 ssl2_server2。例如 Make 直接在根目录运行 make 即可。
git clone https://github.com/Mbed-TLS/mbedtls.git
git submodule update --init
cd mbedtls
make
对于不同测试可以参考 Mbed TLS 的测试用例,比如 0-RTT (Early Data)。测试脚本会告诉你使能哪些宏,使用哪些参数,以及输出日志。
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3
requires_config_enabled MBEDTLS_SSL_CLI_C
requires_config_enabled MBEDTLS_SSL_SRV_C
requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
requires_config_enabled MBEDTLS_HAVE_TIME
requires_config_enabled MBEDTLS_SSL_EARLY_DATA
requires_config_enabled MBEDTLS_DEBUG_C
requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
requires_any_configs_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED \
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
run_test "TLS 1.3 m->m: resumption with early data" \
"$P_SRV debug_level=4 early_data=1 crt_file=../framework/data_files/server5.crt key_file=../framework/data_files/server5.key" \
"$P_CLI debug_level=3 early_data=1 new_session_tickets=1 reco_mode=1 reconnect=1" \
0 \
-c "Protocol is TLSv1.3" \
-c "Saving session for reuse... ok" \
-c "Reconnecting with saved session" \
-c "HTTP/1.0 200 OK" \
-c "received max_early_data_size" \
-c "NewSessionTicket: early_data(42) extension received." \
-c "ClientHello: early_data(42) extension exists." \
-c "EncryptedExtensions: early_data(42) extension received." \
-c "bytes of early data written" \
-C "0 bytes of early data written" \
-s "Protocol is TLSv1.3" \
-s "key exchange mode: psk" \
-s "Select PSK ciphersuite" \
-s "Sent max_early_data_size" \
-s "NewSessionTicket: early_data(42) extension exists." \
-s "ClientHello: early_data(42) extension exists." \
-s "EncryptedExtensions: early_data(42) extension exists." \
-s "early data bytes read"
如上宏配置可以通过 scripts/config.py
检查和设置当前配置。
scripts/config.py --file include/mbedtls/mbedtls_config.h get MBEDTLS_SSL_EARLY_DATA ;echo $? #1表示未使能,0表示使能
scripts/config.py --file include/mbedtls/mbedtls_config.h set MBEDTLS_SSL_EARLY_DATA
scripts/config.py --file include/mbedtls/mbedtls_config.h get MBEDTLS_SSL_EARLY_DATA ;echo $? #1表示未使能,0表示使能
对于当前需要测试的会话恢复。其他宏都默认使能(当前你可以通过 get 检查),你只需要使能如上 MBEDTLS_SSL_EARLY_DATA
。
scripts/config.py --file include/mbedtls/mbedtls_config.h set MBEDTLS_SSL_EARLY_DATA
make
ls programs/ssl/ssl_*2
TLS 1.2 PSK KE 本地连接测试。
服务端
programs/ssl/ssl_server2 debug_level=3 nbio=0 min_version=tls12 max_version=tls13 auth_mode="required" tickets=0 server_addr=127.0.0.1 force_ciphersuite=TLS-PSK-WITH-AES-128-GCM-SHA256 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity"
客户端
programs/ssl/ssl_client2 nss_keylog=1 nss_keylog_file=tls_client_nss_key.log debug_level=3 nbio=0 server_name="localhost" tickets=0 server_addr=127.0.0.1 force_version=tls12 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity"
TLS 1.3 PSK ECDHE 本地连接测试。
服务端
programs/ssl/ssl_server2 debug_level=3 nbio=0 min_version=tls12 max_version=tls13 auth_mode="required" force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 tickets=1 server_addr=127.0.0.1 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity"
客户端
programs/ssl/ssl_client2 nss_keylog=1 nss_keylog_file=tls_client_nss_key.log debug_level=3 nbio=0 server_name="localhost" tickets=0 new_session_tickets=0 reco_mode=0 server_addr=127.0.0.1 server_addr=127.0.0.1 force_version=tls13 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity"
TLS 1.3 双向证书 ECDHE 本地连接测试。
服务端
programs/ssl/ssl_server2 debug_level=3 nbio=0 min_version=tls12 max_version=tls13 auth_mode="required" crt_file=framework/data_files/server5.crt key_file=framework/data_files/server5.key
客户端
programs/ssl/ssl_client2 nss_keylog=1 nss_keylog_file=tls_client_nss_key.log debug_level=3 nbio=0 server_name="localhost" tickets=0 new_session_tickets=0 reco_mode=0 server_addr=127.0.0.1 server_addr=127.0.0.1 force_version=tls13 auth_mode="required" crt_file=framework/data_files/server5.crt key_file=framework/data_files/server5.key key_opaque_algs=ecdsa-sign,none
TLS 1.3 开启 Session Tickets,进行会话恢复。
服务端
programs/ssl/ssl_server2 debug_level=3 nbio=0 min_version=tls12 max_version=tls13 auth_mode="required" force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 tickets=1 server_addr=127.0.0.1 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity"
客户端
programs/ssl/ssl_client2 nss_keylog=1 nss_keylog_file=tls_client_nss_key.log debug_level=3 nbio=0 server_name="localhost" tickets=1 new_session_tickets=1 reco_mode=0 server_addr=127.0.0.1 server_addr=127.0.0.1 force_version=tls13 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity" reconnect=1
TLS 1.3 0-RTT Early Data 测试。
服务端
programs/ssl/ssl_server2 debug_level=3 nbio=0 min_version=tls12 max_version=tls13 auth_mode="required" force_ciphersuite=TLS1-3-AES-128-GCM-SHA256 tickets=1 server_addr=127.0.0.1 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity" early_data=1 max_early_data_size=200
客户端
programs/ssl/ssl_client2 nss_keylog=1 nss_keylog_file=tls_client_nss_key.log debug_level=3 nbio=0 server_name="localhost" tickets=1 new_session_tickets=1 reco_mode=0 server_addr=127.0.0.1 server_addr=127.0.0.1 force_version=tls13 psk=6162636465666768696a6b6c6d6e6f70 psk_identity="PSK Identity" reconnect=1 early_data=1
2. 测试汇总
如下测试日志汇总通过此脚本自动生成 run_tls_test.sh。
测试用例 | 抓包文件 | 客户端日志 | 服务端日志 | Key Log |
---|---|---|---|---|
TLS 1.2 PSK KE | tls12_psk_capture.pcapng | tls12_psk_client.log | tls12_psk_server.log | tls12_psk_nss_key.log |
TLS 1.3 PSK ECDHE | tls13_psk_ecdhe_capture.pcapng | tls13_psk_ecdhe_client.log | tls13_psk_ecdhe_server.log | |
TLS 1.3 证书 ECDHE | tls13_cert_capture.pcapng | tls13_cert_client.log | tls13_cert_server.log | |
TLS 1.3 会话恢复 | tls13_session_resumption_capture.pcapng | tls13_session_resumption_client.log | tls13_session_resumption_server.log | |
TLS 1.3 0-RTT Early Data | tls13_0rtt_capture.pcapng | tls13_0rtt_client.log | tls13_0rtt_server.log |
3. VS Code 调试
为了方便 VS Code 单步调试走断点阅读代码,你需要编译配置优化和调试等级。
scripts/config.py --file include/mbedtls/mbedtls_config.h set MBEDTLS_SSL_EARLY_DATA
make
make CFLAGS='--coverage -g3 -O0' LDFLAGS='--coverage'
参考 Using C++ on Linux in VS Code 或者 Using Clang in Visual Studio Code 进行 VSCode 调试。
这里把如上所有测试命令配置 launch.json。直接保存在 根目录 .vscode 即可以调试。
4. Wireshark 抓包分析
因为 Wireshark 暂时还不支持 TLS 1.3 SSLKEYLOGFILE https://wiki.wireshark.org/TLS 解密。所以 TLS 1.3 信息有限。但是对于一些关键扩展和握手消息还是值得分析。
4.1 TLS 1.2 完整握手
因为 TLS 1.3 握手消息都有加密,这里解释 TLS 1.2 PSK KE 抓包分析。
- 使用 Wireshark 打开 tls12_psk_capture.pcapng 抓包文件。
- Wireshark -> Preferences... -> (Pre)-Master-Secret log filename -> tls12_psk_nss_key.log
- Wireshark -> Statistics -> Flow Graph
4.2 TLS 1.3 0-RTT Early Data
tls13_0rtt_capture.pcapng
- Early Data 指示
- Early Data 数据
4.3 会话恢复
tls13_session_resumption_capture.pcapng
- 完整握手初次会话,预设 PSK。
- 会话恢复,同时包含预设 PSK 和会话恢复的 PSK,前面一个为 Ticket。
还不快抢沙发