GM/T 0129-2023 SSH 密码协议实战:基于 Tongsuo 部署国密 OpenSSH 完整指南
前言
SSH(Secure Shell)是远程服务器管理、文件传输和端口转发的事实标准。等保 2.0 和密评要求远程管理通道必须采用国密算法,但标准 OpenSSH 8.x 只支持国际算法(curve25519、aes256-ctr、hmac-sha2)。
GM/T 0129-2023《SSH 密码协议规范》于 2023-12-04 发布、2024-06-01 实施,定义了 SSH 协议中国密算法套件的协商机制、密钥交换、身份认证和会话加密。本文基于该标准原文,结合阿里巴巴 Tongsuo 项目的 OpenSSH 补丁,给出国密 SSH 的完整部署方案。
标准概述
GM/T 0129-2023 的核心设计目标是在不改变 SSH 协议框架的前提下,将算法替换为国密算法族。它沿用了 RFC 4251-4253 的协议架构(SSH Architecture、Authentication、Transport Layer),仅在算法协商和密钥派生两个环节做了国密适配。
配套标准体系
| 标准编号 | 名称 | 在 SSH 中的角色 |
|---|---|---|
| GM/T 0003.3-2012 | SM2 密钥交换协议 | 密钥协商(前向保密) |
| GM/T 0003.2-2012 | SM2 数字签名算法 | 服务器身份认证 |
| GM/T 0004-2012 | SM3 密码杂凑算法 | KDF、HMAC |
| GM/T 0002-2012 | SM4 分组密码算法 | 会话数据加密 |
| GM/T 0009-2023 | SM2 密码算法使用规范 | 密钥格式定义 |
算法套件
GM/T 0129-2023 定义了完整的国密算法套件,覆盖 SSH 协议的四个层次:
| 层次 | 算法标识 | 说明 | 推荐度 |
|---|---|---|---|
| 密钥交换 | sm2-exch-sm3 | SM2 密钥交换 + SM3 KDF | ★★★★★ |
| 密钥交换 | sm2-exch | SM2 密钥交换(基础版) | ★★★☆☆ |
| 服务器认证 | sm2-sign-sm3 | SM2 签名 + SM3 哈希 | ★★★★★ |
| 对称加密 | sm4-ctr | SM4 CTR 模式(流式加密,无需填充) | ★★★★★ |
| 对称加密 | sm4-cbc | SM4 CBC 模式(兼容旧实现) | ★★★☆☆ |
| MAC | hmac-sm3 | HMAC-SM3(256-bit 认证标签) | ★★★★★ |
注意:sm4-ctr 是首选加密模式。CTR 模式将分组密码转换为流密码,天然支持任意长度数据,无需填充,避免了 CBC 模式的填充 Oracle 攻击面。
握手流程
GM/T 0129-2023 的握手在 SSH 协议框架内完成,关键消息如下:
客户端 服务器
| |
|---- SSH_MSG_KEXINIT (算法列表) ------------->|
|<- SSH_MSG_KEXINIT (算法列表) ----------------|
| |
|---- SSH_MSG_KEX_SM2_INIT ------------------>|
| (R_client || random_client) |
| |
|<- SSH_MSG_KEX_SM2_REPLY --------------------|
| (server_pubkey || R_server || signature) |
| |
|==== 会话密钥派生(双方独立计算)====|关键消息格式
SSH_MSG_KEX_SM2_INIT(byte = 30):
byte SSH_MSG_KEX_SM2_INIT
string SM2_POINT(R_client) ; 客户端临时公钥(非压缩格式 0x04||x||y)
string random_client ; 客户端随机数(32 字节)SSH_MSG_KEX_SM2_REPLY(byte = 31):
byte SSH_MSG_KEX_SM2_REPLY
string server_public_key ; 服务器长期 SM2 公钥
string SM2_POINT(R_server) ; 服务器临时公钥
string server_signature ; SM2-SM3 签名共享密钥派生
双方从共享点 S = (Sx, Sy) 派生会话密钥:
# 简化示意(实际遵循 GM/T 0003.3-2012 第 6.1 节)
K = KDF(Sx || Sy || session_id, key_length)派生的密钥材料按顺序分割为 6 段:
| 序号 | 密钥用途 | 长度 |
|---|---|---|
| 1 | IV (client → server) | 16 字节 |
| 2 | Encryption Key (client → server) | 16 字节 |
| 3 | IV (server → client) | 16 字节 |
| 4 | Encryption Key (server → client) | 16 字节 |
| 5 | Integrity Key (client → server) | 32 字节 |
| 6 | Integrity Key (server → client) | 32 字节 |
服务器签名构造
服务器对握手过程进行签名,防止中间人攻击:
Z = session_id || SSH_MSG_KEXINIT || client_kexinit_payload
|| server_kexinit_payload || server_host_key_blob
|| R_client_payload || R_server_payload
|| shared_secret_hex签名算法为 sm2-sign-sm3,即对 Z 做 SM3 哈希后使用 SM2 签名。
核心算法验证
SM4-CTR 加密验证
SM4-CTR 是 GM/T 0129-2023 推荐的会话加密模式。以下代码验证加解密的正确性:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
# 生成随机密钥和 IV
key = os.urandom(16)
iv = os.urandom(16)
# 加密
cipher = Cipher(algorithms.SM4(key), modes.CTR(iv), backend=default_backend())
encryptor = cipher.encryptor()
data = b'Hello, GM SSH test data!!'
ct = encryptor.update(data) + encryptor.finalize()
# 解密(使用相同 key 和 iv)
cipher2 = Cipher(algorithms.SM4(key), modes.CTR(iv), backend=default_backend())
decryptor = cipher2.decryptor()
pt = decryptor.update(ct) + decryptor.finalize()
assert pt == data, "SM4-CTR 加解密失败"
print(f"明文: {data.hex()}")
print(f"密文: {ct.hex()}")
print(f"解密: {pt.hex()}")运行输出:
明文: 48656c6c6f2c20474d20535348207465737420646174612121
密文: dd89efa1123c2beff77b5bc9bc7d18a8d4b02ffc3332cbce0c
解密: 48656c6c6f2c20474d20535348207465737420646174612121SM3-KDF 密钥派生验证
以下代码演示从共享秘密派生会话密钥的 KDF 流程(基于 SM3 的简化实现):
import hashlib
import struct
def sm3_kdf(z: bytes, key_length: int) -> bytes:
"""
基于 SM3 的 KDF(GM/T 0003.3-2012 附录 A)
Z: 共享秘密(Sx || Sy || session_id)
key_length: 需要的密钥材料长度(字节)
"""
ct = 0x00000001
key = b""
while len(key) < key_length:
# SM3(ct || Z)
h = hashlib.new("sm3", struct.pack(">I", ct) + z)
key += h.digest()
ct += 1
return key[:key_length]
# 模拟:从共享点 S 和 session_id 派生密钥
# 实际场景中 Sx/Sy 是 SM2 密钥交换输出的 32 字节坐标
Sx = bytes.fromhex("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" * 2)
Sy = bytes.fromhex("f6e5d4c3b2a1f0e1d2c3b4a596877869" * 2)
session_id = bytes.fromhex("0102030405060708090a0b0c0d0e0f10")
# 派生 128 字节密钥材料(6 段密钥)
key_material = sm3_kdf(Sx + Sy + session_id, 128)
iv_c2s = key_material[0:16]
enc_c2s = key_material[16:32]
iv_s2c = key_material[32:48]
enc_s2c = key_material[48:64]
mac_c2s = key_material[64:96]
mac_s2c = key_material[96:128]
print(f"Key material length: {len(key_material)}")
print(f"Key material hex: {key_material.hex()}")运行输出:
Key material length: 128
Key material hex: baf91f257f0aae697abfb7cb6e92f347a8799b4fb8fa7e822a6104118a7530b06e5fdf7012acb06a6bf713b40327030c45d2c0ea4122fc6f19f9f74caba3e8d1763947ff743422d7c55bc98cafc4af043d72a333576c79f1ffcae785e4aaedd6115917f6e35ac243605a45957aa853d069d11c2f9954e3bb0c4aec36272df0f9部署实战
环境要求
| 组件 | 推荐版本 | 获取方式 |
|---|---|---|
| Tongsuo | 8.4.x | https://github.com/Tongsuo-Project/Tongsuo |
| OpenSSH | 9.x + 国密补丁 | https://github.com/Tongsuo-Project/OpenSSH |
| 操作系统 | CentOS 7+/Ubuntu 20.04+ | — |
编译部署 Tongsuo
# 克隆并编译 Tongsuo
git clone https://github.com/Tongsuo-Project/Tongsuo.git
cd Tongsuo
./config --prefix=/opt/tongsuo --libdir=lib
make -j$(nproc)
sudo make install
# 验证安装
/opt/tongsuo/bin/openssl version
# 预期输出:Tongsuo 8.x.x编译部署国密 OpenSSH
# 克隆国密 OpenSSH
git clone https://github.com/Tongsuo-Project/OpenSSH.git
cd OpenSSH
autoreconf
# 配置:指向 Tongsuo 的 SSL 库
./configure \
--prefix=/usr \
--sysconfdir=/etc/ssh \
--with-ssl-dir=/opt/tongsuo \
--with-privsep-path=/var/empty/sshd \
--with-privsep-user=sshd
make -j$(nproc)
sudo make install生成国密主机密钥
# 生成 SM2 主机密钥
sudo /opt/tongsuo/bin/openssl genpkey -algorithm SM2 \
-out /etc/ssh/ssh_host_sm2_key
sudo chmod 600 /etc/ssh/ssh_host_sm2_key
# 查看公钥
sudo /opt/tongsuo/bin/openssl pkey -in /etc/ssh/ssh_host_sm2_key \
-pubout -out /etc/ssh/ssh_host_sm2_key.pubsshd_config 配置
# 密钥交换算法(优先国密,兼容国际)
KexAlgorithms sm2-exch-sm3,sm2-exch,curve25519-sha256
# 服务器认证算法
HostKeyAlgorithms sm2-sign-sm3,rsa-sha2-512
# 对称加密算法
Ciphers sm4-ctr,aes256-ctr
# MAC 算法
MACs hmac-sm3,hmac-sha2-256
# 国密主机密钥
HostKey /etc/ssh/ssh_host_sm2_key
# 安全加固
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3⚠️ 密评合规提示:如果目标是通过密评,建议移除所有国际算法,仅保留国密算法。保留国际算法会导致"算法协商不一致"扣分。生产环境中可通过独立端口(如 2222)提供纯国密 SSH,端口 22 保留国际算法兼容。
验证部署
# 重启 sshd
sudo systemctl restart sshd
# 查看支持的算法列表
ssh -Q kex server@localhost | grep sm2
ssh -Q cipher server@localhost | grep sm4
ssh -Q mac server@localhost | grep sm3
# 客户端连接测试
ssh -o KexAlgorithms=sm2-exch-sm3 \
-o Ciphers=sm4-ctr \
-o MACs=hmac-sm3 \
user@your-server生产注意事项
1. 前向保密
SM2 密钥交换通过临时密钥对 (rA, rB) 提供前向保密:每次会话结束后临时密钥即被销毁,即使服务器长期私钥泄露,攻击者也无法从截获的网络流量中恢复会话密钥。这与 ECDHE 的前向保密机制类似。
2. 降级攻击防护
如果 KexAlgorithms 同时包含国密和国际算法,攻击者可通过中间人篡改 KEXINIT 消息迫使双方降级到较弱的算法。建议在生产环境中:
- 管理网段仅开放国密端口
- 在
sshd_config中禁用国际算法 - 使用
ssh -Q定期审计算法列表
3. 兼容性处理
| 客户端类型 | 兼容方案 |
|---|---|
| Linux + 国密 OpenSSH | 原生支持 sm2-exch-sm3 |
| PuTTY / Windows | 需使用 Tongsuo 编译的 PuTTY 或 OpenSSH for Windows |
| 跳板机/堡垒机 | 齐治、绿盟等厂商已内置国密 SSH 服务端 |
| Ansible/SaltStack | 需配置 ansible_ssh_common_args 指定国密算法 |
4. 密钥轮换
国密主机密钥建议每年轮换一次:
# 生成新密钥
sudo /opt/tongsuo/bin/openssl genpkey -algorithm SM2 \
-out /etc/ssh/ssh_host_sm2_key_new
# 备份旧密钥并替换
sudo mv /etc/ssh/ssh_host_sm2_key /etc/ssh/ssh_host_sm2_key_old
sudo mv /etc/ssh/ssh_host_sm2_key_new /etc/ssh/ssh_host_sm2_key
sudo chmod 600 /etc/ssh/ssh_host_sm2_key
# 重启 sshd
sudo systemctl restart sshd
# 通知所有客户端更新 known_hosts5. 审计日志
# 查看所有 SSH 登录记录
sudo grep "sshd" /var/log/secure | tail -50
# 监控失败的密钥交换
sudo grep "Unable to negotiate" /var/log/secure
# 监控国密连接
sudo grep "sm2-exch-sm3" /var/log/secure已知限制
- 公钥格式未国际标准化:标准 OpenSSH 8.x 不理解
ssh-sm2公钥格式,无法直接复制国密公钥到authorized_keys - 证书链复杂性:SM2 数字证书遵循 GM/T 0015-2023,国际 CA 不签发,需自建国密 PKI
- KDF 缺少域分离:与 HKDF 相比,GM/T 0129-2023 的 KDF 缺少 salt 和 info 扩展,多会话场景下需确保 session_id 唯一性
- 后量子迁移路径不明确:SM2 基于 ECDLP,可被量子计算机破解。目前 NIST PQC 标准化的 ML-KEM(Kyber)和 SLH-DSA 尚未在 SSH 国密标准中定义
总结
GM/T 0129-2023 为国密 SSH 提供了完整的协议规范,核心要点:
- 算法套件:
sm2-exch-sm3(密钥交换)+sm4-ctr(加密)+hmac-sm3(完整性) - 前向保密:通过 SM2 临时密钥对实现,每次会话独立
- 部署路径:Tongsuo(国密 OpenSSL)+ OpenSSH 国密补丁
- 密评合规:纯国密配置可通过密评,保留国际算法会被扣分
- 生产建议:独立端口部署、定期密钥轮换、审计日志监控
参考来源
- GM/T 0129-2023《SSH 密码协议规范》(国家密码管理局,2023-12 发布,2024-06-01 实施)
- GM/T 0003.3-2012《SM2 椭圆曲线公钥密码算法 第 3 部分:密钥交换协议》
- GM/T 0002-2012《SM4 分组密码算法》
- RFC 4251-4253(SSH Protocol Architecture, Authentication, Transport Layer)
- Tongsuo 项目:https://github.com/Tongsuo-Project/Tongsuo
- GM/T 0009-2023《SM2 密码算法使用规范》