土法炼钢兴趣小组的算法知识备份

PKI 证书体系:X.509 vs 国密双证书,从哲学到字节的完整对比

目录

一、开场:一张证书,两个世界

你跑一个 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 只负责证明公钥与身份的绑定关系。

这意味着:

  1. 密钥生成:用户在本地(浏览器、服务器、HSM)生成密钥对
  2. CSR(Certificate Signing Request):用户把公钥和身份信息打包成 CSR 发给 CA
  3. CA 签发:CA 验证身份后,用自己的私钥签名证书
  4. 私钥安全: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 握手要发两张——复杂度直接翻倍。

标准 PKI 信任链 vs 国密 PKI 双证书信任链

三、证书格式: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 不同

X.509 v3 证书 ASN.1 结构对比

四、双证书体系:一个人,两张证书

为什么不能一张证书搞定?

在标准 PKI 里,一张证书的 Key Usage 可以同时包含 digitalSignaturekeyEncipherment。一把钥匙既能签名又能加密,方便省事。

国密 PKI 不能这么做,因为:

  1. 签名私钥必须用户自己生成 → 保证不可否认性
  2. 加密私钥必须 KMC 生成并托管 → 保证可恢复性
  3. 一个私钥不可能”既是用户自己生成的,又是 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 等):

  签名证书路径  加密证书路径
  ┌─────────────┐  ┌─────────────┐
  │  用户设备  │  │  KMC  │
  │ SM2 密钥生成 │  │ HSM 密钥生成│
  └──────┬──────┘  └──────┬──────┘
  │  │
  公钥 (CSR)  加密公钥
  │  │
  ▼  ▼
  ┌─────────────┐  ┌─────────────┐
  │  CA  │  │  CA  │
  │ 签发签名证书 │  │ 签发加密证书 │
  └──────┬──────┘  └──────┬──────┘
  │  │
  签名证书  加密证书 + 加密私钥
  │  │
  ▼  ▼
  ┌───────────────────────────────────────┐
  │  用户设备  │
  │  签名私钥 (自己生成) + 签名证书  │
  │  加密私钥 (KMC 给的) + 加密证书  │
  └───────────────────────────────────────┘
KMC 密钥管理中心架构

GM/T 0020 对证书内容的要求

GM/T 0020 规定了证书申请和管理的接口规范,其中对证书内容有几个关键要求:

  1. 签名证书必须且仅包含 digitalSignature 和/或 nonRepudiation Key Usage
  2. 加密证书必须且仅包含 keyEncipherment 和/或 dataEncipherment Key Usage
  3. 两张证书的 Subject DN 必须相同
  4. 证书中必须标明使用的国密算法 OID
  5. 证书有效期通常与签名证书一致

Dirty Truth:实际上有些早期的国密 CA 实现并不严格遵守 GM/T 0020。我见过签名证书里带 keyEncipherment bit 的”国密证书”——这基本就是用标准 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: OK

CRL 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 Signature

Step 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 Encipherment

Step 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:changeit

USB 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% 以上的潜在用户。双栈不是”最佳实践”,是活下去的唯一选择

移动端生态

对于 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 两边都趟过坑的工程师,我的建议:

  1. 面向公网:双栈是唯一选择。标准证书用 Let’s Encrypt 自动化,国密证书找 CFCA/BJCA
  2. 面向内网/VPN:纯国密没问题,但一定要搞定自动化证书管理
  3. 移动端 App:自带国密 SDK,不要依赖系统 TLS
  4. 密钥管理:把签名和加密的 key pair 管理逻辑分开写,不要试图用同一套代码处理
  5. 测试:一定要测国密 + 标准的双向降级。客户端不支持国密时要优雅回落到标准 TLS
  6. 合规:搞清楚你的项目到底需不需要国密。等保三级以上是硬要求,其他情况看行业规定
  7. 工具链:铜锁(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 深度拆解


By .