我制作了一个简单的测试应用程序以进行快速调试。我发送一些字节,在手机屏幕上打印我发送的内容并打印我收到的内容。
当我发送错误命令时,我会在两个字节 SW1SW2 中收到相应的错误代码。 我还可以调用我自己的命令并用我自己的值覆盖 SW1SW2,我可以获得它们。
问题在于:当我发送正确的命令时,收发命令失败,并出现信息性异常“收发失败”。
如果我发送正确的命令,但将 SW1SW2 覆盖为 90 00 以外的值,那么我会得到我设置的 SW 值,但没有响应数据。 (可能是因为当 SW1SW2 <> 90 00 时卡不发送 ODATA)
那么我为什么这么确定我发送了正确的命令呢?除了搞乱我自己的测试命令之外,我还调用了 GetAppId 命令 - 该命令失败了,说明我必须在卡中定义 AppId。 所以我在卡中定义它,发送相同的命令,但收发失败。
所以我很确定问题是存在 ODATA 时收发失败,但我不明白为什么或如何修复它..请帮忙!
编辑:我的卡是 ZeitControl 的 7.5 D 非接触式基本卡。
EDIT2:我已将超时设置为 2000 毫秒,行为没有变化。我正在尝试返回一个字节的数据,并且我调用的系统命令听起来也不重。
然后我下载并附上了Android源码并进行了调试。有一些段它仍然不会进入 - 但卡似乎在有效命令上返回 null,除非我返回一些手动设置的 SW1SW2,在这种情况下,这是唯一收到的东西。
EDIT3:我尝试的系统命令是: 192 14 0 0 0 (或 C0 0E 00 00 00) (或 CLA INS P1 P2 Lc) 我不是 100% 确定我做对了,但我尝试过使用各种长度(最多 22)的 Le,并且没有像上面那样的 Le,只有没有它才不会给我 6700(错误的 Le/Lc) 当然,它似乎返回 null 而不是 6700...
另一个命令是我自己定义的20 0A(值为Byte)并且在.BAS文件中没有指定P1/P2。 我这样称呼它: 32 10 1 0 1 (或 20 0A 01 00 01) (或 CLA INS Lc IDATA Le) 这应该意味着 1 字节数据输入,设置为 0,并且预期输出 1 字节(+ SW1/SW2 一如既往)。 (设置 P1/P2 给出 6700,所以除非在命令声明中定义,否则我认为它们不应该在那里) 这也返回 null。我预计 00 90 00 会在这里返回。 (如果我将“值”设置为 00)
我使用的是 HTC One X。
编辑4: MinSdk 版本 = 14 和目标 18。
if(bitcoinCard != null){
try {
String sentmsg, receivedmsg;
byte[] send = getBytes(commandBytes.getText().toString());
byte[] data = null;
if(send != null){
bitcoinCard.setTimeout(2000);
data = bitcoinCard.transceive(send);
}
//bitcoinCard.close();
/*if(data != null && data.length == 2)
{
mainLabel.setText("SW1SW2: " + (data[0] < 0 ? -data[0] +
128 : data[0]) + " " + (data[1] < 0 ? -data[1] + 128 : data[1]));
}else */if (data != null && send != null)
{
sentmsg = "" + (send[0] < 0 ? send[0] + 256 : send[0]);
for(int i = 1; i < send.length; i++)
{
sentmsg = sentmsg + " " + (send[i] < 0 ? send[i] +
256 : send[i]);
}
receivedmsg = "" + (data[0] < 0 ? data[0] + 256 : data[0]);
for(int i = 1; i < data.length; i++)
{
receivedmsg = receivedmsg + " " + (data[i] < 0 ? data[i] + 256 : data[i]);
}
mainLabel.setText("Sent: " + sentmsg + "\n" +
"Response: " +
receivedmsg);
}else
{
mainLabel.setText("Sent or received null.");
}
} catch (IOException e) {
mainLabel.setText("Tried to talk to card, but had error: " +
e.getMessage());
}
}
首先,当您发送 APDU 时,您应该使用
IsoDep
对象(而不是 NfcA
)。 Android 应该显示这两种标签技术均可用于您的卡。这里的问题是,如果您使用 IsoDep
,Android 通常只会在 ISO 14443-4 协议模式下激活卡。因此,当使用 NfcA
时,您的卡将无法接受 APDU。
我刚刚测试过,至少在运行 Android 4.1.2 的 Nexus S 上是这样。事实上,尝试使用
NfcA
对象进行收发会导致某些卡出现 TagLostException
,并且我尝试过的另一张卡会出现其他一些非常奇怪的行为。
第二,如果你发送
byte[] cmd = { (byte)0xC0, (byte)0x0E, (byte)0x00, (byte)0x00, (byte)0x00 };
我希望该卡返回实际的应用程序 ID。但是,此命令的答案(即
<data> <SW1=61> <SW2=len>
)不符合 ISO 7816-4(不应为 61xx 状态代码返回任何数据),因此这可能会导致问题。
更新:我刚刚使用 Nexus S(Android 4.1.2)对此进行了测试,接收此类响应不是问题。
最后,你的另一个命令(
20 0A
)不是你所期望的:
0x00
或 0x80
的 CLA 字节,除非您知道自己在做什么(使用安全消息传递、使用逻辑通道……)。虽然,Android(至少使用 NXP 的 NFC 芯片组)并不关心 APDU 的结构,但您的卡可能会关心!<CLA> <INS> <P1> <P2> [Lc [DATA]] <Le>
形式(特殊情况为 <CLA> <INS> <P1> <P2>
)。所以这意味着你不能简单地省略 P1 和 P2。