别再硬啃手册了!用CANoe官方示例手把手拆解SeedKey诊断流程(附CAPL脚本调试技巧)
从官方示例工程实战解析CANoe诊断安全中的Seed&Key机制
在汽车电子开发领域,诊断安全访问机制是保护ECU免受非法访问的重要防线。对于刚接触UDS诊断协议的工程师来说,Seed&Key算法往往是最令人困惑的环节之一。本文将带你深入CANoe官方示例工程,通过三种不同的实操方式,完整呈现安全访问流程的实现细节。
1. 诊断控制台视角下的安全访问流程
打开CANoe安装目录下的Sample Configurations 11.0.55\CAN\Diagnostics\UDSSystem示例工程,这是我们探索的起点。这个预配置好的环境已经包含了完整的诊断通信仿真模型,特别适合学习安全访问机制。
典型的安全访问流程包含以下步骤:
- 通过
10 03指令进入扩展会话模式 - 发送
27 01请求种子(Seed) - 接收ECU返回的随机种子值
- 本地通过算法计算密钥(Key)
- 发送
27 02附带计算得到的密钥 - ECU验证密钥的正确性
在示例工程的DoorFL仿真节点中,关键处理逻辑位于CAPL脚本的on diagRequest事件处理程序中。当诊断控制台发送27 01请求时,会触发以下代码段:
on diagRequest DoorFL.SeedLevel_0x01_Request { diagResponse this resp; write("****** (27 01 reponse) is 11111 step exec ******"); refreshS3Timer(); if (ExtendedSession==@sysvar::%NODE_NAME%::CurrentSession || ProgrammingSession==@sysvar::%NODE_NAME%::CurrentSession) { gLastSecuritySeedLevel1=random(0x10000); write("****** return Seed is 0x%x ******",gLastSecuritySeedLevel1); diagSetParameter(resp, "SecuritySeed", gLastSecuritySeedLevel1); diagSendPositiveResponse(resp); } else { ResetSession(); diagSendNegativeResponse(this, cNRC_ConditionsNotCorrectOrRequestSequenceError); } }这段代码展示了几个关键点:
- 只有在扩展会话或编程会话下才会响应种子请求
- 使用
random()函数生成16位的随机种子 - 通过
diagSetParameter设置响应参数 - 最终通过
diagSendPositiveResponse发送肯定响应
2. Network Node CAPL脚本实现安全访问
除了使用诊断控制台,我们还可以通过Network Node的CAPL脚本实现完整的安全访问流程。这种方式更接近实际诊断仪的工作方式。
创建一个名为TesterPanel的Network Node,在其关联的CAPL脚本中,我们需要处理以下几个关键环节:
种子请求处理:
on key 'a' // 触发种子请求 { diagRequest DoorFL.SeedLevel_0x01_Request rqRequestSeed; rqRequestSeed.SendRequest(); }密钥生成与发送:
on diagResponse DoorFL.SeedLevel_0x01_Request { diagRequest DoorFL.KeyLevel_0x01_Send reqKeySend; word seed = this.GetParameter("SecuritySeed"); byte seedArray[2] = {(byte)(seed>>8), (byte)(seed&0xFF)}; byte keyArray[2]; dword keyActualSizeOut; diagGenerateKeyFromSeed("DoorFL", seedArray, 2, 1, "", "", keyArray, elcount(keyArray), keyActualSizeOut); word securityKey = (keyArray[1]<<8) | keyArray[0]; reqKeySend.SetParameter("SecurityKey", securityKey); reqKeySend.SendRequest(); }这种实现方式的优势在于:
- 完全通过编程控制诊断流程
- 可以灵活添加调试信息和错误处理
- 便于集成到自动化测试系统中
3. 回调函数方式处理密钥生成
CANoe还提供了另一种异步处理密钥生成的方式,通过DiagStartGenerateKeyFromSeed函数和回调函数机制实现。这种方式特别适合密钥计算耗时较长的情况。
回调函数实现示例:
on diagResponse DoorFL.SeedLevel_0x01_Request { byte seed[2]; this.GetParameterRaw("SecuritySeed", seed, elcount(seed)); DiagStartGenerateKeyFromSeed("DoorFL", seed, elcount(seed), 1); } _Diag_GenerateKeyResult(long result, BYTE computedKey[]) { if(0 != result) { write("Error: computing key returned %d", result); return; } diagRequest DoorFL.KeyLevel_0x01_Send reqKeySend; reqKeySend.SetParameterRaw("SecurityKey", computedKey, elcount(computedKey)); reqKeySend.SendRequest(); }与同步方式相比,回调函数方式具有以下特点:
- 不会阻塞CAPL脚本执行
- 需要额外实现错误处理回调
_Diag_GetError - 更适合复杂的密钥算法实现
4. XML Test Node中的安全访问实现
对于需要集成到自动化测试系统中的场景,XML Test Node提供了更结构化的实现方式。我们可以创建一个测试用例专门处理安全访问流程。
测试用例实现示例:
testcase TCSendDiagnosticRequest(int tcId) { diagRequest DoorFL.SeedLevel_0x01_Request rqRequestSeed; diagRequest DoorFL.ExtendedDiagnosticSession_Start reqExtSession; // 进入扩展会话 DiagSendRequest(reqExtSession); TestWaitForDiagResponse(reqExtSession,100); // 请求种子 rqRequestSeed.SendRequest(); TestWaitForDiagResponse(rqRequestSeed,100); // 获取种子并计算密钥 word seed = diagGetRespParameter(rqRequestSeed,"SecuritySeed"); byte seedArray[2] = {(byte)(seed>>8), (byte)(seed&0xFF)}; byte keyArray[2]; dword keyArraySize; diagGenerateKeyFromSeed("DoorFL", seedArray, 2, 1, "", "", keyArray, 2, keyArraySize); // 发送密钥 diagRequest DoorFL.KeyLevel_0x01_Send reqKeySend; reqKeySend.SetParameterRaw("SecurityKey", keyArray, 2); reqKeySend.SendRequest(); }在实际项目中调试Seed&Key机制时,有几个实用技巧值得分享:在CAPL脚本中添加详细的调试打印信息;使用CANoe的Trace窗口监控诊断报文;对于复杂的密钥算法,可以先用简单的固定密钥验证通信流程正确性。
