首页 > 解决方案 > Android HostApduService 通信

问题描述

我已使用本指南制作 APDU 过滤器和服务:https ://medium.com/the-almanac/how-to-build-a-simple-smart-card-emulator-reader-for-android-7975fae4040f

这是 HostAPDUService.kt 的代码

import android.nfc.cardemulation.HostApduService
import android.os.Bundle
import android.util.Log

class HostAPDUService: HostApduService() {

    companion object {
        val TAG = "Host Card Emulator"
        val STATUS_SUCCESS = "9000"
        val STATUS_FAILED = "6F00"
        val CLA_NOT_SUPPORTED = "6E00"
        val AID = "A0000002471001"
        val SAID = "A0000003790000"
        val SETUKORTTI = "A00000037900005005424f4e5553870103"
        val INS = "A4"
        val CLA = "00"
        val P1 = "04"
        val P2 = "00"
        /*
        +-----+-----+-----+-----+-----+-------------------------+-----+
        | CLA | INS | P1  | P2  | Lc  | DATA                    | Le  |
        +-----+-----+-----+-----+-----+-------------------------+-----+
        | 00  | A4  | 04  | 00  | XX  | AID                     | 00  |
        +-----+-----+-----+-----+-----+-------------------------+-----+
         */
    }

    override fun onDeactivated(reason: Int) {
        Log.d(TAG, "Deactivated: " + reason)
    }

    override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray {
        if (commandApdu == null) {
            return Utils.hexStringToByteArray(STATUS_FAILED)
        }

        val hexCommandApdu = Utils.toHex(commandApdu)

        // Check that command starts with 0x00
        if (hexCommandApdu.substring(0, 2) != CLA) {
            return Utils.hexStringToByteArray(CLA_NOT_SUPPORTED)
        }

        // SELECT PHASE
        if (hexCommandApdu.substring(0, 8) == CLA + INS + P1 + P2) {
            // READER: 2PAY.SYS.DDF01 RESPONSE: SETUKORTTI
            if (hexCommandApdu.substring(10, 24) == AID)  {
                return Utils.hexToByteArray(SETUKORTTI + STATUS_SUCCESS)
            }
            // READER: SELECT INITIALIZATION DATA
            if (hexCommandApdu.substring(10, 24) == SAID)  {
                return Utils.hexToByteArray("a0000003790000a5145005424f4e55539f38039f1a02bf0c04df650102" + STATUS_SUCCESS)
            }
        }

        // TRANSMIT PHASE
        if (hexCommandApdu.length < 12) {
            // READ #1 BLOCK
            if (hexCommandApdu == "00B2010C00") {
                return Utils.hexToByteArray("700cdf660960040706174935922f" + STATUS_SUCCESS)
            }
            // READ #2 BLOCK
            if (hexCommandApdu == "00B201140C") {
                return Utils.hexToByteArray("700adf700100df7103991231" + STATUS_SUCCESS)
            }
            // READ #3 BLOCK
            if (hexCommandApdu == "00B2011C00") {
                return Utils.hexToByteArray("7017df74140100000000000000000000000000000000000000" + STATUS_SUCCESS)
            }
            // READ END BLOCK
            if (hexCommandApdu == "0084000008") {
                return Utils.hexToByteArray("5c2d95593687c87b" + STATUS_SUCCESS)
            }
        }

        else {
            return Utils.hexStringToByteArray(STATUS_FAILED)
        }
    return Utils.hexToByteArray(STATUS_SUCCESS)
    }
}

我的服务有效,我可以使用 ACR122U 选择此 AID“A0000002471001”。现在我想开始与读卡器通信。我设法选择了 AID,但“TRANSMIT PHASE”给我一个错误:6A 82(找不到文件)。

那么由于某种原因,HostAPDUService 在返回时关闭了连接?出于某种神奇的原因,它第一次工作并完成了整个过程,但我无法重现。

Insert a card within 10 seconds
connecting to ACS ACR122U PICC Interface 0
AID test
> 00 A4 04 00 07 A0 00 00 03 79 00 00 00
< A0 00 00 03 79 00 00 A5 14 50 05 42 4F 4E 55 53 9F 38 03 9F 1A 02 BF 0C 04 DF 65 01 02 90 00
144 0
detected, selecting...
> 00 A4 04 00 07 A0 00 00 03 79 00 00
< A0 00 00 03 79 00 00 A5 14 50 05 42 4F 4E 55 53 9F 38 03 9F 1A 02 BF 0C 04 DF 65 01 02 90 00
144 0
> 00 B2 01 0C 00   <-- HERE WE ARE AT READ BLOCK #1
<  [] 6A 82        <-- RESPONSE: FILE NOT FOUND.
106 130
'Status word exception: !' 6a 82
disconnecting from ACS ACR122U PICC Interface 0
press Enter to continue

标签: androidnfcapdu

解决方案


所以只有读卡器可以发起与 HCE 的通信

HCE 使用 ISO7816 命令模拟 4 类 NFC 标签,您链接的指南链接到 ISO7816 参考。

阅读器如何读取 4 类卡的一般/简化概述。

  • 阅读器发送 APDU 以选择要使用的 AID,如果标签(或本例中的 HCE)支持该 AID,则它会做出响应。你已经完成了这部分
  • 阅读器发送 APDU 以选择卡片应用程序支持的文件,这是使用A4h带有正确文件标识符的 ADPU 命令完成的。
  • 阅读器发送读取命令,例如READ BINARYB0

更通用的 ISO7816 命令记录在https://cardwerk.com/smart-card-standard-iso7816-4-section-6-basic-interindustry-commands

因此,在 HCE 方面,您processCommandApdu处理这些发送的 APDU 并且应该做出适当的响应。你已经响应了select AIDAPDU(虽然“if”“if”“if”逻辑并不容易响应多个APDU命令,确实需要用到ANDelse逻辑)。

因此,在解析通用工作流程时,如果有读者要求的文件,commandApdu您将成功响应命令。select file

您还将解析并返回成功的数据负载以响应read binaryAPDU 命令

NFC 类型 4 规范http://apps4android.org/nfc-specifications/NFCForum-TS-Type-4-Tag_2中详细介绍了使用 4 类标签做一些有用的事情所需的逻辑类型的更好更完整的示例。 0.pdf 第 5.4 节

这详细说明了您将如何选择NDEF AID.
选择 Capability Container 文件并读取它的数据 然后选择正确的 NDEF 文件 ID 并读取它的数据。

您的 HCE 只需要解析传入APDU并做出适当的响应,实际上,在AID选择之后,如果您真的想要,您可以构建自己的私有命令集,但最好坚持行业规范命令。

抱歉没有代码,因为我自己没有这样做,但是您已经拥有所有必需的代码,您只需要发送(从阅读器)并响应(在 HCE 代码中)选择APDU以外的不同命令AIDAPDU


推荐阅读