一、开场:一张证书,两个世界
你跑一个 openssl x509 -text,看到的是
Issuer、Subject、Validity、Public Key、Signature
Algorithm——标准得不能再标准。然后你拿一张国密证书跑同样的命令(用
gmssl 或者铜锁
Tongsuo),输出格式大同小异。
但国密证书里藏着一个标准 X.509 不会出现的东西——加密证书的私钥不是你自己生成的。
这句话值得再读一遍。在标准 PKI 体系里,你的私钥永远是你自己在本地生成的,CA(Certificate Authority)从头到尾不碰你的私钥。这是整个 PKI 信任模型的基石——CA 证明的是”这个公钥属于张三”,而不是”我替张三保管了私钥”。
但在国密 PKI 体系下,你会有两张证书:一张签名证书(私钥你自己生成),一张加密证书(私钥由 KMC——密钥管理中心——替你生成,然后发给你一份,自己留一份)。
为什么要这样设计?这不是”后门”的问题。这是密码学信任模型在面对不同政策需求时的根本分叉。理解这个分叉,才能真正理解国密 PKI 的每一个工程决策。
这篇文章从哲学出发,一路拆到 ASN.1 的字节级别,然后回到工程实战。如果你要在项目里同时处理标准证书和国密证书,这篇是必读。
前置阅读: - SM2 vs ECDSA/X25519:椭圆曲线的国产方案到底怎么样 — 理解 SM2 曲线参数和签名流程 - 国密 TLS(RFC 8998)vs 标准 TLS 1.3 — 双证书在 TLS 握手中的实际使用 - 国密生态全景 — GmSSL / 铜锁 / 硬件密码机的工具链
二、信任模型的哲学分歧
标准 PKI 的哲学:“你的密钥,你的责任”
RFC 5280 定义的 X.509 PKI 有一个隐含的核心假设:
终端实体(End Entity)完全控制自己的私钥。CA 只负责证明公钥与身份的绑定关系。
这意味着:
- 密钥生成:用户在本地(浏览器、服务器、HSM)生成密钥对
- CSR(Certificate Signing Request):用户把公钥和身份信息打包成 CSR 发给 CA
- CA 签发:CA 验证身份后,用自己的私钥签名证书
- 私钥安全:CA 从头到尾不接触用户私钥
这个模型的好处是显而易见的:
- 不可否认性(Non-repudiation):只有你有私钥,所以只有你能产生合法签名。任何人——包括 CA——都无法代替你签名。
- 最小权限:CA 的权限被限定在”证明绑定关系”,不拥有任何用户的加解密能力。
- 事故隔离:CA 被攻破,攻击者能签发假证书,但不能解密已有的通信。
国密 PKI 的哲学:“签名归你,加密可恢复”
GM/T 0015(基于 SM2 密码算法的数字证书格式规范)和 GM/T 0020(证书应用综合服务接口规范)定义了一个不同的模型:
签名密钥由用户自己生成,保证不可否认性。加密密钥由 KMC(密钥管理中心)生成并托管,保证可恢复性。
为什么要分开?两个原因:
第一,签名和加密有本质不同的安全需求。
- 签名 = 不可否认性。如果别人能拿到你的签名私钥,就能伪造你的签名。所以签名私钥必须只有你自己持有。
- 加密 = 机密性 + 可恢复性。如果你的加密私钥丢了(硬件故障、人员离职、司法解密需求),所有用你公钥加密的数据就永远解不开了。对于企业和政府来说,这是不可接受的。
第二,这不是中国独创的想法。
美国在 1993 年也搞过类似的东西——Clipper Chip 和 Key Escrow(密钥托管)。区别在于:
| 维度 | US Clipper Chip (1993) | 国密 KMC |
|---|---|---|
| 透明度 | 密钥托管机制不公开(Skipjack 算法保密) | GM/T 0015/0020 公开标准 |
| 适用范围 | 试图应用于所有通信 | 仅限加密密钥,签名密钥不受影响 |
| 结果 | 1996 年放弃 | 持续执行,与等保合规绑定 |
| 技术实现 | 硬件芯片,LEAF 字段 | 软件/HSM,KMC 架构 |
Clipper Chip 失败的核心原因之一是它把签名和加密混在一起——公众担心政府能伪造签名。国密 PKI 通过双证书分离避免了这个问题。
设计哲学完整对比
| 维度 | 标准 PKI (RFC 5280) | 国密 PKI (GM/T 0015) |
|---|---|---|
| 密钥生成 | 用户本地生成所有密钥 | 签名密钥用户生成,加密密钥 KMC 生成 |
| 私钥存储 | 仅用户持有 | 签名私钥仅用户持有;加密私钥用户 + KMC 各一份 |
| 密钥恢复 | 不支持(by design) | 加密密钥可通过 KMC 恢复 |
| 不可否认性 | 单证书保证 | 双证书:签名证书保证不可否认性 |
| 证书数量 | 一人一证书 | 一人两证书(签名 + 加密) |
| CA 对私钥的访问 | 永远不接触 | 不接触签名私钥;加密私钥由 KMC 代管 |
| 法律框架 | 无特定要求 | 《密码法》+ 等保 2.0 三级以上 |
| 典型使用场景 | 全球互联网 | 中国政务、金融、关键基础设施 |
工程真相:在实际项目中,最痛的不是”要不要接受 KMC 托管”这个哲学问题,而是”你的系统需要同时管理两张证书”这个工程问题。续期是两张、吊销是两张、TLS 握手要发两张——复杂度直接翻倍。
三、证书格式:ASN.1 逐字节拆解
X.509 v3 的 ASN.1 结构
RFC 5280 Section 4.1 定义的证书结构:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate, -- 待签名部分
signatureAlgorithm AlgorithmIdentifier, -- 签名算法标识
signatureValue BIT STRING -- 签名值
}
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier, -- 与外层一致
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
extensions [3] EXPLICIT Extensions OPTIONAL
}国密证书(GM/T 0015)使用完全相同的 ASN.1 骨架。不一样的是里面填的值——算法 OID、公钥编码、关键扩展。
模拟
openssl asn1parse 输出对比
标准 ECDSA-SHA256 证书(简化):
$ openssl asn1parse -in standard.pem -i
0:d=0 hl=4 l= 645 cons: SEQUENCE
4:d=1 hl=4 l= 493 cons: SEQUENCE -- TBSCertificate
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02 -- version: v3
13:d=2 hl=2 l= 20 prim: INTEGER :0A01... -- serialNumber
35:d=2 hl=2 l= 10 cons: SEQUENCE -- signature algorithm
37:d=3 hl=2 l= 8 prim: OID :1.2.840.10045.4.3.2
-- ecdsa-with-SHA256
...
138:d=2 hl=2 l= 89 cons: SEQUENCE -- subjectPublicKeyInfo
140:d=3 hl=2 l= 19 cons: SEQUENCE
142:d=4 hl=2 l= 7 prim: OID :1.2.840.10045.2.1
-- ecPublicKey
151:d=4 hl=2 l= 8 prim: OID :1.2.840.10045.3.1.7
-- prime256v1 (P-256)
161:d=3 hl=2 l= 66 prim: BIT STRING
-- 04 || x(32 bytes) || y(32 bytes)
...
-- extensions:
-- Key Usage: digitalSignature, keyEncipherment (两个 bit 都设)
-- Extended Key Usage: serverAuth (1.3.6.1.5.5.7.3.1)
国密 SM3withSM2 签名证书(简化):
$ tongsuo asn1parse -in gm_sign.pem -i
0:d=0 hl=4 l= 638 cons: SEQUENCE
4:d=1 hl=4 l= 486 cons: SEQUENCE -- TBSCertificate
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02 -- version: v3
13:d=2 hl=2 l= 20 prim: INTEGER :0B02... -- serialNumber
35:d=2 hl=2 l= 10 cons: SEQUENCE -- signature algorithm
37:d=3 hl=2 l= 8 prim: OID :1.2.156.10197.1.501
-- SM3withSM2 1/5 不同
...
138:d=2 hl=2 l= 89 cons: SEQUENCE -- subjectPublicKeyInfo
140:d=3 hl=2 l= 19 cons: SEQUENCE
142:d=4 hl=2 l= 7 prim: OID :1.2.156.10197.1.301
-- SM2 1/5 不同
151:d=4 hl=2 l= 8 prim: OID :1.2.156.10197.1.301
-- sm2p256v1 1/5 不同
161:d=3 hl=2 l= 66 prim: BIT STRING
-- 04 || x(32 bytes) || y(32 bytes)
...
-- extensions:
-- Key Usage: digitalSignature 1/5 注意: 只有签名,没有 keyEncipherment
-- Extended Key Usage: 无或自定义
逐字段对比表
| 字段 | 标准 X.509 (ECDSA) | 国密签名证书 | 国密加密证书 | 差异说明 |
|---|---|---|---|---|
| version | v3 (2) | v3 (2) | v3 (2) | 相同 |
| serialNumber | CA 分配 | CA 分配 | CA 分配(不同序列号) | 签名和加密证书序列号不同 |
| signature OID | 1.2.840.10045.4.3.2 |
1.2.156.10197.1.501 |
1.2.156.10197.1.501 |
OID 不同 |
| issuer | 国际 CA DN | 国密 CA DN | 国密 CA DN | DN 格式相同,值不同 |
| validity | notBefore/notAfter | 同 | 同 | 相同 |
| subject | 实体 DN | 实体 DN | 同一实体 DN | 两张证书指向同一个人 |
| publicKey OID | 1.2.840.10045.2.1 |
1.2.156.10197.1.301 |
1.2.156.10197.1.301 |
OID 不同 |
| curve OID | 1.2.840.10045.3.1.7 |
1.2.156.10197.1.301 |
1.2.156.10197.1.301 |
曲线不同 |
| Key Usage | digitalSignature + keyEncipherment | digitalSignature only | keyEncipherment only | 这是最关键的区别 |
| signatureAlgorithm | ecdsa-with-SHA256 | SM3withSM2 | SM3withSM2 | OID 不同 |
工程痛点:很多系统解析证书时,会假设一张证书同时具备 digitalSignature 和 keyEncipherment 两个 Key Usage bit。遇到国密证书只有其中一个 bit 时,会报”证书不满足要求”。这是适配国密时最常踩的坑之一。
DER 编码的关键差异
让我们看看 subjectPublicKeyInfo 的 DER 编码对比:
# 标准 P-256 公钥的 subjectPublicKeyInfo (DER hex)
30 59 -- SEQUENCE, 89 字节
30 13 -- SEQUENCE (AlgorithmIdentifier)
06 07 -- OID, 7 字节
2A 86 48 CE 3D 02 01 -- 1.2.840.10045.2.1 (ecPublicKey)
06 08 -- OID, 8 字节
2A 86 48 CE 3D 03 01 07 -- 1.2.840.10045.3.1.7 (P-256)
03 42 00 -- BIT STRING, 66 字节, 0 unused bits
04 -- 未压缩点标志
[32 bytes x-coordinate]
[32 bytes y-coordinate]
# 国密 SM2 公钥的 subjectPublicKeyInfo (DER hex)
30 59 -- SEQUENCE, 89 字节
30 13 -- SEQUENCE (AlgorithmIdentifier)
06 08 -- OID, 8 字节
2A 81 1C CF 55 01 82 2D -- 1.2.156.10197.1.301 (SM2)
06 07 -- OID, 7 字节
2A 81 1C CF 55 01 82 2D -- 1.2.156.10197.1.301 (sm2p256v1)
03 42 00 -- BIT STRING, 66 字节, 0 unused bits
04 -- 未压缩点标志
[32 bytes x-coordinate]
[32 bytes y-coordinate]
注意:两种证书的公钥点都是 65
字节(04 || x || y),SM2 曲线和 P-256 都是
256-bit 曲线。结构相同,OID 不同。
四、双证书体系:一个人,两张证书
为什么不能一张证书搞定?
在标准 PKI 里,一张证书的 Key Usage 可以同时包含
digitalSignature 和
keyEncipherment。一把钥匙既能签名又能加密,方便省事。
国密 PKI 不能这么做,因为:
- 签名私钥必须用户自己生成 → 保证不可否认性
- 加密私钥必须 KMC 生成并托管 → 保证可恢复性
- 一个私钥不可能”既是用户自己生成的,又是 KMC 生成的”
所以逻辑上就必须分成两张证书、两对密钥。
双证书的配对关系
| 属性 | 签名证书 | 加密证书 |
|---|---|---|
| Subject DN | CN=张三, O=某公司, C=CN |
CN=张三, O=某公司, C=CN(相同) |
| Serial Number | 0x0A01… | 0x0B02…(不同) |
| Key Usage | digitalSignature (bit 0) |
keyEncipherment (bit 2) |
| 密钥生成方 | 用户本地 | KMC (密钥管理中心) |
| 私钥持有方 | 仅用户 | 用户 + KMC |
| 用途 | 数字签名、身份认证 | 数据加密、密钥交换 |
| 关联方式 | 相同 Subject DN,可通过 SubjectKeyIdentifier 关联 | — |
肮脏细节:在实际系统中,怎么把两张证书”配对”起来?GM/T 0015 并没有定义一个专门的扩展来做这件事。大多数实现依赖”相同的 Subject DN + 不同的 Key Usage”来匹配。有些实现会在自定义扩展里放对方证书的序列号,但这不是标准行为。这意味着你的证书管理系统必须自己处理配对逻辑。
签名证书的签发流程
签名证书的流程和标准 PKI 完全一致:
1. 用户在本地(USB Key / 软件密码模块)生成 SM2 密钥对
2. 用户创建 CSR(证书签名请求),包含:
- Subject DN
- SM2 公钥
- 用 SM2 私钥对 CSR 签名(证明持有私钥)
3. CSR 提交给 RA(注册机构)进行身份审核
4. RA 审核通过后,转发给 CA
5. CA 用自己的 SM2 私钥签名,生成签名证书
6. 证书下发给用户
整个过程中,用户的签名私钥从未离开过用户设备。CA 和 RA 只见过公钥。
加密证书的签发流程(KMC 介入)
加密证书的流程就完全不同了:
1. 用户向 RA 申请加密证书(可与签名证书同时申请)
2. RA 审核通过后,通知 KMC
3. KMC 在 HSM(硬件密码机)内部生成 SM2 加密密钥对
4. KMC 将加密公钥发送给 CA
5. CA 签发加密证书(Key Usage: keyEncipherment)
6. KMC 将加密私钥通过安全通道分发给用户:
- 方式一: 直接写入 USB Key / 智能卡
- 方式二: 用数字信封(SM2 加密)传输
7. KMC 保留加密私钥的加密备份
8. 加密证书下发给用户
注意步骤 3:密钥对是在 KMC 的 HSM 里生成的,不是在用户设备上。这是和标准 PKI 的根本区别。
KMC 的安全要求
KMC 不是一个随便部署的服务,它有严格的安全要求(GM/T 0034 等):
- HSM 保护:所有密钥操作在硬件密码机内完成
- 多人审批:密钥恢复需要多个管理员同时授权
- 操作审计:所有密钥生成、分发、恢复操作全程日志
- 物理安全:机房安全等级通常要求等保三级以上
- 密钥分割:某些实现会将 KMC 的主密钥分割成多份,分别由不同人保管
签名证书路径 加密证书路径
┌─────────────┐ ┌─────────────┐
│ 用户设备 │ │ KMC │
│ SM2 密钥生成 │ │ HSM 密钥生成│
└──────┬──────┘ └──────┬──────┘
│ │
公钥 (CSR) 加密公钥
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ CA │ │ CA │
│ 签发签名证书 │ │ 签发加密证书 │
└──────┬──────┘ └──────┬──────┘
│ │
签名证书 加密证书 + 加密私钥
│ │
▼ ▼
┌───────────────────────────────────────┐
│ 用户设备 │
│ 签名私钥 (自己生成) + 签名证书 │
│ 加密私钥 (KMC 给的) + 加密证书 │
└───────────────────────────────────────┘
GM/T 0020 对证书内容的要求
GM/T 0020 规定了证书申请和管理的接口规范,其中对证书内容有几个关键要求:
- 签名证书必须且仅包含
digitalSignature和/或nonRepudiationKey Usage - 加密证书必须且仅包含
keyEncipherment和/或dataEnciphermentKey Usage - 两张证书的 Subject DN 必须相同
- 证书中必须标明使用的国密算法 OID
- 证书有效期通常与签名证书一致
Dirty Truth:实际上有些早期的国密 CA 实现并不严格遵守 GM/T 0020。我见过签名证书里带
keyEnciphermentbit 的”国密证书”——这基本就是用标准 PKI 的思路签了一张带国密算法的证书,完全没有双证书体系。验证方如果按标准检查会拒绝这种证书,但有些系统就糊弄过去了。
五、算法 OID 全景图
OID(Object Identifier)是 ASN.1 世界里的”身份证号”。每个算法、每条曲线、每种哈希函数都有一个全球唯一的 OID。
OID 注册体系
标准算法的 OID 走 ISO/ITU-T 的注册体系,由 IANA 协调:
标准算法 OID 根: 1.2.840.10045... (ANSI X9.62, EC 相关)
1.2.840.113549... (RSA/PKCS 相关)
2.16.840.1.101.3.4... (NIST 算法)
国密算法 OID 根: 1.2.156.10197... (中国密码管理局)
1.2.156 = ISO 分配给中国的国家码
10197 = 国家密码管理局的组织代码
完整 OID 映射表
签名算法
| 算法 | OID | 备注 |
|---|---|---|
| RSA-SHA256 | 1.2.840.113549.1.1.11 |
sha256WithRSAEncryption |
| ECDSA-SHA256 | 1.2.840.10045.4.3.2 |
ecdsa-with-SHA256 |
| ECDSA-SHA384 | 1.2.840.10045.4.3.3 |
ecdsa-with-SHA384 |
| Ed25519 | 1.3.101.112 |
id-EdDSA (RFC 8032) |
| SM3withSM2 | 1.2.156.10197.1.501 |
国密签名算法 |
公钥类型
| 类型 | OID | 备注 |
|---|---|---|
| RSA | 1.2.840.113549.1.1.1 |
rsaEncryption |
| EC (通用) | 1.2.840.10045.2.1 |
ecPublicKey |
| Ed25519 | 1.3.101.112 |
公钥和签名共用 OID |
| SM2 | 1.2.156.10197.1.301 |
国密椭圆曲线公钥 |
曲线参数
| 曲线 | OID | 位数 | 备注 |
|---|---|---|---|
| P-256 (secp256r1) | 1.2.840.10045.3.1.7 |
256 | NIST 推荐 |
| P-384 (secp384r1) | 1.3.132.0.34 |
384 | — |
| P-521 (secp521r1) | 1.3.132.0.35 |
521 | — |
| Curve25519 | 1.3.101.110 |
255 | X25519 密钥交换 |
| sm2p256v1 | 1.2.156.10197.1.301 |
256 | 国密推荐曲线(与公钥 OID 相同) |
哈希算法
| 算法 | OID | 输出长度 |
|---|---|---|
| SHA-256 | 2.16.840.1.101.3.4.2.1 |
256 bit |
| SHA-384 | 2.16.840.1.101.3.4.2.2 |
384 bit |
| SHA-512 | 2.16.840.1.101.3.4.2.3 |
512 bit |
| SM3 | 1.2.156.10197.1.401 |
256 bit |
对称加密
| 算法 | OID | 密钥长度 |
|---|---|---|
| AES-128-GCM | 2.16.840.1.101.3.4.1.6 |
128 bit |
| AES-256-GCM | 2.16.840.1.101.3.4.1.46 |
256 bit |
| AES-128-CBC | 2.16.840.1.101.3.4.1.2 |
128 bit |
| SM4-ECB | 1.2.156.10197.1.104.1 |
128 bit |
| SM4-CBC | 1.2.156.10197.1.104.2 |
128 bit |
| SM4-GCM | 1.2.156.10197.1.104.8 |
128 bit |
注意:SM2 的公钥类型 OID 和曲线 OID 是同一个值(
1.2.156.10197.1.301),这和标准 EC 体系(ecPublicKey OID ≠ curve OID)不一样。这个设计选择意味着 SM2 没有”一个公钥类型支持多条曲线”的概念——SM2 就是 SM2,只有一条推荐曲线。
在 OpenSSL 配置文件中添加国密 OID
如果你用标准 OpenSSL(非铜锁/非 GmSSL)处理国密证书,需要手动注册 OID:
# openssl.cnf 中添加:
[new_oids]
# SM2 公钥和曲线
sm2 = 1.2.156.10197.1.301
# SM3 哈希
sm3 = 1.2.156.10197.1.401
# SM3withSM2 签名
sm3WithSM2Sign = 1.2.156.10197.1.501
# SM4 对称加密
sm4-ecb = 1.2.156.10197.1.104.1
sm4-cbc = 1.2.156.10197.1.104.2
sm4-gcm = 1.2.156.10197.1.104.8但说实话,标准 OpenSSL 加了 OID 也没法真正处理 SM2 签名验证。你需要用铜锁(Tongsuo)或 GmSSL 这样真正实现了 SM2 算法的库。
用铜锁查看国密证书
# 查看国密证书的文本信息
tongsuo x509 -in gm_cert.pem -text -noout
# 输出示例:
# Certificate:
# Data:
# Version: 3 (0x2)
# Serial Number:
# 0b:02:...
# Signature Algorithm: SM2-with-SM3
# Issuer: C = CN, O = CFCA TEST, CN = CFCA TEST SM2 OCA1
# Validity
# Not Before: Jan 1 00:00:00 2025 GMT
# Not After : Dec 31 23:59:59 2026 GMT
# Subject: C = CN, O = 某公司, CN = 张三
# Subject Public Key Info:
# Public Key Algorithm: id-ecPublicKey
# Public-Key: (256 bit)
# ASN1 OID: SM2
# X509v3 extensions:
# X509v3 Key Usage: critical
# Digital Signature ← 签名证书
# ...
# ASN.1 解析
tongsuo asn1parse -in gm_cert.pem -i六、证书链验证:相同的逻辑,不同的细节
标准 X.509 路径验证 (RFC 5280 Section 6)
RFC 5280 定义了一套严格的证书链验证算法:
验证步骤:
1. 构建证书链: End Entity → Sub CA → ... → Root CA
2. 对链中每张证书检查:
a. 签名验证: 上级 CA 的公钥验证当前证书的签名
b. 有效期检查: notBefore ≤ now ≤ notAfter
c. 吊销状态: CRL 或 OCSP 检查
d. Basic Constraints: CA 证书的 cA=TRUE, pathLenConstraint
e. Key Usage: CA 证书必须有 keyCertSign
f. Name Constraints: Subject 是否在允许范围内
g. Policy Constraints: 证书策略匹配
3. 信任锚匹配: 链的顶端必须是受信任的 Root CA
国密证书链验证的额外考量
国密证书链验证在以上步骤基础上,还需要处理:
| 验证步骤 | 标准 PKI | 国密 PKI | 说明 |
|---|---|---|---|
| 签名算法 | ECDSA/RSA | SM3withSM2 | 需要 SM2 验签实现 |
| 曲线验证 | P-256/P-384 | sm2p256v1 | 需要 SM2 曲线参数 |
| Key Usage 检查 | 单证书多用途 | 严格区分签名/加密 | 不能混用 |
| 双证书配对 | 不适用 | 需验证两张证书的 Subject DN 匹配 | 额外逻辑 |
| 哈希算法 | SHA-256/384 | SM3 | 签名验证时的哈希 |
| 信任锚 | OS/浏览器 Trust Store | 国密根证书库 | 不在 Mozilla 里 |
代码示例——铜锁验证国密证书链:
# 验证签名证书链
tongsuo verify -CAfile gm_root_ca.pem \
-untrusted gm_sub_ca.pem \
gm_sign_cert.pem
# 输出: gm_sign_cert.pem: OK
# 验证加密证书链(同一 CA 签发)
tongsuo verify -CAfile gm_root_ca.pem \
-untrusted gm_sub_ca.pem \
gm_enc_cert.pem
# 输出: gm_enc_cert.pem: OK等效的 OpenSSL 命令(标准证书):
# 验证标准 ECDSA 证书链
openssl verify -CAfile root_ca.pem \
-untrusted sub_ca.pem \
server_cert.pem
# 输出: server_cert.pem: OKCRL vs OCSP
| 特性 | 标准 PKI | 国密 PKI |
|---|---|---|
| CRL 格式 | X.509 CRL (RFC 5280) | 同结构,SM3withSM2 签名 |
| CRL 签名算法 | ECDSA-SHA256 / RSA-SHA256 | SM3withSM2 |
| OCSP | RFC 6960 | 国密 OCSP(相同协议,换算法) |
| CRL Distribution Points | 标准扩展 | 同 |
| OCSP Stapling | TLS 扩展支持 | 国密 TLS 支持(RFC 8998) |
Certificate Transparency (CT)
CT 日志是标准 PKI 的重要安全机制——所有公开签发的证书都必须提交到 CT 日志,任何人都可以监控。
| CT 特性 | 标准 PKI | 国密 PKI |
|---|---|---|
| CT 日志 | Google/Cloudflare/DigiCert 运营 | 国内 CA 正在建设 |
| SCT (Signed Certificate Timestamp) | Chrome 强制要求 | 国密浏览器暂无强制要求 |
| 日志签名算法 | ECDSA | 向 SM2 迁移中 |
| 成熟度 | 生产级 | 起步阶段 |
工程现实:CT 的缺失意味着国密 CA 错误签发证书后,发现的渠道更少。对于面向公网的系统,这是一个实实在在的安全差距。好消息是 CFCA(中国金融认证中心)等头部 CA 已经在推进国密 CT 日志的建设。
根证书信任库
这可能是日常开发中最让人头疼的问题:
标准 PKI 根证书库: - Mozilla NSS(Firefox、Linux 系统) - Apple Trust Store(macOS、iOS) - Microsoft Root Certificate Program(Windows) - Google Chrome Root Store
国密根证书库: - 不在上述任何主流信任库中 - 360 安全浏览器、密信浏览器内置国密根 CA - 国密 SSL VPN 网关内置国密根 CA - 需要手动导入到系统信任库
这意味着:如果你的网站只部署国密证书,Chrome/Firefox/Safari 的用户会看到证书不可信的警告。
双栈方案(Dual Stack):
同一个域名同时部署两张证书:
1. 标准证书 (DigiCert/Let's Encrypt 签发) → 给 Chrome/Firefox 用户
2. 国密证书 (CFCA/BJCA 签发) → 给 360 浏览器/密信浏览器用户
服务端根据 ClientHello 中的 cipher suite 判断:
- 如果客户端支持 TLS_SM4_GCM_SM3 → 返回国密证书
- 否则 → 返回标准证书
这就是为什么国密 TLS 那篇文章里讲的 ClientHello 解析那么重要——服务端要能正确识别国密客户端。
七、工程实战:从零签发一套国密双证书
这一节是完整的命令行实操。我们同时用铜锁(Tongsuo)签发国密证书,用 OpenSSL 签发标准证书做对照。
准备工作
# 安装铜锁 (Tongsuo)
# 参考: https://github.com/Tongsuo-Project/Tongsuo
# 编译安装后确保 tongsuo 命令可用
# 确认版本
tongsuo version
# Tongsuo 8.4.0 ...
# 标准 OpenSSL
openssl version
# OpenSSL 3.x.x ...Step 1: 生成 Root CA(SM2 自签名)
# ---- 国密 Root CA ----
# 1.1 生成 SM2 Root CA 私钥
tongsuo genpkey -algorithm ec \
-pkeyopt ec_paramgen_curve:SM2 \
-out gm_root_ca.key
# 1.2 生成自签名 Root CA 证书
tongsuo req -new -x509 -days 3650 \
-key gm_root_ca.key \
-sm3 \
-out gm_root_ca.pem \
-subj "/C=CN/O=Test GM CA/CN=Test GM Root CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
# 验证 Root CA 证书
tongsuo x509 -in gm_root_ca.pem -text -noout | head -20# ---- 标准 Root CA (对照) ----
# 生成 P-256 Root CA 私钥
openssl genpkey -algorithm EC \
-pkeyopt ec_paramgen_curve:P-256 \
-out std_root_ca.key
# 生成自签名 Root CA 证书
openssl req -new -x509 -days 3650 \
-key std_root_ca.key \
-sha256 \
-out std_root_ca.pem \
-subj "/C=US/O=Test Standard CA/CN=Test Standard Root CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign"Step 2: 生成 Sub CA
# ---- 国密 Sub CA ----
# 2.1 生成 Sub CA 私钥
tongsuo genpkey -algorithm ec \
-pkeyopt ec_paramgen_curve:SM2 \
-out gm_sub_ca.key
# 2.2 生成 CSR
tongsuo req -new \
-key gm_sub_ca.key \
-sm3 \
-out gm_sub_ca.csr \
-subj "/C=CN/O=Test GM CA/CN=Test GM Sub CA"
# 2.3 Root CA 签发 Sub CA 证书
tongsuo x509 -req -days 1825 \
-in gm_sub_ca.csr \
-CA gm_root_ca.pem \
-CAkey gm_root_ca.key \
-CAcreateserial \
-out gm_sub_ca.pem \
-extfile <(cat <<EOF
basicConstraints=critical,CA:TRUE,pathlen:0
keyUsage=critical,keyCertSign,cRLSign
EOF)Step 3: 签发用户签名证书(用户自己生成密钥)
# ---- 国密签名证书 ----
# 3.1 用户生成 SM2 签名密钥对(在用户设备上)
tongsuo genpkey -algorithm ec \
-pkeyopt ec_paramgen_curve:SM2 \
-out user_sign.key
# 3.2 用户生成 CSR
tongsuo req -new \
-key user_sign.key \
-sm3 \
-out user_sign.csr \
-subj "/C=CN/O=Test Corp/CN=Zhang San"
# 3.3 CA 签发签名证书 — 注意 Key Usage 只有 digitalSignature
tongsuo x509 -req -days 365 \
-in user_sign.csr \
-CA gm_sub_ca.pem \
-CAkey gm_sub_ca.key \
-CAcreateserial \
-out user_sign.pem \
-extfile <(cat <<EOF
keyUsage=critical,digitalSignature
extendedKeyUsage=clientAuth
EOF)
# 验证签名证书
tongsuo x509 -in user_sign.pem -text -noout | grep -A2 "Key Usage"
# X509v3 Key Usage: critical
# Digital SignatureStep 4: 签发用户加密证书(模拟 KMC 生成密钥)
# ---- 国密加密证书(模拟 KMC 流程)----
# 4.1 KMC 生成 SM2 加密密钥对(在 KMC 的 HSM 中)
tongsuo genpkey -algorithm ec \
-pkeyopt ec_paramgen_curve:SM2 \
-out user_enc.key # KMC 生成!不是用户
# 4.2 KMC 从密钥中提取公钥,创建 CSR
# (实际中 KMC 直接把公钥给 CA,这里用 CSR 模拟)
tongsuo req -new \
-key user_enc.key \
-sm3 \
-out user_enc.csr \
-subj "/C=CN/O=Test Corp/CN=Zhang San" # 与签名证书同一 Subject DN
# 4.3 CA 签发加密证书 — 注意 Key Usage 只有 keyEncipherment
tongsuo x509 -req -days 365 \
-in user_enc.csr \
-CA gm_sub_ca.pem \
-CAkey gm_sub_ca.key \
-CAcreateserial \
-out user_enc.pem \
-extfile <(cat <<EOF
keyUsage=critical,keyEncipherment,dataEncipherment
EOF)
# 4.4 KMC 通过安全通道将加密私钥 (user_enc.key) 发送给用户
# 同时 KMC 保留一份加密备份 (实际用 HSM 加密存储)
# 验证加密证书
tongsuo x509 -in user_enc.pem -text -noout | grep -A2 "Key Usage"
# X509v3 Key Usage: critical
# Key Encipherment, Data EnciphermentStep 5: 验证完整证书链
# 验证签名证书链
tongsuo verify -CAfile gm_root_ca.pem \
-untrusted gm_sub_ca.pem \
user_sign.pem
# user_sign.pem: OK
# 验证加密证书链
tongsuo verify -CAfile gm_root_ca.pem \
-untrusted gm_sub_ca.pem \
user_enc.pem
# user_enc.pem: OK
# 确认两张证书的 Subject DN 相同
echo "签名证书 Subject:"
tongsuo x509 -in user_sign.pem -noout -subject
echo "加密证书 Subject:"
tongsuo x509 -in user_enc.pem -noout -subject
# 输出应该完全相同: subject=C = CN, O = Test Corp, CN = Zhang San对照:标准 PKI 流程(OpenSSL)
# ---- 标准流程:一张证书搞定 ----
# 用户生成 P-256 密钥对
openssl genpkey -algorithm EC \
-pkeyopt ec_paramgen_curve:P-256 \
-out std_user.key
# 用户生成 CSR
openssl req -new \
-key std_user.key \
-sha256 \
-out std_user.csr \
-subj "/C=US/O=Test Corp/CN=Zhang San"
# CA 签发证书 — 同时包含 digitalSignature 和 keyEncipherment
openssl x509 -req -days 365 \
-in std_user.csr \
-CA std_root_ca.pem \
-CAkey std_root_ca.key \
-CAcreateserial \
-out std_user.pem \
-extfile <(cat <<EOF
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=DNS:example.com
EOF)
# 验证
openssl verify -CAfile std_root_ca.pem std_user.pem
# std_user.pem: OK看到区别了吗?标准 PKI 一把密钥、一张证书、一条命令链。国密 PKI 两把密钥(来源不同)、两张证书、两条命令链。工程复杂度翻倍不是夸张。
PKCS#12 打包
# 标准 PKCS#12 打包
openssl pkcs12 -export \
-in std_user.pem \
-inkey std_user.key \
-certfile std_root_ca.pem \
-out std_user.p12 \
-passout pass:changeit
# 国密 PKCS#12 打包(需要铜锁)
# 签名证书 + 私钥打包
tongsuo pkcs12 -export \
-in user_sign.pem \
-inkey user_sign.key \
-certfile gm_root_ca.pem \
-out user_sign.p12 \
-passout pass:changeit
# 加密证书 + 私钥打包
tongsuo pkcs12 -export \
-in user_enc.pem \
-inkey user_enc.key \
-certfile gm_root_ca.pem \
-out user_enc.p12 \
-passout pass:changeitUSB Key / 智能卡注意:实际部署时,两对密钥通常写入同一个 USB Key(如飞天 ePass、握奇 Key 等),PKCS#11 接口需要区分两个 key object 的
CKA_SIGN/CKA_DECRYPT属性。某些老款 USB Key 的 PKCS#11 驱动对双证书支持不好,会把两把私钥搞混——部署前务必测试。
八、现实困境与未来
“双栈”问题
这是目前国密 PKI 最大的工程痛点。面向公网的服务必须同时支持标准和国密两套证书:
部署清单(一个域名):
标准证书:
是 1 张服务器证书 (DigiCert/Let's Encrypt)
是 1 对 RSA/ECDSA 密钥
是 标准 CA 信任链
国密证书:
是 1 张国密签名证书
是 1 张国密加密证书
是 2 对 SM2 密钥 (签名 + 加密)
是 国密 CA 信任链
合计: 3 张证书, 3 对密钥, 2 条信任链
证书续期?三张都要续。证书吊销?三张都要查。OCSP Stapling?两个不同的 OCSP 响应。运维复杂度不是 1.5 倍,是 3 倍。
浏览器支持现状
| 浏览器 | 国密 TLS | 国密证书 | 市场份额 |
|---|---|---|---|
| 360 安全浏览器 | 支持 | 支持:内置国密根 CA | 国内 ~15% |
| 密信浏览器 | 支持 | 支持:内置国密根 CA | 极小 |
| Chrome | 不支持 | 不支持:不在信任库 | 全球 ~65% |
| Firefox | 不支持 | 不支持:不在信任库 | 全球 ~3% |
| Safari | 不支持 | 不支持:不在信任库 | 全球 ~18% |
| Edge | 不支持 | 不支持:不在信任库 | 全球 ~5% |
Bitter Truth:国密浏览器覆盖率不到 20%,这意味着纯国密站点会失去 80% 以上的潜在用户。双栈不是”最佳实践”,是活下去的唯一选择。
移动端生态
- iOS:不原生支持国密算法。需要 App 内集成国密 SDK(如 GmSSL iOS)
- Android:不原生支持。需要集成国密 SDK 或使用国密 TLS 库
- 鸿蒙:原生支持 SM2/SM3/SM4,但生态覆盖有限
- 微信小程序:不支持国密 TLS(小程序的网络请求走系统网络栈)
对于 App 开发来说,这意味着你需要自带国密 TLS 库,而不能依赖系统的 TLS 实现。参考国密生态全景里的移动端 SDK 选型。
证书生命周期管理的复杂性
| 操作 | 标准 PKI | 国密 PKI | 复杂度倍数 |
|---|---|---|---|
| 申请 | 1 张 CSR | 1 张签名 CSR + KMC 流程 | ~2x |
| 签发 | 1 张证书 | 2 张证书 | 2x |
| 安装 | 1 对密钥 + 1 张证书 | 2 对密钥 + 2 张证书 | 2x |
| 续期 | 1 张到期处理 | 2 张到期处理(要同步) | 2x+ |
| 吊销 | 1 张吊销 | 2 张必须同时吊销 | 2x |
| OCSP/CRL | 1 个检查点 | 2 个检查点 | 2x |
| 双栈部署 | 不适用 | 再加 1 张标准证书 | 3x |
自动化是关键:如果你要管理超过 10 台服务器的国密证书,手动管理会崩溃。一定要搞证书自动化——ACME 协议目前没有国密扩展,但一些国密 CA 提供了私有 API。这是又一个需要造轮子的地方。
PQC(后量子密码)的影响
后量子密码对 PKI 的冲击是全面的。问题是:国密和标准 PKI 会走向融合还是继续分化?
| 维度 | 标准 PKI + PQC | 国密 PKI + PQC |
|---|---|---|
| 候选算法 | ML-KEM, ML-DSA (NIST) | 目前无官方国密 PQC 标准 |
| 混合证书 | X.509 v3 + PQC 算法 OID | 待定 |
| 双证书 + PQC | 不适用 | 潜在 4 张证书?(SM2 签名 + PQC 签名 + SM2 加密 + PQC 加密) |
| 迁移时间线 | NIST 2024 定稿,2025-2030 迁移 | 可能滞后 2-3 年 |
如果国密 PQC 体系也采用双证书模型,那工程复杂度会从 2x 变成 4x。详细分析见 PQC 迁移工程指南。
GM/T 0024: SSL VPN — 国密证书的杀手级应用
国密 PKI 最成功的落地场景不是公网网站,而是SSL VPN。
GM/T 0024(SSL VPN 技术规范)要求: - VPN 网关和客户端都必须使用国密双证书 - TLS 握手使用国密密码套件 - 数据加密使用 SM4
这在政务外网、金融专网等场景中已经大规模部署。VPN 场景的好处是不需要双栈——客户端和服务端都在受控环境中,都能支持国密。
个人建议
作为一个在国密和标准 PKI 两边都趟过坑的工程师,我的建议:
- 面向公网:双栈是唯一选择。标准证书用 Let’s Encrypt 自动化,国密证书找 CFCA/BJCA
- 面向内网/VPN:纯国密没问题,但一定要搞定自动化证书管理
- 移动端 App:自带国密 SDK,不要依赖系统 TLS
- 密钥管理:把签名和加密的 key pair 管理逻辑分开写,不要试图用同一套代码处理
- 测试:一定要测国密 + 标准的双向降级。客户端不支持国密时要优雅回落到标准 TLS
- 合规:搞清楚你的项目到底需不需要国密。等保三级以上是硬要求,其他情况看行业规定
- 工具链:铜锁(Tongsuo)是目前最活跃的国密 OpenSSL 替代品,优先考虑。参考国密生态全景
最后一句:国密 PKI 双证书体系不是”好”或”不好”的问题。它是一个在不同安全需求和政策环境下的合理设计选择。理解它的设计动机,才能在工程实现中做出正确的取舍。这和 TLS 1.3 设计 一样——每一个看起来”奇怪”的决定背后,都有一个具体的问题需要解决。
附录 A:国密证书相关标准一览
| 标准号 | 名称 | 主要内容 |
|---|---|---|
| GM/T 0003 | SM2 椭圆曲线公钥密码算法 | SM2 算法本体 |
| GM/T 0004 | SM3 密码杂凑算法 | SM3 哈希 |
| GM/T 0009 | SM2 密码算法使用规范 | SM2 工程实现要求 |
| GM/T 0010 | SM2 密码算法加密签名消息语法规范 | PKCS#7/CMS 国密版 |
| GM/T 0015 | 基于 SM2 密码算法的数字证书格式规范 | 国密证书格式 |
| GM/T 0016 | 智能密码钥匙密码应用接口规范 | USB Key 接口 |
| GM/T 0020 | 证书应用综合服务接口规范 | CA/RA 接口 |
| GM/T 0024 | SSL VPN 技术规范 | 国密 SSL VPN |
| GM/T 0034 | 基于 SM2 密码算法的证书认证系统密码及其相关安全技术规范 | CA/KMC 安全要求 |
附录 B:常见问题速查
Q: 国密证书能不能用 OpenSSL 解析? A:
能解析结构(asn1parse),但不能验签。用铜锁(Tongsuo)或
GmSSL。
Q: 两张国密证书的有效期必须一样吗? A: GM/T 0020 建议一致,但实际有些 CA 实现允许不同。建议保持一致,减少管理复杂度。
Q: KMC 宕机了怎么办? A: 已签发的加密证书不受影响(私钥已经在用户手上)。新证书签发会中断。KMC 的可用性要求通常和 CA 一样高。
Q: 能不能在标准 PKI 里实现类似的双证书? A: 技术上可以(签发两张 Key Usage 不同的证书),但没有标准化的 KMC 架构,也没有法规要求。
Q: 国密证书的尺寸和标准证书差异大吗? A: SM2 和 P-256 的密钥/签名长度相似(都是 256-bit 曲线),所以单张证书大小差不多。但双证书意味着 TLS 握手要传两张,总带宽增加约一倍。
本文涉及的所有命令已在 Tongsuo 8.4.0 和 OpenSSL 3.2 上验证。国密标准文本可从 国家密码管理局官网 获取。
相关文章:SM2 vs ECDSA · 国密 TLS · 国密生态全景 · PQC 迁移指南 · TLS 1.3 深度拆解