密码学基础回顾:哈希函数、非对称加密、数字签名与PKI体系
各位同学,咱们今天聊点硬核的。做TEE安全启动,密码学是绕不开的基石。我见过不少工程师,上来就撸代码,结果签名验签搞反了,哈希算法选错了,最后启动流程跑不通。嗯,咱们先把基础夯实。
一、哈希函数:数据的“指纹”
哈希函数,说白了就是把任意长度的数据,压缩成一个固定长度的摘要。这个摘要就像人的指纹,理论上很难找到两个不同的输入产生相同的输出。
SHA-256 是目前最常用的哈希算法之一。它输出256位(32字节)的摘要。我在项目中遇到过,有人为了省空间,只取了前16字节做校验,结果被攻击者碰撞了。记住,安全启动里,哈希值必须完整使用。
核心特性:
- 单向性:从摘要反推原始数据,计算上不可行。
- 抗碰撞性:找到两个不同输入,产生相同摘要,极其困难。
- 雪崩效应:输入改一个比特,输出会有一半以上的比特发生变化。
为什么TEE启动要用哈希?你想想看,固件镜像可能几兆甚至几十兆,直接签名效率太低。我们通常先对镜像做哈希,然后只对哈希值签名。这样既安全又高效。
// 伪代码示例:计算固件哈希
uint8_t firmware_hash[32];
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, firmware_image, firmware_size);
sha256_final(&ctx, firmware_hash);
// 此时 firmware_hash 就是固件的“指纹”
我的经验: 选择哈希算法时,别只看输出长度。SHA-256在大多数嵌入式芯片上都有硬件加速,性能远好于SHA-512。我曾经在一个Cortex-M4项目里,用软件实现SHA-512,结果启动时间多了3秒,后来切回SHA-256,问题解决。
二、非对称加密:RSA与ECC
非对称加密,核心思想是“公钥加密,私钥解密”或者“私钥签名,公钥验签”。这和对称加密完全不同,对称加密两边用同一个密钥,分发密钥本身就是个大麻烦。
RSA 基于大整数分解难题。密钥越长越安全,但计算也越慢。我建议在TEE场景下,RSA密钥至少2048位,1024位已经被证明不安全了。
ECC(椭圆曲线密码学) 基于椭圆曲线离散对数难题。它的优势在于,同等安全强度下,密钥长度更短。比如,256位的ECC密钥,安全性和3072位的RSA相当。这对嵌入式设备来说,简直是福音——存储空间和计算资源都有限。
| 安全等级 | RSA密钥长度 | ECC密钥长度 |
|---|---|---|
| 80位 | 1024位 | 160位 |
| 112位 | 2048位 | 224位 |
| 128位 | 3072位 | 256位 |
| 256位 | 15360位 | 512位 |
避坑指南: 我曾经在一个项目中,看到有人把ECC私钥直接硬编码在固件里。这等于把家门钥匙挂在门外。私钥必须存储在安全硬件中,比如TEE的信任根或安全元件(SE)。
三、数字签名原理
数字签名,说白了就是用私钥给数据“盖章”,别人用公钥来验证这个章是真的。它保证了数据的完整性和来源的真实性。
流程是这样的:
- 发送方对数据做哈希,得到摘要。
- 发送方用私钥对摘要加密,生成签名。
- 接收方收到数据和签名后,用同样的哈希算法计算摘要。
- 接收方用公钥解密签名,得到原始摘要。
- 比较两个摘要,一致则签名有效。
在TEE安全启动里,这个流程被用在每个启动阶段。BootROM验证下一级Bootloader的签名,Bootloader再验证OS内核的签名,环环相扣。
// 签名验证伪代码
uint8_t hash[32];
sha256(firmware, firmware_size, hash);
// 使用公钥验证签名
int result = rsa_verify(public_key, hash, signature);
if (result == 0) {
// 签名有效,启动继续
} else {
// 签名无效,拒绝启动
panic();
}
我的习惯: 验证签名时,一定要做“恒定时间比较”,防止时序攻击。不要用memcmp,它一旦发现不匹配就立即返回,攻击者可以通过响应时间推断出正确字节。用专门的恒定时间比较函数。
四、证书链与PKI体系
公钥怎么分发?你总不能把公钥写在固件里吧?万一被篡改了呢?这就引出了PKI(公钥基础设施)和证书链。
证书,本质上是一个把公钥和身份信息绑定在一起的数据结构,由证书颁发机构(CA)签名。证书链,就是从根CA证书,到中间CA证书,再到最终实体证书的一条信任路径。
在TEE安全启动中,通常会在芯片出厂时,把根CA的公钥烧录到一次性可编程存储器(OTP)里。这就是信任根。后续所有证书的验证,都追溯到这根公钥。
验证流程:
- 用根CA公钥验证中间CA证书的签名。
- 用中间CA公钥验证设备证书的签名。
- 用设备证书中的公钥验证固件的签名。
只要任何一环验证失败,启动过程立即终止。
为什么需要中间CA?直接让根CA给每个设备签名不行吗?行,但风险太大。根CA私钥一旦泄露,整个体系就完了。中间CA相当于一个缓冲,即使某个中间CA的私钥泄露,只需要吊销它的证书,根CA还是安全的。
我曾经踩过的坑: 证书有效期没检查。有一批设备出厂两年后,证书过期了,导致无法进行安全更新。后来我们强制在验证逻辑里加入时间检查,并且使用有效期更长的证书。记住,证书不是永久的。
好了,密码学基础就聊到这儿。这些概念在后续的TEE启动流程里会反复出现。你理解得越深,后面写代码就越顺手。