Primo step: garantire l'INTEGRITA' di una informazione

Osservazione chiave:
non è necessario rendere illeggibile tale file (anzi...), l'importante è che sia inalterabile.

Conseguenza:
serve una tecnologia non per cifrare il file, ma per
marcarlo in modo indelebile cosicché ogni tentativo di manomissione venga subito intercettato.

Risposta:
sfruttare il concetto di
digest (impronta digitale) di un messaggio, che è fatto apposta per garantire l'integrità di un messaggio.

Il digest è un messaggio di lunghezza fissa e corta (tipicamente 20 byte) che viene generato con opportuni algoritmi hash a partire dal messaggio originale.
Ogni modifica anche minima del messaggio originale comporta un digest profondamente diverso.

Per la verifica, basta "allegare" al file questa impronta: l'applicazione non dovrà fare altro che rileggere il file, ricalcolare il digest, e verificarlo confrontandolo con quello "originale".

Schemi di codice Java

L' Esempio0 mostra come si genera il digest di un messaggio. Usa solo classi Java standard, quindi non richiede il montaggio di estensioni o plugin particolari. Richiede Java 1.1.x.

L'idea è di avere disponibile il messaggio (nello schema è cablato nel codice, nella realtà sarà letto da file) sotto forma di array di byte: ciò è sufficiente per generare il digest, che qui viene mostrato a video (nella realtà sarà salvato su file).

Si pone poi il problema di verificare tale impronta.

L' Esempio1 mostra come si effettua la verifica: anche questa fase usa solo classi Java 1.1.x standard, quindi non richiede il montaggio di estensioni o plugin particolari.

L'idea è di acquisire in qualche modo il digest "originale" (nello schema è letto da tastiera, nella realtà sarà letto da file o da rete), poi rileggere il file e ricalcolare il digest (replicando esattamente lo schema 0), e quindi procedere a una verifica dei due digest (tramite MessageDigest.isEqual())

Commento

Questo approccio è molto semplice da realizzare, ma è soddisfacente solo per dati inalterabili (e non segreti, visto che il file in sé non viene cifrato).

Non va bene, quindi, per memorizzare su file il famoso contatore.


Secondo step: garantire l'INACCESSIBILITA' a una informazione

E' necessario cifrare il file che contiene l'informazione.

Risposta:
sfruttare il concetto di cifratura a chiave singola di un messaggio.

Poiché l'applicazione deve sia decifrare sia ri-cifrare il messaggio, è opportuno un algoritmo a chiave simmetrica (chiave singola), che in questa ipotesi è cablata dentro l'applicazione.

Schemi di codice Java

Attenzione: questo codice richiede una implementazione della Java Cryptography Extension (JCE).

Poiché la JCE della Sun non è esportabile dagli USA, occorre scaricare e installare la implementazione internazionale IAIK-JCE (gratuita per uso accademico).

Passi fondamentali

NB: queste attività verranno mostrate nel seguito, per reagioni di chiarezza e modularità, in tre programmi separati. Ognuno di questi, perciò, dovrà preventivamente istanziare il provider IAIK. Ovviamente, in una applicazione reale che svolga più attività il provider sarà istanziato una sola volta, all'inizio.

Generazione della chiave

Per generare una chiave occorre, dopo aver istanziato il provider IAIK:

La chiave è un'istanza dell'interfaccia Key (più precisamente, nel nostro caso, una istanza della classe iaik.security.cipher.SecretKey): una sua rappresentazione come array di byte è ottenibile col metodo getEncoded().

La struttura di fondo (gestione delle eccezioni a parte) è quindi la seguente:

IAIK provider = new IAIK();
Security.addProvider(provider);

KeyGenerator gen = KeyGenerator.getInstance("DES","IAIK");
gen.init(new SecureRandom());

Key k = gen.generateKey();

NB: la generazione della chiave è un'operazione computazionalmente pesante, che può richiedere parecchi secondi.

Cifratura di un messaggio

Per cifrare un messaggio occorre, dopo aver istanziato il provider IAIK ed essersi procurati una chiave adatta all'algoritmo prescelto:

Sia il messaggio in chiaro sia il messaggio cifrato hanno la forma di array di byte.

La struttura di fondo (gestione delle eccezioni a parte) è quindi la seguente:

IAIK provider = new IAIK();
Security.addProvider(provider);

// ... sia k la chiave, e msg il messaggio in chiaro
Cipher des = Cipher.getInstance("DES","IAIK");
des.init(Cipher.ENCRYPT_MODE, k);

byte [] xxx = des.doFinal(msg); // xxx è il messaggio cifrato

Decifratura di un messaggio

Per decifrare un messaggio si procede esattamente come per la cifratura, inizializzando però il Cipher in modo DECRYPT anziché ENCRYPT.

La struttura di fondo (gestione delle eccezioni a parte) è quindi la seguente:

IAIK provider = new IAIK();
Security.addProvider(provider);

// ... sia k la chiave, e xxx il messaggio cifrato
Cipher des = Cipher.getInstance("DES","IAIK");
des.init(Cipher.DECRYPT_MODE, k);

byte [] msg = des.doFinal(xxx); // msg è il messaggio decifrato

Gli esempi

Gli esempi seguenti mostrano passo-passo come si genera una chiave DES (Esempio2), come si cifra un messaggio con tale chiave (Esempio3), e come lo si decifra (Esempio4).

Tutti gli esempi richiedono Java 1.1.x o 1.2, piu' la IAIK-JCE sopra citata. L' EsempioGlobale riunisce tali esempi in un programma unico.

Importante: in questi esempi, il 90% del codice gestisce l'I/O interattivo da console.
In un'applicazione reale, in cui si leggono / scrivono array di byte direttamente su stream, la struttura sarà molto più semplice.