密码学基础回顾:对称加密与非对称加密、哈希函数与数字摘要、数字签名原理与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做固件签名,哪怕硬件支持。

哈希函数的三个核心特性

  1. 抗碰撞性:找不到两个不同的输入产生相同的摘要
  2. 单向性:从摘要反推原始数据在计算上不可行
  3. 雪崩效应:输入微小变化,输出变化巨大

数字签名原理与RSA/ECDSA算法

数字签名,就是「签名 + 验证」两步走。签名用私钥,验证用公钥。固件发布时,发布者用私钥对固件哈希签名;设备启动时,用公钥验证签名是否匹配。

签名流程(以RSA为例)

  1. 计算固件哈希:hash = SHA256(firmware.bin)
  2. 用私钥对哈希签名:signature = RSA_sign(private_key, hash)
  3. 将签名附加到固件尾部或单独存储

验证流程

  1. 提取固件哈希:hash = SHA256(firmware.bin)
  2. 用公钥验证签名:result = RSA_verify(public_key, hash, signature)
  3. 如果验证通过,固件未被篡改;否则拒绝启动

注意:验证时一定要用公钥,不要用私钥。我见过有人把私钥也放到设备里做验证——那等于把锁的钥匙挂在门上。私钥必须永远留在签名服务器端。

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("签名验证失败 ✗")

避坑指南:我曾经在项目中直接用私钥签名整个固件,没先算哈希。结果签名过程耗时十几秒,设备启动时验证又花了十几秒。后来改成「先哈希后签名」,整个流程从秒级降到毫秒级。记住:签名是对哈希签名,不是对原始数据签名。

知识体系总览

下面这张图把本章的核心逻辑串起来了。你可以看到对称加密、非对称加密、哈希函数各自的位置,以及它们如何组合成数字签名流程。

密码学基础与数字签名知识体系 密码学基础与数字签名知识体系 对称加密 AES / DES / SM4 密钥分发困难 不适合签名 非对称加密 RSA / ECDSA / SM2 公钥公开,私钥保密 签名核心方案 哈希函数 SHA-256 / SM3 数据指纹 固定长度输出 数字签名流程 先哈希 → 再签名 → 后验证 签名方(发布者) 持有私钥 计算固件哈希 用私钥签名哈希 验证方(设备) 持有公钥 计算固件哈希 用公钥验证签名 核心原则:私钥签名,公钥验证;先哈希后签名,提升效率

小结

这一章我们覆盖了三个核心概念:

  • 对称加密:速度快,但密钥分发是硬伤,不适合签名
  • 非对称加密:公钥/私钥分离,签名验证的基础
  • 哈希函数:数据指纹,让签名变得高效
  • 数字签名:先哈希后签名,RSA和ECDSA各有优劣

记住一句话:固件签名的本质,是用私钥对固件哈希做非对称加密操作,再用公钥验证。后面的章节我们会把这个流程落地到实际硬件上,包括密钥存储、签名工具链、安全启动等。打好这个基础,后面会顺很多。