数字签名基础概念
各位同学,今天咱们来聊聊数字签名。我最早接触这玩意儿是在十年前做 PKI 项目的时候,那时候心里直犯嘀咕:不就是把文件加密一下吗?后来被同事按着头啃了几个月标准文档,才真正明白——数字签名可不是加密的简单翻版,它解决的是完全不同的信任问题。
公钥密码学:一切信任的起点
要理解数字签名,必须先搞懂公钥密码学。说白了,就是我给你一把锁(公钥),我自己留一把钥匙(私钥)。别人可以用这把锁锁上消息,但只有我能打开;反过来,我用钥匙加个标记,任何人都能用锁来验证这个标记是不是我加的。
传统对称加密就好比两个人共享同一把钥匙,你想想看,要是钥匙被截获了,整个体系就塌了。公钥密码学的核心突破在于:加密和解密能力是分离的。在实际场景中,我见过不少开发人员把公钥和私钥混着存,结果私钥泄露出的惨案……嗯,咱们后面会在 HSM 封装章节专门讲怎么安全存储私钥。
数字签名原理:不是加密,是“盖章”
很多人问我:数字签名和加密到底有什么区别?我习惯这样解释——加密是为了不让别人看,签名是为了证明是你写的。打个比方:你在合同上签了个名,旁人无法篡改,但签名本身对合同内容是“可见的”。
具体流程是这样的:
- 哈希摘要:对原始消息做一次哈希运算,得到固定长度的摘要。我最常用的是 SHA-256。
- 私钥加密(签名):用发送方的私钥对这个摘要进行加密运算,结果就是数字签名。
- 打包发送:原始消息 + 数字签名一起发给接收方。
你可能会问:为什么不直接加密整个消息?原因很简单——效率。公钥加密很慢,尤其是大文件。哈希之后只加密一个固定长度的摘要,性能就上来了。而且,哈希的单向性保证了原始消息的完整性。
验签流程:三步确认信任
接收方拿到消息和签名后,验签也分三步走:
- 哈希对比:对收到的原始消息重新计算哈希摘要。
- 公钥解密:用发送方的公钥对签名进行解密,得到一个摘要值。
- 比对:两个摘要值一致 → 验签通过;不一致 → 验签失败。
这里有一个关键点:公钥解密签名这个过程,在数学上等同于用私钥加密的逆操作。一旦解密成功,就说明这个签名一定是持有私钥的人生成的,同时也证明消息没有被篡改。
签名与验签的数学模型(简单看一眼)
我不打算在这里堆公式,但有个基础概念得说清楚。以 RSA 签名算法为例:
- 私钥:d(保密),公钥:n, e(公开)
- 签名值 s = md mod n(其中 m 是哈希后的摘要)
- 验签:计算 se mod n,看是否等于 m
为什么这套东西能工作?因为模幂运算有单向性——知道公钥 e 和 n 很难反推出私钥 d。我在调试一个签名库时遇到过一个大坑:模数 n 长度不够 2048 位,结果几分钟就被暴力分解了。所以 rsa_key_size >= 2048 现在基本是底线。
| 算法 | 用途 | 签名长度 | 我推荐 |
|---|---|---|---|
| RSA (PKCS#1 v1.5) | 通用签名 | 与模长相同 | ❌ 已不推荐 |
| RSA-PSS | 现代签名 | 与模长相同 | ✅ 可接受 |
| ECDSA (P-256) | 高性能场景 | 64字节 | ✅ 强烈推荐 |
| EdDSA (Ed25519) | 极致性能 | 64字节 | ✅ 最新推荐 |
一个完整的流程图:看清全貌
下面这张图把刚刚讲的签名和验签两个阶段串起来了,你仔细看箭头方向——左边是签名阶段,右边是验签阶段。我个人习惯把“摘要计算”这个节点单独标出来,因为它容易被忽略。
实际项目中容易忽略的细节
好,流程讲完了,我来分享几个实战中踩过的坑:
- 哈希算法的选择:我曾经在旧项目里看到 MD5 做签名,结果被碰撞攻击打得体无完肤。现在 SHA-256 是底线,SHA-384/512 更保险。
- 签名填充:RSA 签名必须使用正确的填充方案。PKCS#1 v1.5 填充容易受 Bleichenbacher 攻击,换成 PSS 或者直接用 ECDSA 吧。
- 密钥生命周期:私钥不能永远有效。我建议每年轮换一次,HSM 内部可以自动管理轮换策略,后面章节会细讲。
.env 文件里,还提交到了 Git……后果你懂的。正确的做法是使用 HSM 或 Key Vault 来托管私钥。
小结一下
这一章我们搞清楚了数字签名的本质:它不是用来隐藏内容的,而是用来建立“你是谁”和“内容有没有被改过”这两个事实的。公钥密码学提供了不对称的钥匙对,哈希保证了效率,私钥签名加公钥验签这个流程你闭上眼睛都能讲出来才行。
下节课我们就要开始实战了。我打算先带大家用 OpenSSL 命令行手搓一次签名验签过程,让你亲眼看到每个步骤的魔法。准备好了吗?