简体   繁体   中英

How a Host application can query a javacard applet on a simulated smartcard in Netbeans?

I am a newbie in the javacard world. I have a wallet applet successfully running in netbeans (I can credit, debit,check-balance, verify pin etc using APDUs in the device console). I am tasked to write a host application to communicate with this card in netbeans without using the smartcard console. From a little research, I figure that I have to import javax.smartcardio class into my host application, however, as you might know, there is very little documentation on this. Any assistance will be appreciated and of help to many of us who are finding it hard to delve into this world of smartcard programming. Below is my applet code. Pin was passed via parameters as 1122334455667788 via project properties.

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

}

How the simulator works?

Netbeans uses Java Card Development kit tools to simulate Java Card. Communication with the simulator requires a little socket programming, but the JCDK provided a library for that already. It is named APDUIO . You can find it and its documentations in the JCDK.

Those ports that Netbeans assign to the simulator is 9025 (for contact) and 9026 (for contactless) by default. But you can change them simply.

How to change Java Card simulator configurations in Netbeans?

1-Go to Services tab, do a right click on Java card Runtime and then select Java Platforms :

在此处输入图片说明

2- Select Bundled Java Card and then click on Manage Devices :

在此处输入图片说明

3- There, you can change the configured ports:

在此处输入图片说明

How to communicate with the simulator via code?

First turn on the simulator in the Netbeans, and then run this program:

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");
        }

    }

}

Note that, as you see in the import s above, you need to add the apduio library from Java Card Development Kit package, to your project'c library in Netbeans .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM