3. 数字签名原理:签名生成流程、签名验证流程、RSA签名算法详解
数字签名,说白了就是现实世界里「盖章签字」在网络世界的翻版。你想想看,一份纸质合同,我签了名、盖了章,对方就能确认这确实是我干的,而且内容没被人改过。数字签名干的就是同样的事——只不过用的是数学,不是印泥。
我记得刚入行那会儿,总觉得数字签名是个很玄乎的东西。后来亲手实现了一遍RSA签名,才发现核心逻辑其实很清晰。今天咱们就把这块彻底掰开揉碎。
3.1 签名生成流程:私钥才是你的「私章」
签名生成,说白了就是「用私钥给消息打个标记」。这个标记只有持有私钥的人能打出来,别人想仿造?几乎不可能。
流程其实就三步:
- 对原始消息做哈希运算——把任意长度的消息,压缩成一个固定长度的「摘要」。
- 用私钥加密这个摘要——这就是签名的核心操作。
- 把原始消息和签名一起发给对方——注意,消息本身是明文的,不需要加密。
关键点:签名不是加密整个消息,而是加密消息的哈希值。这样做有两个好处:一是速度快(哈希很短),二是安全性高(哈希不可逆)。
我在项目中遇到过有人问:「既然私钥加密了摘要,那公钥解密后得到的不就是摘要吗?这不就等于加密传输了吗?」嗯,这里要区分清楚——签名不是为了隐藏内容,而是为了证明「这个摘要确实来自私钥持有者」。说白了,签名是「身份证明」,不是「保密信封」。
3.2 签名验证流程:公钥就是你的「验章机」
验证流程更简单,也是三步:
- 用公钥解密签名——得到发送方声称的「摘要A」。
- 对收到的原始消息做同样的哈希运算——得到「摘要B」。
- 比较摘要A和摘要B——如果一致,签名有效;否则无效。
为什么会这样?因为只有持有私钥的人,才能生成一个「用公钥能解密出正确摘要」的签名。如果消息被篡改过,哈希值就会变,比对自然失败。
个人经验:我曾经调试过一个签名验证失败的问题,折腾了半天,最后发现是发送方和接收方用的哈希算法不一样——一个用了SHA-256,另一个用了SHA-1。这种坑,你想想看,光看代码根本看不出来,必须两边对齐算法参数。
3.3 RSA签名算法详解
RSA签名,其实就是RSA加密算法在签名场景下的应用。不过要注意:签名用的是私钥,验证用的是公钥——这和加密正好反过来。
3.3.1 RSA密钥对生成
RSA的密钥对生成,核心是依赖大整数分解的数学难题。我简单说一下流程:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 选择两个大素数 p 和 q | 通常要求 1024 位以上,p 和 q 长度相近 |
| 2 | 计算 n = p × q | n 就是公钥和私钥都会用到的模数 |
| 3 | 计算 φ(n) = (p-1)(q-1) | 欧拉函数值,用于后续计算 |
| 4 | 选择公钥指数 e | 常用值 65537,要求 1 < e < φ(n) 且 gcd(e, φ(n)) = 1 |
| 5 | 计算私钥指数 d | d ≡ e⁻¹ mod φ(n),即 e 关于 φ(n) 的模逆元 |
最终:公钥 = (n, e),私钥 = (n, d)。p、q、φ(n) 必须销毁或严格保密。
3.3.2 RSA签名与验证的数学表达
用公式表示,其实特别简洁:
签名:signature = hash(message) ^ d mod n
验证:hash' = signature ^ e mod n
比较:hash' == hash(message) ? 有效 : 无效
你想想看,这里面的数学原理其实很巧妙。因为 d 和 e 是互为逆元的,所以:
(hash ^ d) ^ e ≡ hash ^ (d × e) ≡ hash (mod n)
也就是说,用私钥签名后再用公钥验证,一定能还原出原始的哈希值。而其他人没有私钥 d,根本算不出正确的签名。
避坑指南:我曾经见过一个项目,直接拿RSA加密整个消息当签名用。这样做有两个问题:一是性能极差(RSA加密长消息很慢),二是存在安全风险(某些攻击场景下会泄露信息)。记住:签名永远是对哈希值操作,不是对原始消息操作。
3.3.3 实际应用中的填充方案
直接对哈希值做RSA运算是不安全的。为什么?因为如果哈希值太短(比如SHA-256只有256位),攻击者可以通过数学变换伪造签名。所以实际应用中必须加填充。
目前主流方案有两种:
| 方案 | 特点 | 推荐场景 |
|---|---|---|
| PKCS#1 v1.5 | 兼容性好,实现简单 | 旧系统兼容,但新系统不推荐 |
| PSS (Probabilistic Signature Scheme) | 安全性更高,有严格证明 | 新系统首选,推荐使用 |
我个人的习惯:新项目一律用PSS填充。虽然实现上比PKCS#1 v1.5多几行代码,但安全性有数学证明保障。我在一个金融项目中吃过PKCS#1 v1.5的亏——某些攻击工具专门针对它做选择密文攻击。从那以后,我再也不敢偷懒了。
3.4 核心流程可视化
下面这张图,把签名和验证的完整流程画清楚了。我建议你多看几遍,把每一步的输入输出都理清楚。
3.5 常见误区与避坑
做签名开发这些年,我踩过不少坑。挑几个典型的说说:
- 哈希算法不匹配:签名方用SHA-256,验证方用SHA-1,结果永远对不上。我建议在协议设计阶段就把算法固定下来,写在文档里。
- 填充方案不一致:一方用PSS,另一方用PKCS#1 v1.5,验证必败。而且这两种填充的输出长度可能不同,会导致更隐蔽的问题。
- 密钥长度太短:1024位RSA现在已经不安全了。我个人的底线是2048位,敏感场景建议4096位。
- 签名和加密混用:有人觉得「签名就是加密的反向操作」,于是用同一对密钥既做签名又做加密。这是大忌——会引入严重的安全漏洞。
曾经踩过的坑:有一次,我在对接第三方支付接口时,对方文档里写的是「RSA签名」,但实际用的是「RSA加密整个报文」。结果两边联调了三天,最后发现是对方把签名和加密搞混了。从那以后,我每次对接都会先确认:你们用的是标准签名流程吗?哈希+私钥加密?还是直接加密整个消息?