1. 为什么车载数据传输会成为安全问题集中爆发点
1.1 车载网络的历史包袱
传统汽车电子电气架构里,控制器局域网络(CAN)总线已经服务了几十年。它设计之初只考虑了实时性和可靠性,压根没想过有人会去攻击一辆车。CAN报文广播式传播、无源认证、明文传输,任何一个能物理接入总线的节点,都可以扮演任意ECU发消息,其他节点几乎没有判断真伪的手段。早期的安全模型是“物理接触即信任”,车辆作为封闭系统,攻击者很难接近内部网络。
但是行业这几年的变化把这份“安宁”彻底撕碎了:OTA远程升级、车联网V2X、云端诊断、手机App远程控车、共享出行车队管理,所有这些能力都需要把数据从车内送到车外,再把命令从云端下到车内。攻击面从物理接触扩展到了远程网络,原来“进不来”的前提不再成立。更麻烦的是,ECU数量和软件体量暴涨,一辆量产车现在有几千万行代码,漏洞在所难免。这时候再回头看CAN总线的设计,就好像给银行金库装了个透明玻璃门,监控报警都齐全,就是门锁形同虚设。
1.2 攻击者的真实动机
很多人觉得汽车安全离自己很远,直到亲眼看到攻击案例才会重视。业内公开报道过的攻击路径有很多:通过蓝牙协议栈漏洞拿到车内网络访问权限,通过OBD诊断口植入恶意固件,劫持T-Box的远程控制通道伪造指令,甚至通过传感器数据注入干扰ADAS判断。攻击者的动机也五花八门——盗窃车辆、勒索解锁、窃取驾驶行为数据、对车队进行批量控制、商业竞争中的逆向工程,等等。
我参与过的安全评估项目里,最常见也最“容易得手”的攻击方式,恰恰不是表演级的高深漏洞利用,而是利用网络里大量明文控制和诊断报文。攻击者只需要在某个可访问的节点上监听一会儿,就能还原出刹车、油门、车门、灯光等关键控制的报文格式,随后重放或篡改。这个事实说明了问题的本质:不是攻击者多高明,而是车载通信本身没有任何机密性和完整性保护。
1.3 现有安全方案的差距
AUTOSAR规范里定义了SecOC(Secure On-board Communication)机制,目的是解决认证和完整性问题,但实际落地推行并不乐观。原因有几层:一是SecOC依赖HSM(硬件安全模块),老平台MCU不支持或算力不够;二是SecOC主要覆盖传统CAN/CAN-FD的报文级保护,对车载以太网里更复杂的面向服务通信(SOA、SOME/IP)支撑较弱;三是密钥管理体系没跟上,很多项目就算上了SecOC,密钥生命周期管理仍然在线下用Excel表维护,面对量产车队和海量密钥分发时基本失控。
AutoSec这个方案恰恰是在这个背景下做的:不依赖特定大算力SoC,兼容传统CAN-FD和车载以太网,把认证、加密、防重放、密钥管理这几件事整合成一套完整的数据传输安全框架。下面我会从架构、协议、密钥管理、实测数据和落地踩坑几个维度来拆这套方案。
2. AutoSec方案总体架构:面向分域的纵深防御
2.1 安全边界与信任域划分
整车网络从安全视角来看,绝对不能做成一个平面。现代车辆按功能域划分:动力域、底盘域、车身域、座舱域、驾驶辅助域。不同域的安全等级天然不同,比如动力域和制动相关报文一旦被篡改直接威胁人身安全,座舱域的信息娱乐数据即使泄漏也只是隐私问题。AutoSec的设计出发点就是按域划分信任级别,域的边界就是安全边界。
具体部署时,每个域都会有一个网关或域控制器作为信任锚点,域内ECU之间的通信采用轻量级保护,跨域通信则必须经过网关的安全转发。这样即使攻击者拿下了座舱域的某个娱乐主机,想要往动力域发非法指令,也需要过网关这一关:网关会校验源IP、源端口、报文认证码、序列号、授权等级,全部通过才转发。
2.2 协议栈中的五个安全功能模块
AutoSec的运行时框架在通信栈中切入了五个模块,这五个模块分别承担不同职责,又可以完整串成一条安全链路。
- 身份认证模块:负责ECU、网关、诊断工具之间的双向身份确认,解决“你是谁”的问题。底层采用挑战-应答机制,配合每个节点的唯一设备证书,防止伪造节点。
- 密钥协商模块:负责在建立安全会话时生成会话密钥,避免长期密钥直接暴露在通信链路上。使用椭圆曲线Diffie-Hellman(ECDH)做密钥交换,具备前向保密能力。
- 数据加解密模块:负责机密性保护。设计上做了分级处理,关键控制指令采用端到端加密,普通状态上报数据只做完整性保护,不强制加密,以节省算力。
- 完整性/认证模块:给每条报文附带消息认证码,接收方验证后才采信,防止数据被篡改或伪造。这里针对不同总线类型选用了不同算法组合。
- 防重放模块:为每条报文生成不可预测的序列号窗口,接收方维护滑动窗口,拒绝过期或重放的报文。
五个模块在实现上被抽象为一个轻量级中间件,向上对接应用层,向下对接不同总线驱动。应用层不用关心底层是CAN-FD还是以太网,安全服务提供的API是一致的。
2.3 位置与部署形态
AutoSec的部署形态分嵌入式和网关旁路两种。嵌入式形态是把安全库直接编译进ECU固件里,适用于算力相对充裕的域控制器。网关旁路形态更灵活,部署在域网关的通信芯片上,对已有ECU透明——原有ECU代码不动,网关负责接入方的安全识别和转发控制,适合存量平台改造。
有一点要注意:所谓“透明”不是真的完全不动,至少需要在网关侧配置每路报文的黑白名单和安全策略。但相比让所有ECU都升级固件,改造量已经小了一个量级。
2.4 方案选型时的对比
在项目立项时,我们内部也对比过市面上的几个主流方案。我把核心对比项列出来供参考:
| 维度 | SecOC(标准方案) | 自研完整安全框架(AutoSec) | 通用TLS/DTLS移植 |
|---|---|---|---|
| 适用总线 | CAN/CAN-FD | CAN/CAN-FD/以太网统一覆盖 | 以太网为主,CAN难适配 |
| 算力开销 | 低 | 低-中,可按需裁剪 | 较高,握手开销大 |
| 密钥管理 | 依赖外部系统 | 内置KMS流程和密钥轮换 | 依赖PKI系统 |
| 防护范围 | 完整性和认证 | 机密性+完整性+认证+防重放 | 机密性+完整性+认证 |
| 存量ECU适配 | 需要ECU配合 | 网关旁路可透明 | 基本无法适配CAN |
| 实施复杂度 | 中 | 中高 | 低(仅以太网场景) |
最终选择自研框架,核心原因是希望一套方案能同时覆盖CAN和以太网,并且把密钥管理握在自己手里,而不是依赖某一家供应商的黑盒子。
3. 核心安全机制的落地设计
3.1 轻量级身份认证与握手流程
车载环境做认证,最大的约束是ECU算力和内存。标准的TLS握手在典型的车规MCU上要跑几百毫秒甚至几秒,这在启动阶段或紧急控制场景下完全不可接受。AutoSec参考了TLS 1.3的设计思想做了精简,把握手过程压缩到两轮消息,但保留了双向认证能力。
实际流程是这样:
- 节点A(如网关)生成一个随机数Nonce_A,连同自己的证书ID发送给节点B。
- 节点B验证A的证书ID在授权列表内,然后生成自己的随机数Nonce_B,连同证书ID和一条签名数据(对Nonce_A和Nonce_B的拼接签名)返回给A。
- 节点A验证B的签名,确认B持有与证书ID对应的私钥。
- 双方各自根据Nonce_A和Nonce_B派生出会话密钥。
这里对比特币挖矿或者复杂的公钥运算开销要小很多的原因在于:我们优先使用基于椭圆曲线的ECDSA签名,密钥长度256位,签名验证在ARM Cortex-M7级别的MCU上大约几毫秒;同时证书ID是预先烧录的简短索引,不需要传送完整证书链,省下了带宽。
3.2 数据完整性保护
完整性保护主要解决报文被篡改的问题。CAN-FD的报文最大支持64字节数据场,扣掉基本ID、序列号、认证码,留给应用的数据空间很紧张。设计时采用了截断消息认证码(CMAC)的做法:对整条报文做AES-128-CMAC计算,然后只取前4字节或前8字节作为认证码填充。
为什么敢截断?这是权衡了安全强度和通信效率后的选择。安全强度与认证码长度直接相关,也不是越长越好。4字节认证码的碰撞概率在单条链路上已经足够低,再配合序列号窗口和业务层重试机制,实际被暴力伪造的成本远大于攻击收益。当然关键控制指令(如刹车、转向)可以配置成8字节认证码,代价是有效载荷减少,但这类报文本身数据需求不大,完全能够接受。
验证流程上,接收端存储最近一段时间内收到的会话密钥和历史MAC,收到报文后先查序列号是否合法,再重新计算MAC比对。发现不一致就标记错误帧,连续错误触发告警并进入安全降级模式。这里所谓的“安全降级”,通常是禁止对该来源的指令执行,只允许状态上报。
3.3 防重放机制
重放攻击是最容易被忽视、但实际危害极大的攻击方式。攻击者不需要破解任何密码,只要把之前录下的合法报文原样再发一遍就能产生效果。比如录下一段“解锁车门”的报文,之后每天反复播放,就能随时打开目标车辆。
AutoSec采用“会话序列号+滑动窗口”的双层防重放机制。每条报文头部的4字节序列号来自伪随机数生成器,收发双方各自维护相同状态的生成器。接收端有一个32位的滑动窗口,只有当前序列号落在窗口内且未被使用过才接受。窗口之外一律视为过期或重放,直接丢弃并记录异常。
这里有一个细节很关键:序列号必须具有不可预测性,不能简单用自增计数器。否则攻击者预知未来序列号后可以提前准备合法报文。所以设计上用密钥和上一序列号做AES加密得到当前序列号,本质上是一个同步式伪随机序列,攻击者拿到一段密文也难以推导下一个值。
3.4 密钥管理与密钥轮换
再强的加密算法,密钥管理一旦拉胯,整套机制等于白搭。AutoSec的密钥体系分三层:
- 根密钥(Root Key):仅存储在每个设备的安全硬件(HSM或安全单元)中,永不出设备。用于解锁和校验下层密钥。
- 设备密钥(Device Key):在生产线上下发,每个设备唯一。用于初始身份认证和生成会话密钥的种子。
- 会话密钥(Session Key):在每次会话建立时协商生成,只存在内存中,断电丢失。
密钥轮换方面,AutoSec支持两种触发方式:时间触发和事件触发。时间触发是每24小时强制轮换一次会话密钥;事件触发是检测到连续认证失败或异常重放时立即重新协商。轮换过程在通信层自动完成,上层应用无感知。
针对量产车队的密钥分发,我们实现了一个轻量级KMS服务器,可以把批量密钥加密下发到生产线工位设备,再烧录进ECU。整个过程审计日志完整,密钥材料以密文存储,运维人员也拿不到明文根密钥。
4. 协议帧格式设计与代码实现
4.1 AutoSec帧头部结构
协议设计遵循一个原则:兼容已有总线协议,不做“推倒重来”。在CAN-FD上,AutoSec在扩展帧ID中使用了几个保留位做标记,在数据场开头放安全头。在车载以太网上,则使用UDP负载的自定义头部,不影响标准IPv4/IPv6编址和路由。
这是CAN-FD场景的安全头结构定义:
typedef struct { uint8_t version; // 协议版本,当前为0x01 uint8_t flags; // 标志位:bit0加密位,bit1认证位,bit2-7保留 uint16_t session_id; // 会话标识,区分不同的安全会话 uint32_t seq; // 防重放序列号 uint8_t cmac[8]; // 消息认证码,缺省截断为8字节 } autosec_header_t;头部在CAN-FD数据场中占用15字节,剩余49字节给上层协议和应用数据。从实现来看,这个长度对多数控制类报文绰绰有余;对于超过剩余空间的应用数据,AutoSec自动切换到以太网承载模式,通过网段传输并用SOME/IP封装。
4.2 加解密负载与尾部
加密模式下,负载是原始应用数据的密文。算法选用AES-128-GCM,因为它同时提供加密和完整性保护,且是硬件加速支持最广泛的分组算法之一。GCM模式会产生额外的认证标签,我们把这个标签复用进安全头的CMAC字段,不额外增加字节。
未加密但需要认证的报文格式更简单:安全头后面直接跟明文负载,CMAC基于安全头+明文负载计算。这样设计的逻辑是:状态上报、诊断读取这类数据不敏感,但必须防篡改;控制指令、用户隐私类数据则必须加密。
4.3 典型发送流程代码示意
下面是一段简化后的发送端处理流程,代码风格贴近实际工程实现:
autosec_status_t autosec_send(autosec_socket_t *sock, const uint8_t *plain_data, uint32_t data_len) { autosec_header_t hdr; uint8_t enc_buffer[AUTOSEC_MAX_PAYLOAD]; // 1. 检查会话状态 if (sock->session_state != SESSION_ESTABLISHED) { return AUTOSEC_ERR_NO_SECURITY_CONTEXT; } // 2. 填充安全头 memset(&hdr, 0, sizeof(hdr)); hdr.version = AUTOSEC_VER; hdr.flags = (sock->need_encrypt ? AUTOSEC_FLAG_ENCRYPT : 0) | AUTOSEC_FLAG_AUTH; hdr.session_id = sock->session_id; hdr.seq = autosec_next_seq(sock); // 3. 加密负载(如需要)并计算CMAC uint8_t *payload_ptr = enc_buffer; uint32_t payload_len = data_len; if (sock->need_encrypt) { aes_gcm_encrypt(sock->session_key, sock->session_iv, plain_data, data_len, enc_buffer, payload_len, &hdr.cmac[0], sizeof(hdr.cmac)); } else { memcpy(enc_buffer, plain_data, data_len); aes_cmac(sock->auth_key, (uint8_t *)&hdr, sizeof(hdr), enc_buffer, data_len, &hdr.cmac[0], sizeof(hdr.cmac)); } // 4. 将安全头和负载拼装后发送到底层总线驱动 return autosec_tx_frame(sock->bus_id, &hdr, sizeof(hdr), payload_ptr, payload_len); }接收端流程是对称的:先校验版本和会话,再检查序列号窗口,最后验证CMAC或解密负载,全部通过才把数据交给上层应用。任何一步失败都会触发错误计数和日志上报。
5. 实测数据:算力开销和传输时延
5.1 测试环境与配置
实测平台选了两套代表不同定位的车规硬件:
- 低算力节点:Infineon TC297(TriCore,主频300MHz),无硬件AES加速,跑CAN-FD通信。
- 域控制器:NXP S32G274A(4核Cortex-A53,主频1GHz),有硬件加密引擎,跑千兆车载以太网。
软件上分别编译了AutoSec裁剪版和完整版,测试脚本连续发送10000条报文,统计吞吐、时延、CPU占用和内存增量。基准组为无安全保护的裸传输。
5.2 内存开销
AutoSec的静态内存开销主要包括安全上下文结构、密钥存储缓存、序列号窗口缓冲区。低算力节点上每个安全会话约占用2.5KB RAM,其中会话密钥和上下文占了大部分。域控制器因为一个网段可能有几十个活动会话,内存开销按会话数量线性增长,实测开启50个会话时RAM增加约128KB,对S32G的512MB内存来说可以忽略。
Flash占用的差异也值得关注:完整版AutoSec库大概增加68KB,便宜版裁剪后可以压到30KB以内。对于动辄几百KB甚至几MB的现代ECU固件来说,这个体积代价在可接受范围内。
5.3 时延与吞吐对比
CAN-FD场景下的单条报文处理时延:
| 处理阶段 | 裸传输 | 仅认证 | 加密+认证 |
|---|---|---|---|
| 发送端协议栈处理 | 0.12 ms | 0.28 ms | 0.61 ms |
| 接收端协议栈处理 | 0.10 ms | 0.25 ms | 0.55 ms |
| 总线传输(64字节@2Mbps) | 0.32 ms | 0.32 ms | 0.32 ms |
| 合计单向时延 | 0.54 ms | 0.85 ms | 1.48 ms |
单条报文最多增加约1毫秒延迟。对于周期性旋变传感器信号等10ms级周期来说,还有充足余量。以太网场景因为数据包更宽且硬件AES加速,加密带来的额外时延只有微秒级,基本不影响整体时延曲线。
吞吐方面,域控制器上跑千兆以太网,实测加密通道最大吞吐约780Mbps,接近线速;如果关闭加密只保留认证,能到920Mbps。瓶颈在TCP/IP栈的报文拷贝,不在加密算力上。
5.4 场景化结论
从实测结果能得出一个明确结论:安全不是免费的,但这个成本完全可控。CAN-FD节点采用“仅认证”模式即可满足多数控制类需求,以太网域间通信可以放心使用“加密+认证”。如果某个低速CAN节点连30KB Flash都挤不出来,那AutoSec还提供一种“最小认证模式”,只对报文计算4字节CDMAC并放入CRC段,代价是安全性降级但仍有基本防伪能力。这个模式我们只推荐给防盗报警等低风险功能,不推荐用于任何与动力安全相关的场景。
6. 量产落地时踩过的几个坑
6.1 E2E校验与SecOC的兼容性问题
第一个大坑是在集成阶段发现的:很多ECU已经在用AUTOSAR的E2E(End-to-End)库做数据完整性保护。E2E和AutoSec的保护逻辑虽然不同,但都占用报文中的CRC字段和数据长度,两个机制叠加后会导致报文设计表冲突。解决思路是让AutoSec兼容E2E的数据CRC替代逻辑,在AutoSec安全头里带一个“嵌套E2E”标志位,接收方先做E2E校验,再做AutoSec校验。这个兼容层花了两周才磨平。
6.2 HSM密钥存储空间不足
市场上不少车规安全芯片的密钥槽位极其有限,某些入门级HSM只有16个密钥槽。AutoSec的完整密钥体系包含根密钥、传输密钥、会话密钥、数字签名密钥等,一上来就要占用近一半槽位。最后我们优化了密钥槽分配策略:签名密钥和传输密钥共用一个槽位,通过密钥用途标签区分;多域的会话密钥不落槽位,只存在于RAM中。这个调整把整体槽位占用压缩到7个,总算留出了生产扩展空间。
6.3 车载以太网VLAN隔离与多播
以太网场景另外一个暗坑是组播流量的安全策略。ADAS摄像头和激光雷达的数据经常通过多播方式分发给多个处理节点,多播报文的接收方不是一个而是一组,标准的一对一会话密钥模型无法直接套用。AutoSec用组密钥来解决,每个多播组分配一个组成员共享的密钥,节点入组时通过控制会话获取组密钥。但这引入了新的风险:组内任何单一节点的密钥泄漏都会影响整组通信。后续建议加密算法层面做前向隔离,节点退组时立即轮换组密钥。
6.4 日志与调试后门的平衡
开发阶段为了方便问题定位,我在协议栈里留了一个“安全调试口”,允许通过诊断工具临时关闭某个节点的安全校验。测试过程中一切顺利,但在量产固件发布前的安全复审里,这个后门被安全审计人员抓住——哪怕有访问权限控制,后门的存在本身就是风险。最终处理方案是把调试口改为“仅允许在制造模式启用并擦除密钥”的条件逻辑,量产固件在收到密钥清除指令后自动永久禁用调试功能。教训很简单:开发便利性不能用安全风险来换,至少不能在量产版本里这么做。
写在最后
AutoSec这个项目从立项到跑通首版Demo前后花了大约5个月,其中真正写协议和算法的时间只占三分之一,其余时间都在跟既有架构、硬件资源约束和兼容性斗智斗勇。想要在真实车载环境里落地安全方案,核心技术能力之外,更考验的是对异构总线、有限算力和海量存量设备的平衡能力。
如果你正在给自己的项目做类似的安全改造,我最大的建议是:不要一上来就铺完整方案,先做最小可信集——选一条关键控制链路、两个节点、一套认证机制,验证性能开销不会破坏原有的实时性指标,再逐步扩展。安全是长跑,起步慢一点没关系,方向对了比什么都重要。