JavaCard to mówiąc najkrócej połączenie Javy i karty inteligentnej. Mówiąc dokładniej JavaCard to karta inteligentna umożliwiająca zastosowanie technologii JavaCard.
Firma Sun Microsytems od kilku lat promuje tę technologię ale największym ukoronowaniem tej polityki jest projekt rządowy Belgii o nazwie EID (Electronic Identity Card) majacy na celu dostarczenie każdemu mieszkańcowi Belgii powyżej 12 lat osobistej karty identyfikacyjnej opartej o technologię JavaCard. Karta ta zastępuje tradycyjny dowód tożsamości ze zdjęciem i kilkoma danymi osobowymi. Pierwsza, pilotowa faza tego projektu rozpoczęła się w kwietniu 2003 roku i objęła dziesiątki tysięcy osób w 11 miastach. Ma ona na celu przetestowanie systemu w realnej skali i będzie trwać nie mniej niż sześć miesięcy. Jeżeli ta faza zakończy się sukcesem to rząd Belgii zamierza rozprowadzić ponad 10 milionów elektronicznych kart identyfikacyjnych wykorzystujących technologię JavaCard.
Zastosowania technologii JavaCard nie kończą się tylko na kartach identyfikacyjnych - obejmują również inne, ważne dziedziny przemysłu i innej działalności ludzkiej a mianowicie:
Już te wymienione dziedziny stanowią dowód niezwykle szerokich możliwości zastosowania tej technologii już dziś jak również w przyszłości.
Karta typu smart (smart card) jest plastykową kartą z wbudowanym procesorem i pamięcią lub tylko pamięcią z nieprogramowalną logiką. Scalony układ mikroprocesorowy może dokonywać różnych manipulacji na danych zawartych w pamięci: np. dodawać, usuwać itd. Karta bez takiego układu może tylko wykonywać predefiniowane, ustalone operacje na danych.
Karta typu smart ma wygląd i rozmiar karty kredytowej. W przeciwieństwie do karty z paskiem magnetycznym może zawierać w sobie wszystkie niezbędne funkcje i informacje i w związku z tym nie wymaga dostępu do zdalnej bazy danych w czasie przeprowadzania transakcji.
Obecnie istnieją trzy kategorie kart typu smart :
Tak więc podstawowy podział kart typu smart to karty z mikroprocesorem (karty inteligentne) i karty z pamięcią.
Europejski Instytut Standardów Telekomunikacyjnych (ETSI) tworzący standardy GSM zaadoptował technologię JavaCard do kart SIM. Standard GSM o numerze 03.19 definiuje Java API dla tworzenia aplikacji GSM uruchamianych na platformie JavaCard.
Od tego momentu najbardziej popularną chyba dziedziną zastosowań kart inteligentnych jest komunikacja GSM. Telefon GSM posiada kartę SIM aktywującą telefon. Karta ta jest modułem identyfikującym użytkownika i dostarczającą klucze szyfrujące do cyfrowej transmisji głosu. Ponieważ na karcie SIM identyfikator jest programowany, użytkownik nie jest ograniczony tylko do jednego telefonu-może używać dowolnego, kompatybilnego z GSM telefonu akceptującego kartę SIM. Jeżeli karta SIM wyposażona jest w technologię JavaCard może dostarczać różnych usług takich jak zdalne transakcje bankowe.
W bankowości karty inteligentne dają użytkownikowi bezpieczny dostęp do usług finansowych t.jak wypłata gotówki, płacenie rachunków, opłaty transportowe.
Technologia JavaCard pozwala programom napisanym w Javie (apletom) wykonywać się w środowisku karty inteligentnej. Technologia ta definiuje środowisko uruchomieniowe JCRE (JavaCard Runtime Enviroment) oraz dostarcza klas i metod do tworzenia apletów przez programistów. Aplety wykonują się wewnątrz JCRE.
W technologii tej możemy wyróżnić trzy komponenty:
Te trzy komponenty dostarczają bezpiecznej, niezależnej od producenta przenośnej platformy Javy dla kart inteligentnych. Platforma ta znakomicie separuje aplety Javy od technologii wprowadzanej przez producentów kart.
Co więcej, technologia JavaCard jest kompatybilna z istniejącymi technologiami zastosowanymi w kartach inteligentnych. Jest zgodna ze standardem ISO 7816 w obsłudze modelu pamięci karty, zgodna z protokołem komunikacyjnym i zgodna z modelem wykonywania aplikacji. Karta JavaCard komunikuje się z urządzeniem akceptującym przy użyciu protokołu ISO 7816 APDU.
Można wymienić kilka unikalnych zalet technologii JavaCard:
Współoperatywność pozwala na pisanie programów które potem mogą być wykonywane w różnych systemach operacyjnych na różnych architekturach sprzętowych. To pozwala na znaczną redukcje kosztów tworzenia oprogramowania i skraca zdecydowanie czas wdrożenia przyspiesza pojawienie się na rynku. Ponadto programiści nie muszą zajmować się szczegółami protokołów komunikacyjnych i zarządzania pamięcią - mogą koncentrować się głównie na funkcjonalności pisanych programów. Jednocześnie ponieważ funkcjonalność karty może być zmieniana w trakcie użytkowania jej czas życia znacznie się wydłuża.
Podsumowując można powiedzieć że technologia JavaCard daje następujące korzyści:
Już dziś można powiedzieć że technologia JavaCard ustanowiła nowy standard który został zaadoptowany w komunikacji bezprzewodowej, w bankowości, w biznesie i nawet w projektach rządowych . Technologię tę wdrożyły takie firmy jak Citibank,Visa International, MasterCard, American Express i wiele innych. Dziesiątki milionów kart inteligentnych z wbudowaną platformą JavaCard zostało już wprowadzonych do użytku.
Przy tworzeniu apletów konieczna jest znajomość mechanizmu komunikacji miedzy urządzeniem akceptującym (CAD) a apletem (JCRE). W momencie kiedy karta inteligentna jest włożona do urządzenia akceptującego (czytnik, terminal) urządzenie to wybiera aplet i wysyła mu serię komend do wykonania. Każdy aplet jest identyfikowany i wybierany za pomocą identyfikatora aplikacji AID (Aplication Identifier). Komendy są odpowiednio formatowane i przesyłane w postaci jednostek danych protokołu aplikacji APDU(Aplication Protocol Data Units). Aplety z kolei wysyłają odpowiedź na każdą komendę wykonania APDU w postaci opcjonalnych danych i słowa statusu SW(Status Word) wskazującego na rezultat wykonania komendy. Komenda wykonania i odpowiedź na komendę wykonania tworzą unikalną parę którą krótko można nazwać komendą. Trzeba tutaj zaznaczyć że przesyłanie komend między aplikacją zarządząjacą a urządzeniem akceptującym a apletem nie odbywa się bezpośrednio- pośrednikiem jest środowisko JCRE w którym działa aplet..
Standard ISO7816 określa że każda aplikacja karty inteligentnej musi być jednoznacznie identyfikowana przez AID. Identyfikator ten jest sekwencją bajtów składającą się z dwóch części : pierwsza część o nazwie RID (Resource Identifier) jest identyfikatorem zasobu a druga część o nazwie PIX (Proprietary Identifier Extension) jest rozszerzeniem poprzedniej części. Część RID jest przydzielana przez organizację ISO różnym firmom, część PIX ustalana jest wewnętrznie w firmie.
Format identyfikatora AID pokazuje poniższa tabela:
część RID | część PIX |
5 bajtów | 0-11 bajtów |
W technologii JavaCard identyfikatory AID są używane do identyfikacji apletów jak również pakietów zawierających aplety.
Przykładowe (fikcyjne) identyfikatory pakietu i apletu podane są poniżej:
Identyfikator pakietu | |||||||
część RID | częśc PIX | ||||||
0xF2 | 0x34 | 0x12 | 0x34 | 0x56 | 0x10 | 0x00 | 0x00 |
Identyfikator apletu | |||||||
część RID | część PIX | ||||||
0xF2 | 0x34 | 0x12 | 0x34 | 0x56 | 0x10 | 0x00 | 0x01 |
Jak widać części RID obu identyfikatorów są identyczne, trzybajtowe części PIX różnią się
od siebie jednym bajtem.
Komenda wykonania APDU ma format < wymagany nagłówek> <opcjonalne ciało >
Komenda wykonania APDU z polem danych |
||||||
Wymagany nagłówek i jego pola | Opcjonalne ciało i jego pola | |||||
CLA(1 bajt) | INS(1 bajt) | P1(1 bajt) | P2(1 bajt) | Lc(1 bajt) | Pole danych (Lc bajtów) | Le(1 bajt) |
|
Standard ISO7816 specyfikuje wartość pola CLA tylko dla jednej kategorii komendy - dla komendy SELECT wybierającej bieżący lub nowy aplet: wartość ta musi być równa 0(CLA=0)a wartość pola INS=0xA4. Pozostałe wartości można wybierać dla innych komend.
Odpowiedź APDU ma format <opcjonalne dane> <wymagana końcówka>
Odpowiedź APDU |
||
Opcjonalne ciało | Wymagany ogon(słowo statusu) | |
Pole danych(sekwencja bajtów) | SW1(1 bajt) | SW2(1 bajt) |
|
Słowo statusu 0x9000 oznacza przetwarzanie zakończone sukcesem, inne słowo oznacza błąd przetwarzania.
Znając już podstawy komunikacji CAD <---> JCRE <---> aplet możemy omówić ogólnie pakiety bibliotek JavaCard API dostępne w technologii JavaCard w wersji 2.0.
Pakiety JavaCard API | |
java.lang | podzbiór jądra J2SE.Zawiera klasy Object i Throwable oraz klasy wyjatków:Exception, ArithmeticException,ArrayIndexOfBoundsException,ArrayStoreException,ClassCastException,IndexOutOfBoundException, NegativeArraySizeException, NullPointerException, RuntimeException |
java.io | podzbiór pakietu java.io J2SE.Zawiera tylko klasę wyjatku IOException |
java.rmi | zawiera interfejs Remote którego metody mogą być wywoływane przez aplikacje klienta od strony CAD. Zawiera również klasę wyjątku RemoteException . |
javacard.framework | zawiera fundamentalne interfejsy i klasy do tworzenia,komunikowania się i działania apletów. |
javacard.framework.service | zawiera interfejsy i klasy pozwalajace zaprojektować aplet jako zestaw komponentów serwisowych |
javacard.security | zawiera interfejsy i klasy pozwalające na implementację zasad bezpieczeństwa i uwierzytelniania |
javacardx.crypto | pakiet rozszerzeń związany z bezpieczeństwem i kryptografią. |
Na wykładzie tym omówimy dokładniej tylko pakiet javacard.framework ze wzgledu na jego fundamentalne znaczenie.
Nastepnie na bazie omówionych bibliotek zaprezentujemy dwa programy ilustrujące zastosowanie tych bibliotek a mianowicie aplet EchoApp i aplet Wallet, które zawarte są między innymi w pakiecie JavaCard DevelopmentKit2.2,który można załadować ze strony java.sun.com.
Interfejsy pakietu javacard.framework | |
ISO7816 | zawiera stałe związane ze standardem ISO7816-3 i ISO7816-4 m.in. słowa statusu |
PIN | reprezentuje pinkod karty |
MultiSelectable | identyfikuje wielokrotnie wybieralny aplet w danym pakiecie-jedna instancja apletu może być aktywna na jednym logicznym kanale a druga instancja na innym kanale .W ramach jednego pakietu albo wszystkie aplety są wybieralne albo żaden. |
Shareable | identyfikuje współdzielone obiekty |
Klasy pakietu javacard.framework | |
Applet | klasa abstrakcyjna reprezentująca aplet - aplikację JavaCard |
AID | identyfikator aplikacji (apletu) |
APDU | reprezentuje komunikację miedzy apletem i aplikacją zewnętrzną CAD. |
JCSystem | zawiera kolekcję metod do sterowania wykonaniem apletu, zarządzaniem zasobami, zarządzaniem transakcjami atomowymi, usuwaniem obiektów i współdzieleniem zasobów |
OwnerPIN | reprezentuje PIN właściciela |
Util | zawiera użyteczne metody m.in. do kopiowania tablic |
Klasy wyjątków pakietu javacard.framework | |
APDUException | reprezentuje wyjątek związany z komunikacją APDU |
CardException | definiuje pole reason i dwie metody dostępu getReason() i setReason() pozwalajace uzyskać informacje o przyczynie błędu. |
CardRunTimeException | definiuje pole reason i dwie metody dostępu getReason() i setReason() pozwalajace uzyskać informacje o przyczynie błędu. |
ISOException | zawiera słowo statusu zgodnie ze ISO71816-4 |
PINException | wyjątek związany z obiektem OwnerPIN |
SystemException | związany z obiektem JCSystem |
TransactionException | reprezentuje wyjątek w podsystemie związanym z transakcjami. |
UserException | reprezentuje błąd użytkownika |
Po tym ogólnym przeglądzie JavaCard API koncentrujemy się na klasie javacard.framework.Applet
Konstruktor klasy Applet | |
protected Applet() | konstruktor ten powinien być użyty w klasie pochodnej w metodzie install() |
Metody klasy Applet | |
void deselect() | wywoływany przez JCRE dla poinformowania ze aktualnie wybrany aplet przestał być aktywny na danym kanale logicznym i żaden aplet z tego pakietu nie jest aktywny na innym kanale logicznym. |
Shareable getShareableInterfaceObject (AID clientAID, byte parameter) |
przeznaczona do komunikacji między apletami; wywoływana przez aplet klienta dla uzyskania obiektu współdzielonego apletu serwera . |
static void install (byte[] bArray, short bOffset, byte bLength) |
wywoływana na początku przez JCRE do utworzenia instancji apletu |
abstract void process(APDU apdu) | wywoływana przez JCRE do przetwarzania komend wykonania |
protected void register() | rejestruje instancję apletu w JCRE i przydziela nazwę apletowi na bazie AID |
protected void register (byte[] bArray, short bOffset, byte bLength) |
rejestruje instancję apletu w JCRE i przydziela nazwę apletowi na bazie AID |
boolean select() | wywoływana przez JCRE informuje że aplet został wybrany i żaden inny aplet z tego pakietu nie jest aktywny na żadnym innym kanale |
protected boolean selectingApplet() | używana przez metodę process() do rozdzielenia komendy SELECT dotyczącej tego apletu od innych komend SELECT . |
Przedstawimy teraz szkielet prostego apletu dla JavaCard:
//import potrzebnych pakietów import javacard.framework.*; public class TemplateApp extends Applet { //pola klasy apletu //konstruktor chroniony //inicjalizacja pól, allokacja pamięci protected TemplateApp() { //instrukcje inicjujące //... register(); //rejestracja apletu w JCRE } //instalacja apletu; bArray-tablica parametrów instalacji public static void install(byte[] bArray, short bOffset, byte bLength) { new TemplateApp(); //utworzenie instancji apletu } //wywoływana przy wybraniu apletu public bolean select() { //czynności inicjujące przed osiągnięciem stanu aktywnego } //przetwarzanie komend APDU; apdu - obiekt komendy wykonania public void process(APDU apdu) { //przetwarzanie komendy APDU } //wywoływana przed przejściem w stan nieaktywny public void deselect() { //czynności finalizujące przed przejściem w stan nieaktywny } //metody realizujące komendy wykonania }//class TemplateApp
W programie tym warto zwrócić uwagę na miejsce przydzielenia potrzebnej pamięci . Alokacja pamięci powinna odbyć się w konstruktorze dzięki czemu już w momencie tworzenia instancji apletu wiadomo czy wielkość dostępnej pamięci jest wystarczająca. Przydzielanie pamięci w trakcie wykonywania apletu może spowodować błąd przepełnienia pamięci i przerwanie wykonania apletu.
Cykl życia apletu zaczyna się po załadowaniu klasy apletu do karty JavaCard, połączeniu z innymi pakietami na karcie, utworzeniu instancji apletu i zarejestrowaniu jej przez JCRE w tablicy rejestrów. W celu utworzenia instancji apletu JCRE wywołuje metodę install() z parametrami reprezentujacymi tablicę bajtów, która zawiera parametry instalacji. Parametry te są umieszczone w tablicy parami (długość, wartość)
bArray[0]=długość(Li) identyfikatora AID instancji apletu, bArray[1..Li] = bajty identyfikatora AID bArray[Li+1]=długość(Lc) części kontrolnej, bArray[Li+2..Li+Lc+1] = bajty części kontrolnej bArray[Li+Lc+2]=długość(La) danych apletu, bArray[Li+Lc+2..Li+Lc+La+1] = dane apletu
Długości Li, Lc lub La mogą być generalnie równe 0 . Bajty kontrolne zależą od implementacji..
Tablica bArray jest tablicą globalną-jeżeli aplet chce zachować dane z tej tablicy powinien ją skopiować do tablicy zawartej w obiekcie apletu, gdyż po powrocie z metody install() tablica globalna bArray jest zerowana przez JCRE. Ponadto referencje do tablicy bArray nie mogą być przechowywane w zmiennych statycznych, niestatycznych lub tablicach.
W metodzie install() musi być wywołana jedna z metod register(...) rejestrująca utworzoną instancję apletu. Metoda z parametrami register(byte[],int,int) pozwala na rejestrację instancji apletu z podanym w tablicy bajtów identyfikatorem AID. Jednakże implementacja może wymagać żeby identyfikator ten był identyczny z identyfikatorem dostarczonym w metodzie install(byte[],int,int).
Jeżeli instalacja instancji apletu nie kończy się sukcesem metoda install() generuje wyjatek ISOException; jeżeli zakończy się sukcesem instancja apletu pozostaje w stanie nieaktywnym ale jest gotowa na przyjęcie komendy wykonania SELECT, a następnie innych komend APDU pochodzących od JCRE..
Kiedy JCRE otrzymuje od CAD komendę SELECT poszukuje w wewnętrznej tablicy rejestrów identyfikatora pasującego do tego podanego w komendzie. Jeżeli znajdzie taki identyfikator to przygotowuje nowy aplet. Jeżeli aktualnie wybrany aplet jest aktywny to wywołuje jego metodę deselect() przeprowadzającą go w stan nieaktywny. Potem JCRE wywołuje metodę select() nowego apletu.Metoda select() zwraca true jeżeli aplet jest gotowy do osiągnięcia stanu aktywnego i przetwarzania komend APDU.
Klasa javacard.framework.Applet dostarcza domyślnej implementacji metod select() i deselect() ale w jej podklasie można je przedefiniować określając zachowanie się apletu w tych dwóch sytuacjach.
Po wybraniu apletu JCRE przesyła komendę SELECT i kolejne komendy APDU do metody process(APDU apdu).W metodzie tej aplet interpretuje odpowiednio komendy i wykonuje zadania odpowiadąjace instrukcji komendy. Dla każdej komendy wykonania aplet wysyła odpowiedź o rezultacie wykonania komendy wykonania.
Metoda process() jest abstrakcyjna zatem nasz aplet musi ją zaimplementować w jakikolwiek sposób.
Przesyłanie komend jest kontynuowane dopóki nie wybrany zostanie nowy aplet lub karta nie zostanie usunięta z urządzenia CAD.Po wywołaniu deselect() aplet może znów stać się aktywny na skutek ponownego wywołania select().
W naszym szkielecie metoda register() wywoływana jest jako ostatnia instrukcja konstruktora apletu, ale może ona być również wywołana poza konstruktorem w metodzie install(). Następny przykład pokazuje to rozwiązanie jak również sposób skorzystania z dostarczonych parametrów instalacji.
public class MyApplet extends javacard.framework.Applet{ static byte[] incomingData; static byte[] responseData; public static void install( byte[] bArray, short bOffset, byte bLength ) throws ISOException { // utworzenie instancji apletu MyApplet myApplet = new MyApplet(); // pobranie parametrów instalacji byte Li = bArray[bOffset]; // długość identyfikatora AID bOffset = (short) (bOffset+Li+1); byte Lc = bArray[bOffset]; // długość części kontrolnej bOffset = (short) (bOffset+Lc+1); byte La = bArray[bOffset]; // długość pola danych apletu // pobranie pierwszego bajtu danych byte data1 = bArray[(short)(bOffset+1)]; if ( data1!=0 ) { incomingData = new byte[data1]; responseData = new byte[3]; myApplet.register(); return; } else ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } public boolean select(){ // inicjalizacja elementu tablicy danych przychodzących incomingData[0] = 12; //inicjalizacja tablicy danych do wysłania responseData[0] = 10; responseData[1] = 20; responseData[2] = 30; return true; } public void process(APDU apdu) throws ISOException{ //pobranie bufora APDU byte[] buffer = apdu.getBuffer(); // sprawdzenie kategorii komendy (pole CLA) if ( buffer[ISO7816.OFFSET_CLA] == (byte)0 ) { //pobranie z bufora kodu instrukcji (pole INS) switch ( buffer[ISO7816.OFFSET_INS] ) { //obsługa instrukcji SELECT case ISO7816.INS_SELECT: ... // ustawienie kierunku przepływu danych na wysyłanie //uzyskanie oczekiwanej długości odpowiedzi Le short Le = apdu.setOutgoing(); // generacja wyjatku ISOException z podana przyczyną błędu if ( Le < 3) ISOException.throwIt( ISO7816.SW_WRONG_LENGTH); //ustalenie długości odpowiedzi apdu.setOutgoingLength( (short)responseData.length ); //wysłanie danych z tablicy responseData począwszy od indeksu 0 apdu.sendBytesLong(responseData, (short)0, (short)responseData.length); break; //obsługa innej niż SELECT instrukcji case ... }//switch }//if }//process }//class MyApplet
W tym fragmencie programu pojawiły się nowe aspekty pisania apletu, które krótko omówimy. |
Pierwszym nowym aspektem jest użycie stałych statycznych z interfejsu ISO7816.Interfejs ten zawiera stałe związane z polami komendy APDU, indeksami przesunięcia w buforze APDU oraz słowem statusu.
Poniższa tabela podaje nazwy niektórych stałe interfejsu ISO7816 i ich wartości.
Niektóre pola interfejsu ISO7816 | |
static byte |
CLA_ISO7816 pole CLA komendy APDU dla ISO 7816 = 0x00 |
static byte |
INS_SELECT Pole INS komendy APDU dla instrukcji SELECT = 0xA4 |
static byte |
OFFSET_CDATA indeks przesunięcia dla pierwszego bajtu danych CDATA komendy APDU = 5 |
static byte |
OFFSET_CLA indeks przesunięcia dla pola CLA komendy APDU = 0 |
static byte |
OFFSET_INS indeks przesunięcia dla pola INS komendy APDU =1 |
static byte |
OFFSET_LC indeks przesunięcia dla pola LC komendy APDU = 4 |
static byte |
OFFSET_P1 indeks przesunięcia dla pola P1 komendy APDU = 2 |
static byte |
OFFSET_P2 indeks przesunięcia dla pola P2 komendy APDU = 3 |
static short |
SW_APPLET_SELECT_FAILED status odpowiedzi : selekcja nieudana = 0x6999; |
static short |
SW_COMMAND_NOT_ALLOWED status odpowiedzi : komenda niedozwolona = 0x6986 |
static short |
SW_DATA_INVALID status odpowiedzi: nieważne dane = 0x6984 |
static short |
SW_FUNC_NOT_SUPPORTED status odpowiedzi: funkcja nieobsługiwana = 0x6A81 |
static short |
SW_INCORRECT_P1P2 status odpowiedzi: niewłaściwe parametry (P1,P2) = 0x6A86 |
static short |
SW_NO_ERROR status odpowiedzi: nie ma błędu = 0x9000 |
static short |
SW_UNKNOWN status odpowiedzi: nieznany błąd (nieprecyzyjna diagnostyka) = 0x6F00 |
static short |
SW_WARNING_STATE_UNCHANGED status odpowiedzi: stan karty niezmieniony = 0x6200 |
static short |
SW_WRONG_DATA status odpowiedzi: niewłaściwe dane = 0x6A80 |
static short |
SW_WRONG_LENGTH status odpowiedzi: niewłaściwa długość = 0x6700 |
static short |
SW_WRONG_P1P2 status odpowiedzi : błędne parametry (P1,P2) = 0x6B00 |
Drugi nowy aspekt to użycie klasy wyjatku ISOException. Zawiera ona status odpowiedzi i przyczynę wystąpienia wyjątku.
Instancjami tej klasy zarządza środowisko JCRE - referencje do instancji nie
mogą być przechowywane w zmiennych statycznych, niestatycznych i tablicach.
Konstruktor klasy ISOException | |
ISOException(short sw) tworzy obiekt klasy z podanym słowem statusu. |
Metoda statyczna klasy ISOException | |
static void |
throwIt(short sw) generuje instancję klasy ISOException z podanym słowem statusu |
Oprócz tego klasa ta dziedziczy z klasy CardRunTimeException dwie metody: void setReason(short reason) i short getReason() ustalające i pobierające przyczynę wystąpienia wyjątku.
Trzeci nowy aspekt to użycie obiektu klasy APDU. Obiekt ten zarządzany jest przez JCRE i zawiera bufor bajtowy używany do transferu dwukierunkowego nagłówka i danych komendy do apletu oraz danych odpowiedzi z apletu. Długość tego bufora musi wynosić co najmniej133 bajty (5 bajtów nagłówka i 128 bajtów danych). Przed przyjęciem komendy z CAD środowisko JCRE zeruje ten bufor aby był przygotowany na przyjęcie nowych danych.Po otrzymaniu komendy APDU, JCRE przesyła ją apletowi w obiekcie klasy APDU jako argument metody process(). Aplet przetwarza komendę APDU wywołując metody klasy APDU. Wywołanie metody getBuffer() dostarcza tablicy bajtów udostępniającej pierwsze pięć bajtów komendy: CLA,INS,P1,P2,P3 ( w przypadku komendy zawierającej dane P3 oznacza ilość bajtów danych Lc). Aplet może analizować nagłówek komendy dla określenia struktury komendy i instrukcji do wykonania.
Jeżeli komenda zawiera opcjonalne dane aplet może wywołać metodę setIncomingAndReceive() dla wprowadzenia tych danych do bufora zaraz za nagłówkiem. Ostatni bajt nagłówka (Lc) zawiera ilość bajtów danych. Jeżeli bufor nie może pomieścić wszystkich danych aplet może przetwarzać te dane porcjami lub skopiować je do wewnętrznego bufora (tablicy). W obu przypadkach powinien wywoływać cyklicznie metodę receiveBytes() do wczytania danych do bufora. Obie metody zwracają liczbę wczytanych bajtów.
Po przetworzeniu komendy aplet może wysłać do urządzenia CAD odpowiedź APDU. W tym celu powinien wywołać metodę setOutgoing() ustawiającą kierunek transferu danych z bufora na wysyłanie. Metoda ta jednocześnie dostarcza ilość oczekiwanych bajtów odpowiedzi (Le) . Następnym krokiem powinno być wywołanie metody setOutgoingLength() informującej CAD o aktualnej długości pola danych odpowiedzi. Teraz aplet może umieścić dane do wysłania w buforze APDU i wysłać je metodą sendBytes(). Jeżeli nie wszystkie dane przeznaczone do transferu mogą być załadowane do bufora to metodę tę można wywołać kilka razy.
Jeżeli dane do wysłania zgromadzone sa w buforze wewnętrznym (tablicy) można użyć metody sendByteLong(byte[],int,int)
Jeżeli w końcu wszystkie dane do wysłania mieszczą się w buforze APDU powinno zastosować się metodę setOutgoingAndSend(). Jest ona połączeniem trzech metod: setOutgoing(),setOutgoingLength() i sendBytes(). Należy jednak pamiętać że może być ona wywołana tylko raz.
Po bezbłędnej realizacji metody process() JCRE automatycznie wysyła słowo statusu 0x9000 informujące o normalnym przetwarzaniu. Jednakże jeżeli trakcie działania aplet wykryje błąd to może wygenerować wyjątek ISOException wywołując jego statyczną metodę throwIt(short reason).W argumencie metody podajemy słowo statusu-stałą statyczną interfejsu ISO7816. Jeżeli wyjątek ten nie jest obsłużony przez aplet będzie on przechwycony przez JCRE.JCRE odzyskuje kod błędu reason i wysyła je jako słowo statusu do CAD.
Poniższy fragment programu ilustruje zastosowanie omówionych metod w procesie przetwarzania komendy przez aplet i wysyłania odpowiedzi.
public void process(APDU apdu){ //pobranie bufora APDU i pól komendy byte[] buffer = apdu.getBuffer(); byte cla = buffer[ISO7816.OFFSET_CLA]; byte ins = buffer[ISO7816.OFFSET_INS]; // ... //zakładamy że komenda ma pole danych //pobieramy wartość Lc podającą długość pola danych short bytesToRead = (short) (buffer[ISO7816.OFFSET_LC] & 0x00FF); if (bytesToRead < (short)55)ISOException.throwIt(ISO7816.SW_WRONG_LENGTH ); //wczytanie do bufora po nagłówku bajtów danych komendy //pobranie ilości wczytanych bajtów short readBytesCount = apdu.setIncomingAndReceive(); bytesToRead -= readBytesCount; //wczytywanie do bufora nie wczytanych danych while (bytesToRead > 0){ //lokowanie bajtów od elementu buffer[5] do elementu buffer[readCount+4] readBytesCount = apdu.receiveBytes ( ISO7816.OFFSET_CDATA ); bytesToRead -= readBytesCount; //przetwarzanie wczytanej porcji danych ... } //konstruowanie odpowiedzi APDU short le = apdu.setOutgoing(); if (le < (short)2) ISOException.throwIt( ISO7816.SW_WRONG_LENGTH); apdu.setOutgoingLength( (short)3 ); //załadowanie danych do wysłania do bufora APDU buffer[0] = (byte)1; buffer[1] = (byte)2; buffer[3] = (byte)3; //wysłanie danych z bufora apdu.sendBytes ( (short)0,(short)3 ); }
Jako pełny przykład apletu rozpatrzymy aplet EchoApplet przetwarzający komendę APDU i wysyłający w odpowiedzi te same dane, które przesłane były w komendzie wykonania.
import javacard.framework.*; public class EchoApplet extends Applet { //tablica dla danych komendy APDU private byte[] echoData; //długość tablicy danych private static final short ECHO_LENGTH = 256; protected EchoApplet() { //alokacja pamięci echoData = new byte[ECHO_LENGTH]; register(); //rejestracja instancji apletu }//EchoApplet() public static void install(byte[] bArray, short bOffset, byte bLength) { new EchoApplet(); //utworzenie instancji apletu }//install() public void process(APDU apdu) { //pobranie bufora APDU byte buffer[] = apdu.getBuffer(); //wprowadzenie porcji danych do bufora za nagłowkiem short bytesReadCount = apdu.setIncomingAndReceive(); short echoOffset = (short)0;//indeks początkowy tablicy echoData while ( bytesReadCount > 0 ) { //przekopiowanie porcji danych z bufora APDU do tablicy echoData //zwiększenie indeksu początkowego echoOffset = Util.arrayCopy(buffer, ISO7816.OFFSET_CDATA, echoData, echoOffset, bytesReadCount); //wczytanie do bufora następnej porcji danych bytesReadCount = apdu.receiveBytes(ISO7816.OFFSET_CDATA); } //ustawienie kierunku transferu i aktualnej długości odpowiedzi apdu.setOutgoing(); apdu.setOutgoingLength( (short) (echoOffset + 5) ); //wysłanie nagłowka z bufora APDU apdu.sendBytes( (short)0, (short)4); //wysłanie danych z tablicy echoData apdu.sendBytesLong( echoData, (short) 0, echoOffset ); }//process() }//class EchoApplet
W programie tym jak łatwo zauważyć pojawiła się nieznana metoda statyczna arrayCopy() z klasy Util. Klasa ta zawiera kilka pożytecznych metod tak więc prezentujemy jej interfejs w poniższej tabeli. Wszystkie metody tej klasy są statyczne.
Metody klasy Util | |
static byte |
arrayCompare(byte[] src,
short srcOff, byte[] dest, short destOff, short length) porównuje tablicę źródłową src od indeksu srcOff z tablicą docelową dest od indeksu destOff od lewej do prawej . |
static short |
arrayCopy(byte[] src,
short srcOff, byte[] dest, short destOff, short length) kopiuje tablicę src od indeksu srcOff do tablicy dest od indeksu destOff - w sumie length bajtów |
static short |
arrayCopyNonAtomic(byte[] src,
short srcOff, byte[] dest, short destOff, short length) kopiuje tablicę źródłową do docelowej (proces jest nie-atomowy; może być przerwany) |
static short |
arrayFillNonAtomic(byte[] bArray,
short bOff, short bLen, byte bValue) wypełnia nie-atomowo podtablicę tablicy bArray wartością bValue. |
static short |
getShort(byte[] bArray,
short bOff) łączy dwa kolejne bajty (począwszy od bOff) tablicy bArray tworząc wartość typu short. |
static short |
makeShort(byte b1,
byte b2) łączy dwa bajty b1 i b2 w jedna wartość typu short |
static short |
setShort(byte[] bArray,
short bOff, short sValue) wartość typu short umieszcza jako dwa kolejne bajty w tablicy bArray począwszy o indeksu bOff |
Jako następny przykład rozpatrzymy bardziej złożony aplet o nazwie Wallet (portfel) dostarczony w pakiecie w pakiecie JavaCard Developmnent Kit 2.2 . Aplet ten pełni rolę elektronicznego portfela - można w nim przechowywać pieniądze, można do niego wkładać i wyjmować pieniądze, można uzyskać również aktualną zawartość portfela. Do portfela powinien mieć dostęp tylko jego właściciel, zatem aplet ten zawiera mechanizm bezpieczeństwa wymagający od użytkownika wprowadzenia pinkodu - identyfikatora PIN (Personal Identification Number). Dzięki temu do portfela ma dostęp tylko użytkownik który wprowadzi uwierzytelniony PIN.
Ponieważ aplet Wallet ma jak widać spełniać określone zadania zdefiniowano w nim metody własne realizujące te zadania :
Klasa OwnerPIN implementuje interfejs PIN i reprezentuje numer identyfikacyjny PIN. Konstruktor tej klasy pozwala na ustalenie maksymalnej ilości prób wprowadzania pinkodu przed zablokowaniem i jego maksymalny rozmiar w bajtach.
Konstruktor klasy OwnerPIN | |
OwnerPIN (byte tryLimit, byte maxPINSize) tworzy identyfikator PIN z maksymalną ilością prób i maksymalnym rozmiarem |
Metody tej klasy pozwalają na porównanie pinkodu z podanym numerem, ustawienie flagi 'validated' oznaczajacej że kod jest ważny, resetowanie pinkodu i ustalanie nowego numeru identyfikacyjnego.
Metody klasy OwnerPIN | |
boolean |
check(byte[] pin,short offset, byte length) porównuje PIN z podanym w tablicy pin numerem. Jeżeli numery pasują ustawia flagę na true i resetuje liczbę prób. Jeżeli numery nie pasują zmniejsza ilość prób - jeżeli osiąga ona zero blokuje PIN. |
byte |
getTriesRemaining() dostarcza ilość pozostałych jeszcze prób wprowadzenia pinkodu bez blokowania. |
protected
boolean |
getValidatedFlag() doatarcza wartość flagi 'validated'. |
boolean |
isValidated() sprawdza flagę 'validated' (czy aktualnie PIN jest ważny) |
void |
reset() jeżeli flaga jest ustawiona resetuje ją i resetuje licznik prób do maksymalnej wartości |
void |
resetAndUnblock() resetuje i odblokowuje PIN. |
protected
void |
setValidatedFlag(boolean value) ustawia flagę 'validated' |
void |
update(byte[] pin,
short offset, byte length) ustawia nową wartość numeru PIN podana w tablicy i resetuje licznik prób do maksymalnej wartości. |
Ponieważ aplet powinien zinterpretować każdą komendę pobierając z bufora
APDU jej pola i ewentualnie wykonać zadanie określone przez instrukcję
komendy aplet najpierw wprowadza stałe określające pola komend które będzie
interpretował w odpowiedni sposób.
Poniżej przedstawiamy formaty komend które interpretuje aplet. Tylko komenda
SELECT ma postać standardową (CLA=0, INS=0xA4, pole danych
zawiera identyfikator AID). Pozostałe komendy zostały zaprojektowane dla tego
apletu.
W poniższych tabelach przedstawiamy postacie komend wykonania
APDU i odpowiedzi na te komendy.
(NU w danym polu oznacza że nie jest ono używane w danej komendzie)
Komenda wykonania SELECT |
||||||
CLA | INS | P1 | P2 | Lc | Pole danych ( identyfikator AID) | Le |
0x0 | 0xA4 | 0x04 | 0x0 | 0x08 | 0xF2, 0x34, 0x12, 0x34, 0x56, 0x10, 0x0, 0x1 | NU |
Odpowiedź na komendę | ||||||
Dane opcjonalne | Słowo statusu | Znaczenie słowa statusu | ||||
nie ma danych
|
0x9000 | sukces przetwarzania | ||||
0x6999 | błąd - aplet nie może być znaleziony lub wybrany |
Komenda wykonania GET_BALANCE |
||||||
CLA | INS | P1 | P2 | Lc | Pole danych | Le |
0xB0 | 0x50 | 0x0 | 0x0 | NU | NU | 2 |
Odpowiedź na komendę | ||||||
Dane opcjonalne | Słowo statusu | Znaczenie słowa statusu | ||||
saldo portfela (2 bajty) | 0x9000 | sukces przetwarzania |
Komenda wykonania CREDIT |
||||||
CLA | INS | P1 | P2 | Lc | Pole danych | Le |
0xB0 | 0x30 | 0x0 | 0x0 | 1 | wielkość wkładanej kwoty | NU |
Odpowiedź na komendę |
||||||
Dane opcjonalne | Słowo statusu | Znaczenie słowa statusu | ||||
nie ma danych | 0x9000 | sukces przetwarzania | ||||
0x6301 | błąd - wymagana weryfikacja PIN-u | |||||
0x6A83 | błąd - niewłaściwa wielkość kredytu | |||||
0x6A84 | błąd - przekroczenie maksymalnej zawartości portfela |
Komenda wykonania DEBIT |
||||||
CLA | INS | P1 | P2 | Lc | Pole danych | Le |
0xB0 | 0x40 | 0x0 | 0x0 | 1 | wielkość pobieranej kwoty | NU |
Odpowiedź na komendę |
||||||
Dane opcjonalne | Słowo statusu | Znaczenie słowa statusu | ||||
nie ma danych | 0x9000 | sukces przetwarzania | ||||
0x6301 | błąd - wymagana weryfikacja PIN-u | |||||
0x6A83 | błąd - niewłaściwa wielkość kredytu | |||||
0x6A85 | błąd - ujemne saldo |
Komenda wykonania VERIFY | ||||||
CLA | INS | P1 | P2 | Lc | Pole danych | Le |
0xB0 | 0x20 | 0x0 | 0x0 | długość PIN-u | pinkod | NU |
Odpowiedź na komendę | ||||||
Dane opcjonalne | Słowo statusu | Znaczenie słowa statusu | ||||
nie ma danych
|
0x9000 | sukces przetwarzania | ||||
0x6300 | weryfikacja nieudana |
Wiedząc już jakie komendy wykonania będzie obsługiwał aplet i jakie będzie wysyłał odpowiedzi możemy przejść do prezentacji programu z odpowiednimi komentarzami ułatwiającymi zrozumienie jego działania.
import javacard.framework.*; public class WalletApp extends Applet { //wartość pola CLA w komendzie APDU final static byte Wallet_CLA = (byte)0xB0; //wartość pola INS w komendzie APDU 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; //maksymalna wielkość salda w portfelu final static short MAX_BALANCE = 10000; //maksymalna wielkość transakcji final static byte MAX_TRANSACTION_AMOUNT = 100; //maksymalna liczba prób podania pinkodu przed zablokowaniem pinu final static byte PIN_TRY_LIMIT =(byte)0x03; //maksymalny rozmiar pinkodu w bajtach final static byte MAX_PIN_SIZE =(byte)0x08; //słowa statusu specyficzne dla apletu 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; //deklaracja pól klasy OwnerPIN pin; //pinkod short balance; //saldo public static void install(byte[] bArray,short bOffset,byte bLength) { //utworzenie instancji apletu new WalletApp(bArray, bOffset, bLength); }//install() private WalletApp (byte[] bArray, short bOffset, byte bLength){ //utworzenie pinkodu o podanym rozmiarze i określoną liczbą prób pin = new OwnerPIN(PIN_TRY_LIMIT,MAX_PIN_SIZE); //tablica bArray zawiera początkowy PIN pin.update(bArray, bOffset, bLength); //rejestracja instancji apletu w JCRE register(); }//WalletApp() public boolean select() { //jeżeli pin jest zablokowany aplet rezygnuje z aktywacji if (pin.getTriesRemaining() == 0)return false; return true; }//select() public void deselect() { //zresetowanie pinu pin.reset(); } public void process(APDU apdu) { byte[] buffer = apdu.getBuffer(); //jeżeli komenda SELECT - nie ma obsługi (powrót z metody) if(selectingApplet())return; //weryfikacja bajtu CLA if(buffer[ISO7816.OFFSET_CLA] != Wallet_CLA) ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); //analiza pola INS w celu wybrania odpowiedniej metody do wykonania 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); } }//process() //dodawanie pieniędzy do portfela private void credit(APDU apdu) { //weryfikacja uwierzytelniania if (!pin.isValidated()) ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED); byte[] buffer = apdu.getBuffer(); //uzyskanie liczby bajtów danych w komendzie APDU byte numBytes = buffer[ISO7816.OFFSET_LC]; //wczytanie danych komendy do bufora zaraz za nagłówkiem //uzyskanie ilości wczytanych bajtów byte byteRead = (byte)(apdu.setIncomingAndReceive()); //jeżeli ilość bajtów danych różna od 1 generacja wyjątku if((numBytes != 1 ) || (byteRead != 1)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); //pobranie z bufora wielkości kredytu-pierwszy bajt danych byte creditAmount = buffer[ISO7816.OFFSET_CDATA]; //sprawdzenie wielkości kredytu if((creditAmount > MAX_TRANSACTION_AMOUNT) || (creditAmount < 0 )) ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT); //sprawdzenie wielkości salda i ew. generacja wyjątku if((short)(balance + creditAmount) > MAX_BALANCE) ISOException.throwIt(SW_EXCEED_MAXIMUM_BALANCE); //zwiększenie salda o wielkość kredytu balance = (short)(balance + creditAmount); return; }//credit() //pobranie pieniędzy z portfela private void debit(APDU apdu) { //weryfikacja uwierzytelnienia i ew. generacja wyjątku if(! pin.isValidated()) ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED); //uzyskanie bufora APDU byte[] buffer = apdu.getBuffer(); //uzyskanie liczby bajtów danych byte numBytes = (byte)(buffer[ISO7816.OFFSET_LC]); //wczytanie do bufora za nagłowkiem porcji danych //pobranie ilości wczytanych bajtów byte byteRead = (byte)(apdu.setIncomingAndReceive()); if(( numBytes != 1 ) || (byteRead != 1)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); //uzyskanie wielkości pobieranej kwoty byte debitAmount = buffer[ISO7816.OFFSET_CDATA]; //kontrola poprawności podanej kwoty if((debitAmount > MAX_TRANSACTION_AMOUNT) || ( debitAmount < 0 )) ISOException.throwIt(SW_INVALID_TRANSACTION_AMOUNT); //sprawdzenie czy po odjęciu podanej kwoty nowe saldo jest poprawne if((short)(balance - debitAmount ) < (short)0) ISOException.throwIt(SW_NEGATIVE_BALANCE); balance = (short)(balance - debitAmount); }//debit() //wysłanie salda portfela private void getBalance(APDU apdu) { byte[] buffer = apdu.getBuffer(); //ustawienie kierunku transferu na wysłanie short le = apdu.setOutgoing(); //ustawienie aktualnej ilości bajtów danych do wysłania apdu.setOutgoingLength((byte)2); //zapisanie salda do bufora APDU do komórki od indeksu 0 Util.setShort(buffer, (short)0, balance); //wysłanie z bufora 2 bajtów salda apdu.sendBytes((short)0, (short)2); }//getBalance() //weryfikacja pinkodu private void verify(APDU apdu) { byte[] buffer = apdu.getBuffer(); //wczytanie do bufora pinkodu do weryfikacji byte byteRead = (byte)(apdu.setIncomingAndReceive()); //werifikacja pinkodu i ew. generacja wyjątku if(pin.check(buffer, ISO7816.OFFSET_CDATA,byteRead) == false) ISOException.throwIt(SW_VERIFICATION_FAILED); }//verify() } //class WalletApp
źródło: JavaCard 2.2 Development Kit
Jak można łatwo zauważyć zasadniczą częścią tego apletu jest sprawdzanie czy nie wystąpił błąd formatowania i transmisji danych lub błąd w procesie przetwarzania. Jest to szczególnie ważne dla JavaCard gdyż nie wyłapany błąd może spowodować zablokowanie karty lub utratę ważnych danych przechowywanych na karcie.
W czasie wykonywania zadania aplet powinien też sprawdzać czy dana operacja nie wprowadza go w niedozwolony stan.
Podsumowując ten rozdział można wypisać kilka etapów projektowania apletu dla JavaCard:
Do testowania apletów służy pakiet JavaCard Development Kit w jego najnowszej wersji 2.2 lub wcześniejszej (http://www.java.sun.com)
Pakiet ten zawiera natępujące komponenty:
Środowisko JCWDE symuluje JCRE na komputerze typu PC lub Workstation - pliki .class są wykonywane w tym środowisku co pozwala na szybkie testowanie aplet bez potrzeby konwersji plików klasowych do innych formatów i umieszczania na karcie. Należy jednak podkreślić że testowanie w JCWDE jest wstępnym etapem i nie obejmuje wszystkich aspektów działania apletu umieszczonego na karcie.
Omówimy tutaj etap testowania wstępnego w JCWDE obejmującego sprawdzenie funkcjonalności apletu.
Po rozpakowaniu pakietu ściągniętego z sieci należy ustawić zmienne środowiskowe JC_HOME , JAVA_HOME,PATH oraz CLASSPATH. Wygodnym sposobem wykonania tego zadania w systemie Windows jest przygotowanie pliku .bat zawierającego następujące dyrektywy:
set JC_HOME=c:\java_card_kit-2_2
set JAVA_HOME=c:\jdk14\j2sdk1.4.1 set PATH=%JC_HOME%\bin;%PATH% set CLASSPATH=%JC_HOME%\lib\api.jar;%CLASSPATH% |
Zakładamy że pakiet JavaCard Development Kit został rozpakowany w katalogu głównym na dysku c:\.
Następnie możemy skompilować nasz aplet wydając komendę javac z opcją -g (generowanie informacji debuggera).
javac -g samples\com\sun\javacard\samples\wallet.Wallet.java |
Następnie uruchamiamy program jcwde symulujący środowisko JCRE. Jako argument komendy jcwde podajemy plik konfiguracyjny jcdwe.app identyfikujący jeden lub kilka apletów.
jcwde jcwde.app |
Po tej komendzie środowisko JCWDE oczekuje na komendy APDU na porcie domyślnym.
Przykładowa postać pliku konfiguracyjnego:
// applet AID com.sun.javacard.installer.InstallerApplet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0x8:0x1 com.sun.javacard.samples.wallet.Wallet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x6:0x1 pierwsza linia jest linią komentarza; druga i trzecia zawiera dwa pola - pełną kwalifikowaną nazwę klasy apletu oraz identyfikator AID apletu |
Aplety identyfikowane w tym pliku są umieszczane w JCWDE tak jakby były umieszczane w pamięci ROM środowiska JCRE karty. Pierwszy aplet identyfikowany w tym pliku jest apletem instalacyjnym dostarczanym w tym pakiecie i musi pojawić się w pierwszej linii pliku konfiguracyjnego.
Po umieszczeniu apletu w JCWDE uruchamiamy program apdutool za pomocą którego wydajemy apletowi komendy APDU. Plikiem wejściowym dla tego programu jest plik skryptowy demo1.scr zawierający komendy wykonania APDU dla apletu jak również inne komendy właściwe dla programu apdutool..
apdutool demo1.scr |
Fragment pliku skryptowego zawiera poniższa tabela:
//komenda wybrania apletu Wallet 0x00 0xA4 0x04 0x00 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x7F; //komenda weryfikacji pinu 0x80 0x20 0x00 0x00 0x05 0x01 0x02 0x03 0x04 0x05 0x7F; //komenda uzyskanie salda 0x80 0x50 0x00 0x00 0x00 0x02; |
Po uruchomieniu apdutool komendy wykonania będą przekazywane do apletu poprzez JCWDE a aplet będzie odpowiadał na te komendy. Odpowiedzi apletu będą pojawiały się w okienku konsolowym programu apdutool (można również przekierować wyjście do pliku).
Przykładowa linia odpowiedzi zawiera pierwotną komendę i słowo statusu(2 bajty).
CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 09, a0, 00, 00, 00, 62, 03, 01, 08, 01, Le: 00, SW1: 90, SW2: 00 |
W ten prosty sposób możemy testować działanie apletu - można tez użyć zewnętrznego debuggera.
Wykład ten był wprowadzeniem w technologię JavaCard. Opisano w nim krótko komponenty technologii JavaCard, jej zastosowania jak również podstawy pisania programów w języku Java dla kart inteligentnych. Ponieważ dziedziny i skala wdrożeń tej technologii są coraz szersze należy spodziewać się że wiedza przedstawiona na tym wykładzie będzie użyteczna jeżeli nie dziś to w najbliższej przyszłości.
Dla pogłębienia wiedzy o technologii JavaCard należy sięgnąć samodzielnie do Internetu np. na stronie java.sun.com można znaleźć łącza do wielu źródeł opisujących ogólnie i szczegółowo tę technologię.
Napisz i przetestuj aplet JavaCard rejestrujący wydatki w czasie podróży i podający aktualną sumę wydatków oraz kwotę pieniędzy która nam pozostała na kontynuowanie podróży - kwota ta została określona z góry i nie może być przekroczona.