Hi All,
After nine months of researching and developing a java card applet and terminal side interface for my final year university project I was frustrated with the lack of sample code and tutorials available online.
My applet is aimed at students as an id card containing a student id, all the features of an electronic wallet, loyalty point system protected by a pin number. Pin is changeable. And can be found in this forum under the heading “Sample Javacard 2.2.1 Electronic wallet, Loyalty Points & StudentID applet”
The terminal interaction or host side application that communicates with this applet
As I have no finished my project I would like to share it with anyone that would like to see it.
While I would love to write a tutorial I simply do not have the time however below is the host side application I used in my project to interact with my applet, I hope for anyone that has read through java sun's tutorials this will be a help:
package terminal;
import javax.smartcardio.*;
import java.awt.HeadlessException;
import javax.swing.JOptionPane;
import java.util.List;
import java.text.DecimalFormat;
/**
* @author Raymond Garrett
*DT 354-4
*/
public class TerminalInteraction {
//PackageID = |College - Hex: 43 6f 6c 6c 65 67 65 41 70 70
// AppletID = |CollegeApp
//the select part of the select apdu is (00 A4 04 00) +(AID leght) + AID(.....)
private static byte[] SELECT = {(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x0A, (byte)0x43, (byte)0x6f, (byte)0x6c, (byte)0x6c, (byte)0x65, (byte)0x67, (byte)0x65, (byte)0x41, (byte)0x70, (byte)0x70 };
final static byte ACCOUNT_CLA = (byte)0xA0;
final static byte VERIFY_PIN_INS = (byte)0x20;
final static byte ADD_CREDIT_INS = (byte) 0x30;
final static byte DEBIT_ACCOUNT_INS = (byte) 0x40;
final static byte UPDATE_PIN_INS = (byte) 0x60;
final static byte P1 = (byte) 0x00;
final static byte P2 = (byte) 0x00;
private static byte[] GET_BALANCE_CLA_INS = {(byte)0xA0,(byte) 0x50, (byte) 0x00, (byte) 0x00};
private static byte[] ADMIN_RESET_CLA_INS = {(byte)0xA0,(byte) 0x70, (byte) 0x00, (byte) 0x00};
private static byte[] PIN_TRIES_REMAINING_CLA_INS = {(byte)0xA0,(byte) 0x80, (byte) 0x00, (byte) 0x00};
private static byte[] STUDENT_NUMBER_CLA_INS = {(byte)0xA0,(byte) 0x90, (byte) 0x00, (byte) 0x00};
private static byte[] GET_LOYALTYPOINTS_CLA_INS = {(byte)0xA0,(byte) 0x45, (byte) 0x00, (byte) 0x00};
private static byte[] CREDIT_LOYALTYPOINTS_CLA_INS = {(byte)0xA0,(byte) 0x47, (byte) 0x00, (byte) 0x00};
private static CommandAPDU SELECT_APDU = new CommandAPDU(SELECT);
private static CommandAPDU ADMIN_RESET_APDU = new CommandAPDU(ADMIN_RESET_CLA_INS);
private static CommandAPDU GET_BALANCE_APDU = new CommandAPDU(GET_BALANCE_CLA_INS);
private static CommandAPDU STUDENT_NUMBER_APDU = new CommandAPDU(STUDENT_NUMBER_CLA_INS);
private static CommandAPDU PIN_TRIES_REMAINING_APDU = new CommandAPDU(PIN_TRIES_REMAINING_CLA_INS);
private static CommandAPDU GET_LOYALTYPOINTS_BALANCE_APDU = new CommandAPDU(GET_LOYALTYPOINTS_CLA_INS);
private static CommandAPDU CREDIT_LOYALTYPOINTS_APDU = new CommandAPDU(CREDIT_LOYALTYPOINTS_CLA_INS);
private static CommandAPDU VERIFY_PIN_APDU; // Command created in verifyPin method.
private static CommandAPDU ADD_CREDIT_APDU; // Command created in AddCredit method.
private static CommandAPDU DEBIT_ACCOUT_APDU; // Command created in debitAccount method.
private static CommandAPDU UPDATE_PIN_APDU; // Command created in updatePin method.
private ResponseAPDU responseAPDU = null;
private TerminalFactory terminalFactory = null;
private CardTerminals cardTerminals = null;
private List<CardTerminal> cardTerminalsList = null;
private Card card = null;
private CardTerminal cardTerminal = null;
private CardChannel cardChannel = null;
private AccountView myAccountView;
private String studentID = null;
private Thread cardPresentThread = null;
private Thread waitForCardRemovalThread = null;
final static short SW_NO_ERROR = (short) 0x9000;
final static short SW_VERIFICATION_FAILED = 0x6300;
final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301;
final static short SW_INVALID_TRANSACTION_AMOUNT = 0x6E83;
final static short SW_EXCEED_MAXIMUM_BALANCE = 0x6E84;
final static short SW_NEGATIVE_BALANCE = 0x6E85;
final static short SW_PIN_TO_LONG = 0x6E86;
final static short SW_PIN_TO_SHORT = 0x6E87;
public TerminalInteraction(AccountView myAccountView) {
this.myAccountView = myAccountView;
initialise();
}
public void initialise() {
setTerminalFactory(getTerminalFactory());
setCardTerminals(getTerminalFactory().terminals());
try {
setCardTerminalsList(getCardTerminalsList());
} catch (Exception e) {
}
startTread();
}
private void startTread(){
Runnable waitForCardRemoval = new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
if(getCardTerminal().waitForCardAbsent(0)) {
logout();
//return;
}
} catch (CardException e) {
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
setWaitForCardRemovalThread(new Thread(waitForCardRemoval));
}
public void startCommunication() {
while (true) {
try {
for ( CardTerminal terminal : getCardTerminals().list(CardTerminals.State.CARD_INSERTION)){
try {
if(!getWaitForCardRemovalThread().isAlive()) {
getWaitForCardRemovalThread().start();
}
else {
}
getMyAccountView().getCardNoticeLabel().setText("Card Inserted!");
getMyAccountView().getInstructionLabel().setText("Please do not remove card!");
getMyAccountView().getInstructionLabel().setVisible(true);
getMyAccountView().getResultLabel().setText("Enter pin");
getMyAccountView().getResultLabel().setVisible(true);
getMyAccountView().getCancelPinUpdateButton().setVisible(false);
//(*) connect to the card using any available protocol (instead of (T=1) which I started with)
setCardTerminal(terminal);
setCard(terminal.connect("*"));
} catch (Exception e) {
System.out.println("Terminal NOT connected: " + e.toString());
logout();
}
getCard().getATR();
setCardChannel(card.getBasicChannel());
try {
if (check9000(getCardChannel().transmit(SELECT_APDU))) {
getMyAccountView().getPinContentPane1().setVisible(true);
if (getStudentID() != "Error") {
getMyAccountView().getDataLog().insertCardInsetTime(getStudentID());
}
else {
JOptionPane.showMessageDialog(null, "Error\nStudent number not found in database\n " +
"Please contact DIT Registrar", "ERROR NOTICE", JOptionPane.ABORT);
logout();
}
}
else {
return;
}
} catch (CardException e) {
e.printStackTrace();
return;
}
return;
}
}
catch (HeadlessException e) {
e.printStackTrace();
} catch (CardException e) {
e.printStackTrace();
}
}
}