SM2签名算法ASN.1DER编码-签名长度"

admin 2026-10-18 06:12:09 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

SM2 签名算法 ASN.1 DER 编码 - 签名长度

原创

利刃信安
利刃信安

利刃信安

2026年8月9日 04:00
北京

在小说阅读器读本章

去阅读

SM2 签名算法 ASN.1 DER 编码 - 签名长度

01

背景

SM2 签名原生格式为 r || s,各 32 字节(256 位),共 64 字节。工程上通常用 ASN.1 DER 格式封装:

...c

SEQUENCE { r INTEGER, s INTEGER }

r、s 均为区间 [1, n-1] 内的正整数(n 为 SM2 曲线阶,仅比 2^256 小约 2^-32 量级)。DER 对正整数的编码只有两条规则:

  1. 1

数值表示为最短的大端字节串:剥离全部冗余前导 0x00(值为 0 时编码为单个 0x00 字节)。
2. 2

若最短字节串首字节的最高位(MSB)为 1,则需在最前面补 0x00,避免被解析为负数。

对 32 字节原始值(r 或 s),设其首部连续 0x00 的个数为 k(0 ≤ k ≤ 31),首个非 0 字节为 B:

形态 编码后值长度 L INTEGER 长度字段
k = 0,B 的 MSB = 0 32 0x20
k = 0,B 的 MSB = 1 33(前补 0x00) 0x21
k ≥ 1,B 的 MSB = 0 32 - k 0x1F 及以下
k ≥ 1,B 的 MSB = 1 33 - k 0x20 及以下

k ≥ 1 且 B 的 MSB=1 时(如原始值 00B1...),前导 00 必须保留:若删去,首字节变为 0xB1(MSB=1),又需补回 00,等于没删。k ≥ 2(如 000035...)的概率约 2^-16 量级,但规则 1 同样要求全部剥离。

02

总长度公式

记 L\_r、L\_s 为 r、s 编码后的值长度,则:

...Code

SEQUENCE 内容长度 = (1 + 1 + L\_r) + (1 + 1 + L\_s) = 4 + L\_r + L\_s

签名总长度       = 2 + (4 + L\_r + L\_s)             = 6 + L\_r + L\_s

常见情形 L ∈ {31, 32, 33},因此总长度 ∈ {68, 69, 70, 71, 72}。SEQUENCE 长度字段取内容长度,66~70 对应 0x42 ~ 0x46,均为单字节。

03

长度分布

r、s 在 [1, n-1] 上近似均匀分布,可精确计算各总长度的占比:

总长度 组成(L\_r, L\_s) 占比
68 (31, 31),或 (30, 32)、(29, 33) 等 ≈ 0.001%
69 (31, 32)、(32, 31) 等 ≈ 0.19%
70 (32, 32),或 (31, 33)、(33, 31) ≈ 25.0%
71 (32, 33)、(33, 32) ≈ 49.8%
72 (33, 33) ≈ 25.0%

71 字节最常见(约一半),70 与 72 各占约四分之一,69 约千分之二,68 平均约 9 万条签名出现一次。

04

71 字节(最常见)

一个分量补 0x00(33 字节),另一个保持 32 字节:

  • 情形 A:r 补 0、s 不变 → 30 45 + 02 21 00 + r(32B) + 02 20 + s(32B)
  • 情形 B:s 补 0、r 不变 → 30 45 + 02 20 + r(32B) + 02 21 00 + s(32B)

示例(对应情形 B):

...Code

30 45

02 20 038A7E7FCADB57D3509E689ABEA97AF39E7CC3C9435ACD545F7191D272B8AE57

02 21 00 CB4C0D66BD68D74538320C969E7564A40C7A08627D136992F440A549030346AF

  • r 原值 038A...,首字节 0x03(MSB=0),保持 32 字节
  • s 原值 CB4C...,首字节 0xCB(MSB=1),补 00 变 33 字节

05

70 字节

三种子情况:

  • (A) r、s 均无前导 0 且首字节 MSB=0,无需任何处理
  • (B) 一个分量补 0、一个分量删 0,净增量 0(示例 1)
  • (C) 一个分量前导 0x00 必须保留,另一个保持 32 字节(示例 2)

示例 1(r 删 0、s 补 0):

...Code

30 44

02 1F 638026332E9FCD750647AFCFC2A5CFD3639D5BF1C9D619F23740241257FD3C

02 21 00 9862D169D9F5BDA16848DB5D1BEDE5A616914680B6CADF1AE373A3D03D365ACF

  • r 原值 006380...,0x63(MSB=0)删 0 变 31 字节
  • s 原值 9862...,0x98(MSB=1)补 0 变 33 字节

示例 2(s 前导 0x00 必须保留):

...Code

30 44

02 20 36A245F75927E7B8B09C9827F92B3913117771679B70838273BA547751778FC5

02 20 00B1E4BCF9FF3ABADC596936FFBA0CA59C1A0E812DD7F5A9BEDC36863C11D119

  • s 原值 00B1...,0xB1(MSB=1),00 必须保留,仍为 32 字节
  • 0084D6C1...

(第 2 字节 0x84,MSB=1)同属此情形

06

72 字节

r、s 均需补 0x00:

...Code

30 46 + 02 21 00 + r(32B) + 02 21 00 + s(32B)

07

69 字节

一个分量删 0(31 字节),另一个保持 32 字节。

情形 A(r 删 0):

...Code

30 43

02 1F 2DF69838E2E40DE378C9B8A2FD40FB87425AF4DB9297B9337A9B6586CBFAF3

02 20 67301CAC496D878723EC946F570922A3D84A6B8D2E618C6AD907E36B13D4833F

  • r 原值 002DF698...,0x2D(MSB=0)删 0 变 31 字节

情形 B(s 删 0):

...Code

30 43

02 20 53EB989118AD14E1CA91A93769CCE234079169A94BFF645240D7DF59FFEF8B5E

02 1F 3B27E6F7849714C6A8EFD43C9E8045BA9B01477C7ADC3C7AFDF8056EBE05CF

  • s 原值 003B27E6...,0x3B(MSB=0)删 0 变 31 字节

08

68 字节(极罕见)

两个分量均删 0(31 + 31 字节);若某分量含多个前导 0x00(如 30 + 32 字节),总长同样为 68。占比约 0.001%,平均约 9 万条签名出现一次。

示例(两个分量各删一个前导 0,随机生成并经 DER 解析校验):

...Code

30 42

02 1F 5AD46B800F372B76CD2609B4F11D6B19E2A4B32864BF89A6EABBBBEEF8965B

02 1F 78F2DFC4670D9D037163CF95EA6C930943FEC3F1FA94B39445AB72BB9CAA66

  • r 原值 005AD46B...,0x5A(MSB=0)删 0 变 31 字节
  • s 原值 0078F2DF...,0x78(MSB=0)删 0 变 31 字节

09

判断流程

对每个分量取 32 字节无符号表示,统计首部连续 0x00 个数 k 与首个非 0 字节 B:

  1. 1

k = 0 且 B 的 MSB=0:原样输出,L = 32(0x20)
2. 2

k = 0 且 B 的 MSB=1:前补 0x00,L = 33(0x21)
3. 3

k ≥ 1 且 B 的 MSB=0:剥离 k 个 0x00,L = 32 - k
4. 4

k ≥ 1 且 B 的 MSB=1:剥离前导 0 后再补 0x00(净效果是保留一个 0x00),L = 33 - k

代入 总长度 = 6 + L\_r + L\_s,先写 SEQUENCE 内容长度(4 + L\_r + L\_s),再依次写两个 INTEGER 的 02、长度、值的 TLV。

参考实现:

...python

def der\_signature(raw\_r: bytes, raw\_s: bytes) -> bytes:

\"\"\"raw\_r / raw\_s 为 32 字节原始签名分量,返回完整 DER 编码。\"\"\"

def encode\_int(raw: bytes) -> bytes:

b = raw.lstrip(b\"\x00\") or b\"\x00\"

if b[0] & 0x80:

b = b\"\x00\" + b

return b\"\x02\" + bytes([len(b)]) + b

body = encode\_int(raw\_r) + encode\_int(raw\_s)

return b\"\x30\" + bytes([len(body)]) + body

10

小结

签名总长度 组成(L\_r, L\_s) SEQUENCE 头 占比
68 0x1F + 0x1F(或含更短分量) 30 42 ≈ 0.001%
69 0x1F + 0x20 30 43 ≈ 0.19%
70 0x20 + 0x20,或 0x21 + 0x1F 30 44 ≈ 25.0%
71 0x21 + 0x20 30 45 ≈ 49.8%
72 0x21 + 0x21 30 46 ≈ 25.0%

工程要点:

  • 接收缓冲区按 72 字节上限预留即可覆盖全部情形;编码时长度字段必须按上述规则动态计算,否则会出现长度与实际内容不符的非法编码。
  • 69 字节及以下合计占比不足 0.2%,但严格验签实现(如 GmSSL、国密安全芯片)会逐字节校验 DER 结构,不应假设签名为固定长度。

我是利刃信安,网络安全和密码安全、数据安全领域的小白。

如果你觉得今天这篇有收获,欢迎点赞、在看、转发三连,我们下篇见

点赞

在看

转发

如有疑问,请联系:Mannix6


SM2签名算法ASN.1DER编码-签名长度" 网络安全文章

SM2签名算法ASN.1DER编码-签名长度"

本文解析了SM2签名ASN.1DER编码规则与长度分布。基于r和s分量的前导零与最高位特征,推导出签名总长度介于68至72字节,其中71字节最常见约占49.8%。建议工程实现时缓冲区按72字节上限预留,且长度须动态计算,切勿假设固定长度,以确保兼容严格验签场景。
《八仙》超喜欢" 网络安全文章

《八仙》超喜欢"

作者沈沉舟带女儿观看动画电影《八仙》,认为该片比《哪吒II》更过瘾,剧情丰富、人物众多,包含对现实生活的吐槽和幽默元素。电影中三星证明身份、反派密码为妈妈等情节令人印象深刻。作者建议有孩子的家庭观看,并期待第二集。文章还回忆了作者童年观看动画片的经历,强调童心未泯的重要性。
【工具推荐】文件上传fuzz字典、自动化工具、bP插件,全部安排到位了,开干!" 网络安全文章

【工具推荐】文件上传fuzz字典、自动化工具、bP插件,全部安排到位了,开干!"

本文推荐了文件上传fuzz字典、Burp插件Upload_Auto_Fuzz和自动化工具UploadRanger,旨在帮助安全人员在攻防演习中快速打点。字典覆盖多种语言可解析后缀,插件支持多种绕过策略,工具具备环境指纹识别和WAF绕过能力。文章提供了工具获取方式并附有免责声明,强调仅限技术研究使用。
评论:0   参与:  0