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 } }