通过 javax.smartcardio 读取虚拟 NFC 标签

Posted

技术标签:

【中文标题】通过 javax.smartcardio 读取虚拟 NFC 标签【英文标题】:Reading phantom NFC tags via javax.smartcardio 【发布时间】:2013-10-05 16:15:48 【问题描述】:

我有一个用于 tikitag 网络服务的旧 NFC 阅读器(后来更名为 touchatag,最终在 2012 年左右放弃)。由于该网站不再可用,我再也找不到原来的 tikitag/touchatag 驱动程序。经过一番搜索,我发现这个 NFC 读卡器是通用的 ACS ACR122U USB 读卡器,并从here 安装了合适的驱动程序。我的系统是 Windows 7(64 位)。

首先,我尝试使用NFC Tools library 对NFC 标签进行高级读写访问。我收到一条错误消息,说遇到了不受支持的标签;尽管阅读器上没有标签,甚至在很远的地方都没有标签。似乎其他开发人员在使用此库时也遇到了相同的错误,如here 所示。请注意,此标记会被无限检测(因此,它不会在被检测到一次后就消失)。

我将所需的低级代码复制到一个单独的类中(即,独立于 NFC 工具库)。您可以在下面找到此代码(类似的代码也可以在教程中找到):

import java.util.List;

import javax.smartcardio.Card;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.TerminalFactory;

import org.nfctools.utils.NfcUtils;

public class NdefTest 

    public static void main(String[] args) throws Exception 
        TerminalFactory factory = TerminalFactory.getDefault();
        List<CardTerminal> terminals = factory.terminals().list();
        CardTerminal terminal = terminals.get(0);

        if (terminal.waitForCardPresent(5000)) 
            Card card = terminal.connect("T=0");
            System.out.println(NfcUtils.convertBinToASCII(card.getATR().getHistoricalBytes()));
        
    

此代码检测到与使用 NFC 工具库时完全相同的“幻像”标签。因此,这个问题似乎与 NFC 工具库无关(正如库开发人员在响应错误报告时暗示的那样)。要么我遗漏了某些东西,要么问题与已安装的驱动程序、NFC 阅读器硬件或 javax.smartcardio 中的一些未修复错误有关(按可能性顺序列出)。

我已尝试卸载上述驱动程序并让 Windows 7 自行安装合适的驱动程序(称为“Microsoft Usbccid Smartcard Reader (WUDF)”),这会导致与上述相同的错误。我没有尝试过其他阅读器,因为我只有一个。

(注意:这个NFC读卡器在Windows设备概览中的名字是“CCID USB Reader”,而不是“ACS ACR122”之类的。不知道这是否重要,只是想我会提一下。 )。

有没有人遇到过这个问题并设法解决了?

更新

好的,我尝试在检测到模拟标签后向阅读器发送 CLF 命令;即获取连接的PICC的ATS(ACR122U manual的第11页):

TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();

// (this is the correct terminal)
CardTerminal terminal = terminals.get(0);

if (terminal.waitForCardPresent(5000)) 
    Card card = terminal.connect("*");

    CardChannel channel = card.getBasicChannel();

// (I tried both 0x00 and 0x01 as P1, as well as 0x05 for Le)
    CommandAPDU getAts = new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x04);
    ResponseAPDU response = channel.transmit(getAts);

    System.out.println(response.getSW1());
    System.out.println(response.getSW2());

但我不断收到错误响应代码 (0x63 0x00)。关于我可能做错的任何想法?

【问题讨论】:

【参考方案1】:

您遇到的问题是此版本的 ACR122U 阅读器以某种非标准方式使用 PC/SC (CCID)。

您使用 PC/SC API 检测到的“卡”实际上是读卡器模拟的虚拟卡(即使没有卡也允许 PC/SC API 打开连接)或智能卡芯片读卡器的 SAM 插槽(读卡器外壳内的接触卡)。

在任何一种情况下,此阅读器都仅将 PC/SC 用作此阅读器 (NXP PN532) 中使用的非接触式前端芯片的本机命令的传输协议。因此,如果您想使用阅读器的非接触式功能,您必须使用 CLF 的本机命令集。有关详细信息,请参阅 ACR122U API documentation 或 libnfc 实现。

【讨论】:

在我看来,API 的使用非常不标准。令人惊讶的是,这个问题在大约 4 小时内就得到了回答,相比之下 - 好吧 - 从来没有 :) 感谢您的快速回复。你是说 Java 智能卡 API 不支持这个特定的 NFC 阅读器吗?奇怪,因为这似乎是一种相对流行的 NFC 阅读器。那么,通过将这些(CLF?)命令从 ACS 文档发送到 NFC 阅读器,我将能够访问它的功能?无论如何,我假设这些命令不能通过 Java 发送,因为这需要打开一个 CardChannel 到检测到的卡(并且只检测到模拟或嵌入式卡).. 不,我不是这么说的。 Java 智能卡 IO API 支持任何公开 PC/SC 接口的阅读器。由于ACR122U是CCID设备,可以通过PC/SC访问。 现在的问题是,PC/SC 以一种(有点)非标准方式使用,将其用作专有 CLF 命令的隧道。由于读卡器总是显示卡片(物理卡或模拟卡),因此您始终可以使用 Java SC IO API 进行连接。您只需连接到读卡器向您显示它“检测到”的任何卡即可。然后,您可以通过该连接将命令发送到 CLF。 CLF 命令包含一个非标准 CLA 字节 (0xFF),它告诉读卡器解包并将命令传递给 CLF,而不是将它们传递给实际卡(如果存在)。 谢谢。在检测到模拟标签后,我尝试向 NFC 阅读器发送 ADPU 命令;请参阅原始帖子以获取更新。【参考方案2】:

(所有功劳归功于 Michael Roland;这篇文章旨在作为解决方案摘要)

好的,Michael,鉴于您上次评论中的示例,我终于明白了您使用 PC/SC 协议隧道 CLF 命令的意思。我测试了 PN532 文档中的一些命令,它们返回了有效的结果。 (但是,您作为示例给出的命令不起作用并导致阅读器崩溃;它必须重置。)

例如,获取固件版本:

CommandAPDU commApdu = new CommandAPDU(0xFF, 0x00, 0x00, 0x00, 
    new byte[]  (byte)0xD4, (byte)0x02 );

InDataExchange 命令:

CommandAPDU commApdu = new CommandAPDU(0xFF, 0x00, 0x00, 0x00, 
    new byte[]  (byte)0xD4, (byte)0x40, 0x01 );

我找到了NFCIP library,它支持使用 InDataExchange 命令在对等点之间发送字节数组(例如 ACS ACR122 和 Nokia 6131)。在阅读 PN532 文档(第 131 页)时,似乎该命令也允许读取标签。 Michael,您是否知道任何处理使用这些低级命令的库以读取(不同类型的)标签为目标?

【讨论】:

呵呵,我提供的命令里面有一个wait-forever(0xFF),所以只有读卡器检测到卡才会返回。 Michael,从您最新的 cmets 来看,您似乎拥有可以使用此阅读器读写各种类型标签的代码;) 您愿意分享您的一些知识吗? :)

以上是关于通过 javax.smartcardio 读取虚拟 NFC 标签的主要内容,如果未能解决你的问题,请参考以下文章

使用 javax.smartcardio 读取 NFC Mifare Ultralight 卡

通过 javax.smartcardio 连接到 micro SD

在 Android 上使用 javax.smartcardio 包?

将 javax.smartcardio 用于 MIFARE Classic 和 Omnikey 5021 CL

javax.smartcardio:如何将本机命令发送到 Desfire 卡?

在 Open JDK 11(Redhat)上找不到 javax.smartcardio.*