Introduzione
Per
comprendere correttamente gli argomenti trattati in quest’articolo, è
consigliabile conoscere i concetti base relativi all’ambiente JavaCard.
Questi possono essere facilmente acquisiti leggendo l’articolo “JAVACARD:
TECNOLOGIA JAVA PER LE SMARTCARD”, scritto dallo stesso autore di quest’articolo,
e pubblicato da MokaByte nel numero 37 (gennaio 2000). E’ altresì
consigliabile aver letto la prima parte dell’articolo, pubblicata da MokaByte
nel numero 38 (febbraio 2000).
Nella
prima parte dell’articolo, pubblicata il mese scorso, sono state trattate
le limitazioni e le principali estensioni dell’ambiente JavaCard rispetto
all’ambiente Java per workstation. In questa seconda parte, viene inizialmente
introdotto un esempio delle estensioni dedicate alle particolari funzionalità
di cui deve essere dotata una SmartCard: la gestione dei PIN (Personal
Identification Number).
Viene
inoltre introdotto il concetto di APDU (Application Protocol Data Unit),
cioè l’unità base di comunicazione utilizzata per far interagire
l’entità esterna di controllo (solitamente una workstation o un
microcontrollore dedicato) con una SmartCard. Si noti che questo concetto
è completamente ereditato dalle SmartCard e che è standardizzato
dalla normativa ISO 7816. Saranno discusse anche le procedure da utilizzare
per accedere ai comandi APDU ricevuti dalla JavaCard e quelle da utilizzare
per restituire le risposte APDU all’entità esterna.
Al
termine di questa seconda parte, vengono infine riportate le conclusioni
relative all’intero articolo (prima e seconda parte).
Informazioni
avanzate sul'ambiente Java Card
In
questa seconda parte dell’articolo viene, inizialmente, descritta l’implementazione
dei meccanismi di gestione dei PIN, necessaria per proteggere i servizi
contenuti sulla JavaCard. Più precisamente, un PIN è un codice
alfanumerico utilizzato per proteggere da accessi non autorizzati i servizi
caricati su una JavaCard Applet. Tale codice alfanumerico deve essere custodito
dal proprietario della JavaCard e da lui mantenuto segreto. Nel caso in
cui il proprietario della JavaCard Applet smarrisca il dispositivo o qualche
malintenzionato se ne impossessi fraudolentemente, questo non può
essere in grado di utilizzare i servizi della JavaCard, poiché non
conosce il PIN necessario ad utilizzarli.
Le
classi più importanti messe a disposizione dal Framework per la
gestione dei PIN sono la classe astratta PIN e la classe OwnerPIN, ambedue
contenute nel package javacard.framework. La prima delle due classi rappresenta
la superlcasse (astratta) di tutte le classi di gestione dei PIN e non
è utilizzabile direttamente. La seconda rappresenta la classe degli
oggetti destinati a fornire supporto per la protezione della JavaCard da
accessi da parte di entità diverse dal proprietario. Notare che
il Framework fornisce un’ulteriore classe destinata alla gestione della
protezione tramite PIN, la classe ProxyPIN, contenuta nello stesso package
delle precedenti. Quest’ultima classe viene utilizzata per distribuire
una stessa istanza della classe OwnerPIN fra più JavaCard Applet.
Per ulteriori informazioni su questa classe si consulti [Snm97c].
Per
essere efficaci contro gli attacchi esaustivi, i PIN della classe OwnerPIN
fanno uso di un contatore che tiene traccia del numero di tentativi di
accesso falliti e di un numero massimo di tentativi. Una volta superato
questo numero, la JavaCard non autorizza l’accesso neanche dopo la specificazione
del PIN corretto. Questo stato degli oggetti OwnerPIN è definito
stato di block, l’operazione di ripristino è invece definita unblocking.
E’ compito del programmatore implementare i meccanismi adatti per il recupero
della piena funzionalità della JavaCard.
Il
passaggio del PIN dall’applicazione esterna alla JavaCard avviene secondo
le modalità preferite dallo sviluppatore. Il metodo consigliato
è comunque quello di inserire il byte array contenente il PIN in
testa al byte array contenente i dati del comando relativo all’operazione
da proteggere. La lunghezza del PIN può quindi essere specificata
fra i parametri del comando, o non specificata per niente nel caso in cui
si intenda utilizzare PIN a lunghezza fissa.
Non
esistendo altri dispositivi di input/output, la comunicazione fra la JavaCard
e l’esterno avviene tramite opportuni segnali elettrici sull’interfaccia
di comunicazione. Per la comunicazione viene utilizzato un apposito protocollo,
corrispondente allo standard ISO 7816, basato sul concetto di APDU (Application
Protocol Data Unit). Questo concetto è particolarmente importante
in quanto gli APDU sono visibili a livello del codice Java sia dalla parte
della JavaCard Applet, sia dalla parte dell’applicazione esterna di controllo.
I comandi APDU inviati dall’applicazione esterna di controllo alla JavaCard
Applet prendono il nome di APDU di comando. Quelli restituiti, come risposta,
dalla JavaCard Applet all’applicazione esterna di controllo, prendono invece
il nome di APDU di risposta. Di seguito verrà analizzata la sintassi
di ambedue le tipologie.
Un
APDU di comando è una sequenza di byte contenente le istruzioni
inviabili dall’applicazione esterna alla JavaCard e gli eventuali dati
associati a queste istruzioni. La struttura degli APDU di comando è
definita dallo standard ISO 7816 parte 4. La struttura generale di un APDU
di comando è quella rappresentata nella figura 1.
|
fig.
1 – APDU di comando
Ogni
APDU di comando può essere suddiviso in due parti, un header ed
una parte dati, come mostrato nelle figure 2 e 3.
|
fig.
2 – Header
|
fig.
3 – Parte dati
L’header
è composto da quattro byte: il CLA (byte di classe), l’INS (byte
di istruzione), il P1 (primo byte del parametro) ed il P2 (secondo byte
del parametro). Il byte di classe codifica, secondo lo standard ISO 7816
parte 4, la classe dell’istruzione. Il byte di istruzione codifica, sempre
secondo lo standard ISO 7816 parte 4, l’istruzione che la JavaCard deve
eseguire. Il primo ed il secondo byte del parametro codificano invece il
parametro a 16 bit relativo all’istruzione.
La
parte dati degli APDU di comando serve per comunicare alla JavaCard Applet
destinataria dell’istruzione eventuali dati. Questa parte dati è
a lunghezza variabile e può anche non essere presente.
Per
chiarire le caratteristiche di questa parte degli APDU di comando conviene
suddividere gli APDU di comando in quattro gruppi in funzione del tipo
di scambio di informazioni con la JavaCard Applet:
-
APDU di
comando di tipo 1: si tratta di APDU di comando che non inviano dati alla
JavaCard e che non si aspettano dati in risposta. In questo caso la parte
dati è completamente mancante, come mostrato nella figura 4.
|
fig.
4 – APDU di comando di tipo 1
-
APDU di
comando di tipo 2: si tratta di APDU di comando che non inviano dati alla
JavaCard ma che richiedono dati in risposta. In questo caso la parte dati
prevede l’utilizzo del byte Le per specificare la lunghezza della risposta
attesa. Notare che né il byte Lc né la parte DATA sono presenti
in APDU di questo tipo, come mostrato nella figura 5.
|
fig.
5 – APDU di comando di tipo 2
-
APDU di
comando di tipo 3: si tratta di APDU di comando che inviano dati alla JavaCard
ma che non si aspettano dati in risposta. In questo caso la parte dati
prevede l’utilizzo del byte Lc per contenere la lunghezza della parte dati
dell’APDU di comando. La parte DATA contiene i dati di comando. Notare
che nessun byte Le è presente in coda all’APDU di comando, come
mostrato nella figura 6.
|
fig.
6 – APDU di comando di tipo 3
-
APDU di
comando di tipo 4: si tratta di APDU di comando che inviano dati di comando
alla JavaCard e che richiedono anche dati di risposta. In questo caso la
parte dati prevede l’utilizzo del byte Lc per contenere la lunghezza della
parte dati dell’APDU di comando, della parte DATA per contenere i dati
di comando e del byte Le per specificare la lunghezza della risposta attesa
(notare che se Le contiene il valore 0 significa che la lunghezza della
risposta è indefinita, cioè la risposta può essere
di lunghezza qualsiasi), come mostrato nella figura 7.
|
fig.
7 – APDU di comando di tipo 4
Notare
che la classificazione degli APDU di comando, necessaria per poterne identificare
correttamente il contenuto, è a carico del produttore della carta
per i metodi generali e a carico del programmatore per le istruzioni indirizzate
alle JavaCard Applet. Notare infine che la codifica dei byte CLA, INS,
P1 e P2, nonché della parte dati, non è standardizzata. In
altre parole, l’APDU di comando utilizzato per eseguire, per esempio, la
cancellazione di tutte le JavaCard Applet presenti su una JavaCard di un
produttore, è generalmente diverso da quello che compie la stessa
azione su una JavaCard di un altro produttore. Per le informazioni relative
ai dettagli degli APDU di comando utilizzabili si consiglia di consultare
la documentazione relativa alla JavaCard che si intende utilizzare.
Un
APDU di risposta è invece una sequenza di byte contenente gli eventuali
dati di risposta e la Status Word restituita dalla JavaCard a seguito dell’invio
di un APDU di comando. La struttura degli APDU di risposta ed il loro contenuto
sono definiti dallo standard ISO 7816 parte 4. La struttura generale di
un APDU di risposta è quella rappresentata dalla figura 8.
|
fig.
8 – APDU di risposta
La
prima parte dell’APDU di risposta non è obbligatoria: la JavaCard
può anche non restituire dati. L’ultima parte è invece presente
in qualsiasi APDU di risposta e contiene la Status Word. Quest’ultima è
una parola a 16 bit che informa l’entità esterna del risultato dell’esecuzione
dell’ultimo APDU di comando inviato alla JavaCard. In caso di esecuzione
corretta, la JavaCard restituisce una Status Word apposita per indicare
questa situazione: si tratta della Status Word 9000 esadecimale. Per l’elenco
ed il significato dei possibili valori delle Status Word si rimanda alla
documentazione ISO 7816 parte 4 (poiché la maggior parte delle Status
Word di risposta è standardizzata) e alla documentazione che accompagna
la JavaCard che si intende utilizzare.
Come
già accennato precedentemente, il concetto di APDU è visibile
a livello di codice sia dal lato applicazione di controllo sia dal lato
JavaCard Applet. Di seguito viene indicato come fare riferimento agli APDU
di comando ricevuti e come restituire APDU di risposta dalla JavaCard Applet.
Purtroppo, dal lato applicazione di controllo, è solitamente possibile
inviare APDU di comando e interpretare APDU di risposta unicamente tramite
metodi proprietari di apposite classi di interfaccia. Per capire come eseguire
questo tipo di operazioni è quindi necessario consultare la documentazione
fornita con il CAD che si intende utilizzare. Come si ricorderà,
il metodo process( ) di una JavaCard Applet è richiamato dal JCRE
ogni volta che questo riceve un APDU di comando dall’entità esterna.
Tale APDU di comando è passato per parametro al metodo process(
) sotto forma di un oggetto di classe javacard.framework.APDU. Questo oggetto
rappresenta il vertice di tutta l’attività di comunicazione con
l’esterno, resa possibile dai metodi disponibili. Di seguito è riportato
l’elenco dei metodi principali dell’oggetto APDU, utilizzabili all’interno
di process( ) per implementare il funzionamento voluto della JavaCard Applet.
Notare che l’ordine in cui sono riportati questi metodi rispecchia il loro
utilizzo generale all’interno del metodo process( ), cioè apertura
del buffer APDU, lettura dell’header e dei dati ed invio dei risultati.
Naturalmente questi metodi non devono essere tutti utilizzati all’interno
del metodo process( ), solo quelli necessari all’esecuzione delle operazioni
che interessano vanno presi in considerazione.
-
getBuffer(
): questo metodo permette di ottenere un riferimento al buffer che contiene
le informazioni relative all’APDU corrente. Questo buffer, spesso detto
buffer APDU, si presenta come un array di byte da cui è possibile
leggere ogni parte dell’APDU di comando e su cui è anche possibile
scrivere gli eventuali dati di risposta richiesti. Questo buffer ha una
lunghezza dipendente dalla piattaforma JavaCard utilizzata, la lunghezza
minima garantita è in ogni caso di 37 byte;
-
setIncomingAndReceive(
): questo metodo permette di informare il JCRE che la JavaCard Applet intende
leggere dati dal buffer APDU. Quando questo metodo è eseguito sono
letti nel buffer quanti più dati possibili dall’APDU di comando
corrente;
-
receiveBytes(
): questo metodo permette di leggere i dati contenuti all’interno del buffer
APDU. E’ possibile specificare l’offset da cui iniziare la lettura tramite
opportune costanti, in questo modo è possibile leggere ogni parte
dell’APDU di comando (byte dell’header compresi). Può essere utilizzato
anche per leggere una quantità di dati non contenibile in un unico
buffer APDU;
-
setOutgoing(
): questo metodo permette di informare il JCRE che la JavaCard Applet intende
restituire dati tramite il buffer APDU;
-
setOutgoingLength(
): questo metodo permette di informare il JCRE della lunghezza del messaggio
da restituire (che in ogni caso deve essere inferiore alla lunghezza specificata
da Le, cioè la lunghezza attesa specificata nell’APDU di comando
inviato dall’entità esterna);
-
sendBytes(
): questo metodo permette di inviare, scrivendoli sul buffer APDU, dati
di risposta verso il CAD. Può essere utilizzato anche quando è
necessario inviare una quantità di dati non contenibile in un unico
buffer APDU;
-
setOutgoingAndSend(
): questo metodo implementa in un’unica istruzione le istruzioni setOutgoing(
), setOutgoingLength( ) e sendBytes( ). Può essere utilizzato solo
quando la risposta è di lunghezza inferiore alla lunghezza massima
del buffer APDU;
-
wait(
): questo metodo permette di segnalare al CAD di non eseguire il timeout
per l’operazione corrente. Queste serve ad evitare che operazioni temporalmente
lunghe portino il CAD al timeout.
Conclusioni
Nella
prima e seconda parte di questo articolo sono state descritte le caratteristiche
peculiari dell’ambiente JavaCard, mantenendo il punto di vista dello sviluppatore
di applicazioni Java per workstation. Questo, secondo il mio parere, è
un ottimo modo per apprendere le caratteristiche avanzate dell’ambiente
JavaCard. Come si è potuto notare, l’ambiente JavaCard fornisce
una quantità di funzionalità estremamente potenti e flessibili,
quali ad esempio la persistenza e la gestione delle transazioni.
Purtroppo,
sviluppando applicazioni reali, ci si rende immediatamente conto che esistono
limiti non imputabili all’ambiente stesso, dovuti principalmente alla ridotta
quantità di risorse disponibili. Se dall’analisi svolta finora appare,
infatti, chiaro che non esistono limiti alla programmazione orientata agli
oggetti, in pratica, dovendosi confrontare con pochi KByte di memoria non
volatile e qualche centinaio di Byte di memoria volatile disponibili, ci
si rende immediatamente conto dell’impossibilità di applicare completamente
il paradigma della programmazione orientata agli oggetti. La strutturazione
ad oggetti in ambiente Java richiede, infatti, un overhead di risorse che
spesso il programmatore JavaCard non può permettersi, pena l’impossibilità
di eseguire le applicazioni sviluppate a causa della mancata disponibilità
di memoria. E’ per questo motivo, e non per le caratteristiche intrinseche
dell’ambiente JavaCard, che spesso le JavaCard Applet vengono realizzate
tramite un’unica classe, sfruttando in pratica solamente i principi della
programmazione strutturata e non quelli della programmazione orientata
agli oggetti. Ribadisco comunque che questo dipende dalle risorse disponibili
e non dall’ambiente JavaCard: avendo a disposizione risorse sufficienti,
qualunque vincolo in questo senso tende a scomparire, il che fa ben sperare
per le generazioni di JavaCard che verranno in futuro.
Tenuto
conto anche di quest’ultimo fattore, è possibile elencare alcune
ulteriori regole che il progettista e lo sviluppatore JavaCard devono conoscere
ed applicare per realizzare JavaCard Applet funzionanti:
-
Viste
le ridotte disponibilità di risorse di memorizzazione e la non garanzia
di presenza di un garbage collector, è consigliabile non creare
nuovi oggetti secondo necessità ma creare ogni oggetto che si prevede
di utilizzare in modo definitivo, mantenendo un riferimento ad esso. In
questo modo è possibile utilizzare l’oggetto tante volte quanto
serve. E’ anche consigliato l’utilizzo di classi static nei casi in cui
queste possano fornire le funzionalità richieste;
-
Se è
necessario l’utilizzo di codice che crea nuovi oggetti dinamicamente, è
buona norma il contenere tutte le istruzioni di creazione all’interno di
una transazione, perché una volta creato un oggetto non si può
liberare la memoria che gli si è allocata (una eventuale interruzione
delle operazioni durante la creazione di nuovi oggetti, ripresa in un secondo
tempo, porterebbe a sprecare la memoria utilizzata dagli oggetti creati
prima dell’interruzione);
-
Vista
la ridotta disponibilità di memoria dedicabile al salvataggio del
bytecode sulla JavaCard, si consiglia di ridurre al minimo il numero di
accessi agli array, salvando in variabili locali i valori che si suppone
dovranno essere utilizzati più volte. Questo permette di ottenere
bytecode notevolmente più compatto, quindi di risparmiare memoria.
Bibliografia
Per
approfondire i concetti trattati in questo articolo si consiglia la lettura
dei seguenti documenti, tutti disponibili nel sito web di SUN dedicato
a Java (www.javasoft.com):
[Snm97a]:
SUN Microsystems, “Java Card 2.0 Language Subset and Virtual Machine Specification
– Revision 1.0 Final”, 1997, www.javasoft.com
[Snm97b]:
SUN MicroSystems, “Java Card 2.0 Programming Concepts – Revision 1.0 Final”,
1997, www.javasoft.com
[Snm97c]:
SUN MicroSystems, “Java Card 2.0 Application Programming Interfaces – Revision
1.0 Final”, 1997, www.javasoft.com
[Snm98]:
SUN MicroSystems, “Java Card Applet Developer’s Guide – Revision 1.12”,
1998, www.javasoft.com |