密码学基础(上):对称加密、非对称加密与哈希函数

各位同学,咱们今天聊聊密码学。说实话,很多搞嵌入式开发的朋友一听到「密码学」三个字就头大,觉得那是数学博士才该碰的东西。但做ECU安全启动,这关你绕不过去。

我个人的习惯是,先别管那些复杂的数学公式,先搞清楚「这东西是干嘛的」、「在车上怎么用」。今天咱们就把对称加密、非对称加密和哈希函数这三板斧讲透。

一、对称加密:AES 在 ECU 中的实战

对称加密,说白了就是「一把钥匙开一把锁」。加密和解密用同一个密钥。你想想看,这就像你家大门钥匙——你自己能用,给老婆一把她也能用,但要是被坏人复制了,那全家都得遭殃。

在ECU领域,最常用的对称加密算法就是 AES(Advanced Encryption Standard)。AES 有几种模式,我重点说两个你在车上一定会遇到的:ECB 和 CBC

⚠️ 避坑指南: 我曾经在某个项目里看到同事直接用 ECB 模式加密 Bootloader 的固件。ECB 模式有个致命问题——同样的明文块会生成同样的密文块。这在固件加密里非常危险,攻击者可以通过模式识别猜出代码结构。所以,ECB 模式在 ECU 开发中基本被禁用,除非你明确知道自己在做什么。

实际项目中,我推荐使用 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 个公钥,这差距就出来了。

💡 实战经验: 我在做 T-Box 项目时,遇到过 RSA 签名验证耗时超过 500ms 的情况。后来换成 ECC-256,验证时间降到了 80ms 以内。对于要求上电即启动的 ECU,这 400ms 的差距可能就是生与死的区别。

三、哈希函数: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 安全启动流程是这样的:

  1. 固件发布阶段(在服务器端):
    • 用 SHA-256 计算固件哈希
    • 用 ECC 私钥对哈希签名
    • 用 AES 密钥加密固件(可选,用于防窃取)
  2. 固件刷写阶段(在产线或 OTA 时):
    • ECU 接收加密固件,用预置的 AES 密钥解密
    • 计算解密后固件的 SHA-256 哈希
    • 用 ECC 公钥验证签名
    • 验证通过后,写入 Flash
  3. 启动验证阶段(每次上电):
    • Bootloader 再次计算固件哈希
    • 与存储在安全区域的参考哈希对比
    • 一致则启动,不一致则报错
🔑 核心要点: 非对称加密(ECC)负责「信任根」——确保固件来源可信;哈希函数(SHA-256)负责「完整性」——确保固件未被篡改;对称加密(AES)负责「机密性」——防止固件被逆向分析。三者缺一不可。

嗯,今天就先聊到这儿。下一节咱们会深入讲「安全启动的具体实现」,包括 Bootloader 的分区设计、信任链的建立,以及如何应对常见的攻击手段。到时候我会拿一个真实的 Infineon TC3xx 项目案例来拆解,敬请期待。

公众号:蓝海资料掘金营,微信deep3321