Specyfikacja JavaCard zawiera przykładową aplikację przeznaczoną do uruchamiania na kartach procesorowych zgodnych z tą specyfikacją. Jest to implementacja prostego elektronicznego portfela. Firma Schlumberger dokonała niewielkich modyfikacji tej aplikacji mających na celu przystosowanie jej do działania na karcie cyberflex. W niniejszym dodatku prezentujemy opatrzony komentarzami kod tej wersji aplikacji.
//
//
// Wallet.java
//
// Ten kod został stworzony przez firmę Schlumberger na podstawie
// przykładu o nazwie ,,Wallet'' zaprezentowanego przez firmę
// JavaSoft w dokumentacji specyfikacji Javacard 2.0.
//
//
import javacard.framework.*;
import javacardx.framework.*;
public class Wallet extends javacard.framework.Applet
{
/* deklaracje stałych */
// klasa instrukcji kardletu
final byte Wallet_CLA =(byte)0x03;
// kody instrukcji kardletu
final byte Deposit = (byte) 0x10;
final byte Debit = (byte) 0x20;
final byte Balance = (byte) 0x30;
final byte Validate = (byte) 0x40;
final byte Select = (byte) 0xA4;
// maksymalna dozwolona liczba nieudanych prób podania numeru PIN
final byte PinTryLimit = (byte) 0x03;
// maksymalna długość numeru PIN
final byte MaxPinSize = (byte) 0x04;
// kody zakończenia operacji: ujemne saldo oraz błędny numer PIN
final short SW_NEGATIVE_BALANCE = (short) 0x6910;
final short SW_WRONG_PIN = (short) 0x6BBB;
/* deklaracje składowych kardletu */
OwnerPIN pin;
byte balance;
byte base_balance = 0x20;
// bufor APDU
byte buffer[];
// konstruktor kardletu
private Wallet ()
{
// wszystkie obszary pamięci, które kiedykolwiek kardlet będzie
// wykorzystywał należy zaalokować w konstruktorze
pin = new OwnerPIN (PinTryLimit, MaxPinSize);
balance = base_balance;
// rejestruje instancję w systemie operacyjnym
register();
}
// metoda służąca do tworzenia instancji kardletu
public static void install (APDU apdu)
{
// tworzy instancję kardletu
new Wallet();
}
// metoda służąca do wyboru aktywnej instancji
public boolean select ()
{
// podany wcześniej numer PIN traci ważność, użytkownik będzie musiał
// podać go ponownie
pin.reset();
// przekazuje wartość TRUE, aby poinformować system operacyjny karty,
// że instancja gotowa jest do wykonywania poleceń APDU
return true;
}
// metoda służąca do wykonania polecenia APDU
public void process (APDU apdu)
{
// obiekt APDU posiada bufor służący do przechowywania przychodzących
// poleceń i wysyłanych odpowiedzi APDU
buffer = apdu.getBuffer();
// polecenie wybierające aktualny kardlet należy pozostawić systemowi
// operacyjnemu karty
if((buffer[ISO.OFFSET_CLA] == (byte)0) &&
(buffer[ISO.OFFSET_INS ]== Select))
{
// zgłoszenie wyjątku powoduje wysłanie podanej wartości jako wyniku
// polecenia (SW)
ISOException.throwIt(ISO.SW_NO_ERROR);
}
// sprawdza, czy polecenie jest odpowiedniej klasy
else if (buffer[ISO.OFFSET_CLA] != Wallet_CLA)
ISOException.throwIt(ISO.SW_CLA_NOT_SUPPORTED);
// na podstawie kodu polecenia wywołuje odpowiednią metodę kardletu
switch (buffer[ISO.OFFSET_INS])
{
case Balance: getBalance(apdu);
return;
case Debit: debit(apdu);
return;
case Deposit: deposit(apdu);
return;
case Validate: validate(apdu);
return;
default: ISOException.throwIt (ISO.SW_INS_NOT_SUPPORTED);
}
}
// metoda służąca do zdeponowania pieniędzy na karcie
private void deposit (APDU apdu)
{
// sprawdza, czy podano właściwy numer PIN
if (!pin.isValidated())
ISOException.throwIt (ISO.SW_PIN_REQUIRED);
// pole Lc nagłówka APDU określa ilość danych znajdujących się w treści
// polecenia
byte numBytes = (byte) (buffer[ISO.OFFSET_LC]);
// odczytuje dane z bufora APDU, zaczynając od pozycji ISO.OFFSET_CDATA
byte byteRead = (byte)(apdu.setIncomingAndReceive());
// ilość odczytanych danych powinna pokrywać się z wartością pola Lc
// nagłówka APDU
if (byteRead != numBytes)
ISOException.throwIt(ISO.SW_WRONG_LENGTH);
// zwiększamy saldo o kwotę wyszczególnioną w treści polecenia APDU
balance = (byte)(balance + buffer[ISO.OFFSET_CDATA]);
// metoda wykonana poprawnie
return;
}
// metoda służąca do pobrania pieniędzy z karty
private void debit (APDU apdu)
{
// sprawdza, czy podano właściwy numer PIN
if (!pin.isValidated())
ISOException.throwIt(ISO.SW_PIN_REQUIRED);
// znaczenie instrukcji takie jak w metodzie deposit
byte numBytes = (byte)(buffer[ISO.OFFSET_LC]);
byte byteRead = (byte)(apdu.setIncomingAndReceive());
if (byteRead != 1)
ISOException.throwIt(ISO.SW_WRONG_LENGTH);
// jeżeli saldo wychodzi ujemne, to zgłasza wyjątek
if ((byte)((byte)balance - (byte)buffer[ISO.OFFSET_CDATA]) < (byte)0)
ISOException.throwIt(SW_NEGATIVE_BALANCE);
// zmniejsza saldo o podaną kwotę
balance = (byte)(balance - buffer[ISO.OFFSET_CDATA]);
}
// metoda służąca do odczytania salda
private void getBalance (APDU apdu)
{
// sprawdza, czy podano właściwy numer PIN
if (!pin.isValidated())
ISOException.throwIt(ISO.SW_PIN_REQUIRED);
// informuje system operacyjny, że metoda gotowa jest wysłania
// odpowiedzi APDU
apdu.setOutgoing();
// określa ilość danych do wysłania
apdu.setOutgoingLength((byte)1);
// kopiuje dane do wysłania do bufora APDU
buffer[0] = (byte) balance;
// wysyła 1 bajt zaczynając od pozycji 0 bufora APDU
apdu.sendBytes((short)0, (short)1);
}
// metoda służąca do weryfikacji numeru PIN
private void validate (APDU apdu)
{
// podany przez użytkownika numer PIN znajduje się w treści
// polecenia APDU
byte byteRead = (byte)(apdu.setIncomingAndReceive());
// sprawdza, czy podany numer PIN jest poprawny, jeżeli nie, to
// zgłasza odpowiedni wyjątek
if (!pin.check(buffer, ISO.OFFSET_CDATA, byteRead))
ISOException.throwIt(SW_WRONG_PIN);
// w przypadku poprawnego zakończenia metody pole SW odpowiedzi APDU
// ma wartość 0x9000
}
}