MokaByte Numero 34  - Ottobre  99
 La firma digitale
di 
Antonio Furone
In che modo Java 2 supporta la firma digitale


 
Con il completamento dell’iter legislativo inerente la firma digitale e relative norme tecniche (DPR n. 513 del 10 Novembre 1997 e successive circolari emanate dall’ AIPA), si apre per la Pubblica Amministrazione e i cittadini, una nuova era che dovrebbe portare a fruire per via telematica di una serie di servizi il cui espletamento era vincolato dalla presenza fisica dei soggetti interessati. Operazioni come la richiesta di un certificato all’anagrafe, la sottoscrizione di una polizza assicurativa o la stipula di un normale contratto tra due società, con il riconoscimento della validità della firma digitale ai fini legali (art. 5 del DPR 513/97), potranno essere svolte stando comodamente seduti in poltrona davanti al computer. 
Del resto con lo sviluppo e la diffusione di applicazione di e-commerce su Internet il problema della sicurezza della rete è stato in questo ultimo periodo enfatizzato tanto da provocare un forte incremento degli investimenti delle aziende in questo settore per il quale è prevista una forte crescita negli anni a venire. 
La piattaforma Java (e mi riferisco ovviamente al JDK 1.2 o Java 2) mette a disposizione, attraverso il framework denominato Java Cryptography Architecture (JCA) , una serie di facility per il supporto della firma digitale e dei servizi di crittografia più comunemente utilizzati.  Inoltre, attraverso la firma digitale è possibile estendere le funzionalità di Applet scaricati dalla rete, consentendogli l’accesso alle risorse di una macchina client dopo averli autenticati per l’appunto come “Signed Applet”. Rimandando, per motivi di tempo e spazio, questo argomento ad una prossima occasione, in questo articolo ci limiteremo a fare una carrellata su quelle che sono le funzionalità messe a disposizione dalla JCA non prima di aver esaminato il paradigma sul quale si basa il funzionamento  della firma digitale. 
 
 
 

L’architettura di riferimento

Nella nostra architettura di riferimento (Figura 1), abbiamo un messaggio e/o documento che deve viaggiare attraverso la rete tra due soggetti (Mittente e Destinatario). Il problema è quello di garantire l’autenticità delle informazioni trasmesse e la loro integrità : dopo che il mittente ha firmato il messaggio, una qualsiasi manomissione del messaggio deve poter essere rilevata dal destinatario. Per soddisfare tale esigenze, è necessario che per ogni soggetto in grado di inviare messaggi, siano generate due chiavi asimmetriche : una pubblica, attraverso la quale coloro che riceveranno i messaggi effettueranno la verifica, e una privata con la quale sarà firmato il messaggio. Queste chiavi saranno in relazione matematica tra loro, ma in nessun caso sarà possibile risalire a partire dalla chiave pubblica a quella privata. 
 
 


Figura 1: firma e verifica di un messaggio

La chiave pubblica sarà distribuita attraverso un certificato reso disponibile da un ente certificatore ( in Italia, le modalità di iscrizione all’albo degli enti certificatori sono prescritte in una circolare dell’AIPA in cui è previsto che, in base al DPR 513/97, si dovrà trattare di società per azioni, SpA, con un capitale sociale di almeno 12,5 miliardi di lire).
A questo punto, per la generazione della firma a partire dalla chiave privata e la verifica della stessa attraverso la chiave pubblica, sarà necessario dotarsi di un algoritmo come ad esempio l’RSA, che tra l’altro in Italia è imposto dalla normativa nella modalità a 1024 bits, o il DSA utilizzato dal governo degli Stati Uniti.
Solitamente, questi algoritmi non vengono applicati direttamente sul messaggio ma su una stringa a lunghezza fissa (impronta o message digest) ricavata da quest’ultimo, la cui univocità è garantita dall’algoritmo di Hashing utilizzato per ottenerla. Questo passaggio, è effettuato per non gravare di costi computazionali non prevedibili (un messaggio può avere una lunghezza qualsiasi) l’esecuzione degli algoritmi di generazione e verifica. Tra gli algoritmi di Hashing più diffusi ricordiamo l’SHA (Secure Hash Algorithm), sviluppato dal NIST (National Istitute of Standards and Technology), e utilizzato dal governo degli Stati Uniti per produrre stringhe Hash a 160 bit.
In definitiva, sulla rete viaggeranno esclusivamente il messaggio in chiaro, sul quale il destinatario ri-applicherà l’algoritmo di Hashing per intercettare eventuali manomissioni, e la firma del mittente che sarà utilizzata per la verifica insieme alla chiave pubblica eventualmente ottenuta da un certificato che ne garantisca il valore e la provenienza.
 
 
 

Java Cryptography Architecture

Come abbiamo già detto, JCA è un framework estendibile ed interoperabile attraverso il quale in Java 2 viene reso disponibile un insieme completo di servizi di crittografia tra cui anche le API per il supporto della firma digitale. 
In JCA uno dei concetti più interessanti è quello del Provider : un vendor di servizi crittografici resi disponibili e manipolabili attraverso il framework stesso. Per aggiungere un Provider  al framework è necessario estendere la classe java.security.Provider. 
Ma ora concentriamoci sul codice da scrivere (per la verità non molto complesso ed anche intuitivo) che ci permette di realizzare i servizi principali dell’architettura precedentemente descritta. Per fare questo, ci serviremo del provider di default (quello della Sun) che il JCA ci mette a disposizione.
Le funzionalità a cui siamo interessati per realizzare la nostra architettura sono i seguenti :
 
  • Generazione dell’impronta del messaggio (message digest) ;
  • Generazione coppia di chiavi asimmetriche ;
  • Generazione e verifica della firma digitale.

 

Generazione del message digest

MessageDigest sha=MessgeDigest.getInstance(“SHA-1”); 
sha.update(par1) ; 
sha.update(par2) ; 
byte[] hash=sha.digest(messaggio) ;


Come si può notare il codice da scrivere per ottenere l’impronta del messaggio è molto semplice, si tratta di selezionare l’algoritmo di Hashing che si vuole applicare attraverso il metodo statico getInstance della classe MessageDigest (in questo caso l’SHA del provider di default non avendo specificato quest’ultimo tra i parametri), di specificare i due parametri richiesti dall’algoritmo attraverso il metodo update e di ottenere i 160 bits che rappresentano il message digest  con il metodo digest che riceve in input il messaggio.
Generazione della coppia di chiavi asimmetriche
 

KeyPairGenerator keyGen=KeyPairGenerator.getInstance(“DSA”) ; 
KeyGen.initialize(1024,new SecureRandom(seed)) ; 
KeyPair pair=KeyGen.generateKeyPair() ;


Per generare la coppia di chiavi, la classe utilizzata è la KeyPairGenerator. Anche in questo caso, attraverso il metodo getInstance viene specificato l’algoritmo da utilizzare (DSA) e con il metodo inizialize viene specificato il numero di bit (1024) da utilizzare nella generazione.

Generazione e verifica della firma digitale
 

Signature dsa=Signature.getInstance(“DSA”) ; 
PrivateKey priv=pair.getPrivate() ; 
dsa.initSign(priv) ; 
dsa.update(hash) ; 
byte[] sig=dsa.sign() ;


Per gerare la firma digitale (un’array di bytes), dopo aver selezionato l’algoritmo da applicare e ottenuto la chiave privata dalla coppia di chiavi precedentemente generata, si utilizzano i metodi initSign e update della classe Signature (classe derivata della SignatureSpi per ragioni di compatibilità con le versioni precedenti) per impostare la chiave privata e il messaggio con cui generare la firma digitale attraverso il metodo sign. 
 

PublicKey pub=pair.getPublic() ; 
dsa.initVerify(pub) ; 
dsa.update(hash) ; 
boolean verifies=dsa.verify(sig) ; 
System.out.println(“signature verifies=” +verifies) ;


La verifica del messaggio è effettuata attraverso il metodo verify della classe Signature al quale viene passato come parametro la firma digitale. Precedentemente è necessario specificare la chiave pubblica da utilizzare e il messaggio sul quale effettuare la verifica rispettivamente con i metodi initVerify e update.
Inoltre, è possibile prelevare la chiave pubblica da un certificato, magari importato nel key store database (affronteremo questo argomento quando parleremo dei Signed Applet), utilizzando la classe X509Certificate. 
 
 
 

Conclusioni

Come abbiamo visto JCA mette a disposizione un set di servizi di crittografia completo, flessibile, facile da usare ed ulteriormente estendibile con il download e l’installazione della Java Cryptography Extension (JCE). Ciascun servizio può essere usato in combinazione con ciascun altro per ottenere il livello di sicurezza desiderato, implementando così l’architettura di security che più si adatta alle proprie esigenze. Ad esempio, in un modello in cui si non vuole garantire l’integrità delle informazioni che viaggiano attraverso la rete, ma si vuole che mittente e destinatario si scambino messaggi criptati, si può utilizzare la chiave pubblica e privata in modo inverso a come erano utilizzate combinate con la firma digitale : criptare i messaggi con la chiave pubblica e decriptarli con quella privata, scambiando così di ruolo anche mittente e destinatario.
Il package di riferimento per chi volesse approfondire tali argomenti è il java.security.
 
Antonio Furone è laureato in Informatica. Attualmente si occupa della progettazione, dello sviluppo e dell’integrazione di applicazioni internet/intranet, distribuite e client/server. E’ uno specialista nella progettazione e nella programmazione object oriented ed event driven.

  
 

MokaByte rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it