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

admin 2026-08-10 04:47:24 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

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


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.1 DER 编码 – 签名长度》

某PHP电商平台代码审计 网络安全文章

某PHP电商平台代码审计

文章总结: 该文档对某PHP电商平台进行代码审计,发现多个高危漏洞,包括未授权使用AI服务、明文存储系统密码、未授权LIKE注入、任意配置覆写和后台命令执行。攻
评论:0   参与:  0