4. Preloader安全签名:Preloader镜像结构、签名算法(RSA/ECDSA)、签名工具链
好,我们进入第四讲。前面我们聊了Preloader是什么,也看了它的启动流程。今天要聊的,是Preloader安全体系中最关键的一环——签名。
说白了,签名就是给Preloader镜像贴上一张「防伪标签」。没有这张标签,芯片就不认你。我当年刚接触MTK平台时,第一次烧录自己编译的Preloader,结果手机直接变砖。后来才发现,原来是签名没做对。嗯,从那以后,我对签名流程就格外上心。
4.1 Preloader镜像结构
在聊签名之前,我们先看看Preloader镜像长什么样。你想想看,一个二进制文件,芯片怎么知道哪里是代码、哪里是签名、哪里是配置信息?
MTK的Preloader镜像,其实是一个复合结构。我习惯把它分成三个区域:
| 区域 | 偏移 | 内容 | 大小 |
|---|---|---|---|
| 头部 | 0x0000 | 镜像头(magic、版本、长度、校验和等) | 0x200字节 |
| 主体 | 0x0200 | Preloader可执行代码 + 数据 | 可变(通常128KB~256KB) |
| 签名区 | 末尾 | 数字签名 + 证书链 | 根据算法不同(RSA: 256/512字节,ECDSA: 64/72字节) |
这里有个细节要注意:签名区并不总是紧跟在主体后面。MTK的某些版本会在头部预留一个「签名偏移量」字段,指向签名区的实际位置。我在项目中遇到过,有人直接按固定偏移去解析签名,结果换了芯片版本就解析失败了。
4.2 签名算法:RSA vs ECDSA
MTK平台支持两种签名算法:RSA和ECDSA。你可能会问,为什么要有两种?说白了,就是安全性和性能的权衡。
4.2.1 RSA签名
RSA是经典的非对称加密算法。在Preloader签名中,我们通常使用RSA-2048或RSA-4096。我个人习惯用RSA-2048,因为它在安全性和计算速度之间取得了不错的平衡。
RSA签名的流程是这样的:
- 对Preloader镜像主体计算哈希值(通常是SHA-256)
- 用私钥对哈希值进行加密,生成签名
- 将签名附加到镜像末尾
- BootROM用公钥解密签名,比对哈希值
代码示例(使用OpenSSL命令行):
# 生成RSA私钥
openssl genrsa -out preloader_private.pem 2048
# 提取公钥
openssl rsa -in preloader_private.pem -pubout -out preloader_public.pem
# 对Preloader镜像签名
openssl dgst -sha256 -sign preloader_private.pem -out preloader.sig preloader.bin
# 验证签名
openssl dgst -sha256 -verify preloader_public.pem -signature preloader.sig preloader.bin
4.2.2 ECDSA签名
ECDSA(椭圆曲线数字签名算法)是近年来越来越流行的选择。它的优势在于:同样的安全强度下,密钥更短、签名更小、计算更快。
举个例子:RSA-2048的安全强度,用ECDSA只需要P-256曲线就能达到。签名大小也从256字节降到了64字节。对于Preloader这种对空间敏感的场景,ECDSA有明显优势。
ECDSA签名的流程:
- 对Preloader镜像主体计算哈希值(SHA-256)
- 用ECDSA私钥对哈希值进行签名,生成(r, s)对
- 将(r, s)序列化后附加到镜像末尾
- BootROM用公钥验证(r, s)对的有效性
代码示例:
# 生成ECDSA私钥(P-256曲线)
openssl ecparam -genkey -name prime256v1 -out ecdsa_private.pem
# 提取公钥
openssl ec -in ecdsa_private.pem -pubout -out ecdsa_public.pem
# 签名
openssl dgst -sha256 -sign ecdsa_private.pem -out preloader_ecdsa.sig preloader.bin
# 验证
openssl dgst -sha256 -verify ecdsa_public.pem -signature preloader_ecdsa.sig preloader.bin
4.2.3 如何选择?
| 对比项 | RSA-2048 | ECDSA P-256 |
|---|---|---|
| 签名大小 | 256字节 | 64字节 |
| 公钥大小 | 256字节 | 64字节 |
| 签名速度 | 较慢 | 较快 |
| 验证速度 | 较快 | 较慢 |
| 硬件支持 | 几乎所有芯片 | 较新芯片支持 |
我个人建议:如果你的芯片支持ECDSA硬件加速,优先选ECDSA。否则,RSA-2048是更稳妥的选择。我在一个低端项目上试过用ECDSA,结果BootROM没有硬件加速,验证花了将近1秒,差点导致启动超时。
4.3 签名工具链
MTK官方提供了一套完整的签名工具链。我习惯把它分成三部分:
4.3.1 密钥管理工具
MTK的 keygen_tool 用于生成和管理密钥对。它支持RSA和ECDSA两种算法。
# 生成RSA密钥对
keygen_tool -t rsa -k 2048 -o ./keys/
# 生成ECDSA密钥对
keygen_tool -t ecdsa -c prime256v1 -o ./keys/
嗯,这里要注意:MTK的工具生成的密钥格式是专有的,不是标准的PEM格式。如果你需要用OpenSSL做交叉验证,需要做格式转换。
4.3.2 签名工具
sign_tool 是实际的签名工具。它会读取Preloader镜像,计算哈希,用私钥签名,然后把签名写入镜像的指定位置。
# 签名Preloader
sign_tool -i preloader.bin -o preloader_signed.bin \
-k ./keys/private.key \
-a rsa -h sha256
这个工具还会自动更新镜像头中的签名信息,比如签名算法类型、签名偏移量、签名长度等。你不需要手动去修改这些字段。
4.3.3 验证工具
verify_tool 用于验证签名是否正确。这个工具在开发阶段非常有用,可以在烧录前就发现签名问题。
# 验证签名
verify_tool -i preloader_signed.bin -p ./keys/public.key
如果验证失败,工具会给出详细的错误信息,比如哈希不匹配、签名格式错误、证书链不完整等。
4.4 实际项目中的注意事项
最后,分享几个我在实际项目中踩过的坑:
- 密钥保护:私钥一定要放在安全的地方。我曾经在一个项目中,开发人员把私钥放在了共享文件夹里,结果被误删了,导致所有已签名的镜像都无法再签名。
- 版本兼容性:不同版本的MTK芯片,签名算法和工具链可能不兼容。升级芯片时,一定要重新生成密钥和签名。
- 签名验证的时机:BootROM只在第一次启动时验证Preloader签名。如果验证通过,后续启动不会再验证。这意味着,一旦签名被绕过,后续的所有安全机制都形同虚设。
- 证书链:MTK支持多级证书链。我建议至少使用两级:根证书和签名证书。根证书烧录在OTP中,签名证书用于日常签名。这样即使签名证书泄露,也不会影响根证书的安全性。
好了,这一讲的内容就到这里。Preloader签名是整个安全启动的基石,一定要重视。下一讲,我们会聊LK的安全启动,看看签名机制是如何延续到下一阶段的。