国密 SSL/TLS 客户端认证与双向认证协议详解
概述
GM/T 0024-2014《SSL VPN 技术规范》中的第 4 部分定义了国密 SSL/TLS 协议(简称 TLCP,TLS Cipher Protocol),它是中国自主设计的传输层安全协议,专门用于满足国密合规要求。
与 TLS 1.2 类似,TLCP 支持服务器单向认证和客户端双向认证两种模式。但国密双向认证有其独特之处:双证书体系(签名证书 + 加密证书)和国密密钥交换协议(SM2 密钥协商)。
协议基础
TLCP 与 TLS 1.2/1.3 的关系
TLCP 协议在设计上借鉴了 TLS 1.2 的框架,但在关键组件上做了国密化改造:
| 组件 | TLS 1.2 | TLS 1.3 | TLCP (GM/T 0024) |
|---|---|---|---|
| 密钥交换 | RSA/DHE/ECDHE | (EC)DHE | SM2 密钥协商 |
| 身份认证 | X.509 + RSA/ECDSA | X.509 + RSA/ECDSA/EdDSA | X.509 + SM2 |
| 对称加密 | AES-128/256 | AES-128/256 | SM4 |
| 哈希算法 | SHA-256/384 | SHA-256/384 | SM3 |
| 伪随机函数 | PRF(SHA-256) | HKDF(SHA-256) | HKDF-SM3 |
双证书体系
国密双向认证的核心特征是双证书体系:
CODE
┌─────────────────────────────────────────────┐
│ 签名证书 (Signature Cert) │
│ - 用途:服务器/客户端身份认证 │
│ - 算法:SM2 签名 │
│ - 密钥用法:digitalSignature │
│ - 有效期:通常 1-3 年 │
├─────────────────────────────────────────────┤
│ 加密证书 (Encryption Cert) │
│ - 用途:密钥交换(SM2 加密) │
│ - 算法:SM2 加密 │
│ - 密钥用法:keyEncipherment │
│ - 有效期:通常 1 年 │
└─────────────────────────────────────────────┘为什么需要双证书?
- 安全隔离:签名密钥和加密密钥分离,即使加密密钥泄露也不影响签名验证
- 密钥轮换:加密证书可以频繁轮换(如每年更换),签名证书保持稳定
- 合规要求:密评要求签名和加密使用不同的密钥对
握手流程详解
单向认证流程
TLCP 单向认证(仅服务器认证)的握手流程如下:
CODE
Client Server
|------ ClientHello ------------->|
| - 支持的密码套件 |
| - SM2SignWithSM3 |
| - ECDHE_SM4_CBC_SM3 |
| - ECDHE_SM4_GCM_SM3 |
| - 随机数 (ClientRandom) |
|<----- ServerHello --------------|
| - 选定的密码套件 |
| - 随机数 (ServerRandom) |
|<----- Certificate --------------|
| - 签名证书链 |
| - 加密证书 |
|<----- ServerKeyExchange --------|
| - SM2 加密证书公钥参数 |
| - 签名证书签名 |
|<----- CertificateRequest -------|
| - 可选:请求客户端证书 |
|<----- ServerHelloDone ----------|
|------ Certificate ------------->|
| - (可选)客户端签名证书 |
|------ ClientKeyExchange --------|
| - SM2 密钥交换预主密钥 |
|<----- CertificateVerify --------|
| - 签名证书的签名 |
|<----- Finished -----------------|
|------ Finished --------------->|
| - 握手消息的 MAC |双向认证流程
TLCP 双向认证(客户端 + 服务器认证)的握手流程在单向认证基础上增加了客户端认证环节:
CODE
Client Server
|------ ClientHello ------------->|
|<----- ServerHello --------------|
|<----- Certificate --------------|
|<----- ServerKeyExchange --------|
|<----- CertificateRequest -------|
| - 允许的签名算法列表 |
| - CA 证书列表 |
|<----- ServerHelloDone ----------|
|------ Certificate ------------->|
| - 客户端签名证书 |
| - 客户端加密证书 |
|------ ClientKeyExchange --------|
| - SM2 密钥交换预主密钥 |
|------ CertificateVerify --------|
| - 用签名证书对握手的签名 |
|<----- CertificateVerify --------|
| - 服务器用签名证书对握手的签名 |
|<----- Finished -----------------|
|------ Finished --------------->|消息格式
#### ServerKeyExchange 消息
CODE
struct {
select (KeyExchangeAlgorithm) {
case sm2:
Sm2ServerKeyExchange;
case rsa:
ServerRSAParams;
case ephemeral:
ServerECDHParams;
};
} ServerKeyExchange;
struct {
// SM2 加密证书参数
NamedCurve curve; // 国密 SM2 曲线
uint8 encrypted_pre_master[128]; // 加密的预主密钥
Signature signature; // 签名证书对握手消息的签名
} Sm2ServerKeyExchange;#### ClientKeyExchange 消息
CODE
struct {
select (KeyExchangeAlgorithm) {
case sm2:
Sm2ClientKeyExchange;
};
} ClientKeyExchange;
struct {
uint8 encrypted_pre_master[128]; // 用服务器加密证书公钥加密
} Sm2ClientKeyExchange;密钥派生
TLCP 使用 HKDF-SM3 进行密钥派生,与 TLS 1.3 的 HKDF-SHA256 类似:
CODE
pre_master_secret = SM2_Encrypt(encrypted_cert_public_key, random_bytes)
master_secret = HKDF-SM3(pre_master_secret, ClientRandom || ServerRandom)
key_block = master_secret + PRF(master_secret, "key expansion", random_bytes)密钥派生链:
CODE
pre_master_secret (SM2 加密得到)
↓
master_secret (HKDF-SM3)
↓
key_block (扩展密钥材料)
├── client_write_MAC_key (SM3 HMAC 密钥)
├── server_write_MAC_key
├── client_write_key (SM4 密钥)
├── server_write_key
├── client_write_IV (SM4-CBC 初始向量)
└── server_write_IV客户端证书验证
验证流程
服务器验证客户端证书的流程:
PYTHON
"""
TLCP 客户端证书验证流程
⚠️ 环境:需要国密 OpenSSL/Tongsuo 编译的 Python 环境
"""
import ssl
from gmssl import sm2, func
def verify_client_certificate(client_cert_pem: str, server_cert_pem: str) -> bool:
"""
验证客户端证书的有效性
验证步骤:
1. 证书链验证(签名证书 + 加密证书)
2. SM2 签名验证
3. 证书有效期检查
4. 证书吊销状态检查(CRL/OCSP)
"""
# 步骤 1:加载证书
# 步骤 2:验证签名证书链
# 步骤 3:验证加密证书链
# 步骤 4:SM2 签名验证
# 步骤 5:检查有效期和吊销状态
pass
def sm2_verify_signature(cert, handshake_hash, signature):
"""
SM2 签名验证
⚠️ 注意:verify() 期望 SM3 哈希字节,不是原始消息
"""
# 正确的验签方式
sm3_hash = sm2.sm3(handshake_hash)
return cert.verify(signature, sm3_hash)签名验证要点
TLCP 中客户端和服务器的 CertificateVerify 消息包含对握手消息的 SM2 签名。验证时需要:
- 收集所有握手消息的哈希(ClientHello 到 CertificateVerify 之前)
- 使用 SM3 计算握手消息摘要
- 用客户端签名证书的公钥验证 SM2 签名
PYTHON
"""
完整握手消息签名验证
"""
from gmssl import sm2, func
def compute_handshake_hash(handshake_messages: list) -> bytes:
"""计算所有握手消息的 SM3 哈希"""
sm3_ctx = func.sm3_create_ctx()
for msg in handshake_messages:
func.sm3_update(sm3_ctx, msg)
return func.sm3_finalize(sm3_ctx)
def verify_tlcp_certificate_verify(
client_sig_cert,
handshake_messages: list,
client_certificate_verify_sig: bytes
) -> bool:
"""
验证 TLCP CertificateVerify 消息
Args:
client_sig_cert: 客户端签名证书(包含 SM2 公钥)
handshake_messages: 所有握手消息列表
client_certificate_verify_sig: CertificateVerify 中的签名
Returns:
bool: 验证结果
"""
# 1. 计算握手消息的 SM3 哈希
handshake_hash = compute_handshake_hash(handshake_messages)
# 2. 使用签名证书公钥验证 SM2 签名
# ⚠️ verify() 期望 SM3 哈希字节,不是原始消息
sm2_crypt = sm2.CryptSM2(
client_sig_cert.public_key_hex(),
client_sig_cert.private_key_hex()
)
return sm2_crypt.verify(client_certificate_verify_sig, handshake_hash)安全属性分析
前向安全性
TLCP 支持前向安全性(Forward Secrecy),前提是使用 SM2 ephemeral 密钥交换:
| 密钥交换模式 | 前向安全 | 说明 |
|---|---|---|
| SM2 静态密钥交换 | ❌ | 服务器加密证书私钥泄露后可解密历史会话 |
| SM2 临时密钥交换 (Ephemeral) | ✅ | 每次会话生成新的临时密钥对 |
| SM2 静态 + SM4 会话密钥 | ❌ | 同上 |
中间人攻击防护
TLCP 通过以下方式防止中间人攻击:
- 双证书体系:签名证书用于身份认证,加密证书用于密钥交换,分离攻击面
- CertificateVerify:双方都用签名证书对握手消息签名,确保握手完整性
- 密钥确认:Finished 消息验证密钥一致性
已知安全分析
- SM2 密钥交换协议缺陷:TLCP 的 SM2 密钥交换协议(GM/T 0018.3)在实现中存在一些已知问题,主要体现在:
- 与 TLS 1.3 对比:TLCP 保留了 TLS 1.2 的灵活握手,但也继承了其复杂性。TLS 1.3 通过简化握手步骤和强制前向安全性解决了部分问题。
与 TLS 1.2/1.3 客户端认证的对比
认证机制对比
| 特性 | TLS 1.2 | TLS 1.3 | TLCP (GM/T 0024) |
|---|---|---|---|
| 客户端认证支持 | ✅ | ✅ | ✅ |
| 认证算法 | RSA/ECDSA/EdDSA | ECDSA/EdDSA | SM2 |
| 证书格式 | X.509 | X.509 | X.509 + 双证书 |
| 密钥交换 | RSA/DHE/ECDHE | (EC)DHE | SM2 密钥协商 |
| 前向安全 | 可选 | 强制 | 可选(需配置) |
| 握手轮次 | 2 RTT | 1 RTT (0-RTT 可选) | 2 RTT |
| 签名算法 | SHA-256/384 | SHA-256/384/256 | SM3 |
实现复杂度对比
CODE
TLS 1.2 客户端认证:
1. ClientHello -> ServerHello
2. Server 发送 CertificateRequest
3. Client 发送 Certificate + CertificateVerify
4. 完成握手
TLS 1.3 客户端认证:
1. ClientHello -> ServerHello + Certificate
2. Server 发送 CertificateRequest (在 HelloRetryRequest 中)
3. Client 发送 Certificate + CertificateVerify
4. 完成握手 (1 RTT)
TLCP 双向认证:
1. ClientHello -> ServerHello
2. Server 发送双证书链 + ServerKeyExchange
3. Server 发送 CertificateRequest
4. Client 发送签名证书 + 加密证书
5. ClientKeyExchange (SM2 密钥交换)
6. ClientCertificateVerify + ServerCertificateVerify
7. 完成握手相关实践
- 国密 TLS 部署实战 — Nginx 国密 TLS 配置指南
- 国密 HTTPS 踩坑实录 — 生产环境中的常见问题
- 双证书 TLS 部署 — 签名证书 + 加密证书完整配置
- 国密双证书自动化 — 基于 acme.sh 的自动化方案
参考来源
- GM/T 0024-2014《SSL VPN 技术规范 第4部分:国密SSL协议》: https://www.oscca.gov.cn/
- GM/T 0018.3-2010《密码模块安全技术要求 第3部分:密钥交换协议》: https://www.oscca.gov.cn/
- RFC 5246 TLS 1.2 Protocol: https://datatracker.ietf.org/doc/html/rfc5246
- RFC 8446 TLS 1.3 Protocol: https://datatracker.ietf.org/doc/html/rfc8446
- RFC 8998 TLS 1.3 with GCM/CCM Cipher Suites: https://datatracker.ietf.org/doc/html/rfc8998
- 国密 SSL/TLS 协议白皮书: https://www.oscca.gov.cn/