本文是利用AI生成的的技术解析与实现示例。所发布的代码与步骤基于第一手资料构建,但未经作者在真实设备上进行运行验证。根据环境和版本的不同,实际运行表现可能会有所差异。
Secure Shell(SSH)协议多年来一直使用传统的椭圆曲线密码学(ECDH)等密钥交换方式。然而,如果未来实现具备足够性能的量子计算机,以往的方式可能会面临安全性受到威胁的风险。针对这些担忧,迫切需要采取对策来防范攻击者保存加密通信、随后试图用量子计算机进行解密的“现在截获,以后解密(Harvest now, decrypt later)”攻击。
为了应对这些挑战,RFC 10042 定义了一套规范,将抗量子计算密码——基于模块格的密钥封装机制(ML-KEM)与传统的椭圆曲线迪菲-赫尔曼(ECDH)密钥交换相结合,引入到 SSH 传输层协议中,实现了“Post-Quantum/Traditional(PQ/T)Hybrid key exchange”。 基于第一手资料,本文将对这种混合密钥交换的抽象结构、消息交互、具体的变体方式以及共享秘密的生成方法进行整理。
1. 背景与混合密钥交换的基本概念
在传统的 SSH 密钥交换中,一直使用基于离散对数问题困难性的 ECDH 方式(如 RFC 5656 和 RFC 8731 等)。与之相对,第一手资料定义了将 NIST 标准化的 ML-KEM 与经典算法相结合的 PQ/T 混合密钥交换。
混合方式中各个独立密钥交换方案的安全性是相互独立的。也就是说,PQ/T 混合密钥交换方案被设计为能够确保其安全性等同于或高于其组成部分中最安全的密钥交换方案。
在 NIST 的框架下,KEM(Key-Encapsulation Mechanism)主要由以下三种算法构成:
KeyGen() -> (pk, sk): 生成公钥
pk和私钥sk的概率算法。Encaps(pk) -> (ct, ss): 以公钥
pk作为输入,输出密文ct和共享秘密ss的概率算法。Decaps(sk, ct) -> ss: 以私钥
sk和密文ct作为输入,输出共享秘密ss(或错误值)的算法。
KEM 的主要安全需求包括针对自适应选择密文攻击的不可区分性(IND-CCA2)以及针对选择明文攻击的不可区分性(IND-CPA)。RFC 10042 中采用的 ML-KEM 是在 2024 年被标准化为 FIPS 203 的算法,作为参数,它存在 ML-KEM-512、ML-KEM-768 和 ML-KEM-1024 三种变体。
2. 通信序列与消息结构的规范
在 PQ/T 混合密钥交换中,定义了新的消息来代替传统的 SSH_MSG_KEXDH_INIT 或 SSH_MSG_KEX_ECDH_INIT。
以下是客户端和服务器之间进行的 PQ/T 混合密钥交换消息流的整体概览。
sequenceDiagram
participant Client as SSH Client
participant Server as SSH Server
Client->>Server: SSH_MSG_KEX_HYBRID_INIT (C_INIT = C_PK2 || C_PK1)
Server->>Client: SSH_MSG_KEX_HYBRID_REPLY (K_S, S_REPLY = S_CT2 || S_PK1, Signature)
客户端发送的消息: SSH_MSG_KEX_HYBRID_INIT
使用分配了消息编号 30 的 SSH_MSG_KEX_HYBRID_INIT,客户端发送以下字符串数据:
byte:
SSH_MSG_KEX_HYBRID_INIT(30)string:
C_INIT
在此,C_INIT 是两个临时公钥的连接值,构成为 C_INIT = C_PK2 || C_PK1(其中 || 表示连接)。
C_PK1: 传统经典密钥交换(如 ECDH 等)的临时公钥。C_PK2: 对应的后量子 KEM 的KeyGen输出的公钥pk。
服务器回复的消息: SSH_MSG_KEX_HYBRID_REPLY
使用分配了消息编号 31 的 SSH_MSG_KEX_HYBRID_REPLY,服务器返回以下数据:
byte:
SSH_MSG_KEX_HYBRID_REPLY(31)string:
K_S(服务器的公有主机密钥)string:
S_REPLYstring: 对交换哈希的签名
在此,S_REPLY 构成为 S_REPLY = S_CT2 || S_PK1。
S_PK1: 传统的临时 ECDH 服务器公钥。S_CT2: 服务器侧的Encaps算法生成的 KEM 密文ct(对客户端公钥C_PK2进行了秘密封装的内容)。
3. 验证处理与安全性保障
在消息的发送和接收过程中,必须进行严格的检查,以防止针对长度的攻击(如长度扩展攻击等)。
服务器侧的检查: 服务器在生成
S_CT2之前,必须(MUST)确认接收到的C_INIT的长度与商定的协商方式中各个公钥(C_PK1和C_PK2)的预期长度总和相匹配。此外,还必须执行 FIPS 203 中定义的封装密钥检查。如果失败,则使用SSH_MSG_DISCONNECT(原因:SSH_DISCONNECT_KEY_EXCHANGE_FAILED)中断连接。客户端侧的检查: 客户端在执行解密(解封装)之前,必须确认
S_REPLY的长度与传统公钥S_PK1和 ML-KEM 密文S_CT2的预期长度总和相匹配。如果发生长度不匹配或解密错误,同样会通过断开连接消息中止连接。
4. 定义的具体混合方式
在 RFC 10042 中,以下三种组合的具体方式名称已在 IANA 注册。这些名称将用于 SSH_MSG_KEXINIT 中。
mlkem768nistp256-sha256传统密钥交换: NIST P-256 曲线(
ecdh-sha2-nistp256)后量子 KEM: ML-KEM-768
哈希函数: SHA-256
mlkem1024nistp384-sha384传统密钥交换: NIST P-384 曲线(
ecdh-sha2-nistp384)后量子 KEM: ML-KEM-1024
哈希函数: SHA-384
mlkem768x25519-sha256传统密钥交换: Curve25519(
curve25519-sha256)后量子 KEM: ML-KEM-768
哈希函数: SHA-256
5. 共享秘密(Shared Secret K)的导出方法
在混合密钥交换中,会确立两个共享秘密:从经典 ECDH 获得的共享秘密 K_CL,以及从 ML-KEM 获得的后量子共享秘密 K_PQ。
最终的共享秘密 K 计算为这两个共享秘密连接后的哈希值。
$$K = text{HASH}(K_{text{PQ}} parallel K_{text{CL}})$$
虽然在 TLS 1.3(RFC 9954)等协议中直接将连接后的秘密输入到密钥调度中,但本规范采取了符合 SSH 密钥导出方法的方式,即在连接后通过一次哈希函数进行处理。
此外,传统方式中 ECDH 的共享秘密编码为整数(mpint),而本规范中则作为固定长度的大端字节数组处理(Curve25519 和 secp256r1 为 32 字节,secp384r1 为 48 字节)。
6. 密钥导出与哈希计算(H)的输入要素
密钥交换方法的哈希函数 $H$ 是通过对连接了以下要素的数据进行哈希计算来获取的:
客户端标识字符串
V_C服务器标识字符串
V_S客户端的
SSH_MSG_KEXINIT有效载荷I_C服务器的
SSH_MSG_KEXINIT有效载荷I_S服务器的公有主机密钥
K_S客户端消息字符串
C_INIT服务器消息字符串
S_REPLYSSH 共享秘密
K
加密密钥将根据此哈希结果 H 和共享秘密 K 导出。需要说明的是,关于消息大小,前提是在传统 SSH 规范(RFC 4253)规定的最小支持有效载荷长度(32768 字节以下)的范围内进行操作。

コメント