第四章:JavaCard通信协议——APDU命令结构、T=0协议、T=1协议、逻辑通道
通信协议这块,说白了就是卡片和终端之间怎么“说话”。我刚开始接触JavaCard时,总觉得APDU就是发几条指令而已,后来踩了不少坑才明白——协议细节没吃透,安全攻防根本无从下手。你想想看,攻击者最常利用的就是通信过程中的漏洞。
4.1 APDU命令结构——卡片世界的“信封”
APDU全称是Application Protocol Data Unit,应用协议数据单元。它分两种:命令APDU(终端发给卡片)和响应APDU(卡片返回给终端)。
命令APDU的结构,我习惯把它拆成两部分:头部和体部。
| 字段 | 长度 | 说明 |
|---|---|---|
| CLA | 1字节 | 指令类别,高4位通常为0x00或0x80,低4位含安全消息和通道号 |
| INS | 1字节 | 指令代码,比如0xA4是SELECT,0xB0是READ BINARY |
| P1, P2 | 各1字节 | 参数,具体含义由INS决定 |
| Lc | 0或1或3字节 | 命令数据长度(Data字段的字节数) |
| Data | Lc字节 | 命令数据体 |
| Le | 0或1或2字节 | 期望响应数据的最大长度 |
响应APDU更简单:
| 字段 | 长度 | 说明 |
|---|---|---|
| Data | 可变 | 响应数据体 |
| SW1, SW2 | 各1字节 | 状态字,0x9000表示成功 |
核心要点:CLA字节的低4位在ISO/IEC 7816-4中定义为通道号(0-3),这是逻辑通道的基础。攻击者经常篡改CLA字节来尝试越权访问。
举个例子,一个典型的SELECT命令APDU:
CLA = 0x00
INS = 0xA4
P1 = 0x04 // 按名称选择
P2 = 0x00 // 第一个或唯一一个
Lc = 0x06 // 后面数据长度6字节
Data = 0xA0 0x00 0x00 0x00 0x62 0x03 // AID
Le = 0x00 // 期望返回最大256字节
嗯,这里要注意:Le=0x00表示期望最多256字节,而不是0字节。我见过不少新手在这里栽跟头。
4.2 T=0协议——字节级别的“一问一答”
T=0是异步半双工字符传输协议。说白了,就是终端发一个字节,卡片回一个字节,像两个人轮流说话。
它的特点很明显:
- 过程字节(Procedure Bytes):卡片在处理过程中可以插入特殊字节来控制流程
- INS字节复用:卡片用INS字节的补码作为过程字节,表示“继续”或“等待”
- SW1/SW2在最后:处理完成后,卡片返回状态字
我记得有一次做安全评估,发现某款卡片在T=0协议下存在一个经典漏洞:攻击者可以在卡片返回SW1之前,插入伪造的过程字节,诱导卡片重复发送敏感数据。这就是所谓的“T=0协议注入攻击”。
避坑指南:我曾经在项目中遇到一个案例,卡片在T=0模式下处理SELECT命令时,没有严格校验过程字节的合法性。攻击者利用这一点,发送精心构造的INS字节,让卡片误以为需要继续发送数据,从而泄露了内部文件结构。修复方案是:在APDU分发器中增加对过程字节的完整性校验。
T=0协议的典型交互流程:
终端发送: CLA INS P1 P2 Lc
卡片返回: INS补码 (表示继续)
终端发送: Data (Lc字节)
卡片返回: SW1 SW2 (处理结果)
或者另一种情况:
终端发送: CLA INS P1 P2 Le
卡片返回: INS补码 (表示继续)
终端发送: (无数据)
卡片返回: Data (Le字节) + SW1 SW2
4.3 T=1协议——块级别的“可靠传输”
T=1是异步半双工块传输协议。它把数据封装成块(Block),每个块都有完整的校验机制。
块结构如下:
| 字段 | 长度 | 说明 |
|---|---|---|
| NAD | 1字节 | 节点地址,源地址和目的地址各占4位 |
| PCB | 1字节 | 协议控制字节,标识块类型(I块、R块、S块) |
| LEN | 1字节 | 信息字段长度(0-254) |
| INF | LEN字节 | 信息字段,即APDU内容 |
| EDC | 1或2字节 | 错误检测码,通常是LRC或CRC |
T=1协议比T=0复杂,但更可靠。它有三种块类型:
- I块(信息块):传输APDU数据,带序列号
- R块(接收就绪块):确认收到,或请求重传
- S块(管理块):用于建立/释放连接、改变参数等
我个人习惯在安全设计中优先推荐T=1协议,因为它天然具备抗重放和完整性校验能力。但要注意,T=1的EDC字段如果使用LRC(纵向冗余校验),其实很容易被绕过——LRC只是简单的异或和,攻击者可以轻松伪造。
经验之谈:我在一个金融卡项目中,发现卡片虽然用了T=1协议,但EDC用的是LRC而不是CRC。攻击者通过修改块内容并重新计算LRC,成功绕过了完整性检查。后来我们强制要求使用CRC-16,才堵住这个漏洞。
4.4 逻辑通道——多路复用的“虚拟连接”
逻辑通道允许终端通过同一个物理连接,同时与卡片上的多个应用或同一应用的多个会话进行通信。ISO/IEC 7816-4定义了4个基本逻辑通道(0-3),扩展后最多支持20个。
逻辑通道的编号就藏在CLA字节的低4位里:
CLA = 0x00 | 通道号 // 基本通道
CLA = 0x80 | 通道号 // 安全消息通道
举个例子:
- 通道0:CLA=0x00,用于SELECT主安全域
- 通道1:CLA=0x01,用于SELECT支付应用
- 通道2:CLA=0x02,用于SELECT门禁应用
逻辑通道的攻击面,我总结了几点:
- 通道隔离失效:卡片没有正确隔离不同通道的安全状态,导致通道1的未认证操作可以影响通道2
- 通道耗尽攻击:攻击者不断打开新通道,耗尽卡片资源
- 通道劫持:在通道建立过程中,攻击者伪造MANAGE CHANNEL命令,抢占已分配的通道号
避坑指南:我曾经审计过一个JavaCard实现,它在MANAGE CHANNEL命令中只校验了CLA的高4位,忽略了低4位的通道号。攻击者发送CLA=0x01的MANAGE CHANNEL命令,成功在通道0上打开了第二个会话,绕过了通道0已有的安全认证。修复方案是:在命令分发器中,必须同时校验CLA的高4位和低4位。
4.5 知识体系总览
下面这张图,是我梳理的本章知识体系。你可以看到APDU是基础,T=0和T=1是传输方式,逻辑通道是上层复用机制。攻击者往往从这些协议的边界和实现漏洞入手。
嗯,这张图把本章的核心脉络串起来了。你想想看,攻击者要攻击JavaCard,通信协议是第一道防线。APDU结构决定了攻击者能构造什么样的恶意指令,T=0和T=1协议决定了攻击者能在传输过程中做什么手脚,逻辑通道则决定了攻击者能否在多应用环境中找到隔离漏洞。
我个人习惯在做安全设计时,先把通信协议的攻击面画出来,然后逐个击破。比如T=0协议,重点关注过程字节的校验;T=1协议,重点关注EDC的强度;逻辑通道,重点关注通道状态的隔离性。
最后说一句:协议规范是死的,但实现是活的。很多漏洞就出在“规范没规定死”的地方。比如T=0协议中,卡片返回过程字节的时机,规范只说了“可以”,但没规定“必须”。这就给了攻击者操作空间。
公众号:蓝海资料掘金营,微信deep3321