第四章: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. 通道隔离失效:卡片没有正确隔离不同通道的安全状态,导致通道1的未认证操作可以影响通道2
  2. 通道耗尽攻击:攻击者不断打开新通道,耗尽卡片资源
  3. 通道劫持:在通道建立过程中,攻击者伪造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协议 逻辑通道 字节级传输 块级传输 多路复用 过程字节控制 一问一答模式 INS字节复用 I/R/S三种块类型 EDC校验(LRC/CRC) 序列号与重传机制 CLA低4位编码 MANAGE CHANNEL命令 通道隔离与安全 攻击面:协议注入 | 通道劫持 | 校验绕过 | 资源耗尽

嗯,这张图把本章的核心脉络串起来了。你想想看,攻击者要攻击JavaCard,通信协议是第一道防线。APDU结构决定了攻击者能构造什么样的恶意指令,T=0和T=1协议决定了攻击者能在传输过程中做什么手脚,逻辑通道则决定了攻击者能否在多应用环境中找到隔离漏洞。

我个人习惯在做安全设计时,先把通信协议的攻击面画出来,然后逐个击破。比如T=0协议,重点关注过程字节的校验;T=1协议,重点关注EDC的强度;逻辑通道,重点关注通道状态的隔离性。

最后说一句:协议规范是死的,但实现是活的。很多漏洞就出在“规范没规定死”的地方。比如T=0协议中,卡片返回过程字节的时机,规范只说了“可以”,但没规定“必须”。这就给了攻击者操作空间。


公众号:蓝海资料掘金营,微信deep3321