后量子密码|前置基础 03|数字签名运行逻辑:签名签发、核验原理与实际业务应用
-
- 前言
- 一、数字签名的基本接口
-
- [1.1 生成密钥](#1.1 生成密钥)
- [1.2 签名与验证](#1.2 签名与验证)
- 二、格签名的抽象运行逻辑
-
- [2.1 公开关系](#2.1 公开关系)
- [2.2 生成承诺](#2.2 生成承诺)
- [2.3 验证者如何检查](#2.3 验证者如何检查)
- 三、为什么要拒绝采样
- 四、签名与业务数据
-
- [4.1 软件发布](#4.1 软件发布)
- [4.2 API 请求签名](#4.2 API 请求签名)
- [4.3 证书体系](#4.3 证书体系)
- 五、常见误区
-
- [5.1 签名了摘要,不等于签名了所有业务语义](#5.1 签名了摘要,不等于签名了所有业务语义)
- [5.2 序列化必须规范](#5.2 序列化必须规范)
- [5.3 验证通过不等于授权通过](#5.3 验证通过不等于授权通过)
- 六、本文小结
专栏说明 :《后量子密码》专栏,本专栏面向零基础读者,循序渐进讲解后量子密码理论、NIST标准算法、攻击分析、工程落地与迁移实践。
前言
加密和签名经常同时出现在协议中,但目标不同:加密保护机密性,签名保护完整性并证明签发者身份。
数字签名通常不是"用私钥加密、用公钥解密"。更准确的理解是:签名者利用私钥构造一个只有自己能生成、但任何持有公钥的人都能检查的数学证明。
通俗区分:加密藏内容;签名盖防伪公章,不隐藏原文
一、数字签名的基本接口
1.1 生成密钥
( p k , s k ) ← K e y G e n ( 1 λ ) (pk,sk)\leftarrow KeyGen(1^\lambda) (pk,sk)←KeyGen(1λ)
K e y G e n KeyGen KeyGen:密钥生成算法
1 λ 1^\lambda 1λ:安全参数,λ越大破解难度越高
← \leftarrow ←:算法输出赋值
公钥 p k pk pk 可以公开,私钥 s k sk sk 必须保密。
1.2 签名与验证
签名者执行:
σ ← S i g n ( s k , M ) \sigma\leftarrow Sign(sk,M) σ←Sign(sk,M)
S i g n Sign Sign:签名函数; M M M:待签名原始消息
σ \sigma σ:最终生成的签名串(电子公章)
验证者执行:
V e r i f y ( p k , M , σ ) → { 0 , 1 } Verify(pk,M,\sigma)\rightarrow\{0,1\} Verify(pk,M,σ)→{0,1}
V e r i f y Verify Verify:校验函数;输出1=合法通过,0=篡改/伪造失败
如果消息被修改为 M ′ M' M′,通常应满足:
V e r i f y ( p k , M ′ , σ ) = 0 Verify(pk,M',\sigma)=0 Verify(pk,M′,σ)=0
实际系统中,签名算法通常先计算带域分离的摘要:
μ = H ( domain ∥ M ) \mu=H(\text{domain}\|M) μ=H(domain∥M)
再对 μ \mu μ 生成签名。域分离用于防止同一摘要在不同协议和不同用途之间混用。
domain \text{domain} domain:业务域标识,规避跨场景冒用攻击
μ \mu μ:消息哈希摘要,实际签名对象
二、格签名的抽象运行逻辑
2.1 公开关系
用一个简化的格签名关系表示:
t = A s 1 + s 2 ( m o d q ) \mathbf t=\mathbf A\mathbf s_1+\mathbf s_2\pmod q t=As1+s2(modq)
s 1 、 s 2 \mathbf s_1、\mathbf s_2 s1、s2:私钥核心秘密短向量
t \mathbf t t:公钥公开参数
真实算法会使用多项式模块和标准参数,但这条关系足以说明签名的逻辑。
2.2 生成承诺
签名者随机选择一个短向量:
y ← D y \mathbf y\leftarrow D_y y←Dy
y \mathbf y y:单次签名全新随机向量; D y D_y Dy:随机取值分布
并计算承诺:
w = A y \mathbf w=\mathbf A\mathbf y w=Ay
w \mathbf w w:承诺值,本次签名临时凭证
然后将消息和承诺哈希为挑战:
c = H ( M ∥ w ) \mathbf c=H(M\|\mathbf w) c=H(M∥w)
c \mathbf c c:挑战值,绑定消息与本次签名
最后计算响应:
z = y + c s 1 \mathbf z=\mathbf y+\mathbf c\mathbf s_1 z=y+cs1
z \mathbf z z:签名响应,依靠私钥才能算出
签名可以抽象成:
σ = ( z , c , 辅助信息 ) \sigma=(\mathbf z,\mathbf c,\text{辅助信息}) σ=(z,c,辅助信息)
2.3 验证者如何检查
验证者利用公钥计算:
w ′ = A z − c t \mathbf w'=\mathbf A\mathbf z-\mathbf c\mathbf t w′=Az−ct
w ′ \mathbf w' w′:验证端反向重构出的承诺
将:
t = A s 1 + s 2 ( m o d q ) \mathbf t=\mathbf A\mathbf s_1+\mathbf s_2\pmod q t=As1+s2(modq)
代入:
w ′ = A ( y + c s 1 ) − c ( A s 1 + s 2 ) = A y − c s 2 \begin{aligned} \mathbf w' &=\mathbf A(\mathbf y+\mathbf c\mathbf s_1)-\mathbf c(\mathbf A\mathbf s_1+\mathbf s_2)\\ &=\mathbf A\mathbf y-\mathbf c\mathbf s_2 \end{aligned} w′=A(y+cs1)−c(As1+s2)=Ay−cs2
验证者再根据标准规定的舍入、提示和压缩规则恢复承诺信息,并检查:
c = ? H ( M ∥ w ′ ) \mathbf c\stackrel?=H(M\|\mathbf w') c=?H(M∥w′)
= ? \stackrel?= =?:校验比对符号
等式成立=签名合法;不相等=消息篡改或签名伪造
如果签名者确实掌握合法的小秘密,重新构造出的挑战应当与签名中的挑战一致。
三、为什么要拒绝采样
如果总是公开:
z = y + c s 1 \mathbf z=\mathbf y+\mathbf c\mathbf s_1 z=y+cs1
而随机向量 y \mathbf y y 的分布较窄,响应 z \mathbf z z 的统计特征可能与秘密 s 1 \mathbf s_1 s1 相关。
直接输出z会泄露私钥的统计规律,攻击者可提取私钥
拒绝采样的做法是:
- 生成候选 y \mathbf y y;
- 计算 z \mathbf z z;
- 检查响应是否落入安全范围;
- 不符合条件就丢弃,重新采样。
这样可以减少签名分布对秘密的依赖。代价是签名操作可能需要多次尝试,参数和实现都必须经过严格设计。
四、签名与业务数据
4.1 软件发布
发布者对软件包摘要签名:
σ = S i g n ( s k , H ( package ) ) \sigma=Sign(sk,H(\text{package})) σ=Sign(sk,H(package))
用户使用发布者公钥验证:
V e r i f y ( p k , H ( package ) , σ ) = 1 Verify(pk,H(\text{package}),\sigma)=1 Verify(pk,H(package),σ)=1
如果安装包被替换,摘要发生变化,验证失败。
但公钥本身也需要建立信任,不能只下载一个陌生公钥就相信它属于官方。
4.2 API 请求签名
请求签名应覆盖真正影响业务结果的字段,例如:
M = method ∥ path ∥ timestamp ∥ nonce ∥ H ( body ) M=\text{method}\|\text{path}\|\text{timestamp}\|\text{nonce}\|H(\text{body}) M=method∥path∥timestamp∥nonce∥H(body)
timestamp时间戳、nonce随机串:用于抵御重放攻击
然后:
σ = S i g n ( s k , H ( M ) ) \sigma=Sign(sk,H(M)) σ=Sign(sk,H(M))
服务端除验证签名外,还要检查时间戳、nonce、权限和请求路径。签名本身不能自动防止重放,必须把防重放字段纳入签名范围。
4.3 证书体系
CA 可以对证书主体签名:
σ C A = S i g n ( s k C A , H ( certificate_body ) ) \sigma_{CA}=Sign(sk_{CA},H(\text{certificate\_body})) σCA=Sign(skCA,H(certificate_body))
验证者用 CA 公钥检查签名,再结合有效期、用途、吊销状态和信任链决定是否接受。
后量子迁移时,需要同时考虑证书签名、握手密钥交换和终端验证逻辑,不能只替换某一个算法名称。
五、常见误区
5.1 签名了摘要,不等于签名了所有业务语义
如果业务关心用户、金额和收款账户,签名内容必须覆盖三者:
M = user ∥ amount ∥ account M=\text{user}\|\text{amount}\|\text{account} M=user∥amount∥account
只签金额,攻击者可能替换账户而不影响签名验证。
5.2 序列化必须规范
同一 JSON 对象如果字段顺序、空格、编码方式不同,可能得到不同摘要。因此签名协议必须规定规范化序列化方法。
5.3 验证通过不等于授权通过
V e r i f y ( p k , M , σ ) = 1 Verify(pk,M,\sigma)=1 Verify(pk,M,σ)=1
只能说明签名关系成立,不能单独说明签发者有权执行这项业务。身份、权限、有效期和防重放仍需由协议和业务逻辑检查。
六、本文小结
数字签名的核心流程是:
( p k , s k ) ← K e y G e n (pk,sk)\leftarrow KeyGen (pk,sk)←KeyGen
σ ← S i g n ( s k , M ) \sigma\leftarrow Sign(sk,M) σ←Sign(sk,M)
V e r i f y ( p k , M , σ ) → { 0 , 1 } Verify(pk,M,\sigma)\rightarrow\{0,1\} Verify(pk,M,σ)→{0,1}
格签名的基本思想可以概括为:签名者用私钥生成响应,验证者用公钥重构承诺并重新计算挑战。
下一篇将进入安全模型,解释为什么签名需要 EUF-CMA,KEM 又为什么必须考虑 CCA 攻击。