主机应用程序如何在 Netbeans 中的模拟智能卡上查询 javacard 小程序?
Posted
技术标签:
【中文标题】主机应用程序如何在 Netbeans 中的模拟智能卡上查询 javacard 小程序?【英文标题】:How a Host application can query a javacard applet on a simulated smartcard in Netbeans? 【发布时间】:2016-05-01 09:49:08 【问题描述】:我是 javacard 世界的新手。我有一个在 netbeans 中成功运行的钱包小程序(我可以在设备控制台中使用 APDU 进行贷记、借记、检查余额、验证 pin 等)。
我的任务是编写一个主机应用程序,以便在不使用智能卡控制台的情况下在 netbeans 中与此卡进行通信。通过一些研究,我认为我必须将 javax.smartcardio 类导入到我的主机应用程序中,但是,您可能知道,这方面的文档很少。我们将不胜感激任何帮助,并为我们中的许多人发现难以深入研究智能卡编程世界提供帮助。下面是我的小程序代码。 Pin 是通过参数作为1122334455667788
通过项目属性传递的。
package classicapplet1;
//package com.sun.javacard.samples.wallet;
import javacard.framework.*;
public class Wallet extends Applet
// codes of CLA byte in the command APDUs
final static byte Wallet_CLA = (byte) 0xB0;
// codes of INS byte in the command APDUs
final static byte VERIFY = (byte) 0x20;
final static byte CREDIT = (byte) 0x30;
final static byte DEBIT = (byte) 0x40;
final static byte GET_BALANCE = (byte) 0x50;
// maximum wallet balance
final static short MAX_BALANCE = 10000;
// maximum transaction amount
final static byte MAX_TRANSACTION_AMOUNT = 100;
// maximum number of incorrect tries before the
// PIN is blocked
final static byte PIN_TRY_LIMIT = (byte) 0x03;
// maximum size PIN
final static byte MAX_PIN_SIZE = (byte) 0x08;
// Applet-specific status words:
final static short SW_VERIFICATION_FAILED = 0x6300;
final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301;
final static short SW_INVALID_TRANSACTION_AMOUNT = 0x6A83;
final static short SW_EXCEED_MAXIMUM_BALANCE = 0x6A84;
final static short SW_NEGATIVE_BALANCE = 0x6A85;
// instance variables declaration
OwnerPIN pin;
short balance;
/**
*
* called by the JCRE to create an applet instance
*/
public static void install(byte[] bArray, short bOffset, byte bLength)
// create a Wallet applet instance
new Wallet(bArray, bOffset, bLength);
// end of install method
/**
*
* private constructor — called by the install method to
*
* instantiate a Wallet instance
*/
private Wallet(byte[] bArray, short bOffset, byte bLength)
pin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE);
// bArray contains the PIN initialization value
pin.update(bArray, bOffset, bLength);
// register the applet instance with the JCRE
register();
// end of the constructor
/**
*
* initialize the applet when it is selected
*/
public boolean select()
// the applet declines to be selected
// if the pin is blocked
if (pin.getTriesRemaining() == 0)
return false;
return true;
// end of select method
/**
*
* perform any cleanup and bookkeeping tasks before
*
* the applet is deselected
*/
public void deselect()
// reset the pin
pin.reset();
/**
*
* process APDUs
*/
public void process(APDU apdu)
// APDU object carries a byte array (buffer) to
// transfer incoming and outgoing APDU header
// and data bytes between the card and the host
// at this point, only the first five bytes
// [CLA, INS, P1, P2, P3] are available in
// the APDU buffer
byte[] buffer = apdu.getBuffer();
// return if the APDU is the applet SELECT command
if (selectingApplet())
return;
// verify the CLA byte
if (buffer[ISO7816.OFFSET_CLA] != Wallet_CLA)
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
// check the INS byte to decide which service method to call
switch (buffer[ISO7816.OFFSET_INS])
case GET_BALANCE:
getBalance(apdu);
return;
case DEBIT:
debit(apdu);
return;
case CREDIT:
credit(apdu);
return;
case VERIFY:
verify(apdu);
return;
default:
ISOException.throwIt
(ISO7816.SW_INS_NOT_SUPPORTED);
// end of process method
/**
*
* add money to the wallet
*/
private void credit(APDU apdu)
// verify authentication
if (!pin.isValidated())
ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
byte[] buffer = apdu.getBuffer();
// get the number of bytes in the
// data field of the command APDU
byte numBytes = buffer[ISO7816.OFFSET_LC];
// recieve data
// data are read into the apdu buffer
// at the offset ISO7816.OFFSET_CDATA
byte byteRead = (byte) (apdu.setIncomingAndReceive());
// error if the number of data bytes
// read does not match the number in the Lc byte
if ((numBytes != 1) || (byteRead != 1))
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
// get the credit amount
byte creditAmount = buffer[ISO7816.OFFSET_CDATA];
// check the credit amount
if ((creditAmount > MAX_TRANSACTION_AMOUNT)
|| (creditAmount < 0))
ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT);
// check the new balance
if ((short) (balance + creditAmount) > MAX_BALANCE)
ISOException.throwIt(SW_EXCEED_MAXIMUM_BALANCE);
// credit the amount
balance = (short) (balance + creditAmount);
return;
// end of deposit method
/**
*
* withdraw money from the wallet
*/
private void debit(APDU apdu)
// verify authentication
if (!pin.isValidated())
ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
byte[] buffer = apdu.getBuffer();
byte numBytes = (byte) (buffer[ISO7816.OFFSET_LC]);
byte byteRead = (byte) (apdu.setIncomingAndReceive());
if ((numBytes != 1) || (byteRead != 1))
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
// get debit amount
byte debitAmount = buffer[ISO7816.OFFSET_CDATA];
// check debit amount
if ((debitAmount > MAX_TRANSACTION_AMOUNT)
|| (debitAmount < 0))
ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT);
// check the new balance
if ((short) (balance - debitAmount) < (short) 0)
ISOException.throwIt(SW_NEGATIVE_BALANCE);
balance = (short) (balance - debitAmount);
// end of debit method
/**
*
* the method returns the wallet’s balance
*/
private void getBalance(APDU apdu)
byte[] buffer = apdu.getBuffer();
// for(short i=0;i<buffer.length;i++)
//
// System.out.println((byte)buffer[i]);
//
// System.out.println((short)0);
// inform the JCRE that the applet has data to return
short le = apdu.setOutgoing();
// set the actual number of the outgoing data bytes
apdu.setOutgoingLength((byte) 2);
// write the balance into the APDU buffer at the offset 0
Util.setShort(buffer, (short) 0, balance);
// send the 2-byte balance at the offset
// 0 in the apdu buffer
apdu.sendBytes((short) 0, (short) 2);
// end of getBalance method
/**
*
* verify the PIN
*/
private void verify(APDU apdu)
byte[] buffer = apdu.getBuffer();
// receive the PIN data for validation.
byte byteRead = (byte) (apdu.setIncomingAndReceive());
// check pin
// the PIN data is read into the APDU buffer
// starting at the offset ISO7816.OFFSET_CDATA
// the PIN data length = byteRead
if (pin.check(buffer, ISO7816.OFFSET_CDATA, byteRead) == false)
ISOException.throwIt(SW_VERIFICATION_FAILED);
// end of verify method
【问题讨论】:
【参考方案1】:模拟器的工作原理是什么?
Netbeans 使用 Java Card Development kit 工具来模拟 Java Card。与模拟器的通信需要一点套接字编程,但 JCDK 已经为此提供了一个库。它被命名为 APDUIO。您可以在 JCDK 中找到它和 its documentations。
Netbeans 分配给模拟器的那些端口默认为9025
(用于接触式)和9026
(用于非接触式)。但您可以简单地更改它们。
如何在 Netbeans 中更改 Java Card 模拟器配置?
1-转到Services选项卡,右键单击Java card Runtime,然后选择Java Platforms:
2- 选择Bundled Java Card,然后点击管理设备:
3- 在那里,您可以更改配置的端口:
如何通过代码与模拟器通信?
首先在Netbeans中打开模拟器,然后运行这个程序:
package testapduio;
import com.sun.javacard.apduio.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
public class TestAPDUIO
public static void main(String[] args)
CadClientInterface cad;
Socket sock;
Apdu apdu = new Apdu();
apdu.command = new byte[](byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x00, (byte) 0x00;
try
sock = new Socket("localhost", 9025);
InputStream is = sock.getInputStream();
OutputStream os = sock.getOutputStream();
cad = CadDevice.getCadClientInstance(CadDevice.PROTOCOL_T1, is, os);
byte[] ATR = cad.powerUp();
System.out.println("Answer To Reset:\n");
System.out.println(Arrays.toString(ATR));
byte[] input = apdu.getDataIn();
byte[] output = apdu.getDataOut();
System.out.println("-----------------");
System.out.println(input);
System.out.println("\n");
System.out.println(output);
cad.exchangeApdu(apdu);
System.out.println("-----------------");
System.out.println(input);
System.out.println("\n");
System.out.println(output);
cad.powerDown();
catch (Exception e)
System.out.println("Exception Occurred");
请注意,正如您在上面的 import
s 中看到的,您需要将 Java Card Development Kit 包中的 apduio
库添加到您在 Netbeans 中的项目 c 库中。
【讨论】:
很遗憾,我现在无法检查代码。它可能有一些问题。如果您有任何问题,请告诉我。 非常感谢@EbraHim,但是,import com.sun.javacard.apduio.*; 出现错误。找不到这个包。 @diddiedee 正如我在回答中的代码下方所说,您需要将此文件添加到您的项目库中。在JCDK lib目录下可以找到它的jar文件, 在 tools.jar 中找到库。您能否执行诸如贷记或借记之类的操作或进行验证,以便我了解如何发送 APDU。我应该使用这段代码的哪一部分。@EbraHim @diddiedee 你可以自己做。以上是关于主机应用程序如何在 Netbeans 中的模拟智能卡上查询 javacard 小程序?的主要内容,如果未能解决你的问题,请参考以下文章
如何从 NetBeans 直接在移动设备上运行 J2ME 应用程序?