4. 哈希算法与数字签名:SHA-256/SM3、签名生成与验证、签名与加密的区别

这一章我们来聊聊哈希和数字签名。说实话,这两个概念是 PKI 体系的基石。你想想看,如果没有哈希算法,数字签名就失去了效率;如果没有数字签名,那整个 PKI 就没了灵魂。

我在做第一个企业级 PKI 项目时,就被客户问过:「你们这个签名和加密到底有啥区别?不都是把数据变来变去吗?」嗯,这个问题其实挺有代表性的。今天我们就把它彻底讲清楚。

4.1 哈希算法:SHA-256 与 SM3

哈希算法,说白了就是一个「数据指纹」生成器。不管你的输入是一句话还是一部电影,哈希算法都能给你一个固定长度的摘要。而且这个摘要几乎不可能反向还原出原始数据。

核心特性:

  • 单向性:从哈希值无法反推出原文
  • 抗碰撞性:很难找到两个不同输入产生相同哈希值
  • 雪崩效应:输入改一个比特,输出完全变样
  • 固定输出长度:SHA-256 输出 256 位,SM3 也是 256 位

4.1.1 SHA-256

SHA-256 是 SHA-2 家族的一员,输出 256 位(32 字节)的哈希值。它在全球范围内被广泛使用,从 HTTPS 证书到区块链,到处都有它的身影。

我个人习惯在项目里优先推荐 SHA-256,原因很简单:兼容性好,安全性经过长期验证。我记得有一次帮一家金融机构做 PKI 升级,他们原来的系统还在用 SHA-1,我直接建议一步到位换 SHA-256。为什么?因为 SHA-1 已经被证明存在实际碰撞攻击了,继续用就是在给自己埋雷。

4.1.2 SM3 国密算法

SM3 是中国国家密码管理局发布的哈希算法标准,同样输出 256 位。它的设计思路和 SHA-256 类似,但在一些细节上做了优化。

这里有个避坑指南:SM3 和 SHA-256 不兼容。你不能拿 SM3 的哈希值去和 SHA-256 的哈希值做比较,它们完全是两套体系。

对比项 SHA-256 SM3
输出长度 256 位 256 位
分组大小 512 位 512 位
轮数 64 轮 64 轮
消息扩展 线性 非线性
应用场景 国际通用 国内合规

我的建议:如果项目有国密合规要求,直接用 SM3。如果是面向全球的业务,SHA-256 更稳妥。两者在安全性上目前没有本质差距。

4.2 数字签名:生成与验证流程

数字签名解决的是「谁发的、内容有没有被改」这两个问题。它的核心思想是:用私钥签名,用公钥验证。

你想想看,如果我把一份合同签上我的名字,别人拿到后能确认是我签的吗?在纸质世界里,笔迹鉴定可以做到。在数字世界里,数字签名就是那个「笔迹鉴定」工具。

4.2.1 签名生成流程

签名生成分为三步:

  1. 计算哈希:对原始消息做哈希运算,得到消息摘要
  2. 私钥加密:用签名者的私钥对摘要进行加密运算
  3. 附加签名:将加密后的结果作为签名附加到消息上
// 伪代码示例:签名生成
message = "这是一份重要合同"
hash = SHA256(message)          // 第一步:计算哈希
signature = RSA_Sign(hash, privateKey)  // 第二步:私钥签名
output = message + "||" + signature     // 第三步:附加签名

注意:签名不是对整个消息做加密,而是对消息的哈希值做加密。这样做的好处是效率高——不管消息多大,签名运算只处理固定长度的哈希值。

4.2.2 签名验证流程

验证流程是签名生成的逆过程:

  1. 分离签名:从收到的数据中分离出原始消息和签名
  2. 计算哈希:对原始消息做同样的哈希运算
  3. 公钥解密:用签名者的公钥解密签名,得到原始摘要
  4. 比较摘要:对比两个摘要是否一致
// 伪代码示例:签名验证
received_message = "这是一份重要合同"
received_signature = "0x1234..."
hash = SHA256(received_message)           // 第一步:计算哈希
decrypted_hash = RSA_Verify(received_signature, publicKey)  // 第二步:公钥解密
if hash == decrypted_hash:
    print("签名验证通过")
else:
    print("签名验证失败")

我在项目中遇到过好几次签名验证失败的情况,最后发现都是因为哈希算法不一致。比如签名方用了 SHA-256,验证方却用了 SM3,那肯定对不上。所以,签名和验证必须使用相同的哈希算法,这一点一定要在协议设计时就约定好。

4.3 签名与加密的区别

这个问题我经常被问到。很多人觉得签名和加密都是「用密钥处理数据」,容易搞混。其实它们的核心区别非常清晰:

对比维度 数字签名 加密
核心目的 身份认证 + 完整性 机密性
使用的密钥 私钥签名,公钥验证 公钥加密,私钥解密
处理对象 消息的哈希值 整个消息
输出结果 签名值(固定长度) 密文(与消息长度相关)
是否可逆 不可逆(哈希) 可逆(解密)

说白了,签名是为了证明「你是谁」和「内容没被改」,加密是为了保证「别人看不到」。这两个目的完全不同,使用的密钥方向也正好相反。

一个经典比喻:

  • 签名 = 你在合同上盖章,别人可以验证这个章是不是你的
  • 加密 = 你把合同放进保险箱,只有有钥匙的人才能打开

我曾经帮一个客户排查问题,他们用 RSA 私钥去「加密」数据,然后用公钥去「解密」。这其实就是在做签名,但他们误以为是加密。虽然数学运算是一样的,但语义完全不同。签名不保证机密性,加密不保证身份认证。这两个概念千万别混用。

4.4 核心知识体系

下面这张图把哈希、签名、加密的关系梳理清楚了:

哈希算法与数字签名核心知识体系 哈希算法与数字签名核心知识体系 哈希算法 SHA-256 SM3 数字签名 签名生成 签名验证 加密 公钥加密 私钥解密 提供摘要 不同目的 核心要点: • 哈希算法为签名提供固定长度的摘要,提高签名效率 • 签名使用私钥,验证使用公钥;加密使用公钥,解密使用私钥 • 签名保证身份认证和完整性,加密保证机密性,两者不可互相替代 SHA-256 和 SM3 都是 256 位输出,安全性相当,但互不兼容 签名流程:消息 → 哈希 → 私钥签名 → 签名值 验证流程:消息 + 签名 → 哈希 → 公钥验证 → 通过/失败

实战建议:在设计 PKI 方案时,先把哈希算法定下来。SHA-256 和 SM3 选一个,然后整个体系都统一用这个。我见过最头疼的情况就是签名用 SHA-256,证书签名用 SM3,最后两边对不上,排查了整整两天。

好了,这一章的内容就到这里。哈希和签名是 PKI 的根基,理解透了,后面讲证书链、CRL、OCSP 的时候你会觉得轻松很多。记住一句话:哈希做摘要,签名做认证,加密做保密,这三个角色各司其职,谁也替代不了谁。