Un sistema di autenticazione basato su impronta digitale e smart card

Sostituire il PIN della smart card con l’impronta digitale nelle procedure di Strong Autenthication e Firma Digitale

Il 2002 è stato definito l’anno della “sicurezza informatica”. Molte aziende, sia pubbliche che private, hanno infatti investito nell’evoluzione dei propri sistemi di sicurezza informatica passando, nella maggioranza dei casi, da meccanismi di autenticazione basati su PIN, password e token, a sistemi basati su strong authentication mediante firma digitale su smart card. In alcune realtà tecnologicamente più avanzate, poi, sono stati adottati sistemi di autenticazione basati su tecnologie biometriche.

I sistemi di autenticazione basati sul riconoscimento dell’impronta digitale, in particolare, rappresentano la naturale evoluzione dei tradizionali meccanismi di autenticazione basati su PIN e password, sia per le caratteristiche intrinseche dei sistemi stessi (descritte nella prima parte di quest’articolo, pubblicata sullo scorso numero di CP), sia per la semplicità d’uso dei dispositivi di acquisizione dell’impronta digitale dell’ultima generazione.

In quest’articolo si analizzeranno brevemente le principali peculiarità dell’impronta digitale, si presenterà uno speciale tipo di smart card dotato di funzionalità di verifica dell’impronta digitale e si mostrerà un esempio di implementazione delle procedure di enrollment e verifica da integrare negli usuali protocolli di Strong Authentication

 

L’impronta digitale

L’impronta digitale è universalmente riconosciuta ed accettata, da più di un secolo, come caratteristica fisiologica unica per ogni individuo. Largamente usata dalle forze dell’ordine per individuare i criminali e come prova indiziaria nei processi giudiziari, l’impronta digitale soddisfa entrambi i prerequisiti su cui si basano i sistemi di riconoscimento biometrico [1]: persistenza e individualità. Se non si considerano eventuali traumi o deformazioni a lungo termine dovute all’invecchiamento, la persistenza è ineluttabile. Tuttavia l’individualità, benché sia universalmente riconosciuta, è stata, per lungo tempo (ed è tuttora) oggetto di studi.

L’esame dell’impronta digitale si attua mediante l’analisi della configurazione delle piccole rughe presenti nel polpastrello (Figura 1).

Figura 1

 

Si individuano due fasi di studio. Una fase preliminare in cui l’impronta viene classificata, sulla base delle sue caratteristiche morfologiche, in una delle seguenti categorie: spirale destra, spirale sinistra, vortice, arco, arco acuto, doppia spirale ecc. [4], [5]; una seconda fase in cui vengono analizzate le rughe che compongono l’impronta. Queste ultime sono considerate come composte da due tratti fondamentali, detti terminazione e biforcazione, che insieme formano la minutiae. L’esame delle minuzie e la loro collocazione all’interno dell’intera figura dell’impronta, consente di stabilire la corrispondenza tra due impronte digitali.

Il primo studio completo sull’univocità dell’impronta digitale si deve a Galton nel 1892 [2] Questi considerò una regione quadrata del polpastrello comprendente sei rughe per lato. Si accorse poi che l’intero polpastrello poteva essere ricoperto con ventiquattro regioni indipendenti tra loro. Con queste premesse, Galton stimò che è possibile ricostruire correttamente ciascuna delle ventiquattro regioni con probabilità 1/2 guardando la conformazione delle regioni circostanti. Da ciò ricavò che la probabilità di ricostruire correttamente un impronta a partire dalle regioni circostanti è pari a (1/2)24. Galton considerò poi la probabilità di classificare l’impronta in una delle sedici categorie da lui individuate, basandosi sulla morfologia del polpastrello, che stimò pari a 1/16. Infine considerò la probabilità di trovare il corretto numero di rughe entranti ed uscenti da ciascuno dei ventiquattro quadrati che stimò pari a 1/256. Da ciò ricavò che la probabilità di trovare una particolare configurazione dell’impronta corrisponde a:

P(Fingerprint) = (1/2)24 * 1/256 * 1/16 = 1.45 * 10-11

Successivi studi, compiuti nell’arco del ventesimo secolo e basati su medesimi ragionamenti, alla luce di nuove interpretazioni date ai vari tratti dell’impronta, hanno modificato sensibilmente tale misura riducendone ulteriormente il valore. Osterburg (1980) ad esempio stimò tale probabilità pari è a 1.33 * 10-27, mentre Stoney (1985) arrivò fino al valore 1.2 * 10-80 (per una trattazione completa di tale tematica si rimanda a [2]).

I modelli appena discussi erano focalizzati sulla misurazione del numero di possibili configurazioni dei diversi tratti di un’impronta e, pertanto, proponevano una stima della probabilità di trovare un’impronta con una particolare configurazione. In un sistema di riconoscimento automatico, tuttavia, occorre concentrarsi principalmente sulle differenze tra diverse immagini della stessa impronta (dovute, ad esempio, ad una diversa postura del dito sulla finestra di acquisizione o ad errori dello strumento di acquisizione). Tali differenze portano inevitabilmente alla definizione di una probabilità di corrispondenza tra due impronte. Trauring (1963) [3] fu il primo a concentrarsi esplicitamente sulla quantità minima di dettagli che occorre per stabilire la corrispondenza tra due impronte provenienti dallo stesso individuo in un sistema di riconoscimento automatico. Egli osservò che la distanza minima per individuare due minuzie corrispondenti è pari a circa 1.5 volte la distanza media tra le rughe. Quindi, applicò la sua osservazione ad un database di circa 50000 impronte digitali (acquisite con l’inchiostro) e ricavò che la probabilità del verificarsi di una falsa associazione, usando il modello delle minuzie, è dell’ordine di 10-97.

 

Determinare la corrispondenza fra due impronte

Nei sistemi di riconoscimento automatico, la corrispondenza tra due impronte digitali è espressa in termini di coefficiente di similarità (che corrisponde alla probabilità che le due impronte appartengano allo stesso individuo).

Per determinare tale valore si definiscono una metrica di similarità e una soglia (threshold): la prima stabilisce le regole mediante le quali calcolare il coefficiente di similarità (tipicamente tale valore è proporzionale al numero di minuzie corrispondenti nelle due impronte), la seconda indica il valore minimo al di sopra del quale due impronte sono considerate “simili”. L’individuazione delle minuzie corrispondenti è attuata applicando in sequenza algoritmi di ottimizzazione, rotazione e traslazione dell’immagine, di estrazione delle minuzie e di pattern-matching (per una descrizione approfondita di tali algoritmi si veda [10]). Il diagramma di Figura 2 mostra il data-flow tipico di un sistema di riconoscimento dell’impronta digitale: un sensore CMOS acquisisce l’immagine dell’impronta che viene prima elaborata e ottimizzata da una funzione di pre-processing e poi sottoposta ad un algoritmo di estrazione delle minuzie. Il matcher confronta l’impronta acquisita con una disponibile nell’archivio (che può essere un database, una smart card, ecc.) e restituisce un valore che corrisponde al coefficiente di similarità. Se tale valore è superiore alla soglia stabilita le due impronte sono considerate provenienti dallo stesso dito.

Figura 2

Poiché la corrispondenza tra due impronte è espressa in termini di probabilità, i sistemi di riconoscimento dell’impronta digitali talora presentano comportamenti errati: talvolta riconoscono erroneamente un impostore (false accept error) o rifiutano un individuo accreditato (false reject error). Tali errori, quantificati dai parametri FRR (False Rejection Rate) e FAR (False Acceptance Rate), dipendono tipicamente dalla sensibilità dello strumento di acquisizione, dalla bontà degli algoritmi di manipolazione e matching dell’impronta e dalla soglia di lavoro prescelta. La relazione tra FAR, FRR e soglia è espressa dalla Receiver Operating Curve (ROC), riportata in Figura 3. Più specificamente la ROC esprime la relazione tra FRR e FAR, per tutti i possibili valori della soglia. Più avanti si vedrà come tenere conto della ROC nella progettazione di un sistema di riconoscimento basato sull’impronta.

Figura 3 

HW e SW di acquisizione dell’impronta

L’acquisizione dell’impronta digitale avviene mediante scanner ottici o sensori capacitivi. I dispositivi più vecchi sono basati su tecnologia a riflessione interna e consentono di acquisire un’impronta molto precisa e chiara con un margine di errore molto piccolo; tali dispositivi tuttavia si rivelano troppo grandi, lenti e molto costosi. I più moderni invece sono basati su sensori capacitivi CMOS. Sono più veloci ed economici dei precedenti, hanno una finestra di acquisizione più piccola (di dimensioni minori di un francobollo), e pertanto possono essere usati in dispositivi mobili quali computer portatili, palmari, telefoni cellulari ecc. Hanno però una sensibilità minore e un errore intrinseco leggermente più alto.

Solitamente, il produttore del dispositivo fornisce, insieme ai driver del sistema operativo, un SDK comprensivo di API per lo sviluppo di applicazioni biometriche personalizzate. Tuttavia, tali API sono spesso proprietarie e pertanto non consentono l’interoperabilità tra diversi dispositivi provenienti da altrettanti produttori. Come descritto nella prima parte di quest’articolo, per ovviare a tale inconveniente si preferisce adottare una API “standard”, ad alto livello d’astrazione, che consenta di svincolare dal particolare strumento di acquisizione proponendo una modello comune per l’acquisizione e il trattamento delle impronte digitali. Molti produttori si stanno uniformando alle specifiche BioAPI, descritte nella prima parte di quest’articolo, che il BioAPI Consortium [7] sta proponendo come standard.

 

La smart card

La smart card è un dispositivo hardware delle dimensioni di una carta di credito che offre potenzialità di elaborazione e memorizzazione dati ad alta sicurezza [5]. È composta da due elementi fondamentali: un microchip, che fornisce potenzialità di calcolo e memorizzazione, e un’interfaccia di comunicazione, tipicamente una contattiera, che gli consente di scambiare informazioni con il mondo esterno.

La grande potenzialità della smart card sta nella capacità di memorizzare informazioni nel microchip salvaguardandone la riservatezza. Più specificamente, i dati inseriti nel microchip sono protetti in lettura/scrittura mediante un PIN (Personal Identification Number) di cui solo il titolare (ossia il possessore della smart card) è a conoscenza. Il microchip consente l'accesso alle informazioni protette solo dopo aver verificato che il PIN ricevuto corrisponde a quello corretto. Qualora si tentasse un attacco per violare la protezione imposta dal microchip ed impossessarsi delle informazioni riservate, il sistema di protezione reagirebbe, in primo luogo, bloccando il PIN e, in secondo luogo, cancellando permanentemente le informazioni contenute (autodistruzione). Per una trattazione approfondita del tema smart card si rimanda a [5].

 

La smart card nelle procedure di autenticazione

Per le sue peculiarità, la smart card è largamente usata nelle procedure di Strong Authentication [6] come dispositivo di memorizzazione sicura delle credenziali utente. Tipicamente, tali credenziali sono rappresentate dalla coppia username-password, o, nei sistemi più avanzati basati su firma digitale, dalla coppia chiave privata, certificato digitale [9]. L’accesso alle credenziali è subordinato alla verifica del PIN che il titolare della smart card è tenuto a digitare durante la procedura di autenticazione. In seguito alla verifica positiva del PIN da parte della smart card, le credenziali utente vengono usate per eseguire l’autenticazione. Nel primo caso la coppia username-password viene inviata al sistema che richiede l’autenticazione. Nel secondo caso, la chiave privata viene usata dal co-processore crittografico della smart card per apporre una firma digitale che viene poi inviata al sistema che richiede l’autenticazione.

Sebbene i sistemi di strong authentication basati su smart card siano estremamente sicuri e robusti, gran parte della sicurezza è basata sulla conoscenza del PIN. Qualora un individuo non autorizzato riuscisse ad impossessarsi di una smart card e del relativo PIN potrebbe accedere al sistema impersonando il reale titolare della smart card.

L’introduzione della tecnologia biometrica in sostituzione del PIN può ovviare a tale inconveniente. L’accesso alle credenziali utente sulla smart card non è più è subordinato alla verifica di un PIN ma alla verifica dell’informazione biometrica del titolare memorizzata sulla smart card. Tale soluzione ha due vantaggi: da un lato l’accesso alle credenziali utente sulla smart card è consentito solo in presenza del titolare (il possessore dell’informazione biometrica), dall’altro il titolare non è tenuto a ricordare alcun PIN.

Alcune smart card dell’ultima generazione supportano un’estensione della funzione di verifica del PIN che consente di eseguire sul microprocessore, in sostituzione dell’usuale algoritmo di verifica del PIN, l’algoritmo di matching specifico per l’impronta digitale. Per semplicità, nel seguito dell’articolo tali smart card saranno chiamate Biometric-SmartCard o per semplicità Bio-SmartCard.

 

Progettazione di un sistema di Strong Authentication con Bio-SmartCard

Nella progettazione di un sistema di autenticazione basato su Bio-SmartCard  sul riconoscimento dell’impronta digitale occorre considerare alcuni fattori che determinano la robustezza e la flessibilità del sistema. In primo luogo il dispositivo di acquisizione deve essere fornito di libreria compatibile con le specifiche BioAPI, in modo da rendere l’intero sistema indipendente dal particolare strumento (al punto che è possibile adottare dispositivi con caratteristiche diverse quali PCMCIA, USB o integrati nel mouse o nella tastiera). In secondo luogo, occorre stabilire la soglia per determinare il punto di lavoro nella ROC. Nelle applicazioni civili, ad esempio,  si tende a prescegliere il punto della ROC dove il FAR è uguale al FRR, detto EER (Equal Error Rate), in modo da mantenere bassi entrambi i valori.

Un sistema di autenticazione con Bio-SmartCard è composto da due sottosistemi che implementano le due fasi caratteristiche di un sistema di riconoscimento biometrico: Enrollment e Verifica.

Il sistema di Enrollment è composto da una speciale postazione di lavoro alla quale è connesso un dispositivo di acquisizione dell’impronta e un lettore di smart card. La procedura di enrollment coincide con la procedure di acquisizione e memorizzazione dell’impronta digitale ed è inclusa nella usuale procedura di personalizzazione della smart card. Più specificamente l’impronta viene acquisita mediante il dispositivo biometrico viene memorizzata sulla smart card insieme alle altre informazioni del titolare, tra cui le credenziali utente (username-password o certificato digitale). Al termine della procedura la smart card viene rilasciata al titolare pronta all’uso.

La procedura di verifica è implementata in un modulo software che viene installato sulle usuali postazioni di lavoro. Tale modulo entra in gioco durante la fase di autenticazione quando sono richieste le credenziali utente. In tal caso, la procedura avvisa l’utente di porre il dito sulla finestra di acquisizione del dispositivo per acquisire un campione dell’impronta e chiama la funzione di verifica della smart card passandogli la struttura dati che rappresenta il campione acquisito. La smart card, grazie al microprocessore, esegue la funzione di verifica confrontando il campione con il template memorizzato nella fase di enrollment e, in caso di esito positivo, concede i permessi di accesso alle credenziali utente.

 

Un esempio di implementazione

Il listato 1 mostra un esempio di implementazione della procedura di Enrollment (in  entrambi gli esempi presentati si suppone che la libreria BioAPI sia già stata inizializzata mediante la funzione mostrata nella prima parte di questo articolo nel listato 1).

Listato 1:

long Enroll()
{
	BioAPI_RETURN 		bioReturn;                                  
	BioAPI_BIR_HANDLE 	EnrolledTemplate;

	// Esegue l'enrollment con la funzione ad alto livello            
	bioReturn = BioAPI_Enroll(gModuleHandle,
	                          BioAPI_PURPOSE_ENROLL,
	                          NULL,
	                          &EnrolledTemplate,
	                          NULL,
	                          -1,
	                          NULL);
	
	if(bioReturn != BioAPI_OK)
		return bioReturn;
	
	// Memorizza il BIR acquisito nella smart card
	long nRet = SaveTemplateOnSmartCard(EnrolledTemplate);
	if(nRet != CKR_OK)
		return nRet;
	
	return OK;
}

 

La funzione BioAPI_Enroll, fornita da BioAPI, ritorna una handle al template acquisito. Tale template viene memorizzato sulla bio-smartcard mediante la funzione SaveTemplateOnSmartCard (listato 2). Quest’ultima estrae dall’handle le informazioni biometriche mediante le funzione BioAPI_GetBIRFromHandle e, mediante la funzione C_SetPIN, definita nelle specifiche PKCS#11, per scrivere il template sulla smart card come se fosse un PIN (in questo articolo non si scenderà nel dettaglio delle funzioni PKCS#11 poiché tale argomento esula dallo scopo di questo articolo; per un approfondimento si veda [5] e [8]). Anche in questo caso si suppone che la libreria PKCS#11 sia stata già inizializzata mediante la funzione C_Initialize e che la variabile ghSession contenga un handle ad una sessione aperta mediante la funzione C_OpenSession.

Listato 2:

long SaveTemplateOnSmartCard(BioAPI_BIR_HANDLE hTemplate)
{
	CK_RV rv;

	BioAPI_BIR_PTR birData = NULL;
	BioAPI_RETURN bioReturn;

	/* Recupera il BIR dall'handle 
	bioReturn = BioAPI_GetBIRFromHandle(gModuleHandle, hTemplate, &birData);						
	if(bioReturn != BioAPI_OK)
		return(bioReturn);
	
	// Calcola la lunghezza del template
	DWORD dwTemplateLen = birData->Header.Length;
			
	// Crea il buffer con il template	
	BYTE* pbtTemplateBuffer = new BYTE(dwTemplateLen)
	
	// Copia l'header nel buffer
	memcpy(pbtTemplateBuffer, birData->Header, sizeof (BioAPI_BIR_HEADER));
	
	// Copia biometric data nel buffer
	memcpy(pbtTemplateBuffer + sizeof (BioAPI_BIR_HEADER), birData->BiometricData, birData->Header.Length - sizeof (BioAPI_BIR_HEADER));
				
	// scrive il template nella smart card al posto del PIN	(PKCS#11)		
	rv = C_SetPIN(ghSession, DEFAULT_PIN, sizeof(DEFAULT_PIN), pbtTemplateBuffer, dwTemplateLen);
	if (rv != CKR_OK) 
		return rv;
	
	// libera la memoria	
	delete pbtTemplateBuffer;		
	GlobalFree(birData->BiometricData);	
	GlobalFree(birData);
	
	return CKR_OK;
}

Il listato 3 mostra invece un esempio di implementazione della procedura di lettura delle credenziali utente GetUserPassword. La funzione BioAPI_Capture consente di acquisire un’immagine live-scan dell’impronta. In base all’implementazione del BSP [1], l’immagine può essere intermediate o processed. Nel primo caso occorre chiamare la funzione BioAPI_Process per trasformare l’immagine da intermediate a processed.

Listato 3:

long GetUserPassword(char* szUserName, char* szPassword)
{
	BioAPI_RETURN  		bioReturn;                              	
	BioAPI_BIR_HEADER 	birHeader;		
	
	BioAPI_BIR_HANDLE CapturedTemplate, ProcessedBir;
	
	BioAPI_INPUT_BIR birCapture
		
	// Acquisisce l'impronta live-scan
	if((bioReturn = BioAPI_Capture(gModuleHandle,
	                               BioAPI_PURPOSE_VERIFY,
	                               &CapturedTemplate,
	                               -1,
	                               NULL)) != BioAPI_OK)
	{
	    return bioReturn;
	}
	
	// estrae l'header del BIR appena acquisito
	if((bioReturn = BioAPI_GetHeaderFromHandle(gModuleHandle,
	                                           CapturedTemplate,
	                                           &birHeader)) != BioAPI_OK)
	{
	    return bioReturn;
	}
	    
	// verifica il tipo di BIR acquisito        
	if(birHeader.Type == BioAPI_BIR_DATA_TYPE_INTERMEDIATE)
	{
		// se il BIR non è stato ancora processato chiama la funzione Process
	    birCapture.Form = BioAPI_BIR_HANDLE_INPUT;
	    birCapture.InputBIR.BIRinBSP = &CapturedTemplate;
	    if((bioReturn = BioAPI_Process(gModuleHandle,
	                                    &birCapture,
	                                    &ProcessedBir)) != BioAPI_OK)
	    {			
	        return bioReturn;
	    }	        	    
	}
	else
	{
		// il BIR acquisito è stato già processato           
		ProcessedBir = CapturedTemplate;		
	}
	
	// Chiama la procedura di verifica sulla smart card
	long nRet = VerifyFingerprintOnSmartCard(ProcessedBir);
	if(nRet != CKR_OK)				
		return nRet;		
	
	nRet = ReadUserPasswordOnSmartCard(szUserName, szPassword)
	if(nRet != CKR_OK)				
		return nRet;		
		
	return OK;
}		

La funzione VerifyFingerprintOnSmartCard (listato 4) esegue la verifica dell’impronta con il template memorizzato sulla smart card. Più specificamente, la verifica dell’impronta, eseguita direttamente sulla bio-smartcard, è attivata dalla funzione C_Login (anch’essa definita nelle specifiche PKCS#11), usata solitamente per inviare il PIN alla smart card. Si noti la costante CKU_USER_FINGEPRINT. Secondo le specifiche PKCS#11 esistono due tipi di PIN: PIN utente, identificato dalle costante CKU_USER e il PIN amministratore, o PUK, identificato dalle costante CKU_SO. Nell’esempio presentato, l’impronta digitale rappresenta un tipo speciale di PIN utente identificato dalla nuova costante CKU_USER_FINGEPRINT.

Listato 4:

long VerifyFingerprintOnSmartCard(BioAPI_BIR_HANDLE hBIR)
{
	CK_RV rv;

	BioAPI_BIR_PTR birData = NULL;
	BioAPI_RETURN bioReturn;

	/* Recupera il BIR dall'handle 
	bioReturn = BioAPI_GetBIRFromHandle(gModuleHandle, hBIR, &birData);						
	if(bioReturn != BioAPI_OK)
		return(bioReturn);
	
	// Calcola la lunghezza del campo BiometricData
	DWORD dwBiometricDataLen = birData->Header.Length - sizeof (BioAPI_BIR_HEADER);
			
	// Crea il buffer con il template	
	BYTE* pbtBiometricData = new BYTE[dwBiometricDataLen]
		
	// Copia il campo biometric data nel buffer
	memcpy(pbtBiometricData, birData->BiometricData, dwBiometricDataLen);
				
	// chiama la funzione di login con verifica del Fingerprint (PKCS#11)		  ..
	rv = C_Login(ghSession, CKU_USER_FINGERPRINT, pbtBiometricData, dwBiometricDataLen);
	if (rv != CKR_OK) 
		return rv;
	
	// libera la memoria	
	delete pbtBiometricData;		
	GlobalFree(birData->BiometricData);	
	GlobalFree(birData);
	
	return CKR_OK;
}

Se la verifica dell’impronta ha avuto esito positivo, e quindi la smart card concede l’accesso alla sua memoria, la funzione ReadUserPasswordOnSmartCard legge i campi username-password che saranno usati per completare la procedura di autenticazione.

 

Conclusioni

La tecnica di protezione delle credenziali utente, esposta in questa articolo, risolve brillantemente le backdoor e gli inconvenienti caratteristici delle usuali procedure di autenticazione basate su PIN e password. Tuttavia, le smart card dotate di funzionalità di verifica dell’impronta digitale sono ancora poco diffuse. Attualmente è in fase di test, e prossimo al rilascio, un nuovo sistema operativo con funzionalità biometriche per le smart card Siemens CardOS con chip Infineon (le stesse usate per la Carta d’Identità Elettronica e per il progetto di voto elettronico E-Poll) e sono state pubblicate, in versione beta, alcune applet Java Card che implementano gli algoritmi definiti da BioAPI [7]. Inoltre, l’attuale versione dello standard PKCS#11, sebbene supporti l’uso di un meccanismo di verifica alternativo al PIN, non si integra perfettamente in sistemi complessi ed eterogenei in cui agiscono molte applicazioni con dispositivi biometrici diversi. Gli sforzi futuri saranno quindi indirizzati a risolvere tali problematiche evolvendo la tecnologia attuale verso sistemi più flessibili basati su Biometric-SmartCard.

 

Bibliografia

[1] U. Chirico, “Riconoscimento biometrico e impronta digitale”, Numero precedente di CP

[2] S. Pakanti, S. Prabhakar, A.K. Jain, “On the individuality of Fingerprints”, http://biometrics.cse.msu.edu, 11/2002

[3] M.Trauring, “Automatic Comparition of Finger-ridge Patterns”, Nature, pp. 938-940, 1963

[4] A. Jain, S. Pakanti, “Automated Fingerprint Identification and Imaging”, http://biometrics.cse.msu.edu, 11/2002

[5] U. Chirico, “Dicesi Smart Card...”, Computer Programming n.90, Aprile 2000

 

Riferimenti

[6] U. Chirico, “La sicurezza nei portali web e nei siti di commercio elettronico”, Computer Programming n.100, Marzo 2001

[7] BioAPI Specification, Version 1.01, http://www.bioapi.org, 11/2002

[8] RSA Laboratories , “PKCS #11 v2.10: Cryptographic Token Interface Standard”, December 1999

[9] U. Chirico, “La Crittografia per la protezione delle informazioni sulla rete”, Mokabyte n.31, Giugno 1999, www.mokabyte.it/0699