Con il completamento dell'iter legislativo inerente la firma digitale e le relative norme tecniche, si apre per la Pubblica Amministrazione e i cittadini una nuova era che dovrebbe consentire di fruire per via telematica di servizi in precedenza vincolati alla 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 contratto, con il riconoscimento della validità legale della firma digitale, potranno essere svolte davanti al computer.
Con lo sviluppo e la diffusione delle applicazioni di e-commerce su Internet, il problema della sicurezza della rete è stato enfatizzato, provocando un forte incremento degli investimenti delle aziende in questo settore.
La piattaforma Java 2 mette a disposizione, attraverso il framework Java Cryptography Architecture (JCA), facility per il supporto della firma digitale e dei servizi di crittografia più comuni. Attraverso la firma digitale è inoltre possibile estendere le funzionalità degli applet scaricati dalla rete, consentendo loro l'accesso alle risorse di una macchina client dopo averli autenticati come Signed Applet. In questo articolo ci limiteremo alle funzionalità messe a disposizione da JCA, dopo aver esaminato il paradigma sul quale si basa la firma digitale.
L'architettura di riferimento
Nella nostra architettura di riferimento abbiamo un messaggio o documento che deve viaggiare attraverso la rete tra due soggetti, mittente e destinatario. Il problema è garantire l'autenticità delle informazioni trasmesse e la loro integrità: dopo che il mittente ha firmato il messaggio, qualsiasi manomissione deve poter essere rilevata dal destinatario.
Per soddisfare tali esigenze è necessario che per ogni soggetto in grado di inviare messaggi siano generate due chiavi asimmetriche: una pubblica, attraverso la quale chi riceve effettuerà la verifica, e una privata, con la quale sarà firmato il messaggio. Le chiavi sono matematicamente correlate, ma dalla chiave pubblica non deve essere possibile risalire a quella privata.
La chiave pubblica sarà distribuita attraverso un certificato reso disponibile da un ente certificatore. Per la generazione della firma a partire dalla chiave privata e la verifica attraverso la chiave pubblica è necessario un algoritmo come RSA, imposto all'epoca dalla normativa italiana nella modalità a 1024 bit, o DSA, utilizzato dal governo degli Stati Uniti.
Solitamente questi algoritmi non vengono applicati direttamente al messaggio, ma a una stringa a lunghezza fissa, l'impronta o message digest, la cui unicità è garantita dall'algoritmo di hashing utilizzato. Questo evita di gravare gli algoritmi di generazione e verifica con costi computazionali non prevedibili, poiché un messaggio può avere qualsiasi lunghezza. Tra gli algoritmi di hashing più diffusi ricordiamo SHA, sviluppato dal NIST e utilizzato per produrre hash a 160 bit.
Sulla rete viaggeranno quindi il messaggio in chiaro, sul quale il destinatario riapplicherà l'algoritmo di hashing per intercettare manomissioni, e la firma del mittente, usata per la verifica insieme alla chiave pubblica eventualmente ottenuta da un certificato che ne garantisca valore e provenienza.
Java Cryptography Architecture
JCA è un framework estendibile e interoperabile attraverso il quale Java 2 rende disponibile un insieme completo di servizi di crittografia, comprese le API per la firma digitale. Uno dei concetti più interessanti è quello del provider: un fornitore di servizi crittografici resi disponibili e manipolabili attraverso il framework. Per aggiungere un provider è necessario estendere la classe java.security.Provider.
Per gli esempi ci serviremo del provider di default di Sun. Le funzionalità alle quali siamo interessati sono:
- generazione dell'impronta del messaggio, o message digest;
- generazione di una coppia di chiavi asimmetriche;
- generazione e verifica della firma digitale.
Generazione del message digest
MessageDigest sha = MessageDigest.getInstance("SHA-1");
sha.update(par1);
sha.update(par2);
byte[] hash = sha.digest(messaggio);
Il codice necessario a ottenere l'impronta del messaggio è semplice: si seleziona l'algoritmo di hashing attraverso il metodo statico getInstance() della classe MessageDigest, si specificano i parametri con update() e si ottengono i 160 bit del message digest con 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 si usa KeyPairGenerator. Anche in questo caso, attraverso getInstance() viene specificato l'algoritmo, DSA, e con initialize() il numero di bit 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 generare la firma digitale, un array di byte, dopo aver selezionato l'algoritmo e ottenuto la chiave privata dalla coppia si utilizzano i metodi initSign() e update() della classe Signature per impostare la chiave privata e il messaggio, quindi sign() per creare la firma.
PublicKey pub = pair.getPublic();
dsa.initVerify(pub);
dsa.update(hash);
boolean verifies = dsa.verify(sig);
System.out.println("signature verifies=" + verifies);
La verifica è effettuata attraverso verify(), al quale viene passata la firma digitale. Prima è necessario specificare la chiave pubblica e il messaggio rispettivamente con initVerify() e update(). È inoltre possibile prelevare la chiave pubblica da un certificato, magari importato nel key store database, utilizzando la classe X509Certificate.
Conclusioni
JCA mette a disposizione un set di servizi di crittografia completo, flessibile, facile da usare e ulteriormente estendibile con la Java Cryptography Extension (JCE). Ciascun servizio può essere usato in combinazione con gli altri per ottenere il livello di sicurezza desiderato e implementare l'architettura più adatta alle proprie esigenze.
Ad esempio, in un modello nel quale non si vuole garantire l'integrità delle informazioni ma si vuole che mittente e destinatario si scambino messaggi cifrati, si possono utilizzare la chiave pubblica e privata in modo inverso rispetto alla firma digitale: cifrare i messaggi con la chiave pubblica e decifrarli con quella privata, scambiando così di ruolo anche mittente e destinatario.
Il package di riferimento per approfondire questi argomenti è java.security.
