哈希算法是密码学中最基础的构件之一。
它可以把任意长度的数据映射成固定长度的摘要。例如,SHA-256 可以输出 32 字节摘要。
在嵌入式系统中,哈希算法常用于:
- 固件完整性校验;
- 消息摘要;
- 证书摘要;
- 配置参数校验;
- 密钥派生;
- 日志防篡改链。
需要注意的是,Hash 可以发现数据变化,但不能单独证明数据来源。
| 关键词 | 工程含义 |
|---|---|
| Hash / 摘要 | Hash 发现数据变化,但不能单独证明数据来源。 |
1. 哈希是什么
哈希算法,也叫摘要算法。
它的作用是把一段任意长度的数据,经过计算后,生成一个固定长度的摘要值。
例如:
输入数据:firmware.bin
哈希算法:SHA-256
输出摘要:32 字节 Hash 值
无论输入数据是几 KB、几 MB,还是几十 MB,SHA-256 最终都会输出固定的 32 字节摘要。
因此,Hash 可以理解为数据的"指纹"。
只要原始数据发生变化,摘要值就会随之变化。
2. 哈希的基本特性
一个安全的哈希算法通常应具备以下特性。
2.1 容易计算
给定一段输入数据,计算它的 Hash 应该是高效、可实现的。
这对嵌入式系统很重要,因为 MCU 的主频、RAM 和 Flash 资源通常比较有限。
2.2 不可逆
从原始数据计算 Hash 很容易,但从 Hash 反推出原始数据在计算上不可行。
也就是说:
数据 → Hash:容易
Hash → 数据:困难
因此,Hash 不是加密。
加密通常可以通过密钥解密还原明文,而 Hash 是单向函数,正常情况下不能还原原文。
2.3 雪崩效应
原文只要发生很小变化,输出摘要也会发生明显变化。
例如,固件中只修改 1 个字节,最终得到的 Hash 也会完全不同。
这使得 Hash 非常适合用于判断数据是否发生变化。
2.4 抗碰撞
理想情况下,很难找到两个不同的输入,使它们得到相同的 Hash 值。
如果两个不同固件能得到相同摘要,就会破坏完整性校验的可靠性。
因此,在安全设计中,应选择当前仍被认为安全的哈希算法,例如 SHA-256,而不是 MD5、SHA-1 这类已经不适合作为安全用途的算法。
3. 哈希能做什么
在嵌入式系统中,Hash 的应用非常广。
3.1 固件镜像校验
设备可以对应用固件计算 Hash,用于判断固件内容是否发生变化。
例如:
Bootloader 读取 App 固件
Bootloader 计算 App Hash
Bootloader 对比期望 Hash
如果 Hash 不一致,说明固件内容可能被破坏或被篡改。
3.2 升级包完整性检查
OTA 或本地升级包下载完成后,可以先计算升级包 Hash,判断下载过程是否出错。
这可以发现:
- 传输错误;
- 存储损坏;
- 文件不完整;
- 数据被修改。
但是,如果只校验 Hash,而不验证签名,仍然不能证明升级包来自官方。
3.3 文件摘要和配置参数校验
Hash 可以用于配置参数、校准参数、生产参数的摘要计算。
例如:
- 出厂校准参数;
- 传感器补偿参数;
- 设备配置文件;
- 生产序列号表;
- 授权配置表。
系统启动时重新计算 Hash,可以判断这些数据是否被修改。
3.4 证书内容摘要
在证书链、数字签名和安全通信中,Hash 经常作为基础步骤。
通常不会直接对完整大文件做签名,而是先计算数据 Hash,再对 Hash 进行签名。
这样可以降低计算量,也便于统一处理固定长度的数据摘要。
3.5 日志防篡改链
Hash 还可以用于构建日志防篡改链。
例如,每条日志不仅包含当前事件内容,还包含上一条日志的 Hash。
Log_1_Hash = Hash(Log_1)
Log_2_Hash = Hash(Log_2 + Log_1_Hash)
Log_3_Hash = Hash(Log_3 + Log_2_Hash)
这样一旦中间某条日志被修改,后续 Hash 链都会不一致。
不过,如果要防止攻击者整体重算日志链,还需要结合密钥、MAC、签名或安全存储。
4. 安全启动中的 Hash
在安全启动中,Hash 通常不是单独使用,而是和数字签名一起使用。
典型流程如下:
- Bootloader 读取应用固件;
- 计算应用固件的 Hash;
- 读取固件签名;
- 使用内置公钥验证签名;
- 判断签名中的 Hash 是否与当前固件 Hash 一致;
- 验证通过后,才允许跳转到应用程序。
这里的关键点是:
Hash 判断固件有没有变化;
签名证明这个 Hash 是否来自合法发布方。
如果只有 Hash,没有签名,攻击者修改固件后也可以重新计算 Hash。
因此,安全启动不能只依赖 Hash 校验。
5. 哈希不能做什么
Hash 很重要,但它不是万能的。
5.1 单独 Hash 不能证明数据来源
Hash 只能证明:
当前数据和摘要是否匹配。
它不能证明:
摘要本身是不是可信的。
如果攻击者修改数据后,重新计算一个新的 Hash,并把新 Hash 一起发给设备,那么接收方仍然可能被骗。
所以,单独 Hash 不能证明数据是谁发来的。
5.2 Hash 不能替代认证
认证要解决的是:
对方是否拥有合法密钥或私钥?
Hash 本身没有密钥参与,任何人都可以计算。
因此,它不能用于证明设备身份,也不能证明升级包来自官方发布方。
工程上需要使用:
- MAC;
- HMAC;
- 数字签名;
- 证书链;
- 挑战响应。
这些机制才能建立身份可信。
5.3 Hash 不能替代加密
Hash 是单向摘要,不是加密算法。
它不能用于还原原文,也不能保护明文内容不被别人读取。
如果数据内容本身需要保密,应使用加密算法,例如:
- AES;
- ECC 密钥交换 + 会话密钥;
- TLS / DTLS;
- 加密 Flash;
- 加密升级包。
6. 与 CRC 的区别
CRC 和 Hash 都可以发现数据变化,但它们的安全属性不同。
| 对比项 | CRC | Hash |
|---|---|---|
| 主要用途 | 检测通信误码、存储误码 | 数据摘要、完整性基础构件 |
| 是否密码学安全 | 否 | 取决于算法,例如 SHA-256 是安全哈希算法 |
| 是否抗恶意篡改 | 否 | 单独使用仍不够,需要结合 MAC 或签名 |
| 是否容易被重新计算 | 容易 | 也可以被重新计算 |
| 是否能证明来源 | 不能 | 不能 |
| 典型场景 | 通信帧校验、Flash 数据误码检测 | 固件摘要、证书摘要、安全启动、签名输入 |
CRC 主要用于检测随机错误,例如通信误码、存储损坏和数据传输异常。
它算法简单、速度快,非常适合底层通信协议。
但是,CRC 不是密码学安全算法。
攻击者可以修改数据后重新计算 CRC,使接收方误以为数据没有变化。
7. 为什么不能用 CRC 替代 Hash 或签名
在安全设计中,不能用 CRC 替代 Hash,更不能用 CRC 替代签名。
原因是:
- CRC 设计目标是检测随机误码,不是抵抗恶意攻击;
- CRC 没有抗碰撞安全性;
- CRC 没有密钥参与;
- CRC 很容易被攻击者重新计算;
- CRC 不能证明数据来源;
- CRC 不能证明固件来自官方发布方。
因此:
CRC 适合做通信可靠性校验;
Hash 适合做安全摘要基础;
MAC / 数字签名才适合做可信验证。
8. 工程判断方法
在项目中,可以用下面几个问题判断是否应该使用 Hash,以及是否还需要进一步安全机制。
8.1 只是为了发现随机错误吗?
如果只是为了发现通信误码、Flash 读写错误或数据包损坏,CRC 通常就够用。
8.2 是否要判断数据有没有被恶意修改?
如果需要抵抗恶意修改,单独 CRC 不够,应至少考虑 Hash,并进一步结合 MAC 或数字签名。
8.3 是否要证明数据来自合法对象?
如果需要证明来源,单独 Hash 也不够,应使用:
- HMAC;
- MAC;
- 数字签名;
- 证书链。
8.4 是否要保护数据内容不被读取?
如果需要隐藏内容,Hash 不够,应使用加密机制。
9. 嵌入式工程示例
示例 1:Bootloader 校验 App
普通可靠性校验:
Bootloader 计算 App CRC
CRC 正确 → 跳转 App
这种方式可以发现 Flash 损坏,但不能防止攻击者替换 App 后重算 CRC。
安全启动设计:
Bootloader 计算 App Hash
Bootloader 使用公钥验证 App 签名
签名正确 → 跳转 App
签名失败 → 拒绝启动
这才可以证明 App 来自合法发布方,并且没有被篡改。
示例 2:升级包下载
普通下载校验:
下载升级包
计算 CRC / Hash
判断文件是否下载完整
安全升级校验:
下载升级包
计算升级包 Hash
验证升级包签名
检查版本号和防回滚计数器
验证通过后写入 Flash
Hash 负责发现内容变化,签名负责证明升级包来源。
示例 3:配置参数保护
如果只是防止掉电写坏或 Flash 位翻转,可以使用 CRC。
如果要防止用户或攻击者私自修改配置参数,则应使用:
- Hash + 签名;
- HMAC;
- 安全存储;
- 权限控制;
- 参数版本号;
- 防回滚计数器。
10. 工程总结
Hash 解决的是:
数据有没有变。
但 Hash 不解决:
数据是谁发来的;
数据是否来自合法发布方;
数据内容是否需要隐藏。
因此,工程上通常要把 Hash 与密钥、MAC、HMAC 或数字签名结合使用。
简单来说:
CRC 解决随机误码;
Hash 解决数据摘要;
MAC / 签名解决可信来源;
加密解决内容保密。
在嵌入式安全设计中,Hash 是基础构件,但不是完整方案。