为啥我在 CREF JavaCard 模拟器的控制台中测试读写 APDU 时收到输入数据长度!= Lc 或 SW1 SW2: 6700?
Posted
技术标签:
【中文标题】为啥我在 CREF JavaCard 模拟器的控制台中测试读写 APDU 时收到输入数据长度!= Lc 或 SW1 SW2: 6700?【英文标题】:Why am i receiving either input data length != Lc or SW1 SW2: 6700 when testing read and write APDU's in the console of the CREF JavaCard simulator?为什么我在 CREF JavaCard 模拟器的控制台中测试读写 APDU 时收到输入数据长度!= Lc 或 SW1 SW2: 6700? 【发布时间】:2017-03-31 20:18:06 【问题描述】:我想知道是否有人可以帮助我,因为我对 Java 很陌生?! 我有一个从这里的代码开发的读/写小程序(基本相同,只是指令代码和小程序名称发生了变化)(http://www.wrankl.de/Javacard/ReadWriteJava.txt),如下所示:
// @(#)SmartTransfer.java 1.0 07/05/17
//Applet package package smartTransfer;
import javacard.framework.*;
//Transfer class extends the base Applet class
public class Transfer extends Applet
// Declare constants
// code of instruction class; CLA - First (1 byte) - in the command APDU header
final static byte CLASS = (byte) 0x80;
// codes of INS byte in the command APDU header - for write instruction
final static byte WRITE_USER_INFO_INS = 0x07;
// codes of INS byte in the command APDU header - for read instruction
final static byte READ_USER_INFO_INS = 0x09;
// Size of storage area
final static byte SIZE_MEMORY = (short) 9;
static byte[] memory;
//Member variables - contain values for objects
//OwnerPIN pin;
/************************************/
// Installs the Applet, constructs the transfer object and registers with JCRE
public static void install(byte[] bArray, short bOffset, byte bLength) throws ISOException
new Transfer().register();
memory = new byte[SIZE_MEMORY];
/************************************/
// Processing the APDU commands
@Override
public void process(APDU apdu)
throws ISOException
if (selectingApplet()) return;
byte[] buffer = apdu.getBuffer();
if (buffer[ISO7816.OFFSET_CLA] !=CLASS)
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
byte ins = buffer[ISO7816.OFFSET_INS];
switch (ins)
case READ_USER_INFO_INS:
readUserInfo(apdu);
break;
case WRITE_USER_INFO_INS:
writeUserInfo(apdu);
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
private void writeUserInfo(APDU apdu)
byte[] cmd_apdu = apdu.getBuffer();
// check if P1=0
if (cmd_apdu[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
// check if offset P2 is inside the bound of the memory array
short offset = (short) (cmd_apdu[ISO7816.OFFSET_P2] & 0x00FF);
// calculate offset
if (offset >= SIZE_MEMORY)
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
// check if expected length is within the memory array
short lc = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF);
// check no. off bytes against that in memory object
if ((offset + lc) > SIZE_MEMORY)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
// check there are bytes in the command
if (lc == 0)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
// points to method to get rest of the APDU
getAPDUBody(apdu);
//Data copied to the memory - atomic procedure
Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), memory, offset, lc);
// command complete message
ISOException.throwIt(ISO7816.SW_NO_ERROR);
//Receive the body of the command APDU method
public void getAPDUBody(APDU apdu)
byte[] buffer = apdu.getBuffer();
// check expected length against actual length in command APDU body
short lc = (short)(buffer[ISO7816.OFFSET_LC] & 0x00FF);
// If not send error message`
if (lc != apdu.setIncomingAndReceive())
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
private void readUserInfo(APDU apdu)
byte[] cmd_apdu = apdu.getBuffer();
//----- check the preconditions -----
// check if P1=0
if (cmd_apdu[ISO7816.OFFSET_P1] != 0) ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
// check if offset P2 is inside the bound of the memory array
short offset = (short) (cmd_apdu[ISO7816.OFFSET_P2] & 0x00FF); // calculate offset
if (offset >= SIZE_MEMORY) ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
// check if offset P2 and expected length Le is inside the bounds of the memory array
short le = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF);
if ((offset + le) > SIZE_MEMORY) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
// check if expected length Le of return bytes is 0
if (le == 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
// set transmission to outgoing data
apdu.setOutgoing();
// set the number of bytes to send to the IFD
apdu.setOutgoingLength((short)le);
// send the requested number of bytes to the IFD
apdu.sendBytesLong(memory, (short)offset, (short)le);
我正在使用 Eclipse IDE 中的 CREF JavaCard 模拟器,并运行了 CAP 文件以及生成的创建和选择小程序脚本,并且一切正常。 我遇到的问题是当我尝试运行 read apdu 时。下面的写命令示例和我尝试的任何操作(在代码中 writeUserinfo APDU 方法设置的范围内)都可以工作,但无论我为 readUserInfo APDU 尝试什么,都没有任何效果。我要么接收数据输入长度!= Lc 要么 Le = 00 | SW1 SW2:6700。我知道这些是什么意思,但我已经尝试了所有我能想到的或从现有文献中找到的东西,但似乎没有任何效果。正如网站上所说,我理解这个小程序已经过试验和测试,但我还没有收到一个 javacard 来测试它是否支持差异,因为我还读到模拟器是基于旧的 JavaCard。
这是我正在使用的 write apdu 示例:
//Select Transfer applet
0x00 0xA4 0x04 0x00 0x07 0xFF 0x00 0x20 0x00 0x00 0x00 0x20 0x7F;
APDU|CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 07, ff, 00, 20, 00, 00, 00, 20, Le: 00, SW1: 90, SW2: 00
//Write Ross to memory byte array at offset 2
CMD>0x80 0x07 0x00 0x02 0x04 0x52 0x6f 0x73 0x73 0x7F;
APDU|CLA: 80, INS: 07, P1: 00, P2: 02, Lc: 04, 52, 6f, 73, 73, Le: 00, SW1: 90, SW2: 00
然后这里是我尝试从偏移量 2 读取 4 个字节的所有类型的读取 APDU;
首先我尝试使用 le = 4 个字节 CMD>0x80 0x09 0x00 0x02 0x04 0x7F; CREF|C-JCRE 已断电。
CREF exited with code 0
User input thread exited
ApduTool thread exited
ApduTool process finished with code: 1
APDU|Input data length != Lc around line 50.
我还读到使用 CREF 是在卡级别而不是终端级别,所以不需要指定 le 字段,所以我尝试了;
CMD>0x80 0x09 0x00 0x02 0x00 0x7F;
APDU|CLA: 80, INS: 09, P1: 00, P2: 02, Lc: 00, Le: 00, SW1: 67, SW2: 00
我还尝试写入许多不同的偏移量, 更改内存字节数组长度。 我将内存字节数组对象更改为在小程序安装下(因为我认为这可能会在小程序中创建对象)。 我尝试在读取和写入方法中检查 Lc = 0(以防影响我读到的 Le = 0)。 我尝试将写入和读取命令更改为不包括 0x7F 以防这被解释为其他内容);写入命令成功的地方; 9000,但是当我运行没有 0x7F 的读取命令时,我得到了这个响应; CMD>0x80 0x09 0x00 0x02 0x00; CREF|C-JCRE 已断电。
CREF exited with code 0
User input thread exited
ApduTool thread exited
ApduTool process finished with code: 1
APDU|Invalid Token after "0x00", was expecting one of <INTEGER_LITERAL> <CHARACTER_LITERAL> <STRING_LITERAL>
对不起,如果这很长而且可能是一个非常简单的问题,但我没有意识到当我接手这个项目时会发生这样的事情(笑我!我知道!),但我一直在这两天尝试了各种方法都无济于事,所以如果你们有好心人可以帮助我,我会非常感激!
【问题讨论】:
您是否尝试过比较两个缓冲区?第 50 行是什么? 50 writeUserInfo(apdu); 那么第二个INS案例 如果我理解正确,那就是阅读给你带来麻烦 读/写前的检查是否一样? 【参考方案1】:Ne 的值,最大 返回的字节数被编码为称为 Le 的字节表示。 Ne 的值可以通过调用setOutgoing
简单地从小程序中检索(检查返回值!)。这还将处理 Ne = 256 编码的转换,这是一个设置为 00
的 Le。
然后您可以检查 Ne 是否足够大以容纳您的数据。如果是这种情况,您可以退货。返回的数据少于请求的数据很好。否则你可能仍然需要“抛出”一个6700
长度无效的状态字。
你的命令:
0x80 0x09 0x00 0x02 0x00 0x7F
当然是不正确的。命令数据大小 (Nc) 的编码 Lc 只是一个空字符串,即命令 APDU 中的 Lc 不存在。所以你在这里展示的是 Le = 0x00
在你的代码中被错误地解释为 Ne = 0。然后有一个伪字节设置为0x7F
。
结合我上面的话,你应该可以发送:
0x80 0x09 0x00 0x02 0x00
因此 Le = 0x00 被解释为 Ne 的最大大小,即 256。如果您的数据适合,则返回它。如果它不适合,您必须检查命令链接或扩展长度 APDU(但根据您的单个 APDU 写入命令,它很容易适合 - 这只是对未来读者的评论)。
【讨论】:
抱歉,直到今天才看到问题。 谢谢 Maarten,对不起,我一直在尝试完成项目的另一部分。我有设置传出命令。在小程序的底部a ... 0x7F 必须用于分隔 CREF 模拟器中的命令或其他内容,否则我会收到错误(预期的整数文字或其他内容)。我不明白第一个命令 byte=lc 和第二个命令 byte=le 是什么意思。我也试过你之前说的,它给出了字面上的预期错误 我确实想向您展示我的代码,对不起,但不能在这里,因为只允许几行.. 基本上在模拟器中运行 cap 并创建小程序文件后,我运行一个脚本,一切正常联系人详细信息以各种偏移量写入内存字节(120),例如; //写罗斯(名字) 0x80 0x07 0x00 0x04 0x04 0x52 0x6f 0x73 0x73 0x72; - 它们都工作正常 9000,但是当我使用相同的偏移量和长度时 - 不工作。 setoutgoing 出现在我正在使用的代码上,它确实说它经过了试验和测试。我只是不明白您是否要求我更改代码中的某些内容? 在我的小程序代码中我有: aped.setOutgoing(); ... apdu.setOutgoingLength((short)le);... apdu.setBytesLong(memory, (short)offset, (short)le); ?【参考方案2】:感谢所有提供帮助的人.. 我真的很感激.. 我一直使用这个网站,并钦佩你们所有人利用空闲时间帮助像我这样的人(免费!),非常感谢!
我已经设法让它工作...... 基本上我是这样做的..
'你能不能换行 if (le == 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);进入 if (le == 0) le = (short)(SIZE_MEMORY - offset);?我从来没有使用过这个模拟器,但是这个 0x7F 附加到 APDU 有什么用(它对我没有任何意义)? -vlp'
...然后我将这一行放在该行之前; '如果 ((offset+le)>SIZE_MEMORY)... 它成功了! - 再次非常感谢!
【讨论】:
以上是关于为啥我在 CREF JavaCard 模拟器的控制台中测试读写 APDU 时收到输入数据长度!= Lc 或 SW1 SW2: 6700?的主要内容,如果未能解决你的问题,请参考以下文章
主机应用程序如何在 Netbeans 中的模拟智能卡上查询 javacard 小程序?
为啥 Javacard 会阻止在单个 javacard 程序的 AID .cap 文件中上传不同的文件?
解锁后无法在 JavaCard 中列出或安装 CAP 文件。为啥?以及如何解决?