一、 综述
在车辆ECU中总是有一些密钥或重要数据需进行机密性保护,但因产品选型、成本等考虑,导致一些ECU的芯片不支持硬件安全模块(例如HSM、TEE等)。此时,为保障数据的机密性,可考虑通过软件实现数据的安全存储:将需保护的数据加密后存储到Flash中。
二、方案
2.1 方案概述
对业务密钥使用AES-256算法(AES-256算法使用的密钥称为保护密钥)进行加密,随后将加密后的业务密钥密文存储在Flash中。此时将对业务密钥的保护转换对保护密钥的保护,后续只需保障保护密钥不被泄露和被篡改,即可保障业务密钥的安全性。方案通过特定的密钥派生算法,基于种子,生成保护密钥。
将种子拆分成多份的主要目的是提升攻击者通过逆向方法获取保护密钥的难度。在攻击者对编译后的Image进行静态分析时,可有效提升分析难度,避免通过扫描String字符串方式直接获取到保护密钥。
- 密钥派生算法:本项目使用PBDKF2。
- 种子:可使用多种形式的种子或互相结合形成种子
1.保存在DFlash中的随机数
2.硬编码在代码中的固定值
3.读取芯片特征值,例如芯片ID
本文档后续以"硬编码在代码中的固定值"作为种子进行举例。若使用其他种子,只需对应扩展即可
2.2 保护密钥生成
将业务密钥进行加密存储后,即可使用把对业务密钥的保护转换为对保护密钥的保护。为提升保护密钥的安全性,需仅在需要保护密钥时,基于特定种子进行生成,避免保护密钥长期直接存在于ECU中。保护密钥生成流程如下:
- 通过硬编码在源代码中的第一处种子,使用密钥派生算法PBKDF2-HMAC-SHA256生成32字节的派生密码;
- 以步骤1中生成的派生密码和硬编码在第二处的种子为基础,使用密钥派生算法PBKDF2-HMAC-SHA256进行运算,得到32字节的派生密码;
- 以步骤2中生成的派生密码和硬编码在第三处的种子为基础,使用密钥派生算法PBKDF2-HMAC-SHA256进行运算,得到32字节的派生密码,即为最终的保护密钥;
保护密钥生成过程应封装为一个函数,便于后续初始化和运行阶段直接调用。通过调用保护密钥生成函数,可直接获得保护密钥。
![](https://i-blog.csdnimg.cn/direct/f59b9a1ee881474dab39b83fd123e4c9.png)
2.3 初始化
实际项目中可能存在业务密钥分默认密钥和生产密钥的情况:
- 默认密钥:在供应商产线即注入,通常为一个特定值,例如全0xFF或全0x00。
- 生产密钥:在OEM产线注入,例如通过UDS 2E服务的某个DID。
2.3.1 初始化准备
定义一个结构体,应包含两个值:
-
业务密钥:用于存储业务密钥明文或密文,长度应为16的整数倍(AES算法填充导致)。
-
标志位:用于表明当前业务密钥是明文还是密文存储。
示例结构体如下:struct bussiness_info {
unsigned int flag;//0:明文存储,1:密文存储
unsigned char bu_key[32];//具体长度需结合实际业务密钥确定,请注意AES加密需填充,故加密后的值必然为16的整数倍
}
2.3.2 默认密钥初始化
默认密钥在FLASH中进行明文存储。故在供应商产线只需将bu_key置为规范中的特定值,flag置为0。
在产品进入OEM产线前,业务密钥均明文存储和使用。
2.3.3 生产密钥初始化
生产密钥通常通过UDS 2E服务的DID在OEM产线进行写入。当通过DID进行写入时,需在写入处理程序中添加加密初始化过程,加密初始化流程如下:
- 接收OEM产线传入的明文业务生产密钥;
- 调用2.2中的保护密钥生成函数,获得保护密钥;
- 将步骤1中接收到的业务生产密钥明文,通过AES-256-CBC加密算法使用保护密钥,得到业务生产密钥密文;
- 构建结构体,其中flag为1,bu_key为步骤3得到的业务密钥密文。并将此结构体覆盖写入默认密钥结构体所在位置(确保覆盖默认密钥所在结构体)。
![](https://i-blog.csdnimg.cn/direct/d4cb5c3572ec4037a8f2c8ba6e6579fc.png)
2.4 运行阶段
当需要使用业务密钥进行业务处理时,应进行如下流程:
- 从FLASH上读取初始化阶段保存的结构体
struct bussiness_info
,判断flag
值,若为0,则表明当前使用默认密钥,故直接读取结构体中bu_key
的值作为业务密钥使用(需注意默认密钥长度); - 若
flag
值为1,则当前使用的为正式密钥,读取结构体中bu_key
的值为密文,需进行解密; - 调用2.2中保护密钥生成函数,获取保护密钥;
- 以读取的
bu_key
密文为输入,通过AES-256-CBC算法使用保护密钥,获得业务生产密钥明文; - 销毁保护密钥;
- 生成的业务生产密钥保存在内存中,并在业务中使用。
![](https://i-blog.csdnimg.cn/direct/debfce1ac0e7453499fb31fdafc31e5d.png)
2.5 完整过程
结合初始化阶段和运行阶段,形成完整的密钥保护工作流程,如下图:
![](https://i-blog.csdnimg.cn/direct/0790a2a4e1e846d2ba11e482f2aad370.png)
三、优化
具体项目中需结合实际使用场景,确定业务密钥明文是常驻内存还是每次使用时解密。若对使用频繁或对性能有要求(例如SecOC密钥等),则业务密钥明文可常驻内存。否则,建议选择更安全的每次使用时解密。
四、算法
4.1 AES算法
本方案对业务密钥进行加解密的算法为AES-256,使用CBC模式。CBC的IV可预定义好,并在代码中硬编码。
4.2 PBKDF2算法
本方案选择密钥派生算法PBKDF2。PBKDF2算法为一种密钥派生算法,PBKDF2算法主要用在防止暴力破解场景,通过增加单次迭代的时间,使暴力破解时间成倍增长,从而提升暴力破解成本。但本方案并不使用此特性,本方案主要目的在通过一组种子作为输入,生成一个关联的派生密钥,作为保护密钥。当种子固定时,通过PBKDF2算法生成的保护密钥必定固定。
PBKDF2算法涉及参数如下:
- 盐值:32字节长度,预定义好并硬编码到代码中
- 迭代次数:2次。因本项目不使用防止暴力破解特性,故次数并不影响安全性,结合效率考虑。需集合实际项目性能,调整次数。
- 伪随机函数:HMAC-SHA-256算法
- 输出密钥长度:32字节
五、扩展
在前面章节的示例中,采用的是硬编码种子的方式。实际项目中种子的选择具有多样性,可采取多种方式选择种子。例如:
1.种子1是硬编码的固定值,种子2为随机数。种子2存储在DFLASH中,当进行2.3.3章节的初始化过程时,由随机数生成器生成并存储在DFLASH中,后续使用时直接读取即可。
2.种子1和种子2都是硬编码的固定值,种子3来自芯片ID。大多数芯片都有一个唯一且固定的序列号,可以读取此序列号作为种子使用。
实际使用时,应考虑具体需求,再确定种子。若需要一机一密,则有种子是随机数或者芯片序列号。若对稳定性要求高,则尽量选择硬编码种子。