370 likes | 605 Views
Emilio Petrangeli, Mauro Pagano. Elementi di Crittografia - Package JCE. Prof.ssa Rota, Merola. Sommario. Introduzione Crittografia Cenni di Crittografia Introduzione JCE JCE Principali classi JCE Hash coding JCE in pratica Esempio con Applicazione. Introduzione.
E N D
Emilio Petrangeli, Mauro Pagano Elementi di Crittografia - Package JCE Prof.ssa Rota, Merola
Sommario • Introduzione Crittografia • Cenni di Crittografia • Introduzione JCE • JCE • Principali classi JCE • Hash coding • JCE in pratica • Esempio con Applicazione
Introduzione Con l`avvento di internet e delle reti di comunicazione in genere, sono sorte una serie di problematiche legate alla sicurezza: riservatezza e certificazione. Riservatezza: l’obiettivo, durante la fase di trasferimento dati da un punto ad un altro, e` rendere possibile la comprensione unicamente a mittente e destinatario, verificando eventuali intercettazioni. La criptazione serve proprio a rendere i dati incomprensibili all’esterno! • Certificazione: risolve il problema dell’identificazione CERTA del mittente.
Introduzione Questa tesina illustra l’implementazione in java di tecniche per la risoluzione di tali problematiche, senza, tuttavia, prendere in considerazione tutti gli aspetti legati alla sicurezza di sistema- argomenti relativi all’amministrazione di rete- piuttosto concentrandosi sulla programmazione.
Cenni Crittografia Crittografia: processo che trasforma, per mezzo di sofisticati algoritmi, una sequenza di byte con senso logico (messaggio) in un’altra del tutto incomprensibile. La trasformazione avviene attraverso una chiave: solo chi la possiede potrà criptare e decriptare il messaggio.
Cenni Crittografia Gli algoritmi più semplici sono quelli a chiave simmetrica: con una sola chiave permettono di criptare e decriptare il messagio
Cenni Crittografia Gli algoritmi a chiave asimmetrica, più sicuri, ma computazionalmente più pesanti, generano codici più ingombranti. Per questo motivo spesso, quando si necessita di leggerezza e velocità, si preferisce utilizzare una tecnica mista: il mittente critta il messaggio con una chiave simmetrica, procede a crittare la stessa chiave simmetrica con la chiave asimmetrica pubblica del destinatario, ed invia poi messaggio e chiave entrambi crittati. Il ricevente, con la sua chiave privata, decritterà prima la chiave simmetrica e poi con tale chiave il messaggio stesso Attualmente questo meccanismo risulta essere piuttosto sicuro. Benché nessuno ne abbia dimostrato l’impossibilità, ad oggi non risultano esserci state violazioni degne di nota.
Introduzione JCE Dopo una rapida panoramica sulla crittografia, vediamo come sia possibile implementare tali tecniche in Java.
API di Sicurezza • Le API di sicurezza, sono organizzate all’interno del package (e relativi subpackage) java.security. • Attraverso esse lo sviluppatore ha la possibilità di inserire funzionalità sia di alto che di basso livello. • Tale package è organizzato secondo un’architettura provider based, ciò permette di avere più implementazioni che operano tra loro.e
Java Criptlogy Extension(JCE) • La Java Criptlogy Extension (JCE): • Fornisce un framework per il crittaggio, il decrittaggio e la generazione di chiavi, permettendo lo scambio di chiavi. • Supporta cifrari simmetrici (DES, RC2, IDEA), asimmentrici (RSA), di flusso (RC4) e algoritmi MAC (Message Authentication Code). La progettazione di JCE ruota intorno a due fondamentali principi: • Indipendenza dagli algoritmi ed estensibilità • Interoperabilità e indipendenza dall'implementazione
Java Criptlogy Extension(JCE) • Indipendenza dagli algoritmi:èottenuta definendo una serie di cryptographics engines e una serie di classi che forniscono le funzionalità di tali motori. • Indipendenza dall’implementazione: si ottiene come conseguenza dell’utilizzo di un’architettura provider-based. • Interoperabilità delle implementazioni: permette ad un provider di utilizzare gli oggetti creati da un altro, rendendo possibile l’utilizzo di una chiave, creata con un provider specifico, con tutti gli altri installati o la verifica della chiave generata da un provider su un altro.
Java Criptlogy Extension(JCE) Note: • Non tutti i provider forniscono i medesimi servizi; un unico provider potrebbe non offrire tutte le funzioni di cui si necessita. • La fase d’installazione di nuovi provider può avvernire sia staticamente che dinamicamente. • La molteplicità di provider installati permette la definizione di un ordine di preferenza secondo il quale interrogare i provider per la ricerca del servizio necessario.
Engine class • Cpytographic service: è in grado di fornire una funzionalità, genera o distribuisce chiavi e parametri necessari per una determinata transazione o crea oggetti che incapsulano le chiavi in modo sicuro. Esempio: In JDK2, è presente un engine class per il calcolo del digest di un certo messaggio, per la generazione di coppie di chiavi, per la firma digitale e creazione del keystore. • Per ogni engine class, esite una serie di sottoclassi che rappresentano il servizio implementato secondo un determinato algoritmo.
Principali Classi: Il Cifrario • E’ la classe più importante del framework; • Fornisce l’oggetto che implementa un cifrario, utilizzato per crittare e decrittare; • E` acceduta come singleton attraverso il metodo getInstance che restituisce un'istanza del cifrario; • Al momento dell'invocazione del metodo, è necessario specificare la tipologia di algoritmo che si vuole fare implementare al cifrario.
Principali Classi: Il Cifrario Es: Cipher c1 = Cipher.getInstance("DES");
Principali Classi: Il Cifrario Una volta ottenuto il cifrario questo va inizializzato secondo il compito che dovra' svolgere, questi 4 modi sono: • ENCRYPT_MODE • DECRYPT_MODE • WRAP_MODE • UNWRAP_MODE
Principali Classi: Il Cifrario Una volta che disponiamo del cifrario inizializzato, possiamo crittare o decrittare dati invocando il metodo doFinal. • E' possibile crittare in un solo passo o in molti passi, utile a seconda di cosa va crittato. • Per il wrapping e unwrapping delle chiavi utilizziamo i metodi wrap e unwrap forniti sempre dall'oggetto cifrario.
Principali Classi: Flusso Cifrato In JCE viene introdotto il concetto di Flusso Cifrato • Flusso Cifrato: ottenuto combinando un InputStream (o OutputStream) con l'oggetto Cipher. • CipherInputStream o CipherOutputStream: altro non sono che delle classi Filter*putStream che crittano (o decrittano) cio' che passa attraverso questo filtro.
Principali Classi: KeyGenerator Il generatore di chiavi KeyGenerator • KeyGenerator: ottenuto anch'esso attraverso il metodo getInstance accompagnato dal tipo di algoritmo da utilizzare. • Il KeyGenerator anch'esso come il cifrario va inizializzato, questa inizializzazione puo' essere dipendente o no da un algoritmo. • Una volta inizializzato possiamo ottenere la chiave invocando il metodo generateKey().
Principali Classi: SealedObject La classe SealedObject • SealedObject: la classe che da la possibilita' di crittare gli oggetti. • Definendo un oggetto che implementa l'interfaccia Serializable e' poi possibile crittare la sua versione “serializzata”. • Dall'altra parte e' possibile decrittare l'oggetto , de-serializzarlo ed ottenere la versione originale.
Principali Classi: SealedObject Esempio: Cipher c = Cipher.getInstance("DES"); c.init(Cipher.ENCRYPT_MODE, sKey); SealedObject so = new SealedObject("This is a secret", c);
Principali Classi: Key Agreement • Key Agreement: allo stesso modo va richiesto attraverso il metoto getInstance (passando come parametro il tipo di algoritmo da utilizzare) e inizializzato. • Una volta che l'oggetto e' pronto possiamo eseguire le varie fasi usando in metodo doPhase a cui passiamo la chiave ed un valore booleano che indica se la fase che andra' eseguita sara' l'ultima o no. • Una volta terminato e' possibile generare la chiave usando il metodo generateSecret (algoritmo).
Hash coding I vari processi basati su crittografia fanno uso di una trasformazione tanto semplice quanto fondamentale, ovvero quella della codifica hash. Tale trasformazione opera su uno stream di dati (detti in chiaro) e restituisce un codice identificativo di tale stringa. Tale trasformazione infatti deve sottostare alle seguenti regole: • Una funzione hash è deterministica: lo stesso documento deve generare sempre lo stesso codice. • Un codice hash deve essere uniformemente distribuito su tutto il dominio disponibile. • Deve essere estremamente difficile il processo inverso decodifica: anche se non è possibile dimostrare l’impossibilità del processo inverso di decodifica, tale trasformazione deve essere il più difficile possibile.
Hash coding • La probabilità che due documenti producano lo stesso codice hash deve essere prossima a zero: anche in questo caso non si può avere la certezza matematica, e ci si deve affidare alla statistica. Questo è comunque è un requisito fondamentale . • Alta dipendenza dalle condizioni iniziali: piccole modifiche del messaggio devono portare a grosse differenze nel codice risultante. • Il codice non deve dare nessuna informazione sul messaggio originale: per garantire una maggiore sicurezza, il codice non deve contenere nessun riferimento diretto relativo al messaggio di origine.
Hash coding Produrre buoni algoritmi di hashing è sicuramente un compito che è bene lasciare agli esperti, affidandosi a quello che si trova già disponibile. Il JCE offre a tal proposito la classe java.secuirity.MessageDigest, una classe astratta che serve come riferimento ad un generico algoritmo di codifica hash. Le sotto-classi concrete (come ad esempio java.security.MessageDigestSPI) implementano un particolare algoritmo. Nota: Nella tabella “Java 1.1 ed algoritmi di Hash” , presente on-line, sono elencati gli algoritmi disponibili con il JCE, e le loro caratteristiche principali
Hash coding Vediamo come si puo ottenere il codice hasch data una qualsiasi origine di byte: • Ricavare un istanza per un particolare algoritmo • Leggere i dati • Passare i dati al metodo update • Invocare il metodo digest() per ottenere il codice
L’esempio mostra come ottenere un codice hash di una particolare pagina web (passando l’URL come parametro a linea di comando). L’algoritmo utilizzato è l’SHA-1 import java.net.*; import java.io.*; import java.security.*; import java.math.*; public class URLDigest { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { try { URL u = new URL(args[i]); printDigest(u.openStream()); } catch (MalformedURLException e) { System.err.println(args[i] + " is not a URL"); } catch (Exception e) { System.err.println(e); } } } CONTINUA..... Hash coding
public static void printDigest(InputStream in) throws IOException, NoSuchAlgorithmException { MessageDigest sha = MessageDigest.getInstance("SHA"); byte[] data = new byte[128]; while (true) { int bytesRead = in.read(data); if (bytesRead < 0) break; sha.update(data, 0, bytesRead); } byte[] result = sha.digest(); for (int i = 0; i < result.length; i++) { System.out.print(result[i] + " "); } System.out.println(); System.out.println(new BigInteger(result)); } } Hash coding
Dopo una panoramica sulla “Teoria” del package JCE vediamo ora come dotare una applicazione di un meccanismo di crittazione a chiave pubblica. JCE in Pratica
La prima cosa che dobbiamo fare è generare la coppia di chiavi necessarie avvalendoci delle classi Key, KeyPair, e KeyPairGenerator. KeyPairGenerator è un generatore di chiavi: tale classe deve essere istanziata con il nome di un particolare algoritmo (useremo in questo caso il DSA), e con un numero casuale. Tale numero deve essere effettivamente il più casuale possibile: spesso si utilizzano valori derivanti dalle pause che l’utente compie fra la pressione di un tasto e l’altro, oppure monitorando i movimenti del mouse. JCE in Pratica
MessageDigest md = MessageDigest.getInstance("SHA"); String string = url + user + (new Date()).toString(); md.update(string.getBytes()); KeyPairGenerator keygen = KeyPairGenerator.getInstance("DSA"); keygen.initialize(512, new SecureRandom(md.digest())); KeyPair pair = keygen.generateKeyPair(); ps.println(" Public-key: " + pair.getPublic().getEncoded()+ " Private-key: " + pair.getPrivate().getEncoded())); JCE in Pratica
L’utilizzo della classe SecureRandom offre maggiori sicurezze per quanto riguarda la reale casualità del numero generato. Ora che abbiamo generato la chiave, possiamo creare il motore di crittazione: Cipher myCipher = Cipher.newInstance("PKA"); myCipher.initEncrypt(key); Byte[] data; myCipher.cript(data); JCE in Pratica
A questo punto possiamo crittare i dati (procedimento effettuato a blocchi di byte per maggior sicurezza) semplicemente con la seguente istruzione CipherOutputStream cos = new CipherOutputStream(new FileOutputStream("name", myCipher)); La classe CipherOutputStream (e l’equivalente CipherInputStream) permettono di inviare dati direttamente in uno stream in forma crittata. Può essere associata ad uno stream di compressione, da un lato, e ad uno di scrittura su file, dall’altro. JCE in Pratica
Crittografia.java: Una semplice applicazione da console, che passatogli come parametro una stringa/frase genera una chiave, cripta questa stringa/frase con la chiave creata, poi la decripta. JCE in Pratica
Codice: public class Criptare{ public static void main(String[] args){ String stringa=new String(""); for (int i=0;i< args.length;i++){ //Leggo la stringa in input stringa+=args[i]; } System.out.println("La frase da Criptare/Decripta da criptare e' "+stringa); try { vai(stringa); //Chiamo il metodo per criptare la stringa } catch (Exception e) { System.err.println(e); } } CONTINUA...... JCE in Pratica
public static void vai(String stringa) throws IOException, NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IllegalBlockSizeException,BadPaddingException { KeyGenerator keygen = KeyGenerator.getInstance("DES"); //Creo il generatore di chiavi SecretKey key = keygen.generateKey(); //Creo la chiave System.out.println("La chiave generata e': " + new String( key.getEncoded())); Cipher cip = Cipher.getInstance("DES/ECB/PKCS5Padding"); //Creo il motore di criptazione con lachiave creata cip.init(Cipher.ENCRYPT_MODE, key); //Inizializzo il motore byte[] stringa_byte = stringa.getBytes(); //Trasformo la stringa in una sequenza di byte byte[] risultato = cip.doFinal(stringa_byte); //Cripto la sequenza di byte System.out.println("La frase criptata e': "+new String(risultato)); cip.init(Cipher.DECRYPT_MODE, key); //Inizializzo di nuovo il motore, ma ora per Descriptare byte[] decriptato = cip.doFinal(risultato); //Decripto la sequenza di Bytes System.out.println(new String(decriptato)); //Stampo la stringa derivata dalla decodificazaione dei bytes ottenuti prima } } JCE in Pratica