ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

车规MCU安全启动、SecOC通信与部件保护落地解析

车规MCU安全启动、SecOC通信与部件保护落地解析 简介这份文档面向从事汽车电子设计、开发与维护的工程师尤其是关注车载信息安全的从业者系统梳理了车载MCU层面的信息安全防护思路。内容围绕三条主线展开借助加密服务引擎CSE与AES-128算法校验Bootloader完整性与真实性的安全启动机制以随机数配合对称加密保障CAN网络消息保密性与完整性的安全通信方案以及通过随机数与ECU自身ID双重比对来防范部件被非法替换的部件保护手段并配有流程示意图帮助理解各环节的校验逻辑。资源包内共1个docx文档约2.45MB阅读时建议具备一定的汽车电子与网络安全基础重点关注各机制的工作原理与实际落地方式。目前已有91人学习适合用于项目实践参考或企业级安全策略的制定。1. 车载电子电器架构里MCU 的安全边界在哪一辆车上几十到上百个 ECU从分布式走到域控、再走到区域控制器加中央计算MCU 始终在最末端干实事采样、驱动、执行。功能上收得越紧安全上就越危险——攻击者拿到一个 CAN 帧或者一段诊断报文就能让刹车灯常亮、让车窗乱动甚至给整车刷进一份伪造固件。标题里的三件事其实是一条链安全启动保证上电后跑的是自家代码通信安全保证车上的报文没被别人改过部件保护保证代码和密钥不会被读走、调走。做 MCU 开发的人迟早会撞上这三块信息安全工程师要评的也是这三块。下面按落地顺序把每一块的机制、参数和坑拆开讲。2. 车规 MCU 安全启动从信任根到镜像校验2.1 信任链的起点BootROM、HSM 与 OEM 根密钥安全启动的本质是一条信任链上电后第一段代码必须不可改它再去验第二段逐级往下。车规 MCU 的常见做法是把第一段固化在芯片的 BootROM 里只读、不可擦写芯片出厂即固定。BootROM 里带一段最小的校验逻辑直接操作片内的安全子系统——英飞凌、恩智浦、瑞萨这些厂商的常见叫法是 HSMHardware Security Module也有按 SHESecure Hardware Extension规范实现的两者都提供密钥槽、加解密和 MAC 运算区别在于 HSM 可编程能力更强SHE 更偏固定功能。根密钥从哪来芯片里通常有一次性可编程区域OTP / eFuse产线刷写阶段把 OEM 根密钥或密钥派生种子烧进去之后再想读出来就只剩一串错误码。这一步是整个链条的地基键一旦灌错了后面所有校验都白做所以产线上一般要求刷写后立刻做一次回读校验且回读走的是校验路径而不是读取路径。信任链的层级大致是BootROM → 二级引导Bootloader→ 应用App。有的项目还会把 HSM 自身的固件放在最前因为它要参与后面的 MAC 计算。每一级的公钥或对称密钥都由上一级持有这样任何一层被替换都会在下一级启动时暴露。2.2 镜像头结构安全启动校验需要哪些字段校验不是把整个 Flash 跑一遍 CRC 那么简单需要一份约定好的镜像头。头部字段少了会缺防回滚能力多了会拖慢启动。下面是一份常见的头部定义/* 镜像头固定 32 字节紧贴镜像体放在分区起始地址 */ typedef struct { uint32_t magic; /* 0x5A5A1234快速判定分区内是否为有效镜像 */ uint32_t img_len; /* 镜像体长度字节不含头部本身 */ uint32_t img_ver; /* 版本号单调递增用于防版本回滚 */ uint32_t reserved; /* 对齐占位也常放目标 ECU 标识 */ uint8_t cmac[16]; /* 头部前 16 字节 镜像体的 AES-CMAC 结果 */ } img_header_t;magic用于上电时快速筛掉空白 Flashimg_len决定校验范围写错就会把相邻分区算进来验证必然失败img_ver是防回滚的唯一依据BootROM 或 Bootloader 里保存一个当前最低版本低于它就拒绝启动否则攻击者可以拿一份有漏洞的旧固件刷回去cmac是整段内容的完整性标签用 HSM 里的密钥槽算出来。校验逻辑用一句伪代码概括就是把头部前 16 字节和镜像体拼起来交给 HSM 做 AES-CMAC结果和头部里的cmac逐字节比对一致才跳转。整个过程中明文密钥不出 HSM主核只能拿到通过/不通过。2.3 用 AES-CMAC 验签的最小实现主核侧调 HSM 的封装一般长这样不同厂商的寄存器名不同但接口形态高度相似/* 主核侧调用 HSM 计算 CMAC密钥句柄由 HSM 内部索引主核看不到明文 */ int secure_boot_verify(uint32_t part_addr) { img_header_t *hdr (img_header_t *)part_addr; uint8_t calc[16]; int ret; if (hdr-magic ! 0x5A5A1234u) { return BOOT_ERR_MAGIC; /* 分区不是有效镜像 */ } if (hdr-img_len 0 || hdr-img_len MAX_IMG_LEN) { return BOOT_ERR_LEN; /* 长度越界直接判失败 */ } /* 密钥槽 1 镜像校验密钥由产线一次性灌入此后不可读写 */ ret hsm_cmac(HSM_KEY_SLOT_IMG, part_addr, 16 hdr-img_len, calc); if (ret ! 0) { return BOOT_ERR_HSM; /* HSM 通信异常不要当成校验失败 */ } if (memcmp(calc, hdr-cmac, 16) ! 0) { return BOOT_ERR_CMAC; /* 内容被篡改或烧写不完整 */ } if (hdr-img_ver boot_min_version()) { return BOOT_ERR_ROLLBACK; /* 版本低于防回滚下限 */ } return BOOT_OK; }三点说明。第一校验范围是16 img_len头部后 16 字节的cmac自己不参与计算否则就成了自引用。第二HSM 调用失败要区分于校验不通过前者可能是硬件异常或时钟没使能排查方向完全不同日志里必须分开记录。第三版本比较放在 MAC 校验之后因为版本号本身也受 MAC 保护先验 MAC 再信版本号顺序反了等于给攻击者留了个绕过点。产线侧要做一次预计算把 CMAC 值填进头部再刷写用 Python 就能验证工具链和固件端算法是否一致from Crypto.Hash import CMAC from Crypto.Cipher import AES def build_header(body: bytes, key: bytes, ver: int) - bytes: # 前 16 字节magic / 长度 / 版本 / 保留 prefix (0x5A5A1234).to_bytes(4, little) \ len(body).to_bytes(4, little) \ ver.to_bytes(4, little) b\x00 * 4 # CMAC 覆盖头部前 16 字节 镜像体不含自己的 16 字节 c CMAC.new(key, ciphermodAES) c.update(prefix body) return prefix c.digest() bodykey要和 HSM 密钥槽里的值一致ver与防回滚下限规则一致输出顺序按固件端结构体布局小端还是大端必须两边对齐这类字节序错配在联调时表现为每次都校验失败很难从错误码看出来。2.4 A/B 分区与回滚启动失败的三级降级单分区方案一旦校验失败就是变砖量产项目基本都会上 A/B 两份镜像。必要参数有三个分区切换标志存在的 NVM 位置、回滚尝试计数上限、以及确认可启动的判定条件。启动流程常见是先验当前活动分区失败则切到备份分区并给计数器加一备份分区也失败进入恢复模式通常是通过 CAN 或以太网接收新固件计数器超过上限就锁死在恢复模式不允许在两份坏镜像之间反复弹跳。计数器阈值一般设 3 到 5 次设太小会被偶发的擦写不良误判设太大则可能把电池耗干。配合这套机制应用起来后要做一件容易被忘掉的事在规定时间内主动把新分区标记为可用。这一步是把能启动和能正常工作区分开——只看 MAC 通过是不够的应用自己起不来、外设初始化挂了下次上电还得回滚。2.5 生命周期状态调试口和启动阶段的联锁安全启动能不能被绕开很大程度看调试口。车规 MCU 一般有几档生命周期状态比如开发、量产、返修状态只能单向往后推靠 OTP 位锁定。开发状态下 SWD/JTAG 全开方便调试切到量产状态时同时完成两件事关闭调试口锁死 OTP 里与调试相关的配置位。注意顺序先切量产状态再灌根密钥。反过来做的话密钥已经写进去了调试口还开着等于把钥匙贴门上。返修场景下如果要保留调试能力常见做法是留下一路受认证的服务接口靠挑战应答放行而不是把物理口重新打开。3. 车载 MCU 通信安全SecOC 报文认证怎么落地3.1 SecOC 的报文结构Authentic PDU 里多出来的几个字节总线上一条普通 CAN 帧只有 ID 和数据任何接入者都能仿造。AUTOSAR 里对应的方案是 SecOC思路是把原始 PDU 加上认证信息再发出去接收端算一遍比对。认证信息由两部分组成截断的 MACAuthenticator和新鲜度值Freshness Value。字段典型长度说明Payload与原 PDU 相同业务数据本身不加密也可认证Authenticator38 字节AES-CMAC 截断结果越短越省带宽、越弱Freshness Value18 字节抗重放可按单调计数器或时间同步SecOC 头部02 字节携带 FV 长度、截断标识等元信息CAN-FD 每帧最多 64 字节截断长度可以放宽到 8 字节经典 CAN 只有 8 字节很多项目被迫截断到 3 字节甚至更短安全性打折扣。这也是为什么新架构里加密报文更倾向走 CAN-FD 或以太网。截断长度不是能随便选的数值它和可容忍的伪造成功率直接相关通常由整车的信息安全需求分析反推出来。3.2 新鲜度值同步最容易出问题的地方MAC 只能证明报文内容没被改过不能防重放——攻击者把一条合法的解锁报文原样再发一次接收端算出的 MAC 完全正确。新鲜度值就是补这个洞的它必须每次不同且接收方能判断是否递增。常见有两种方案。一是纯计数器发送端每发一帧加一接收端维护一份期望值允许窗口内的偏差问题是 ECU 断电重启后计数器要从 NVM 恢复恢复策略没设计好就会出现上电后前十帧全丢。二是主从同步总线上有一路专用的同步报文周期广播 FV其他节点跟着走风险是同步报文自己如果被干扰全网跟着失步。失步后的处理有讲究。我一般会要求接收端保留一个容错窗口而不是只接受严格相等的值窗口外的报文直接丢并计数计数超过阈值就把该 PDU 标为不可信触发上层降级而不是静默接受。这个阈值要配合整车诊断记录否则线上出现批量丢帧时根本查不到源头。3.3 生成 Authenticator 的代码与参数发送侧的核心操作就是把数据、密钥、新鲜度值拼起来做 CMAC 再截断/* SecOC 发送侧计算 Authenticator 并组装认证 PDU */ uint8_t secoc_build_frame(const uint8_t *pdu, uint8_t pdu_len, uint64_t fv, uint8_t *out) { uint8_t mac[16]; uint8_t fv_bytes[8]; uint8_t mac_len SECOC_TRUNC_LEN; /* 项目配置3~8 字节 */ /* FV 按大端放进 MAC 输入和接收端必须完全一致 */ for (int i 0; i 8; i) { fv_bytes[i] (uint8_t)(fv (56 - i * 8)); } /* 输入 数据 || FV密钥槽 3 为 SecOC 认证密钥 */ hsm_cmac_start(HSM_KEY_SLOT_SECOC); hsm_cmac_update(fv_bytes, 8); hsm_cmac_update(pdu, pdu_len); hsm_cmac_finish(mac); /* PDU 有效数据按位打包在前Authenticator 取 MAC 高位截断 */ pack_bits(out, pdu, pdu_len); memcpy(out ((pdu_len 7) / 8), mac, mac_len); return (uint8_t)(((pdu_len 7) / 8) mac_len); }参数上要注意三处。SECOC_TRUNC_LEN必须两边配置一致差一字节就是永久性认证失败。FV 的字节序和拼接顺序FV 在前还是数据在前属于同一类问题通常由 AUTOSAR 配置工具生成改一个地方就要重新导出。密钥槽选择也要统一同一辆车不同 ECU 之间一般用不同的密钥或不同的密钥派生路径避免一个节点泄露导致全网失效。CAN 上的数据是按位排列的pack_bits这类按位拷贝不能用手写的memcpy代替——两个字节的业务数据可能只占 12 位剩下 4 位是填充填充位对齐错误在实际抓包里看不出来只在特定数据值上偶发失败。3.4 密钥注入产线和售后怎么把密钥灌进去密钥不能跟着固件一起刷固件是可复制的。常见做法是产线上一道单独的密钥注入工序诊断仪或刷写工具通过安全通道和 ECU 建立会话把密钥加密传输ECU 侧解密后写进 HSM 密钥槽或受保护的 NVM 区域。写完必须回读校验但回读的是能否用该密钥算出正确 MAC而不是把密钥读出来比对。产线上还有一条铁律密钥注入必须在生命周期切到量产状态之后完成。售后换件时备件 ECU 通常是空密钥状态需要走一套受权限控制的注入流程这个流程本身也要有审计记录。4. 部件保护Flash、调试口与密钥存储4.1 Flash 分区与存储保护单元部件保护的第一层是别让人把代码读走。车规 MCU 普遍有存储保护单元可以按地址区间设置读、写、执行权限。典型划分是Bootloader 区只允许执行不允许读应用区允许执行和读标定参数区允许读不允许执行密钥区任何主核访问都不允许只有 HSM 能碰。这里有个容易踩的坑很多 MCU 的保护粒度是按块block或按扇区sector对齐的边界没对齐可能出现想保护的没保护上不该保护的锁死了。配置完后建议用调试器实际尝试读一次受保护区确认返回的是总线错误而不是正常数据否则说明配置没生效。4.2 调试接口封锁与生命周期迁移调试口是最直接的攻击面。封锁手段有几档最简单的是一次性烧断调试使能位更精细的是按生命周期状态分级量产状态下物理口关闭但保留一条需要认证的服务通道用于返修。迁移过程本身要防掉电。OTP 位的写入如果中途断电可能停在一个半开半合的状态。常见做法是在迁移前先把状态写入 NVM 并存一份镜像上电时比对 OTP 与 NVM不一致就走异常处理流程报错而不是继续往下启动。这一步的日志要落到非易失区域方便售后定位。4.3 密钥放哪SHE 密钥槽与 NVM 加密存储的取舍密钥存储有两条路。一是有硬件安全模块的芯片密钥直接进密钥槽主核只能引用句柄读不出来二是不带 HSM 的低成本 MCU只能把密钥加密后放 NVM密钥的加密密钥再从别处派生这种方案的理论安全性明显弱一档。方式读取难度适用场景主要风险SHE / HSM 密钥槽主核不可读安全启动、SecOC槽位数量有限NVM 加密存储可读但为密文成本敏感的从节点派生密钥泄露即全失OTP 直存不可改、不可读根密钥、UID写错无法返工选型建议是参与安全启动和报文认证的密钥走硬件密钥槽纯粹用于本地数据的密钥可以放 NVM根密钥或芯片唯一标识走 OTP。同一颗芯片上混用也要保证不出现高价值密钥放在低保护等级区域的情况。4.4 诊断安全访问与 Seed Key 流程诊断服务里有一组安全访问服务用来在上位机和 ECU 之间建一个受控会话。它的典型流程是上位机请求种子ECU 返回一个随机数上位机用约定算法算出应答ECU 验证后解锁。/* ECU 侧种子生成与应答校验密钥以常量形式存在受保护区 */ uint8_t diag_seed[4]; uint8_t diag_try_count; int diag_request_seed(uint8_t *out) { if (diag_try_count DIAG_MAX_TRY) { return DIAG_ERR_LOCKED; /* 失败次数超限进入延时锁定 */ } /* 用 HSM 的随机源不要用软件 rand否则种子可预测 */ return hsm_random_bytes(diag_seed, sizeof(diag_seed)); } int diag_verify_key(const uint8_t *key) { uint8_t expect[4]; /* 种子与密钥做带密钥的散列具体算法按 OEM 规范常见是 AES 或 CMAC */ hsm_kdf_response(diag_seed, HSM_KEY_SLOT_DIAG, expect); if (memcmp(expect, key, 4) ! 0) { diag_try_count; return DIAG_ERR_DENIED; } diag_try_count 0; /* 成功即清零避免正常操作被误锁 */ return DIAG_OK; }两个参数必须处理失败次数上限和成功后清零。次数不设或设得太大攻击者可以离线爆破成功后不清零维修人员操作几次就把 ECU 锁死了。种子必须来自硬件随机源软件伪随机在复位后可能产生相同序列等于把种子变成固定值。5. 安全措施的验证怎么证明它真的生效写完不等于生效MCU 安全里最容易被忽略的是验证环节。常见验证手段集中在三块功能验证、故障注入和侧信道。功能验证解决的是逻辑对不对。安全启动这一块要覆盖的用例包括正常镜像能否启动、篡改一个字节后能否被拒、低版本镜像能否被防回滚规则拦住、A/B 切换是否按计数上限收敛、断电重启后计数器能否正确恢复。SecOC 要覆盖的正常帧放过、篡改帧拒绝、重放帧拒绝、失步后重同步。这些用例最好固化成产线或台架脚本每次改配置都跑一遍。故障注入解决的是逻辑对了物理上会不会被绕过。常见方式是给 MCU 的供电或时钟加极短毛刺看校验逻辑会不会被跳过或跳错分支。做这类测试要盯三件事失败时芯片停在什么状态、是否出现未认证代码执行、日志能不能记录下来。有一条经验值得记住故障注入往往在 MAC 比对那一步找缝隙所以比对逻辑本身最好用定长比较、不提前返回减少可被精确命中的分支。侧信道解决的是密钥会不会从功耗或电磁里漏出来。对 MCU 来说AES 运算期间的功耗曲线如果和密钥强相关理论上可以被统计分析出来。缓解手段一般是硬件层的掩码和随机化对开发者来说能做的是确认使用的密钥槽开启了这些保护特性并在选型阶段把这项能力列为必选项而不是等出了问题再补。一个可以直接用的验证清单长这样。验证项手段通过判据镜像篡改改一个字节后启动拒绝启动并进恢复模式版本回滚烧旧版本镜像拒绝启动并记录版本错误码调试口封锁量产态下接调试器连接被拒或总线报错报文重放重发已采集的 SecOC 帧接收端丢弃并计数密钥读取主核直接读密钥区触发总线异常无数据外泄掉电恢复校验中断电再上电状态一致无半开状态最后补一个实操细节这些验证用例跑完之后日志要落到独立于被测固件的区域否则一旦被测镜像本身有问题日志也跟着没了。我一般会要求把关键拒绝事件同时写进 HSM 侧的受保护区域和整车诊断事件两边对得上才算一次可信的验证记录。本文还有配套的精品资源点击获取
返回列表