密码学基础回顾:对称加密与非对称加密、哈希函数与数字摘要、数字签名原理与RSA/ECDSA算法
做固件签名,说白了就是给固件贴上一张「防伪标签」。这张标签怎么贴、怎么撕、怎么验证,全靠密码学这套工具。我刚开始接触嵌入式安全时,总觉得这些算法离底层很远,直到有一次产品被爆出固件被篡改……嗯,从那以后我老老实实把基础补了一遍。
这一章我们快速过一遍核心概念。如果你已经熟悉,可以当复习;如果还有点模糊,我建议你跟着思路走一遍,后面实战才不会卡壳。
对称加密与非对称加密
加密这事儿,核心就两个问题:用什么钥匙锁门?怎么把钥匙交给对方?
对称加密:一把钥匙开一把锁
加密和解密用同一个密钥。速度快,适合大量数据。但问题也很明显——密钥怎么安全地传给对方?
常见算法:AES、DES、3DES、SM4(国密)
典型场景:固件包内部数据加密、通信信道加密(如TLS中的对称部分)
我在项目中遇到过一个问题:两个设备之间用AES加密通信,密钥写死在代码里。结果固件被逆向,密钥直接暴露。所以对称加密的短板不在算法本身,而在密钥分发。
| 特性 | 对称加密 | 非对称加密 |
|---|---|---|
| 密钥数量 | 1个(共享密钥) | 2个(公钥+私钥) |
| 计算速度 | 快(硬件加速常见) | 慢(比对称慢100-1000倍) |
| 密钥分发 | 困难(需要安全通道) | 容易(公钥公开) |
| 典型算法 | AES-128/256 | RSA、ECDSA、SM2 |
| 固件签名适用性 | 不适用(无法防抵赖) | 核心方案 |
非对称加密:公钥锁门,私钥开门
公钥可以公开,私钥自己藏好。别人用你的公钥加密数据,只有你能用私钥解密。反过来,你用私钥签名,别人用公钥验证。
你想想看,这正好解决了固件签名的核心需求:
- 私钥签名 = 只有固件发布者能生成合法签名
- 公钥验证 = 任何设备都能验证签名是否有效
我的习惯:在嵌入式设备中,公钥通常烧录在一次性写入的存储区(如OTP或eFuse),防止被篡改。私钥则放在离线的HSM(硬件安全模块)或专用签名服务器上,绝不让它出现在开发机里。
哈希函数与数字摘要
哈希函数,说白了就是「数据指纹」。不管输入多大,输出固定长度。而且只要改一个比特,整个摘要就面目全非。
为什么固件签名需要哈希?因为非对称加密太慢了。你不可能把整个固件(几MB甚至几十MB)直接用私钥加密。所以先算哈希,再对哈希值签名。
常见哈希算法:SHA-256、SHA-384、SHA-512、SM3(国密)
输出长度:SHA-256 → 32字节;SHA-384 → 48字节;SM3 → 32字节
我曾经踩过一个坑:某款MCU的硬件哈希加速器只支持SHA-1,但SHA-1已经被证明存在碰撞攻击。为了兼容旧芯片,我们不得不在签名流程中额外加一层SHA-256软件计算。嗯,这里要注意:永远不要用SHA-1做固件签名,哪怕硬件支持。
哈希函数的三个核心特性
- 抗碰撞性:找不到两个不同的输入产生相同的摘要
- 单向性:从摘要反推原始数据在计算上不可行
- 雪崩效应:输入微小变化,输出变化巨大
数字签名原理与RSA/ECDSA算法
数字签名,就是「签名 + 验证」两步走。签名用私钥,验证用公钥。固件发布时,发布者用私钥对固件哈希签名;设备启动时,用公钥验证签名是否匹配。
签名流程(以RSA为例)
- 计算固件哈希:
hash = SHA256(firmware.bin) - 用私钥对哈希签名:
signature = RSA_sign(private_key, hash) - 将签名附加到固件尾部或单独存储
验证流程
- 提取固件哈希:
hash = SHA256(firmware.bin) - 用公钥验证签名:
result = RSA_verify(public_key, hash, signature) - 如果验证通过,固件未被篡改;否则拒绝启动
注意:验证时一定要用公钥,不要用私钥。我见过有人把私钥也放到设备里做验证——那等于把锁的钥匙挂在门上。私钥必须永远留在签名服务器端。
RSA vs ECDSA:怎么选?
| 对比项 | RSA | ECDSA |
|---|---|---|
| 密钥长度 | 2048/4096位 | 256/384位(同等安全强度) |
| 签名速度 | 慢(私钥操作) | 快 |
| 验证速度 | 快(公钥操作) | 较快 |
| 签名大小 | 与密钥长度相同(256/512字节) | 固定64/96字节 |
| 硬件支持 | 广泛(老芯片也支持) | 较新芯片才支持 |
| 推荐场景 | 兼容性优先、资源充足 | 资源受限、签名空间敏感 |
我个人习惯:如果芯片硬件支持ECDSA,优先用ECDSA P-256。签名短、速度快,对嵌入式设备友好。如果芯片比较老,或者需要兼容已有基础设施,RSA 2048也是稳妥的选择。
代码示例:Python模拟固件签名与验证
from cryptography.hazmat.primitives import hashes, asymmetric
from cryptography.hazmat.primitives.asymmetric import rsa, ec, padding
# 生成密钥对(RSA 2048)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
# 模拟固件数据
firmware = b"Hello, this is my firmware binary data..."
# 1. 计算哈希
digest = hashes.Hash(hashes.SHA256())
digest.update(firmware)
firmware_hash = digest.finalize()
# 2. 签名
signature = private_key.sign(
firmware_hash,
padding.PKCS1v15(),
hashes.SHA256()
)
# 3. 验证
try:
public_key.verify(
signature,
firmware_hash,
padding.PKCS1v15(),
hashes.SHA256()
)
print("签名验证通过 ✓")
except Exception as e:
print("签名验证失败 ✗")
避坑指南:我曾经在项目中直接用私钥签名整个固件,没先算哈希。结果签名过程耗时十几秒,设备启动时验证又花了十几秒。后来改成「先哈希后签名」,整个流程从秒级降到毫秒级。记住:签名是对哈希签名,不是对原始数据签名。
知识体系总览
下面这张图把本章的核心逻辑串起来了。你可以看到对称加密、非对称加密、哈希函数各自的位置,以及它们如何组合成数字签名流程。
小结
这一章我们覆盖了三个核心概念:
- 对称加密:速度快,但密钥分发是硬伤,不适合签名
- 非对称加密:公钥/私钥分离,签名验证的基础
- 哈希函数:数据指纹,让签名变得高效
- 数字签名:先哈希后签名,RSA和ECDSA各有优劣
记住一句话:固件签名的本质,是用私钥对固件哈希做非对称加密操作,再用公钥验证。后面的章节我们会把这个流程落地到实际硬件上,包括密钥存储、签名工具链、安全启动等。打好这个基础,后面会顺很多。