14. JavaCard 


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.


14.1. Karty inteligentne.

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.


14.2. Technologia JavaCard

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.


14.3. Tworzenie apletów dla JavaCard

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
 
Tak więc identyfikator AID może zawierać 5-16 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)
  • CLA - klasa (kategoria) instrukcji
  • INS -  kod instrukcji do wykonania
  • P1,P2 - parametry instrukcji-dalsza specyfikacja instrukcji
  • Lc -  liczba bajtów w polu danych
  • Pole danych - sekwencja bajtów danych
  • Le - oczekiwana maksymalna liczba bajtów w polu danych odpowiedzi                                     
 

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)
  • Pole danych o zmiennej długości - sekwencja bajtów danych zawartych w odpowiedzi
  • SW1, SW2 - słowo statusu określające stan przetwarzania komendy wykonania

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 : 

Do weryfikacji PIN-u program będzie używał obiektu klasy OwnerPIN o której dotąd nie mówiliśmy.

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:

 


14.4. Testowanie apletów 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.


14.5. Podsumowanie

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ę.


14.6. Ćwiczenia i zadania

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.


14.7. Literatura,źródła