密码学基础(上):对称加密与非对称加密、哈希函数、数字签名原理
各位同学,咱们今天聊点硬核的。密码学这东西,听起来高大上,其实说白了就是「藏」和「认」两个字。藏,是把数据藏起来不让别人看;认,是确认这东西到底是谁发的、有没有被改过。
我在固件开发这行干了十几年,见过太多因为密码学基础不牢导致的翻车事故。有一次,某款 IoT 设备固件升级包没做签名校验,结果被黑客塞了个恶意固件进去,整批设备变成了「肉鸡」。嗯,从那以后,我带的项目,签名验签这块绝不含糊。
核心观点:没有密码学保护的固件,就像没上锁的保险柜。你只是「假装」安全。
一、对称加密:一把钥匙开一把锁
对称加密,顾名思义,加密和解密用的是同一把密钥。你想想看,就像你家大门钥匙,你自己能开,你老婆也能开——只要钥匙一样。
常见的对称加密算法有:
- AES(Advanced Encryption Standard)—— 目前最主流,我项目里 90% 的场景都用它
- DES —— 老古董了,密钥太短,现在基本没人用
- SM4 —— 国标算法,国内项目必须支持
对称加密最大的优点就是快。我在做嵌入式固件加密时,AES-128 在 Cortex-M4 上跑,加密 1KB 数据只需要几十微秒。但缺点也很明显——密钥怎么安全地传给对方?
我的经验:对称加密适合「自己跟自己玩」的场景。比如固件存在 Flash 里,你用 AES 加密后存进去,启动时用预置的密钥解密。密钥不出设备,问题不大。
但如果你要把加密固件通过网络发给成千上万台设备,每台设备都预置同一个密钥?那只要一台被破解,全军覆没。这就是对称加密的「密钥分发难题」。
二、非对称加密:公钥私钥,各司其职
非对称加密就聪明多了。它有一对密钥:公钥(Public Key)和私钥(Private Key)。公钥可以公开,私钥自己藏好。
怎么用?举个例子:
- 别人用你的公钥加密数据,只有你用私钥能解密
- 你用私钥签名数据,别人用你的公钥验证签名
常见的非对称加密算法:
| 算法 | 密钥长度 | 特点 |
|---|---|---|
| RSA | 2048 / 4096 位 | 最经典,但计算慢,密钥长 |
| ECC(椭圆曲线) | 256 位 | 同等安全强度下密钥更短,适合嵌入式 |
| SM2 | 256 位 | 国密标准,国内项目必备 |
我个人习惯,在资源受限的 MCU 上首选 ECC。256 位的 ECC 密钥,安全强度相当于 3072 位的 RSA,但计算量小得多。我曾经在一个只有 64KB Flash 的芯片上跑 ECC 签名验证,完全跑得动。
注意:非对称加密虽然解决了密钥分发问题,但速度比对称加密慢 1000 倍以上。所以实际项目中,我们通常混合使用:用非对称加密传输对称密钥,再用对称密钥加密大量数据。
三、哈希函数:数据的「指纹」
哈希函数,说白了就是把任意长度的数据,压缩成一个固定长度的「摘要」。这个摘要就像人的指纹——数据不同,指纹就不同;数据一样,指纹一定一样。
哈希函数有几个关键特性:
- 单向性:从哈希值无法反推出原始数据
- 抗碰撞性:很难找到两个不同数据有相同的哈希值
- 雪崩效应:输入改一个 bit,输出完全变样
常用的哈希算法:
- MD5 —— 128 位,已被破解,别用了
- SHA-1 —— 160 位,也有碰撞风险,建议淘汰
- SHA-256 —— 256 位,目前最安全,我项目里都用它
- SM3 —— 国密哈希,256 位
为什么会这样?因为哈希函数在固件签名中扮演着「压缩」的角色。固件可能几 MB 大,非对称加密一次只能加密几百字节。所以先对固件算哈希,得到一个 32 字节的摘要,再对这个摘要签名——效率就上来了。
避坑指南:我曾经遇到一个项目,工程师用 MD5 做固件完整性校验。结果黑客伪造了一个固件,MD5 值竟然和原版一样。从那以后,我规定所有项目哈希必须用 SHA-256 或 SM3。
四、数字签名:给固件盖上「防伪章」
数字签名,就是把哈希函数和非对称加密结合起来。流程是这样的:
- 对固件计算哈希值(比如 SHA-256)
- 用私钥对哈希值加密,得到签名
- 把签名附加在固件后面一起发布
验签时反过来:
- 用公钥解密签名,得到哈希值 A
- 对收到的固件计算哈希值 B
- 比较 A 和 B,相等则固件未被篡改
你想想看,这个流程保证了什么?
- 完整性:固件有没有被改过?哈希值一比对就知道
- 真实性:固件是不是你发的?只有你的私钥能生成有效签名
- 不可否认性:你签了名,就不能说「这不是我发的」
下面我用一个简单的 Python 示例,展示签名和验签的核心逻辑:
# 伪代码示例:固件签名与验签
from cryptography.hazmat.primitives import hashes, asymmetric
from cryptography.hazmat.primitives.asymmetric import ec
# 生成密钥对(ECC P-256)
private_key = ec.generate_private_key(ec.SECP256R1())
public_key = private_key.public_key()
# 固件数据
firmware = b"Hello, this is my firmware v1.0"
# === 签名过程 ===
# 1. 计算哈希
digest = hashes.Hash(hashes.SHA256())
digest.update(firmware)
hash_value = digest.finalize()
# 2. 用私钥签名
signature = private_key.sign(firmware, ec.ECDSA(hashes.SHA256()))
# === 验签过程 ===
try:
public_key.verify(signature, firmware, ec.ECDSA(hashes.SHA256()))
print("验签通过!固件是完整的,且来自可信源。")
except:
print("验签失败!固件可能被篡改或来源不可信。")
关键点:签名不是加密数据,而是加密数据的哈希值。公钥是公开的,任何人都能验签,但只有私钥持有者能签名。
五、知识体系总览
为了让你更直观地理解这些概念之间的关系,我画了一张图:
从这张图你能看到,三大模块各有分工,但又紧密配合。数字签名正是把哈希函数和非对称加密结合起来,才实现了固件的完整性和真实性验证。
六、实际项目中的选择建议
说了这么多理论,最后给点实在的。你在做固件签名方案时,可以参考这个思路:
- 哈希选 SHA-256 或 SM3,别碰 MD5 和 SHA-1
- 签名算法选 ECC P-256 或 SM2,嵌入式资源有限,RSA 太费劲
- 对称加密选 AES-128-GCM,带认证加密,能同时保证机密性和完整性
- 密钥存储用硬件安全单元(HSM 或 SE),别把私钥明文放 Flash 里
血的教训:我曾经见过一个产品,私钥硬编码在代码里,而且用的是对称加密来做「签名」。结果黑客反编译固件,直接拿到了密钥,所有设备都能伪造升级包。记住:签名必须用非对称加密的私钥,而且私钥必须物理隔离。
好了,这一章的内容就到这。密码学基础是固件安全的基石,理解透了,后面讲签名验签流程时你才能游刃有余。下一章我们接着聊数字证书和 PKI 体系,到时候你会看到,光有签名还不够,还得解决「公钥是谁的」这个问题。
可可精品资料库