android - 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
解决方案
所以只有读卡器可以发起与 HCE 的通信
HCE 使用 ISO7816 命令模拟 4 类 NFC 标签,您链接的指南链接到 ISO7816 参考。
阅读器如何读取 4 类卡的一般/简化概述。
- 阅读器发送 APDU 以选择要使用的 AID,如果标签(或本例中的 HCE)支持该 AID,则它会做出响应。你已经完成了这部分
- 阅读器发送 APDU 以选择卡片应用程序支持的文件,这是使用
A4h
带有正确文件标识符的 ADPU 命令完成的。 - 阅读器发送读取命令,例如
READ BINARY
或B0
更通用的 ISO7816 命令记录在https://cardwerk.com/smart-card-standard-iso7816-4-section-6-basic-interindustry-commands
因此,在 HCE 方面,您processCommandApdu
处理这些发送的 APDU 并且应该做出适当的响应。你已经响应了select AID
APDU(虽然“if”“if”“if”逻辑并不容易响应多个APDU命令,确实需要用到AND
和else
逻辑)。
因此,在解析通用工作流程时,如果有读者要求的文件,commandApdu
您将成功响应命令。select file
您还将解析并返回成功的数据负载以响应read binary
APDU 命令
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
以外的不同命令AID
APDU
推荐阅读
- django - 如何将我的简单自定义 django 模板标签与 if 语句一起使用?
- swift - 在两个视图控制器之间传递一个巨大的二维数组
- azure-devops - 使用 Azure DevOps App Service Deploy 任务部署自包含的 .NET Core 应用程序
- elasticsearch - 如何根据折叠项目的数量对弹性搜索结果进行排序?
- identityserver4 - IdentityServer4 中的帐户管理
- vue.js - 在 Vue 中启用(控制台)记录路由器事件
- php - 没有 VPN,Wordpress 无法打开
- mysql - MySQL选择多个值并添加到一列
- wordpress - WordPress Woocommerce。我有促销中的产品。想为我使用 php 代码创建的每个产品添加一个贴纸“促销”
- java - Vaadin 将值添加到列表