理解 APDU —— 面向软件开发者的指南

1. 引言

APDU 可类比为:
在这里插入图片描述

智能卡被广泛应用于银行、交通和访问控制系统等各种场景。这些卡片存储敏感信息,并配备微控制器,可执行加密操作。为了与智能卡通信,需要使用一种特定的通信协议,称为应用协议数据单元(Application Protocol Data Unit,APDU)。

APDU 是一种用于智能卡和读卡器之间通信的二进制格式。它定义了两者之间交换的数据结构,包括从读卡器发送到智能卡的命令,以及从智能卡发送到读卡器的响应。对于希望开发与智能卡交互的应用程序的软件开发者而言,理解 APDU 至关重要。

本文将解释 APDU 消息的结构、不同类型的命令以及智能卡可能返回的响应类型。此外,本文还将提供使用不同编程语言实现 APDU 命令的示例。阅读本文后,开发者将清楚了解 APDU,并能够构建安全、高效的应用程序,与智能卡进行交互。

2. 什么是 APDU?

APDU(Application Protocol Data Unit,应用协议数据单元)是一种用于智能卡和读卡器之间通信的协议。它是一种简单且标准化的格式,用于在两个设备之间发送和接收数据。APDU 消息包含两个部分:命令 APDU(Command APDU)和响应 APDU(Response APDU)。

命令 APDU 包含一个头部(Header),用于指定发送的命令类型,如读取或写入数据、用户身份验证或选择智能卡上的特定应用程序。头部还包括关于所发送数据长度的信息,以及该特定命令所需的其他参数。命令 APDU 由读卡器发送至智能卡。

响应 APDU 由智能卡返回给读卡器,并包含一个头部,用于指定命令的执行状态以及命令 APDU 请求的任何数据。头部还包括返回数据的长度信息,以及该特定响应所需的其他参数。响应 APDU 可以包含任何类型的数据,包括文本、二进制数据或错误代码。

APDU 协议广泛应用于银行、身份认证和安全领域。它是一种简单且高效的智能卡通信方式,标准化确保了不同制造商的设备可以无缝通信。对于从事智能卡技术或涉及设备间安全通信的开发者和工程师而言,理解 APDU 至关重要。

2.1 APDU示例

本小节将更详细地解析 “Select Application” 命令及其 CLA、INS、P1、P2 和 Data 字段的组成

示例命令 APDU:
00 A4 04 00 07 D2 76 00 00 85 01 01

  • CLA(Class,类别字节):00
    CLA 指定发送到智能卡的命令类型。对于 APDU 命令,CLA 字节通常设为 00

  • INS(Instruction,指令字节):A4
    INS 指定所发送的具体命令。在本例中,A4 表示 “Select File”(选择文件)命令。请注意,还有其他指令,可在 EMV 规范 中找到。

  • P1(参数 1 字节):04
    P1 提供有关所发送命令的附加信息。在本例中,04 表示数据字段包含要选择的应用程序的 AID(Application Identifier,应用程序标识符)。

  • P2(参数 2 字节):00
    P2 也是一个附加参数字节,在本例中,其值设为 00

  • 数据字段(Data Field):07 D2 76 00 00 85 01 01
    数据字段包含所选择应用程序的 AID。在本示例中,该 AID 长度为 7 字节,对应十六进制值 07 D2 76 00 00 85 01 01

总而言之,“Select Application” 命令使用 A4 指令来选择智能卡上的特定文件(即应用程序)。要选择的文件的 AID 被放入命令的数据字段,并由 P1 和 P2 参数字节作为前缀。对于 APDU 命令,CLA 字节通常设为 00

需要注意的是,CLA、INS、P1、P2 和数据字段的具体值取决于所使用的智能卡和应用程序。尽管 ISO/IEC 7816 规范提供了 APDU 命令的通用指南和要求,但各个字段的实际值可能因具体实现而异。

2.2 Kotlin APDU代码示例

使用 Kotlin 发送 APDU 命令到智能卡的示例(不使用任何库):

fun selectByname(isoDep: IsoDep): SelectPPSEData {
    
    
        val data = HexUtil.convertStringToHex("2PAY.SYS.DDF01")
        console.log("select application data", data)
        val command  = "00A404000E${
      
      data}00"
        console.log("select application", command)
        val result = isoDep.transceive(Conversions.HexStringToByteArray(command))
        console.log("select application isoResult", HexUtil.toHexString(result))
        var bertlvs = Conversions.parseBERTLV(result)

        val selectPPSEData = SelectPPSEData(
            APPLICATION_IDENTIFIER = TAGHelper.getTagFromTlv(bertlvs, SelectApplicationData.APPLICATION_IDENTIFIER.tag),
            APPLICATION_PRIORITY_INDICATOR = TAGHelper.getTagFromTlv(bertlvs, SelectApplicationData.APPLICATION_PRIORITY_INDICATOR.tag)
        )

        return selectPPSEData

    }

该示例执行 PSE(Payment System Environment,支付系统环境)的 “Select Application” 操作,后续文章中将详细解释。

  • 首先,将要发送的数据转换为十六进制(HEX)格式,
  • 然后构造 APDU 命令并转换为 ByteArray
  • 接着使用 ISODEP(一种 NFC 通信协议)发送 APDU 命令,并接收响应数据。
  • 接收到的响应数据会解析为 BERTLV 格式(将在下一篇文章中讨论)。
  • 最后,从 TLV(标签-长度-值)数据中提取必要的标签信息。

3. 总结

APDU 是智能卡的重要通信协议,允许在卡片与读卡器之间进行安全高效的数据交换。理解 APDU 消息的结构、不同类型的命令以及可能的响应,对于希望开发智能卡交互应用的开发者来说至关重要。

通过遵循本文提供的指南,开发者可以使用 Java、Python、C# 和 Kotlin 等多种编程语言,构建安全高效的智能卡应用程序。随着智能卡技术在各类应用中的需求不断增长,开发者必须理解 APDU,以确保其应用程序的安全性和可靠性。

希望本文能帮助软件开发者深入理解 APDU 及其在不同编程语言中的实现。掌握这些知识后,开发者将能够自信地构建与智能卡交互的应用程序,并促进该技术的发展和进步。

参考资料

[1] Understanding APDU for Software Developers