在安卓系统上使用NFC软件卡模拟控制完整的APDU。

5
我正在开发一个应用程序,以模拟Nexus 7与CM10.1对ACR122U102读写器的普通APDU通信。我找到了这篇关于软件卡模拟的博客,并编写了一个应用程序,使我的设备(Nexus)看起来像一张卡片。现在我正在尝试在该设备和ACR122u之间来回发送消息。到目前为止,我只能通过发送D4 40 01(InDataExchange第127页)APDU与Nexus 7进行通信。对于我正在编写的应用程序,这应该足够了。
问题在于我从设备发送到读卡器的答案。使用transcieve函数(android.nfc.tech.IsoPcdA with reflection),我可以回复一个长度大于0的字节数组。这将显示在读卡器端,就像普通的InDataExchange响应一样(例如:D5 41 00 01 02 03,其中{01 02 03}是提供给transcieve函数的字节数组)。但我无法控制响应中的状态字节和SW字节(D5 41 XX和两个SW)。除了源代码本身,找不到关于该IsoPcdA类的任何文档。

我想做的是将XX更改为我选择的字节,并发送长度为0的答案(例如:D5 41 01没有任何额外数据)。这可能吗?

1个回答

6
我不太确定你想要达到什么目的。你使用IsoPcdA的transceive方法传输的是完整的APDU(如ISO/IEC 7816-4中定义的,或者是ISO-DEP传输协议中的任何PDU)。因此,transceive的返回值是完整的C-APDU(命令APDU),而transceive的字节数组参数是包括状态字(SW1 | SW2)在内的完整R-APDU(响应APDU)。因此,该参数的最后两个字节是状态字。在你的示例中,SW1将会是02,SW2将会是03。
你在PN532 NFC控制器的InDataExchange命令中看到的状态字节并不是APDU的状态字,而是PN532 NFC控制器内部命令执行的状态。这个状态字节提供了有关缓冲区溢出、通信超时等信息,而不是卡片侧返回的内容。
编辑:样例代码+测试命令:
样例代码在Galaxy Nexus(CM 10)上运行。
try {
  Class isoPcdA = Class.forName("android.nfc.tech.IsoPcdA");
  Method isoPcdA_get = isoPcdA.getDeclaredMethod("get", Tag.class);

  final IsoPcdA techIsoPcdA = (IsoPcdA)isoPcdA_get.invoke(null, tag);

  if (techIsoPcdA != null) {
    if (mWorker != null) {
      mInterrupt = true;
      mWorker.interrupt();
      try {
        mWorker.join();
      } catch (Exception e) {}
    }

    mInterrupt = false;
    mWorker = new Thread(new Runnable() {
      public void run () {
        try {
          techIsoPcdA.connect();

          byte[] command = techIsoPcdA.transceive(new byte[]{ (byte)0x90, (byte)0x00 });
          Log.d(CardEmulationTest.class.getName(), "Connected.");

          while (!mInterrupt) {
            Log.d(CardEmulationTest.class.getName(), "C-APDU=" + StringUtils.convertByteArrayToHexString(command));
            command = techIsoPcdA.transceive(command);
          }
        } catch (Exception e) {
          Log.e(CardEmulationTest.class.getName(), "Exception while communicating on IsoPcdA object", e);
        } finally {
          try {
            techIsoPcdA.close();
          } catch (Exception e) {}
        }
      }
    });

    mWorker.start();
  }
} catch (Exception e) {
  Log.e(CardEmulationTest.class.getName(), "Exception while processing IsoPcdA object", e);
}

测试(使用ACR122U):

InListPassivTargets(1个目标,速率为106kbps)

> FF00000004 D44A 0100 00
< D54B 010100046004088821310578338800 9000

使用数据为0x01的InDataExchange函数

> FF00000004 D440 01 01 00
< D541 00 01 9000

当我们从卡片读取器收到错误代码0x00时(InDataExchange命令的状态;不是实际响应APDU的一部分),我们得到响应0x01(这是IsoDepA响应APDU),并且我们得到卡片读取器封装APDU的状态码0x9000(不是实际响应APDU的一部分)。

InDataExchange带有数据0x01 0x02。

> FF00000005 D440 01 0102 00
< D541 00 0102 9000

当读卡器返回错误代码0x00时(InDataExchange命令的状态,不是实际响应APDU的一部分),我们得到响应0x01 0x02(这是IsoDepA响应APDU),并且我们得到卡片读取器包装APDU的状态码为0x9000(不是实际响应APDU的一部分)。

InDataExchange使用DATA = 0x01 0x02 0x03

> FF00000006 D440 01 010203 00
< D541 00 010203 9000

我们从读卡器获得了一个错误代码0x00(InDataExchange命令的状态,不属于实际响应APDU),我们获得了0x01 0x02 0x03作为响应(这是IsoDepA响应APDU),并且我们获得了0x9000作为读卡器包装的APDU的状态代码(不属于实际响应APDU)。
使用DATA = 0x01 0x02 0x03 0x04进行InDataExchange。
> FF00000007 D440 01 01020304 00
< D541 00 01020304 9000

因此,我们从读卡器收到了错误代码0x00(InDataExchange命令的状态;不是实际响应APDU的一部分),我们得到了响应为0x01 0x02 0x03 0x04的数据(这是IsoDepA响应APDU),并且我们从读卡器封装APDU的状态代码中获得了0x9000(不是实际响应APDU的一部分)。
因此,我们恰好可以得到与我们发送的命令APDU相同的数据作为响应APDU(请注意,这些APDU都没有按照ISO 7816-4格式进行格式化,但对于IsoPcdA卡模拟,任何ISO 14443-4传输协议格式都可以使用)。
状态码0x9000属于读卡器APDU封装(CLA=FF INS=00 P1P2=0000 Lc [PN542 COMMAND] Le=00),这是必需的,因为ACR122U的PN532是通过CCID(PC/SC)接口访问的。这些是纯阅读器命令封装,与ISO-DEP通信无关。 D440 01 [DATA]是PN532用于在ISO-DEP上交换数据(例如APDU)的命令,而D541 00 [DATA]则是相应的响应。

1
顺便问一下,这个(http://www.mroland.at/fileadmin/mroland/tutorials/201209_NFCCongress_AndroidNFCWorkshop_slides.pdf)是你的吗?我昨天看了它,让我想到在Android上使用卡模拟控制完整的ADPU是可能的,因为你已经将ISO-7816智能卡列为Type 4标签,这正是问题中第一个链接所说的在Android上使用SCE是可能的。 - Fons
1
可以控制完整的APDU(正如我在我的答案中所写)。然而,您所提到的“状态字节”不是APDU的一部分。该状态字节是您的读卡器应用程序和读卡器芯片之间通信的一部分。您无法在APDU级别上影响它。 - Michael Roland
是的,那是我的教程。 - Michael Roland
1
然而,模拟读者和Nexus 7之间的正常通信并不完全可能。当我在IsoPcdA上使用transcieve时,我提供的参数不包含SW字节。它们会自动添加。当我提供{0x01}时,字节{0x90,0x00}会附加到0x01并显示在另一端。而且发送零长度的APDU也是不可能的(例如用于身份验证的Mifare OK)。你有什么解决办法吗? - Fons
谢谢,编辑得很好!SWs(9000)除非真的出了问题,否则不能更改,对吗?您在代码中没有尝试的是零长度响应(数据)。有没有办法更改PN532头状态字节?例如,PC看到的响应为D541 13 9000(我知道这是Mifare而不是14443-4,但仍然如此)。我猜不行?@Michael - Fons
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接