SM9 标识密码实战:无证书加密与身份认证部署
前言
传统 PKI 体系需要 CA 签发证书来绑定"身份"和"公钥",证书管理(申请、签发、吊销、续期)是整个体系中最繁琐的环节。SM9 标识密码算法提供了一种完全不同的思路:用身份本身作为公钥,无需证书。
SM9 是国家密码管理局发布的标识密码算法标准,基于双线性对(Bilinear Pairing)数学构造。它包含三个核心协议:
- IBE(Identity-Based Encryption):基于身份的加密
- IBS(Identity-Based Signature):基于身份的签名
- IBA(Identity-Based Authenticated Key Exchange):基于身份的认证密钥协商
一、SM9 的核心思想
1.1 从 PKI 到 IBE
传统 PKI 的公钥分发问题:
Alice 要给 Bob 发加密消息,需要:
- 从 CA 获取 Bob 的证书
- 验证证书链(签名、有效期、吊销状态)
- 提取证书中的公钥
- 用公钥加密
Alice 只需要知道 Bob 的标识(如邮箱 bob@example.com),就可以直接加密。
PKI: 明文 → 获取证书 → 验证证书 → 提取公钥 → 加密
SM9: 明文 → 用标识加密1.2 系统架构
┌─────────────────────────────────────────┐
│ KGC (密钥生成中心) │
│ │
│ 主密钥(Master Secret) │
│ │ │
│ ▼ │
│ 为每个用户生成私钥 │
│ d_ID = s · H₁(ID) │
└──────────────┬──────────────────────────┘
│ 安全信道分发私钥
┌───────┴───────┐
▼ ▼
Alice Bob
私钥 d_Alice 私钥 d_BobKGC 是 SM9 的核心组件,负责:
- 生成系统主密钥
- 根据用户标识生成用户私钥
- 公开系统参数
1.3 安全假设
SM9 的安全性基于双线性 Diffie-Hellman(BDH)假设:
给定 P, aP, bP, cP(其中 a, b, c 是秘密值),计算 ê(P, P)^(abc) 是计算不可行的。
这与 SM2 基于的椭圆曲线离散对数问题(ECDLP)不同,但同样被认为在经典计算模型下是安全的。
二、数学基础(简要)
2.1 双线性对
SM9 定义在椭圆曲线的循环子群上,使用 Weil 对或 Tate 对构造双线性映射:
ê: G₁ × G₂ → G_T其中 G₁ 和 G₂ 是椭圆曲线上的加法循环群,G_T 是乘法循环群。双线性性质:
ê(aP, bQ) = ê(P, Q)^(ab) = ê(bP, aQ)2.2 系统参数
| 参数 | 说明 | 大小 |
|---|---|---|
| p | 有限域素数 | 256 位 |
| E(F_p) | 椭圆曲线方程 y² = x³ + b | - |
| N | 基点 P 的阶(素数) | 256 位 |
| P₁ | G₁ 的生成元(F_p 上) | 512 位 |
| P₂ | G₂ 的生成元(F_p² 上) | 1024 位 |
| s | KGC 主密钥(秘密) | 256 位 |
| P_pub = s·P₂ | KGC 公钥(公开) | 1024 位 |
三、SM9 三个核心协议
3.1 SM9 数字签名(IBS)
签名者用 KGC 颁发的私钥 d_A 签名,验证者用签名者的标识(如邮箱)直接验证。
签名过程:
输入:消息 M,签名者私钥 d_A
1. 计算 g = ê(P_pub, P₁)^w (w 是随机数派生)
2. 计算 r = H(M || w) mod N
3. 输出签名 (r, S),其中 S = d_A · (1/(r + s)) 的哈希验证过程:
输入:消息 M,签名 (r, S),签名者标识 ID_A
1. 计算 h = H(ID_A || hid) mod N
2. 计算 P = [h]P₂ + P_pub
3. 验证 ê(S, P) · ê(P_pub, P₁)^r 是否等于 w3.2 SM9 加密(IBE)
加密者用接收方标识加密,接收方用 KGC 颁发的私钥解密。
加密过程:
输入:明文 M,接收方标识 ID_B
1. 计算 h = H(ID_B || hid) mod N
2. 计算 Q_B = [h]P₁ + P_pub (接收方公钥)
3. 选择随机数 r
4. 计算 C₁ = [r]Q_B
5. 计算 g = ê(Q_B, P_pub)^r
6. 计算 C₂ = M ⊕ H(g || C₁)
7. 输出密文 (C₁, C₂)解密过程:
输入:密文 (C₁, C₂),接收方私钥 d_B
1. 计算 w = ê(C₁, d_B)
2. 计算 M = C₂ ⊕ H(w || C₁)
3. 输出明文 M3.3 SM9 密钥协商(IBA)
双方通过标识协商共享密钥,无需证书交换。
四、KGC 密钥生成中心搭建
4.1 环境准备
# 安装依赖
pip install gmssl charm-crypto
# 或使用纯 Python 实现(仅用于学习)
pip install py-pairing4.2 KGC 初始化
#!/usr/bin/env python3
"""
KGC 密钥生成中心初始化
基于 GM/T 0044-2016 标识密码算法标准
"""
import os
import hashlib
import json
from pathlib import Path
class SM9KGC:
"""SM9 密钥生成中心"""
def __init__(self, kgc_dir: str = "/opt/sm9-kgc"):
self.kgc_dir = Path(kgc_dir)
self.kgc_dir.mkdir(parents=True, exist_ok=True)
# 系统参数文件
self.master_key_file = self.kgc_dir / "master.key"
self.public_params_file = self.kgc_dir / "public_params.json"
self.user_keys_dir = self.kgc_dir / "user_keys"
self.user_keys_dir.mkdir(exist_ok=True)
def initialize(self):
"""初始化 KGC:生成主密钥和系统参数"""
if self.master_key_file.exists():
print("⚠️ 主密钥已存在,跳过初始化")
return
# 生成主密钥(256 位随机数)
master_secret = os.urandom(32)
# 安全存储主密钥
self.master_key_file.write_bytes(master_secret)
os.chmod(self.master_key_file, 0o400)
# 生成系统公开参数
# 注意:实际参数应符合 GM/T 0044-2016 标准
public_params = {
"algorithm": "SM9",
"curve": "SM9-256",
"hash": "SM3",
# P_pub = master_secret · P₂(实际计算需要椭圆曲线库)
"P_pub": "placeholder_for_actual_pub_key",
}
self.public_params_file.write_text(
json.dumps(public_params, indent=2)
)
print("✅ KGC 初始化完成")
print(f" 主密钥: {self.master_key_file}")
print(f" 系统参数: {self.public_params_file}")
def generate_user_key(self, identity: str) -> dict:
"""为用户生成私钥"""
if not self.master_key_file.exists():
raise RuntimeError("KGC 未初始化")
master_secret = self.master_key_file.read_bytes()
# 检查用户私钥是否已存在
user_key_file = self.user_keys_dir / f"{identity.replace('@', '_at_')}.key"
if user_key_file.exists():
print(f"⚠️ 用户 {identity} 的私钥已存在")
return json.loads(user_key_file.read_text())
# 计算用户私钥
# d_ID = s · H₁(ID || hid) mod N
# 这里使用简化的派生方式,实际应符合 GM/T 0044-2016
hid = 0x02 # 签名私钥生成标识符
identity_hash = hashlib.new('sm3', f"{identity}:{hid}".encode()).digest()
# 私钥派生(简化版,实际需要使用椭圆曲线标量乘法)
from gmssl import sm3, func
h1 = sm3.sm3_hash(func.bytes_to_list(identity_hash))
user_key = {
"identity": identity,
"private_key": h1.hex(),
"created_at": str(os.times().system),
}
# 安全存储用户私钥
user_key_file.write_text(json.dumps(user_key, indent=2))
os.chmod(user_key_file, 0o400)
print(f"✅ 已为用户 {identity} 生成私钥")
return user_key
def get_public_params(self) -> dict:
"""获取系统公开参数"""
return json.loads(self.public_params_file.read_text())
# 使用示例
if __name__ == "__main__":
kgc = SM9KGC("/opt/sm9-kgc")
kgc.initialize()
# 为用户生成私钥
kgc.generate_user_key("alice@example.com")
kgc.generate_user_key("bob@example.com")
# 获取公开参数
params = kgc.get_public_params()
print(f"系统参数: {json.dumps(params, indent=2)}")4.3 KGC 的安全要求
KGC 是整个 SM9 体系中最关键的安全节点:
安全要求:
主密钥保护:
- 存储在 HSM(硬件安全模块)中
- 使用 M-of-N 分割知识方案
- 定期审计密钥使用记录
私钥分发:
- 使用安全信道(如 TLS + 客户端认证)分发用户私钥
- 支持私钥的加密传输
- 记录所有私钥分发日志
高可用:
- KGC 应部署为主从模式
- 主密钥的备份应使用秘密共享(Shamir's Secret Sharing)
- 定期进行灾难恢复演练五、SM9 签名和加密 Python 实现
5.1 基于 gmssl 的 SM9 签名
#!/usr/bin/env python3
"""
SM9 数字签名示例
"""
from gmssl import sm9, sm3, func
def sm9_sign_example():
"""SM9 签名和验证示例"""
# 1. KGC 初始化
kgc = sm9.KGC()
kgc.setup() # 生成主密钥和系统参数
# 2. 为用户生成私钥
signer_id = "alice@example.com"
private_key = kgc.generate_private_key(signer_id)
# 3. 签名
message = "这是一条需要签名的消息".encode('utf-8')
signature = sm9.sign(private_key, message)
print(f"消息: {message.decode()}")
print(f"签名: {signature.hex()}")
# 4. 验证
is_valid = sm9.verify(
signer_id, # 签名者标识
message,
signature,
kgc.master_public_key # KGC 公钥
)
print(f"验证结果: {'✅ 有效' if is_valid else '❌ 无效'}")
if __name__ == "__main__":
sm9_sign_example()5.2 SM9 加密通信
#!/usr/bin/env python3
"""
SM9 基于身份的加密通信示例
"""
from gmssl import sm9
def sm9_encrypt_example():
"""SM9 加密和解密示例"""
# 1. KGC 初始化
kgc = sm9.KGC()
kgc.setup()
# 2. 为通信双方生成私钥
alice_id = "alice@example.com"
bob_id = "bob@example.com"
alice_private_key = kgc.generate_private_key(alice_id)
bob_private_key = kgc.generate_private_key(bob_id)
# 3. Alice 用 Bob 的标识加密消息
plaintext = "你好 Bob,这是加密消息".encode('utf-8')
ciphertext = sm9.encrypt(
bob_id, # 接收方标识(即公钥)
plaintext,
kgc.master_public_key
)
print(f"明文: {plaintext.decode()}")
print(f"密文: {ciphertext.hex()[:64]}...")
# 4. Bob 用自己的私钥解密
decrypted = sm9.decrypt(bob_private_key, ciphertext)
print(f"解密: {decrypted.decode()}")
assert decrypted == plaintext, "解密失败!"
print("✅ 加解密验证通过")
if __name__ == "__main__":
sm9_encrypt_example()5.3 SM9 密钥协商
#!/usr/bin/env python3
"""
SM9 基于身份的密钥协商示例
"""
from gmssl import sm9
import os
def sm9_key_exchange_example():
"""SM9 密钥协商示例"""
kgc = sm9.KGC()
kgc.setup()
alice_id = "alice@example.com"
bob_id = "bob@example.com"
alice_private_key = kgc.generate_private_key(alice_id)
bob_private_key = kgc.generate_private_key(bob_id)
# Alice 发起密钥协商
alice_random = os.urandom(32)
alice_token = sm9.key_exchange_init(
alice_private_key, bob_id,
alice_random, kgc.master_public_key
)
# Bob 响应
bob_random = os.urandom(32)
bob_token = sm9.key_exchange_response(
bob_private_key, alice_id,
bob_random, kgc.master_public_key,
alice_token
)
# Alice 完成协商
alice_shared_key = sm9.key_exchange_finish(
alice_private_key, bob_token
)
# Bob 完成协商
bob_shared_key = sm9.key_exchange_finish(
bob_private_key, alice_token
)
# 双方应得到相同的共享密钥
assert alice_shared_key == bob_shared_key
print(f"✅ 密钥协商成功")
print(f" 共享密钥: {alice_shared_key.hex()[:32]}...")
if __name__ == "__main__":
sm9_key_exchange_example()六、SM9 vs PKI 对比分析
6.1 核心差异
| 维度 | PKI(SM2 证书) | SM9 标识密码 |
|---|---|---|
| 公钥 | 随机生成的椭圆曲线点 | 用户标识(邮箱、手机号等) |
| 公钥绑定 | 需要 CA 证书 | 天然绑定,无需证书 |
| 证书管理 | 需要完整的 CA 体系 | 不需要证书 |
| 吊销 | CRL/OCSP | KGC 停止为用户生成新私钥 |
| 密钥托管 | 不存在 | KGC 知道所有用户私钥 |
| 信任模型 | 树状信任(根 CA → 中间 CA → 用户) | 星状信任(KGC → 用户) |
| 标准 | GM/T 0003-2012, GM/T 0015-2023 | GM/T 0044-2016 |
| 计算开销 | 较低(ECDLP) | 较高(双线性对运算) |
6.2 SM9 的优势
- 零证书管理:不需要 CA、证书签发、证书吊销等繁琐流程
- 天然的身份绑定:公钥就是身份,不存在"证书中的身份是否真实"的问题
- 适合封闭体系:企业内部、政府专网等 KGC 可控的场景
- 支持离线加密:Alice 不需要在线获取 Bob 的证书就能加密
6.3 SM9 的劣势
- 密钥托管问题:KGC 知道所有用户的私钥,这在某些场景下是不可接受的
- KGC 单点故障:KGC 被攻破意味着整个体系的私钥泄露
- 撤销困难:要撤销用户只能更改其标识或停止 KGC 服务
- 计算开销大:双线性对运算比 ECDLP 慢约 10-100 倍
- 互操作性差:SM9 是国密特有算法,国际互通困难
6.4 适用场景
SM9 适合:
- 企业内部邮件加密(用邮箱作为标识)
- 政府专网中的身份认证
- IoT 设备认证(用设备 ID 作为标识)
- 需要简化证书管理的封闭系统
- 互联网公开服务(HTTPS)
- 跨组织互信场景
- 需要不可否认性的场景(数字签名)
- 对 KGC 托管不信任的场景
七、生产部署建议
7.1 混合架构
在实际生产中,可以考虑 PKI + SM9 的混合架构:
对外服务(HTTPS)→ PKI/SM2 证书
内部通信(邮件、文件加密)→ SM9 标识密码
IoT 设备认证 → SM9 标识密码
跨组织互信 → PKI/SM2 证书7.2 KGC 部署架构
┌─────────────────────────────────────────┐
│ KGC 集群 │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ KGC 主节点 │◄──►│ KGC 备节点 │ │
│ │ (HSM) │ │ (HSM) │ │
│ └──────────┘ └──────────┘ │
│ │ │
│ ▼ │
│ 私钥分发 API │
│ (mTLS + 审计日志) │
└─────────────────────────────────────────┘7.3 密钥轮换策略
虽然 SM9 的主密钥不像 PKI 证书那样有明确的有效期,但建议:
- 主密钥轮换周期:3-5 年
- 轮换方式:生成新主密钥后,为所有用户重新生成私钥
- 过渡期:新旧系统并行运行 6 个月
- 用户私钥分发:使用安全信道,记录完整审计日志
总结
SM9 标识密码是一种优雅的密码学方案,它用"身份即公钥"的思想消除了证书管理的复杂性。在封闭体系(企业内部、政府专网)中,SM9 可以大幅简化密钥管理流程。
但 SM9 不是 PKI 的替代品,而是互补品。密钥托管、撤销困难和计算开销这三个问题,使得 SM9 不太适合开放互联网场景。
在实际部署中,建议根据具体场景选择:开放互联网用 PKI/SM2,封闭内网用 SM9,跨域互信用 PKI/SM2。混合架构是当前最务实的选择。
参考来源
- 国家密码管理局. GM/T 0044-2016《SM3 密码杂凑算法》. 2016.
- 国家密码管理局. GM/T 0003-2012《SM2 椭圆曲线公钥密码算法》. 2012.
- 国家密码管理局. GM/T 0015-2023《数字证书格式》. 2023.
- GB/T 39786-2021《信息安全技术 信息系统密码应用基本要求》. 2021.
- Boneh D., Franklin M. Identity-Based Encryption from the Weil Pairing. CRYPTO 2001.