一、27 服务的本质:对暗号
把 ECU 想象成一栋有门禁的大楼。你想进去干活(刷写、写参数、清码),得先跟保安对暗号。
- 你说:"我要对暗号。"→ ECU 给你一个随机数(种子 / Seed)
- 你按约定规则把随机数变换一下,报回去→ 这就是密钥(Key)
- 保安核对:你报的跟它算的一样吗?→ 一样就放行,不一样就拒绝
就这么简单。
27 服务是单向的:诊断仪向 ECU 证明"我是自己人"。ECU 不需要反过来向你证明它是谁。如果你需要验证 ECU 身份(比如确认对面不是伪造节点),那是读 DID、走 TLS 握手、或者 OEM 自定义协议的事,不是 27 的事。
二、报文格式:就那几个字节,多一个都是错
请求种子
27 <奇数子功能> [可选附加数据]没有附加数据时,就 2 个字节:
27 01有附加数据时(比如 OEM 要求携带设备标识),直接跟在子功能后面:
27 01 12 34没有"数据长度"字段。没有填充零。没有保留字节。报文总长度由传输层隐式确定,ECU 通过总长度推断数据部分有多少字节。
发送密钥
27 <偶数子功能> <密钥数据>27 02 BA EF 21 70子功能 0x02(安全等级 1 的"发送密钥"),后面跟 4 字节密钥。没有长度字段。
ECU 响应
| 场景 | 响应 |
|---|---|
| 种子响应 | 67 <奇数子功能> <种子数据> |
| 密钥验证通过 | 67 <偶数子功能> |
| 否定响应 | 7F 27 <NRC> |
具体示例:
你发:27 01 ECU回:67 01 AB CD 12 34 ← 种子,4字节 你发:27 02 BA EF 21 70 ECU回:67 02 ← 验证通过,就2字节 你发:27 02 00 00 00 00 ← 故意发错 ECU回:7F 27 35 ← 密钥不对种子长度
种子多长由 OEM 定义:4 字节、8 字节、16 字节都有可能。你从响应总长度减去 2(SID + 子功能)就知道种子有几字节。不需要额外的"长度字段"告诉你。
三、子功能配对:奇偶规则
27 服务唯一需要"记"的规则:
| 安全等级 | 请求种子(奇数) | 发送密钥(偶数) |
|---|---|---|
| 1 | 0x01 | 0x02 |
| 2 | 0x03 | 0x04 |
| 3 | 0x05 | 0x06 |
| 4 | 0x07 | 0x08 |
| …… | …… | …… |
| 63 | 0x7D | 0x7E |
规律:同一等级,请求种子 = 2n−1,发送密钥 = 2n。最多 63 个安全等级。
但每个等级"能干什么",标准不管
ISO 14229-1 只规定了编号配对规则。每个等级对应什么权限、需要什么会话前提、用什么算法——全是 OEM 在诊断规范(CDD/ODX)里定义的。
你看到的"等级 1 = 读 VIN"、"等级 2 = 清 DTC"、"等级 3 = 刷写"——那是某个 OEM 自己的划分。换一个品牌,等级 1 可能压根不存在,等级 3 可能是清码权限。
永远查诊断规范。别"记"等级含义。
四、安全等级隔离:解锁了不等于万能
这是很多人踩过的坑:以为解锁了一个安全等级,就能干所有事。
标准规定:安全等级之间互相独立
ISO 14229-1 明确说:每个安全等级是独立的权限域。解锁等级 1 不代表你拥有等级 2 的权限,解锁等级 3 也不代表你自动拥有等级 1 和 2 的权限。
实际表现
你解锁了等级 1(27 01/02) → 可以执行等级 1 允许的操作 → 尝试执行等级 2 的操作 → 7F xx 33(securityAccessDenied) 你又解锁了等级 3(27 05/06) → 可以执行等级 3 允许的操作 → 等级 1 的权限还在吗?取决于 OEM 实现 → 有些 ECU:解锁新等级后,旧等级权限保留 → 有些 ECU:解锁新等级后,旧等级权限被替换三种常见的 OEM 实现策略
| 策略 | 行为 | 典型场景 |
|---|---|---|
| 累加型 | 解锁等级 N 后,等级 1~N 的权限全部可用 | 老车型、低安全需求 |
| 替换型 | 解锁等级 N 后,只有等级 N 的权限可用,旧权限失效 | 高安全需求、新能源 BMS |
| 独立型 | 每个等级完全独立,互不影响,需要分别解锁 | 最严格的安全架构 |
怎么知道你的 ECU 是哪种?查规范。或者实测:解锁等级 3 后,试着执行一个等级 1 的操作,看是否返回 0x33。
实操建议
如果你的诊断流程需要多个安全等级的权限:
- 查规范确认隔离策略
- 如果是替换型或独立型,按顺序从低到高解锁,或者在需要时重新解锁目标等级
- 不要假设"高等级包含低等级"——这个假设在很多 ECU 上是错的
五、种子不过期。安全访问不过期。
这是流传最广的两个误解,必须掰清楚。
种子没有倒计时
标准不规定种子有效期。种子在以下情况失效:
| 事件 | 种子是否失效 |
|---|---|
| 你又请求了一个新种子 | ✅ 旧种子被替换 |
会话切换(如10 01切回默认) | ✅ 失效 |
ECU 复位(11 01) | ✅ 失效 |
| 你拿着种子想了 30 秒再发密钥 | ❌ 不失效 |
| 你拿着种子想了 5 分钟再发密钥 | ❌ 不失效(只要会话还在) |
种子没有"5 秒有效期"、"10 秒有效期"这种说法。只要你不做上面前三件事,种子一直有效。
安全访问没有独立的"权限有效期"
安全访问的生命周期与会话绑定:
| 事件 | 安全访问是否失效 |
|---|---|
| 会话存续中,你啥也不干 | 不失效 |
你发3E 00保活 | 不失效 |
| S3 超时(TesterPresent 超时),会话退回默认 | 失效(会话变了) |
| 你主动切会话 | 失效 |
| ECU 复位 | 失效 |
| 诊断仪拔线 | 失效(会话无法保活,最终 S3 超时) |
不是"30 秒没操作就回收"。只要会话活着(靠3E 00保活),安全访问一直在。
实操建议
解锁后如果你要连续做很多操作(写 VIN、写标定、跑例程),每隔 3~4 秒发一次3E 00。不是为了"保活安全访问",是为了保活会话——安全访问跟着会话走。
六、NRC 速查:别望文生义
27 服务相关的 NRC(完整版)
| NRC | 标准名称 | 人话翻译 | 你大概干了什么 |
|---|---|---|---|
| 0x12 | subFunctionNotSupported | 这个安全等级我不认识 | 请求了 ECU 没定义的等级 |
| 0x13 | incorrectMessageLengthOrInvalidFormat | 报文格式不对 | 多塞了零,或少了字节 |
| 0x22 | conditionsNotCorrect | 现在不是时候 | 前提条件不满足 |
| 0x24 | requestSequenceError | 你步骤错了 | 没请求种子就直接发密钥;或发了密钥后又发密钥 |
| 0x31 | requestOutOfRange | 超出范围 | 安全等级编号超了 |
| 0x33 | securityAccessDenied | 权限不够 | 没解锁,或解锁的等级不对 |
| 0x35 | invalidKey | 密钥算错了 | 算法不对、密钥值不对、字节序反了 |
| 0x36 | exceededNumberOfAttempts | 你错太多次了 | 连续 N 次 0x35 后触发 |
| 0x37 | requiredTimeDelayNotExpired | 惩罚时间没到,别急 | 触发了 0x36 后,等待期内又来请求 |
| 0x78 | requestCorrectlyReceived-ResponsePending | 在处理,等一下 | 别重发,等最终响应 |
| 0x7F | serviceNotSupportedInActiveSession | 当前会话不允许 27 | 没切到对的会话 |
重点讲讲 0x24:requestSequenceError
这个 NRC 专门针对违反 27 服务交互顺序的情况。
触发场景:
| 你干了什么 | 为什么错 |
|---|---|
没发27 01就直接发27 02 | 没请求种子就想发密钥,ECU 不知道你要验证哪个种子 |
发了27 02后又发27 02 | 密钥只能发一次,第二次就是序列错误 |
发了27 01拿到种子后,又发27 01(同等级) | 有些 ECU 认为这是序列错误(旧种子被丢弃,新种子生成);有些 ECU 允许(视为刷新种子)。查规范确认 |
解锁成功后,又发27 01(同等级) | 已经解锁了还请求种子,部分 ECU 返回 0x24 |
0x24 vs 0x35 的区别:
- 0x35 = 你发了密钥,但算错了(格式对,内容错)
- 0x24 = 你发的时机就不对(根本不该在这个时候发这个报文)
实操排查:收到 0x24 时,检查你的诊断仪状态机是不是乱了。最常见的原因是:上一轮解锁失败后,诊断仪没有重置状态,直接又发了一个27 02。
最容易搞混的三个
0x35 vs 0x36:
- 0x35 = 你这一次算错了,还能再来
- 0x36 = 你已经连续错了好几次了,ECU 触发惩罚
0x36 vs 0x37:
- 0x36 = "你错太多次了"(触发惩罚的那一刻)
- 0x37 = "惩罚时间还没过,你急什么"(惩罚期内)
0x12 vs 0x31:
- 0x12 = ECU 压根不支持这个子功能(比如 ECU 只实现了等级 1 和 2,你请求等级 5)
- 0x31 = 子功能编号超出 ECU 定义的范围
惩罚机制(Delay Time)
第 1 次密钥错误:7F 27 35("算错了,再来") 第 2 次密钥错误:7F 27 35("又算错了") 第 3 次密钥错误:7F 27 36("你错太多次了,等 10 秒") ↓ 等待期内 你又来请求:7F 27 37("我说了等 10 秒,你急什么") ↓ 10 秒后 重新来:27 01 → 拿新种子 → 算密钥 → 27 02注意:
- 尝试次数上限和惩罚时间都是 OEM 定义的,标准不规定具体值
- 惩罚期间,请求种子也会被拒绝(回 0x37)
- 惩罚结束后,必须重新请求种子,旧种子已经无效
七、完整实操:解锁安全等级 1,从头到尾
场景:我要解锁安全等级 1,然后写一个配置参数。
先查规范:这个 ECU 的安全等级 1 需要扩展会话(10 03)。算法是简单异或,固定密钥 =11 22 33 44。
第 1 步:切会话 你发:10 03 ECU回:50 03 00 32 01 F4 ("好,扩展会话已开") 第 2 步:请求种子 你发:27 01 (就 2 个字节) ECU回:67 01 AB CD 12 34 ("好,种子:AB CD 12 34,4 字节") 第 3 步:算密钥 (异或算法,固定密钥 = 11 22 33 44) AB ^ 11 = BA CD ^ 22 = EF 12 ^ 33 = 21 34 ^ 44 = 70 密钥 = BA EF 21 70 第 4 步:发送密钥 你发:27 02 BA EF 21 70 ECU回:67 02 ("验证通过。你进来了。") 第 5 步:干活 你发:2E 02 00 03 20 ECU回:6E 02 00 第 6 步:保活(如果还有后续操作) 你发:3E 00 ECU回:7E 00 第 7 步:搞定,切回默认会话 你发:10 01 ECU回:50 01 (安全访问随会话结束而失效)整个 27 服务交互,核心就 4 个报文:27 01→67 01 <seed>→27 02 <key>→67 02。
八、AES 算法详解:高安全等级的标配
当 OEM 觉得异或、CRC 不够安全时,就会上 AES。这是目前车载 ECU 高安全等级的主流选择。
AES 在 27 服务中的角色
AES 是对称加密算法。在 27 服务中,它被当作一个"伪随机函数"使用——不是用来加密数据,而是用来把种子变换成密钥。
输入:种子(Seed)+ 预共享密钥(Secret Key) 输出:密钥(Key) Key = AES_Encrypt(Secret_Key, Seed)ECU 和诊断仪持有相同的 Secret Key。诊断仪用 Secret Key 加密 Seed 得到 Key,发给 ECU。ECU 用同样的 Secret Key 加密同一个 Seed,对比结果。一致就通过。
AES-128 vs AES-256
| 参数 | AES-128 | AES-256 |
|---|---|---|
| 密钥长度 | 16 字节 | 32 字节 |
| 块大小 | 16 字节(固定) | 16 字节(固定) |
| 安全强度 | 足够应对当前车载威胁 | 更高,面向未来 |
| ECU 计算开销 | 较低 | 较高(约慢 30%~40%) |
| 典型应用 | 多数乘用车 ECU | ADAS、BMS、网关等高安全 ECU |
注意:AES 的块大小永远是 16 字节。这和密钥长度无关。AES-128 和 AES-256 的区别只在密钥长度,不在块大小。
种子长度与 AES 块大小的匹配问题
AES 每次处理 16 字节。但种子不一定是 16 字节。怎么办?
| 种子长度 | 处理方式 | 说明 |
|---|---|---|
| 16 字节 | 直接加密 | 完美匹配,一次 AES 运算 |
| < 16 字节 | 填充(Padding)到 16 字节 | 常见填充方式见下文 |
| > 16 字节 | 分块处理 | CBC 模式或截断取前 16 字节 |
填充方式(Padding)
当种子不足 16 字节时,必须填充到 16 字节才能送入 AES。常见填充方式:
| 填充方式 | 规则 | 示例(种子 = AB CD 12 34,4 字节) |
|---|---|---|
| Zero Padding | 末尾补 0x00 | AB CD 12 34 00 00 00 00 00 00 00 00 00 00 00 00 |
| PKCS#7 | 补 N 个值为 N 的字节 | AB CD 12 34 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C |
| OEM 自定义 | 补固定值或特定模式 | 查规范 |
填充方式由 OEM 定义。用错了填充方式,算出来的密钥一定不对,但你不会收到 0x13(格式错误),只会收到 0x35(密钥错误)——因为报文格式是对的,只是内容错了。
工作模式
AES 有多种工作模式。在 27 服务中:
| 模式 | 是否常用 | 说明 |
|---|---|---|
| ECB | ✅ 最常用 | 种子作为明文块直接加密。简单、快速、无需 IV |
| CBC | 少见 | 需要 IV(初始化向量),增加复杂度 |
| CTR / GCM | 极少见 | 通常用于数据传输加密,不用于 27 服务 |
绝大多数 OEM 在 27 服务中使用 AES-ECB。原因很简单:27 服务只需要一个"确定性变换",不需要流加密或认证加密的特性。ECB 在这个场景下完全够用。
密钥派生:Secret Key 从哪来?
AES 的 Secret Key 不是硬编码在代码里的字符串。通常有两种来源:
| 来源 | 说明 |
|---|---|
| OEM 统一分配 | 同一平台所有 ECU 使用相同 Secret Key,诊断仪内置 |
| ECU 个体绑定 | Secret Key 与 ECU 序列号/DID 绑定,每个 ECU 不同。诊断仪需要从 OEM 服务器动态获取 |
后者更安全(一个 ECU 的密钥泄露不影响其他 ECU),但诊断工具的实现也更复杂。
AES 调试排查清单
当你用 AES 算法却总是收到 0x35 时,按这个顺序排查:
- Secret Key 对不对?版本是否匹配?是否用了旧的密钥文件?
- 种子有没有看错?是不是把上一个种子当成了当前的?
- 填充方式对不对?Zero Padding 还是 PKCS#7?
- 工作模式对不对?ECB 还是 CBC?如果用 CBC,IV 是什么?
- 字节序对不对?Secret Key 和种子的字节序是大端还是小端?
- 输出截取规则对不对?AES 输出 16 字节,但 ECU 可能只取前 4/8 字节作为密钥。取哪几个字节?
第 6 点是最容易忽略的。AES-128-ECB 输出永远是 16 字节,但很多 ECU 只取前 4 字节或前 8 字节作为最终密钥。剩余字节丢弃。查规范确认截取规则。
一个完整的 AES-128-ECB 示例
已知: Secret Key = 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 种子 = AB CD 12 34(4 字节) 填充方式 = Zero Padding 工作模式 = ECB 输出截取 = 前 4 字节 步骤: 1. 填充种子到 16 字节: AB CD 12 34 00 00 00 00 00 00 00 00 00 00 00 00 2. AES-128-ECB 加密: Plaintext = AB CD 12 34 00 00 00 00 00 00 00 00 00 00 00 00 Key = 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF Ciphertext = 6B 1C 4E 8A ...(16 字节输出) 3. 截取前 4 字节: Key = 6B 1C 4E 8A 4. 发送: 27 02 6B 1C 4E 8A九、会话与安全等级的关系
标准不规定"哪个会话支持哪个安全等级"
"默认会话支持等级 1,扩展会话支持等级 2/3,编程会话支持等级 4"——这不是标准规定,是某个 OEM 的配置。
实际情况千差万别:
| 可能的配置 | 说明 |
|---|---|
| 默认会话不允许 27 | 很常见。想解锁?先切会话 |
| 扩展会话允许等级 1~3 | 常见 |
| 编程会话允许等级 1~5 | 常见 |
| 默认会话允许等级 1 | 少见,但存在 |
查规范。别猜。
读 VIN 需要安全验证吗?
大多数 OEM:不需要。
你发:22 F1 90 ECU回:62 F1 90 4C 5A 57 41 ...默认会话,无安全验证,直接读。VIN 是车辆标识,年检、保险、二手车交易都要读。
写 VIN 才需要:编程会话 + 安全验证 + 通常只允许写一次。
十、常见问题排查
发27 01,ECU 回7F 27 7F
当前会话不允许 27。
→ 先切会话:10 03(或 10 02,查规范) → 再发:27 01发27 01,ECU 回7F 27 12
这个安全等级 ECU 不支持。
→ 查规范,确认 ECU 支持哪些等级 → 也许只支持等级 1(27 01/02),你发了 27 05 它不认识发密钥,ECU 回7F 27 24
步骤错了。
→ 检查状态机:是不是没请求种子就发了密钥? → 是不是发了密钥后又发了一次密钥? → 重置诊断仪状态,重新走完整流程:27 01 → 67 01 → 27 02发密钥,ECU 回7F 27 35
密钥算错了。排查清单:
- 算法对不对?(异或?AES?CRC?)
- Secret Key / 固定密钥对不对?(是不是用了旧版本?)
- 字节序对不对?(大端还是小端?)
- 种子有没有看错?
- 密钥长度对不对?
- (AES 专属)填充方式对不对?
- (AES 专属)工作模式对不对?
- (AES 专属)输出截取规则对不对?
发密钥,ECU 回7F 27 36
连续错太多次了。
→ 等。等惩罚时间过去(查规范,通常 10 秒~10 分钟) → 等完了,重新走:27 01 → 拿新种子 → 算密钥 → 27 02 → 旧种子已经废了,必须重新请求惩罚期内发27 01,ECU 回7F 27 37
惩罚时间没到。
→ 继续等。别反复请求 → 等够了再发 27 01解锁成功了,但执行操作时报7F xx 33
安全等级不对,或权限已被替换。
→ 查规范,确认操作需要什么等级 → 确认 ECU 的安全等级隔离策略(累加/替换/独立) → 如果是替换型,重新解锁目标等级十一、传输层:CAN ID 不是 27 服务的一部分
有些资料把报文写成:
18 DA F1 00 27 01让人以为18 DA F1 00是 27 服务报文的一部分。
不是。那是传输层的事:
| 层 | 内容 | 标准 |
|---|---|---|
| CAN 传输层 | CAN ID = 0x18DAF100 / 0x18DA00F1 | ISO 15765-4 |
| UDS 应用层 | 27 01/67 01 AB CD 12 34 | ISO 14229-1 |
27 服务的报文就是27 01。CAN ID 负责路由,跟 27 服务的格式无关。以太网(DoIP)上,27 服务的报文还是27 01,只是搬运方式变了。
十二、27 服务在诊断流程里的位置
10 03 ← 切扩展会话 ↓ 27 01 / 27 02 ← 解锁安全等级 1 ↓ 22 F1 90 ← 读 VIN(如果需要) ↓ 10 02 ← 切编程会话(如果需要更高权限) ↓ 27 05 / 27 06 ← 解锁安全等级 3 ↓ 2E F1 90 ... ← 写 VIN ↓ 31 01 02 02 ← 校验写入(如果 OEM 定义了) ↓ 10 01 ← 切回默认会话,安全访问自动失效27 在这个链条里是门禁卡——它本身不干活,但没有它,后面的活干不了。
十三、速查卡
┌──────────────────────────────────────────────────────────────┐ │ 27 服务(SecurityAccess)速查 │ │ │ │ 请求种子:27 <奇数> (2 字节起步) │ │ 发送密钥:27 <偶数> <key> │ │ │ │ 种子响应:67 <奇数> <seed> │ │ 密钥确认:67 <偶数> (就 2 字节) │ │ 否定响应:7F 27 <NRC> │ │ │ │ 没有"数据长度"字段 │ │ 没有种子"有效期"(标准不规定) │ │ 没有独立的"权限有效期"(与会话同生命周期) │ │ 单向验证(诊断仪 → ECU) │ │ 安全等级互相独立(隔离策略由 OEM 定义) │ │ 安全等级含义、算法、尝试次数、惩罚时间 → 全是 OEM 定义 │ │ │ │ 子功能配对: │ │ 0x01/0x02 = 等级1 0x03/0x04 = 等级2 │ │ 0x05/0x06 = 等级3 ……最多到 0x7D/0x7E = 等级63 │ │ │ │ NRC: │ │ 0x12 = 子功能不支持 │ │ 0x24 = 步骤错了(没请求种子就发密钥 / 重复发密钥) │ │ 0x33 = 权限不够(等级不对或被替换) │ │ 0x35 = 密钥错了 │ │ 0x36 = 错太多次了 │ │ 0x37 = 惩罚时间没到 │ │ 0x7F = 会话不对 │ │ │ │ AES 调试优先级: │ │ Secret Key → 种子 → 填充 → 模式 → 字节序 → 输出截取 │ │ │ │ 生命周期: │ │ 会话在 → 安全访问在 │ │ 会话断 / 切换 / 复位 → 安全访问失效 │ │ 用 3E 00 保活会话 │ └──────────────────────────────────────────────────────────────┘写在最后
27 服务的报文格式是 UDS 里最简单的之一——请求 2 个字节起步,响应 2 个字节起步,没有子功能嵌套,没有掩码,没有例程参数。
它之所以让人觉得复杂,是因为三样东西叠在一起:安全的神秘感 + OEM 的自定义 + 算法的细节。但只要拆开看:
- 格式:就那几个字节,标准写得清清楚楚
- 权限:查规范,别猜
- 算法:AES 也好异或也好,本质都是"确定性变换",调通了就是调通了
下次在台架前坐下,打开诊断工具,发一个27 01,看着 ECU 回一串种子——那一刻你会觉得,所谓"安全访问",也不过就是两个字节的事。剩下的,不过是耐心和细心。