主机应用程序如何在 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");
        

    


请注意,正如您在上面的 imports 中看到的,您需要将 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 应用程序?

如何增强NetBeans的智能提示功能

您如何在 Netbeans 中清除您的 midlet 的 RecordStore?

NFC开发(一)——HCE基于主机的卡模拟简述

在 Eclipse 中模拟 Netbeans 行为

如何在 NetBeans 中使用 Microsoft C++ 编译器?