资讯详情

资讯详情

汽车OTA安全:升级包验签与ECU Secure Boot双验证

做车载ECU开发或者整车OTA的同学对下面这个场景应该不陌生云端推送一条升级任务车机把升级包拉到本地再通过CAN总线或者车载以太网把新固件刷进ECU最后ECU重启上电。很多人以为到这一步升级就算完成了其实真正的“鬼门关”才刚开始——ECU上电的一瞬间Bootloader会对新固件再做一次签名验证验证不过直接拒绝启动。这也是我在好几个项目里反复看到的翻车点团队只盯着升级包下发时的签名校验忽略了上电阶段的Secure Boot验证。结果要么是刷进去的固件被人为改过还能照样跑要么是升级包在车机本地被替换后ECU照样起来整条安全链路形同虚设。这篇文章我想结合自己搭建汽车OTA升级链路、做ECU安全启动的实战经历把“升级包下发验签”和“ECU上电验签”这套双验证流程完整拆一遍为什么必须验两次、签名怎么算、密钥怎么管、Bootloader怎么验以及我在实际项目里踩过的坑。无论你是在车厂、Tier1做嵌入式还是刚进入车联网安全领域这篇都值得花十分钟仔细看一遍。1. 先看懂整条链路为什么一次升级要验两道签名1.1 从“下载、刷写、启动”三个动作看OTA的两道关口一段OTA升级表面上只有一个“把新固件放进去”的动作实际上可以分为三个阶段下载、刷写、启动。下载云端把升级包推到车端车机下载到本地存储。刷写车机通过诊断仪协议最常见的是UDSISO 14229把固件写到ECU的Flash里。启动ECU复位上电Bootloader检查固件合法性然后跳转到App运行。绝大多数团队把安全精力放在第一阶段也就是下载阶段做HTTPS、做校验但我认为整条链路里最容易出问题的是第二阶段和第三阶段。原因很简单升级包一旦落到了车端本地就离开了云端的可控范围。攻击者可能拿到车机Root权限替换本地升级包可能拔掉Flash用编程器物理改写甚至可能在OTA刷写过程中断电导致Flash里的固件是不完整的。所以行业里的做法是升级包由云端私钥签名车端在下载完成后验签一次确认“这个包确实是云端发的、没被篡改过”固件刷进ECU后ECU在启动时再用硬件里烧录的公钥验签一次确认“Flash里现在跑的这个镜像依然是合法的”。这两道验签杀的其实是两拨完全不同的攻击者。1.2 两道验签分别防什么传输篡改与本地持久化攻击第一道验签发生在车端拿到升级包之后、刷写ECU之前。它防的是“传输链路被劫持”和“云端存储被污染”。比如中间人把升级包换成了一个带恶意代码的假包或者CDN节点被入侵导致下发的包被人动过手脚第一道验签会直接拦截。第二道验签发生在ECU上电瞬间。它防的是“本地持久化攻击”。升级包即便通过了第一道验签被成功刷写ECU重启之后Bootloader校验的是Flash里的实际内容。如果攻击者在刷写完成后对Flash做物理篡改或者通过调试接口、漏洞在App里注入了恶意代码第二道验签会拒绝启动这个被污染过的固件。为什么不能只靠其中一道我打个比方第一道验签相当于快递员送货上门时你检查了包裹完好、签了字第二道验签相当于你把东西拿回家后每次开机都要再确认一遍里面的东西真的是你要的。前者防的是“路上被掉包”后者防的是“放家里被人换掉”。两道验签覆盖的是两条完全不同的攻击路径缺一道都会留下缺口。2. 升级包侧签名、打包与密钥管理2.1 签名算法选型与计算过程在搭建签名服务之前首先要确定用哪套非对称签名算法。目前行业里最常见的是RSA-2048 SHA-256和ECDSA P-256 SHA-256。RSA的优势是成熟、工具链完善几乎所有密码库都原生支持PKCS#1 v1.5和PSS两种填充方式缺点是密钥尺寸大、验签性能偏慢。在几百MHz的MCU上RSA-2048验签一次大约需要几十毫秒甚至上百毫秒对于一些对启动时间极其敏感的ECU来说要仔细评估。ECDSA P-256的优势是密钥短、签名体积小64字节、验签性能好特别适合资源受限的MCU缺点是实现细节多随机数质量不过关会直接导致私钥泄露而且有些老旧PKI工具链对ECDSA不够友好。我自己的经验是低算力的传感器ECU优先考虑ECDSA域控制器、车机这类算力充足的平台可以放心用RSA-2048。如果你是第一次搭这套体系建议先选RSA-2048 SHA-256不是因为性能最好而是因为踩坑成本最低网上资料多、排查工具多等整条流程真的跑通了再迁移到ECDSA也不迟。签名计算流程本身不复杂可以拆成四步对固件镜像做SHA-256哈希得到摘要。用私钥对摘要做非对称加密RSA-PSS或ECDSA得到签名值。把固件镜像、镜像元信息版本号、ECU类型、长度、CRC、签名值一起打包成升级包。车端/ECU端用公钥对镜像做同样哈希再用公钥验签比对摘要。这里有个很多人容易搞错的点签名的是“摘要”而不是“整个镜像”。非对称算法计算开销大直接对几十MB的固件做RSA运算不现实所以先通过哈希把任意长度的镜像压缩成固定长度的摘要再对摘要做签名。SHA-256的抗碰撞性保证了“镜像变了摘要几乎必然变”因此只要摘要验签通过就等价于镜像没被改过。2.2 密钥分级、HSM与证书链签名算法选好了接下来的问题是私钥放哪、怎么管、怎么用。很多开发团队一开始图省事把私钥放在一台普通开发机上开发、测试、量产全用同一把密钥这是我在审计项目里见过最多的高危隐患。汽车软件私钥一旦泄露攻击者就可以用这把私钥给任意恶意固件签名整条OTA安全体系直接报废。正确做法是把密钥分级管理。至少分成两级根密钥Root Key最高权限只在离线环境生成私钥永不联网通常存放在硬件加密机HSM或物理隔离的签名服务器里用于签发次级密钥。签名密钥Signing Key用于日常升级包签名同样建议放进HSM或专业密钥管理系统访问要留审计日志。如果车型多、ECU型号多还可以在根密钥和签名密钥之间插入中间CA证书形成“根CA → 中间CA → 终端证书”的证书链。这样即使某个车型的签名密钥泄露只需要吊销和重签该车型的中间证书不用把根密钥也换掉。硬件安全模块HSM是这套体系里很关键的一环。签名私钥一旦进入HSM外部只能调用签名接口永远拿不到私钥本身。哪怕是内部开发人员也无法通过API导出私钥。我建议从项目启动第一天就把HSM纳入预算因为产品量产后想再补密钥管理成本和痛苦程度会翻好几倍。2.3 打包格式约定升级包格式是另一个容易被低估的问题。格式不统一车机端解析脚本就越来越乱验签也容易漏。一个标准的升级包建议至少包含以下字段字段说明包头魔数用于识别升级包类型例如“OTA1”协议版本升级包格式版本号目标ECU标识对应哪个ECU或硬件平台固件版本号用于版本校验和防回滚镜像长度固件镜像字节数镜像SHA-256哈希值车端对镜像做哈希并与该字段比对固件镜像数据实际固件内容签名值对“元信息镜像”整体计算出的签名签名时要注意签名的范围要覆盖整个元信息加镜像而不只是镜像本身。否则攻击者可以不动镜像偷偷把版本号改成旧版本配合刷写旧固件实现回滚攻击。把版本号、ECU标识、长度一起纳入签名范围是防回滚的第一道保险。3. 下发链路从云端到车端怎么保证包没被掉包3.1 车云通道的三层保护升级包从云端到车端传输链路本身需要保护。这个保护是三层叠加的不是单选一个就完事。第一层是传输层安全。车机和云端通信通常走HTTPSTLS 1.2或1.3至少保证升级包在公网传输过程中不被中间人窃听或篡改。这里涉及TLS证书校验车机端必须锁固定根证书避免攻击者用自签名证书做中间人攻击。第二层是应用层验签。也就是下载完成后车机先对升级包做一次整体验签通过后才允许进入刷写流程。这一步的意义在于即使TLS被攻破攻击者拿到了传输中的升级包并替换成恶意包应用层验签仍然会拒绝。第三层是存储层保护。下载到车机本地存储的升级包建议加密存储。为什么因为车机的文件系统可能被Root、可能被物理拆解明文升级包一旦被拷走攻击者虽然无法伪造签名但可以分析固件内容反向挖掘漏洞。加密存储能让这种攻击的成本显著提高。三层保护缺一不可但真正的底气来自第二层应用层验签因为只有这一层是端到端的信任校验TLS只保护传输过程保护不了端点本身。3.2 差分包、断点续传与分块校验车载OTA经常会遇到弱网环境所以业界普遍用差分升级来减少下载流量。差分包只包含新旧固件之间的差异部分常见的差分算法有bsdiff、hdiffpatch等。这里要注意的是差分包本身也要验签。有些团队在做差分升级时由于包体较小就在服务器端直接拼好完整镜像再签名这样没问题但有些场景下完整的镜像不会在车端组装车端拿到的是差分包加补丁文件。这时必须对差分包做签名验证否则攻击者只需替换差分包就能在车端制造出一个带恶意代码的“新固件”。断点续传功能同样不能在安全上打折扣。下载中断后重新续传如果只校验分块的CRC而不校验整体签名那攻击者可以精心构造一个CRC相同但内容不同的分块。正确做法是每个分块到达后做CRC校验全部下载完成后对完整升级包做SHA-256哈希和整体验签。分块校验用于快速发现传输错误整体验签才是真正的安全关口。3.3 刷写前后不能漏掉的检查项车端完成验签后接下来是通过UDS把固件刷进ECU。刷写前后有三项检查不能漏刷写前要确认目标ECU标识和固件版本匹配防止把A车型的固件刷进B车型的ECU。刷写前要确认升级包里的版本号高于当前版本低于或等于当前版本直接拒绝这是防回滚的关键一道闸门。刷写完成后ECU要对Flash中的镜像做一次CRC或哈希校验确认数据完整然后才允许复位启动。有些团队把“验签能不能过”当成唯一标准忽略了版本号匹配和ECU匹配结果出现过把测试固件刷进量产车的事故这个锅最后往往还扣不到验签头上纯属流程缺失。4. ECU上电验签Secure Boot的完整时序4.1 信任根与三级引导链第二道验签发生在ECU上电时这就是通常所说的Secure Boot安全启动。它的核心思路是建立一条从硬件到应用层的信任链每一级引导程序在跳转之前都要验证下一级的签名。典型的三级引导链是这样的BootROM芯片出厂固化的只读代码是整个信任链的根。它校验Bootloader的第一阶段。Bootloader分为BL1/BL2两个阶段BL1完成基础硬件初始化和安全校验然后加载BL2BL2负责完整的启动逻辑校验App镜像。App应用固件被Bootloader验证通过后才允许被执行。每一级验证通过后才把控制权交给下一级任何一级验证失败芯片都不会继续启动。这就像进公司大楼保安确认你的工牌BootROM验Bootloader电梯闸机再确认一次Bootloader验App两道门都过了你才能进工位。设备公钥通常烧录在一次性可编程存储OTP/efuse中芯片出厂后无法改写。如果公钥都能被攻击者改写安全启动就是空中楼阁。所以硬件上要确保两点公钥区域不可改写以及芯片有调试接口保护比如JTAG/SWD的熔丝或者锁定机制否则攻击者通过调试接口直接读Flash甚至修改内存验签逻辑再强也没用。4.2 验签失败的处理回滚、恢复与看门狗Secure Boot不只是“验签过了就启动验签不过就死机”这么简单。实际工业产品必须设计好验签失败后的恢复路径否则一次失败就变砖售后成本会非常高。我推荐的做法是设计一个带版本回滚的启动策略Bootloader先在Flash的备份分区里保留上一版能正常启动的固件。当主分区固件验签失败时Bootloader尝试加载备份分区并在启动后设置一个“回滚事件”标志。FOTA管理器读取这个标志向云端上报本次升级失败请求重新下发或者切换到回滚状态。如果备份分区也验签失败那是真出大事了只能进入恢复模式通过UDS重新刷写或者走售后刷机流程。除了分区回滚硬件看门狗也要参与进来。有些ECU在App侧跑飞后Bootloader并没有死但App一直没有喂狗看门狗超时后会强制复位系统。复位后Bootloader要能识别“上次启动失败”的状态自动进入恢复模式而不是反复尝试启动一个坏固件否则就会出现“重启-失败-重启”的死循环。安全启动最终要保证两件事一是不合法的固件永远跑不起来二是合法固件一旦失败系统有路可退。5. 实操过程与关键配置5.1 用OpenSSL生成密钥对与CSR聊了这么多原理下面进入实操。工具我用的是OpenSSL这几乎是行业标准先演示怎么生成一套用于OTA签名的RSA-2048密钥。生成私钥推荐使用PKCS#8格式openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \ -out sign_private_key.pem从私钥导出公钥用于车端/ECU端验签openssl pkey -in sign_private_key.pem -pubout -out verify_public_key.pem如果你的安全策略要求证书链就先生成CSR然后交给CA签发openssl req -new -key sign_private_key.pem -out signing_key.csr \ -subj /CNOTA-Signer-2024/OYourCompany/CCN我强烈建议正式环境用硬件安全模块而不是普通文件保存私钥。上面这些文件操作适合开发测试量产签名必须把私钥放进HSM或者至少部署一个只有API访问权限的独立签名服务私钥不落任何开发人员本地。5.2 升级包签名与验签的最小示例接下来演示一下最小可行的升级包签名与验签流程。假设我们有一个固件镜像文件firmware.bin计算镜像哈希并保存openssl dgst -sha256 -binary firmware.bin firmware.bin.sha256使用私钥对哈希值签名得到签名文件这里用RSA-PSSopenssl pkeyutl -sign \ -in firmware.bin.sha256 \ -inkey sign_private_key.pem \ -pkeyopt rsa_padding_mode:pss \ -pkeyopt rsa_pss_saltlen:32 \ -out firmware.bin.sig车端或ECU端验签时使用公钥openssl pkeyutl -verify \ -in firmware.bin.sha256 \ -sigfile firmware.bin.sig \ -pubin -inkey verify_public_key.pem \ -pkeyopt rsa_padding_mode:pss \ -pkeyopt rsa_pss_saltlen:32如果输出“Signature Verified Successfully”说明镜像摘要验签通过。实际工程里这一步会放到Bootloader中执行但逻辑完全一致先算哈希再用硬件存储的公钥验签。需要特别提醒这里我只对镜像的SHA-256值签名了。实际产品里签名的输入范围要包含版本号、ECU标识、镜像长度这些元信息绝不能只签裸镜像。否则攻击者改掉版本号不影响验签就能轻松构造回滚攻击。5.3 在ECU侧接入验签的工程思路在单片机端做验签一般不会直接裸调OpenSSL而是用mbedTLS或类似密码库。mbedTLS对RSA、ECDSA、SHA-256的支持都很完整而且内存占用可控非常适合车规MCU。下面是一段示意性的伪代码展示Bootloader里验签App镜像的逻辑int secure_boot_verify(const uint8_t *image, uint32_t image_len, const uint8_t *signature, const uint8_t *pubkey) { uint8_t hash[32]; mbedtls_sha256(image, image_len, hash, 0); mbedtls_rsa_context rsa; mbedtls_rsa_init(rsa); mbedtls_rsa_import_pubkey(rsa, pubkey); int ret mbedtls_rsa_pkcs1_verify(rsa, MBEDTLS_MD_SHA256, 32, hash, signature); mbedtls_rsa_free(rsa); return ret; // 0 表示验签通过 }这段代码的关键点是先对整个镜像做SHA-256然后验签。注意这里用的是RSA-PKCS1-v1-5填充如果你在签名侧用的是PSS验签侧也要改成对应的PSS模式两边的填充方式不一致是最常见的“签名能过验签不过”的原因之一。公钥怎么存进ECU也有讲究。最稳妥的方式是烧录进OTP区或者受保护的KeyStore配合芯片的Secure Boot硬件特性。如果MCU没有专门的KeyStore也要确保公钥所在的Flash区域在量产线结束后被锁定为只读线下再通过对Bootloader自身的签名校验保证Bootloader不会被替换。6. 常见问题与排查技巧实录6.1 常见问题速查表下面这张表是我在项目里遇到的高频问题整理成速查表建议直接转给团队问题现象可能原因排查方向车端下载后验签失败公钥与签名私钥不匹配确认车端烧录的公钥和云端签名私钥是同一对验签时哈希值一直对不上签名时哈希的输入范围和验签时不一致核对“元信息镜像”的拼接顺序和范围RSA签名在PC端成功MCU端失败填充模式不一致确认签名和验签都用PKCS1 v1.5或都用PSS固件能刷写但ECU无法启动Bootloader验签使用的公钥被错误覆盖或锁定区域未生效检查OTP/KeyStore烧录记录升级后系统反复重启固件验签通过但App运行异常看门狗超时检查看门狗超时策略与回滚机制回滚被误判为升级失败版本号比较逻辑写反确认为“新版本大于当前版本”才能校验下载到一半续传后验签失败续传后拼接了不完整分块整包下载完成后必须重新做全文校验6.2 容易被忽略的几个坑最后说几个我真实踩过、或者在别人项目里见过的坑每一个都是拿排期和售后换来的。第一签名输入范围不统一。“开发环境的签名脚本只签了镜像测试环境的脚本签了元信息加镜像”这种不一致会直接导致同一个升级包在不同环境表现完全不同。建议在打包服务里做一次完整的范围约定并在测试用例里强制覆盖“篡改元信息”和“篡改镜像内容”两种场景。第二私钥权限管理形同虚设。很多团队的签名脚本放在CI机器上凡是能登录CI的人都能拿到私钥文件。一旦内部人员有意或无意泄露整个OTA体系就要推倒重来。就算不上HSM也至少要做到私钥加密存储、每次签名操作有审计日志、不同开发环境使用不同的密钥。第三只验签不复核版本。我在一个项目里见过攻击者思路的“白帽测试”把升级包里的固件内容换成正常的历史版本由于签名依然有效验签通过了结果ECU被回滚到旧版本旧版本的已知漏洞全都能用。解决方式很简单版本号必须纳入签名范围Bootloader在验签通过后还要再校验一次版本号是否大于等于最小允许版本。第四调试接口没关。Secure Boot做得再严密如果JTAG/SWD调试口在量产固件里还开着攻击者直接连上调试器改内存、改Flash安全启动就成了摆设。量产前一定要锁定调试接口并做一次实机渗透验证别让最后一道防线漏风。这套“双验签”体系说到底不是某一个环节的技术而是从密钥管理、打包签名、传输校验到Bootloader安全启动的整条链。我在项目里最大的体会是安全设计必须提前进架构等OTA跑通了再补安全几乎等于把整个链路重做一遍。尤其是密钥分级和HSM采购这些一定不要在项目后期才启动。另外也想提醒大家任何流程层面的安全措施最后都要落到实车验证上。升级包在实车上能跑通一百次都不如做一次“篡改固件之后还能不能启动”的安全用例来得踏实。如果你正要开始搭这套体系建议先把签名、验签、回滚三个闭环跑通再逐步加密钥分权、证书链和云端审计不用一上来就追求完美。这套方法并不神秘但细节很多。希望这篇实战记录能帮你少踩几个坑。如果你在实际落地中遇到其他诡异问题欢迎一起讨论。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →