密码学基础(上):对称加密、非对称加密与哈希函数
各位同学,咱们今天聊聊密码学。说实话,很多搞嵌入式开发的朋友一听到「密码学」三个字就头大,觉得那是数学博士才该碰的东西。但做ECU安全启动,这关你绕不过去。
我个人的习惯是,先别管那些复杂的数学公式,先搞清楚「这东西是干嘛的」、「在车上怎么用」。今天咱们就把对称加密、非对称加密和哈希函数这三板斧讲透。
一、对称加密:AES 在 ECU 中的实战
对称加密,说白了就是「一把钥匙开一把锁」。加密和解密用同一个密钥。你想想看,这就像你家大门钥匙——你自己能用,给老婆一把她也能用,但要是被坏人复制了,那全家都得遭殃。
在ECU领域,最常用的对称加密算法就是 AES(Advanced Encryption Standard)。AES 有几种模式,我重点说两个你在车上一定会遇到的:ECB 和 CBC。
实际项目中,我推荐使用 AES-128 CBC 模式。为什么是128位?因为ECU的硬件加速器通常只支持128位密钥长度,256位虽然更安全,但计算时间翻倍,对实时性要求高的场景不友好。
来看一段伪代码,展示在刷写流程中如何用 AES 解密固件:
// 假设从 CAN 总线收到加密固件块
uint8_t encrypted_block[16]; // AES 块大小 16 字节
uint8_t key[16] = {0x2B, 0x7E, ...}; // 预置密钥
uint8_t iv[16] = {0x00, 0x01, ...}; // 初始化向量
// 硬件 AES 解密(通常由 HSM 或 Crypto 模块完成)
HSE_AES_Decrypt_CBC(encrypted_block, key, iv, decrypted_block);
// 验证解密后的数据(比如 CRC 校验)
if (CRC32_Check(decrypted_block) == PASS) {
// 写入 Flash
Flash_Write(decrypted_block);
} else {
// 触发安全事件
Security_Event_Trigger(SEC_EVENT_DECRYPT_FAIL);
}
这里有个细节要注意:IV(初始化向量)每次刷写都应该不同。我见过有人把 IV 写死在代码里,那 CBC 模式就退化成 ECB 了,等于白忙活。
二、非对称加密:RSA 与 ECC 的取舍
非对称加密,就是「公钥加密,私钥解密」。公钥可以公开,私钥自己藏着。这解决了对称加密里「密钥怎么安全分发」的难题。
在ECU上,非对称加密主要用在两个地方:
- 数字签名验证:验证固件是不是来自官方
- 密钥交换:安全地协商出对称加密的会话密钥
常用的算法有两个:RSA 和 ECC(椭圆曲线密码学)。
| 对比项 | RSA(2048位) | ECC(256位) |
|---|---|---|
| 安全等级 | 高 | 高(同等安全下密钥更短) |
| 签名速度 | 慢(验证快) | 快(签名和验证都快) |
| 密钥长度 | 2048位(256字节) | 256位(32字节) |
| ECU存储开销 | 大 | 小 |
| 硬件加速支持 | 常见 | 较新芯片才支持 |
我个人建议:如果你的芯片支持硬件 ECC 加速(比如 NXP S32K3 系列),优先用 ECC-256。为什么呢?因为 ECU 的 Flash 空间寸土寸金,RSA 的 2048 位公钥要占 256 字节,而 ECC 只要 32 字节。你想想看,一个 Bootloader 里可能要存 3-5 个公钥,这差距就出来了。
三、哈希函数:SHA-256 的不可逆性
哈希函数,说白了就是「指纹提取器」。不管你的固件有多大(1KB 还是 10MB),经过 SHA-256 计算后,都会得到一个 256 位(32 字节)的固定长度摘要。
哈希函数有三个核心特性:
- 不可逆:从摘要反推原始数据,理论上不可能
- 抗碰撞:几乎找不到两个不同的输入产生相同的输出
- 雪崩效应:输入改一个比特,输出完全变样
在 ECU 安全启动中,SHA-256 的典型用法是这样的:
// 步骤1:计算固件镜像的哈希值
uint8_t firmware_hash[32];
SHA256_Calculate(firmware_start_addr, firmware_size, firmware_hash);
// 步骤2:用 RSA/ECC 私钥对哈希值签名
uint8_t signature[256]; // RSA 签名长度
RSA_Sign(private_key, firmware_hash, signature);
// 步骤3:将签名附加到固件末尾,一起刷入 ECU
// 验证时(Bootloader 中):
// 步骤A:重新计算固件哈希
uint8_t computed_hash[32];
SHA256_Calculate(firmware_start_addr, firmware_size, computed_hash);
// 步骤B:用公钥验证签名
if (RSA_Verify(public_key, computed_hash, stored_signature) == PASS) {
// 签名验证通过,固件未被篡改
Jump_To_Application();
} else {
// 签名验证失败,进入安全模式
Enter_Safe_Mode();
}
这里有个坑,我必须要提醒你:哈希计算一定要在安全环境中执行。我曾经见过一个案例,攻击者通过修改哈希计算的输入地址,让 Bootloader 计算了错误的区域,从而绕过了签名验证。所以,建议把哈希计算放在 HSM(硬件安全模块)里完成,或者至少确保输入参数不能被篡改。
四、三者在 ECU 安全启动中的协同
讲完了三个基础工具,咱们来看看它们是怎么配合的。一个典型的 ECU 安全启动流程是这样的:
- 固件发布阶段(在服务器端):
- 用 SHA-256 计算固件哈希
- 用 ECC 私钥对哈希签名
- 用 AES 密钥加密固件(可选,用于防窃取)
- 固件刷写阶段(在产线或 OTA 时):
- ECU 接收加密固件,用预置的 AES 密钥解密
- 计算解密后固件的 SHA-256 哈希
- 用 ECC 公钥验证签名
- 验证通过后,写入 Flash
- 启动验证阶段(每次上电):
- Bootloader 再次计算固件哈希
- 与存储在安全区域的参考哈希对比
- 一致则启动,不一致则报错
嗯,今天就先聊到这儿。下一节咱们会深入讲「安全启动的具体实现」,包括 Bootloader 的分区设计、信任链的建立,以及如何应对常见的攻击手段。到时候我会拿一个真实的 Infineon TC3xx 项目案例来拆解,敬请期待。
公众号:蓝海资料掘金营,微信deep3321