|
Appunti informatica |
|
Visite: 2857 | Gradito: | [ Medio appunti ] |
Leggi anche appunti:Dalle Smart Card alle Java CardDalle Smart Card alle Java Card Il principale limite delle Smart Card era La firma digitaleLa firma digitale Un utilizzo molto diffuso della crittografia è per processi La politica di sicurezza dinamica mediante FirewallLa politica di sicurezza dinamica mediante Firewall La politica di sicurezza |
La cifratura è il concetto più semplice della crittografia, consiste nel prendere un messaggio in chiaro e tradurlo in un messaggio umanamente incomprensibile. Questa operazione viene fatta sulla base di una chiave, chiamata chiave di cifratura, la quale determina il modo per passare dal testo in chiaro al testo cifrato. In base al tipo della chiave possiamo distinguere tra cifratura simmetrica e asimmetrica. Ci soffermeremo sulla cifratura asimmetrica in quanto è quella che abbiamo affrontato e implementato nella tesi. Il procedimento è il seguente: il testo in chiaro viene fatto passare in un cifrario che è stato precedentemente inizializzato con una chiave pubblica. È proprio il cifrario a compiere l'operazione di cifratura vera e propria. Per quanto riguarda la decifratura il procedimento è completamente analogo, ossia si prende il testo cifrato e si fa passare da un cifrario, inizializzato con la chiave privata corrispondente alla chiave pubblica usata per la cifratura.
La Figura 2‑ mostra lo schema a blocchi del processo di cifratura e decifratura con chiavi asimmetriche:
Figura Schema di cifratura e decifratura con chiavi asimmetriche - tratta da [11]
La cifratura asimmetrica è molto più vantaggiosa di quella simmetrica in quanto permette lo scambio di un messaggio segreto senza dover instaurare un canale sicuro. Con il concetto di cifratura è d'obbligo introdurre anche il concetto di modalità e padding che sono direttamente correlati al cifrario selezionato.
La modalità specifica come un cifrario deve applicare un algoritmo; cambiare la modalità può permettere ad una certa tipologia di cifrari di funzionare come un'altra tipologia. I cifrari si suddividono in due categorie:
cifrari a blocchi, e
cifrari a flusso
I cifrari a blocchi, come è facile intuire, prendono il messaggio e lo spezzano in blocchi di un qualsiasi numero di bit, anche se le dimensioni più utilizzate sono 64 e 128 bit.
I cifrari a flusso invece permettono di cifrare i messaggi un solo byte alla volta, e per questa loro caratteristica sono molto utilizzati nelle applicazioni sequenziali come la comunicazione di rete.
Le modalità più comuni e presenti nelle JCE esaminate in questa tesi sono: ECB, CBC, CFB e OFB.
La modalità più semplice è, senza dubbio, ECB[1], in cui ad ogni blocco del testo in chiaro corrisponde sempre lo stesso testo cifrato. Questa corrispondenza uno a uno non rende l'ECB molto indicato per trasmettere un flusso di informazioni, in quanto questa corrispondenza statica tra testo in chiaro e testo cifrato rende più semplice un attacco da parte di terze parti.
Un altro tipo di modalità è il CBC[2], che risolve in qualche modo quelli che sono i problemi visti nell'ECB. Nel CBC ogni blocco di testo in chiaro viene cifrato utilizzando le informazioni del blocco di testo in chiaro precedente. Il problema di questo metodo è che i messaggi identici verranno sempre cifrati in maniera uguale. Per risolvere questo problema basta ricorrere ad un vettore di inizializzazione.
Il vettore di inizializzazione è un blocco di dati usato per inizializzare casualmente il cifrario in modo che anche due messaggi uguali siano cifrati con blocchi differenti. Il CBC generalmente è utilizzato in trasmissioni di testo, ma richiede che i blocchi generalmente abbiano una lunghezza di 8 caratteri.
Gli ultimi due metodi sono il CFB[3] e l'OFB . Il CFB ha lo stesso funzionamento del CBC e cambia solo per il fatto che i blocchi sono di dimensioni minori, generalmente di 8 bit, mentre l'OFB offre maggiore protezione rispetto alle eventuali perdite di informazioni durante il trasferimento.
Nella cifratura asimmetrica la modalità più utilizzata è l'ECB, dove generalmente viene cifrato un solo singolo blocco di testo in chiaro. Siccome, in genere, la dimensione di un blocco si avvicina molto a quella di una chiave, per una chiave RSA di 1024 bit possono essere cifrati blocchi fino a 128 byte. Nel caso in cui il testo da cifrare sia maggiore occorre usare una cifratura diversa.
Il padding, invece supplisce all'impossibilità di un messaggio di essere di una dimensione esattamente multipla della dimensione di un blocco. Quindi, prima della cifratura, occorre aggiungere la spaziatura mancante. Esistono due modi di fornire questo padding:
Nessun padding;
Padding definiti nello standard PKCS.
Nessun padding significa che non viene applicata nessuna spaziatura, mentre PKCS[5] indica, in base alla versione dello standard, come deve essere implementato il riempimento dei blocchi. Ad esempio nello standard del PKCS#5, il più utilizzato, soprattutto nelle cifrature simmetriche, si usa aggiungere, come padding, un numero; tale numero corrisponde proprio ai byte che mancano per finire il blocco, come si può ben vedere in Figura ‑
Figura : Esempio di padding per lo standard PKCS#5 - tratto da [19]
Per quanto riguarda, invece, la cifratura asimmetrica, generalmente vengono utilizzati due tipologie di padding:
Il primo rappresenta la forma standard utilizzata con RSA. Il suo funzionamento è abbastanza semplice e prevede di aggiungere al messaggio tre blocchi in testa allo stesso. Il primo blocco, con dimensione di un singolo byte, indica il tipo del secondo blocco, il quale può avere una dimensione che in genere si attesta sugli 8 byte, e il cui contenuto è casuale. Infine è presente il terzo blocco avente la costante nulla, e che serve per riconoscere la fine del padding e il conseguente inizio del messaggio.
Per quanto riguarda l'OAEP, rappresenta un miglioramento dello standard precedente.
Non ci resta che vedere come si può gestire la cifratura vera e propria in Java. La cifratura, come abbiamo già detto in precedenza, non è un servizio implementato dalla JCA, quindi la sua implementazione sarà presente nella JCE, all'interno del package javax.crypto, e i rispettivi package correlati.
Il concetto principale che caratterizza una cifratura asimmetrica rispetto a quella simmetrica, è la presenza di due chiavi, una pubblica e una privata, che servono l'una per la cifratura e l'altra per la decifratura.
Per prima cosa, quindi, occorre possedere una coppia di chiavi, oppure generarsela. Il servizio di generazione e gestione di una coppia di chiavi è direttamente offerto dalla JCA, all'intero dei seguenti package:
java.security.KeyPair: un oggetto di tipo KeyPair contiene una coppia di chiavi pubblica e privata. In questa classe sono presenti soltanto due metodi, getPublic() e getPrivate() che permettono il recupero delle chiavi, rispettivamente pubblica e privata, dal contenitore.
java.security.KeyPairGenerator: questa classe fornisce gli strumenti necessari per la creazione di una coppia di chiavi. Le chiavi asimmetriche vengono sempre generate in coppia, perchè non c'e modo, da una chiave, di risalire alla sua corrispondente. La generazione di una coppia di chiavi si articola in tre passi: istanziamento del generatore con l'algoritmo di creazione delle chiavi, inizializzazione dello stesso con la dimensione delle chiavi e infine creazione dell'oggetto KeyPair con le due chiavi al suo interno. I metodi necessari a svolgere queste operazioni sono rispettivamente getInstance() initialize() e uno tra i due seguenti metodi: genKeyPair() o generateKeyPair(). Infatti esistono due modi per generare un oggetto KeyPair: un modo indipendente dall'algoritmo, oppure specificando l'algoritmo. La differenza tra i due modi sta semplicemente nell'inizializzazione dell'oggetto KeyPairGenerator. Naturalmente chiamate ripetute dell'ultimo metodo producono coppie di chiavi sempre differenti.
java.security.PublicKey: rappresenta un'interfaccia per una chiave pubblica. È una sotto-interfaccia per la classe Key, ma non aggiunge nessun metodo o costante. Serve solamente come interfaccia per tutti i gruppi di chiavi pubbliche. Questa classe presenta anche delle sotto-interfacce che la estendono, e che estendono alcuni tipi di chiave come ad esempio RSAPublicKey e DSAPublicKey. Queste sotto-interfacce sono presenti all'interno del package java.security.interfaces
java.security.PrivateKey: è identica alla classe precedente se non per il fatto che rappresenta una chiave privata. Anche questa classe presenta delle sotto-interfacce nel package java.security.interfaces, che specializzano alcuni tipi di chiave, come ad esempio RSAPrivateKey e DSAPrivateKey. Anche l'interfaccia RSAPrivateKey ha una sua sotto-interfaccia che è RSAPrivateCrtKey, che presenta la variante CRT , la quale permette la possibilità di memorizzare informazioni extra sulla chiave per rendere il calcolo della chiave stessa più veloce.
Quindi una volta create le due chiavi sarà possibili utilizzarle per la cifratura. Il procedimento di cifratura prevede l'utilizzo della classe Cipher, presente nel package di JCE javax.crypto.Cipher. Questa è una delle classi più importanti della piattaforma JCE. Anche questa classe svolge l'operazione di cifratura in tre passi, ossia un passo iniziale in cui viene istanziato un oggetto cifrario, quindi viene inizializzato ed infine viene eseguita la cifratura. I metodi coinvolti in questi tre passi sono rispettivamente:
getinstance()
init()
doFinal()
Il primo metodo, che permette di generare un oggetto Cipher, richiede in ingresso la specifica di una trasformazione.
Il secondo metodo, init(), inizializza il cifrario con una chiave. Il cifrario deve essere inizializzato con una costante che descrive una delle seguenti quattro operazioni:
codifica;
decodifica;
wrap;
unwrap.
Le ultime due costanti sono prettamente relative alla gestione di chiavi segrete.
L'ultimo metodo, doFinal(), effettua la cifratura finale, restituendo il risultato finale in un buffer di byte. Una volta conclusa l'operazione il cifrario viene resettato, quindi per eseguire nuovamente una cifratura, occorre ripartire con la chiamata alla init()
Una trasformazione è una stringa che descrive l'operazione, o l'insieme delle operazioni da eseguire su un dato ingresso. Una trasformazione include sempre il nome dell'algoritmo crittografico da usare, ed eventualmente può essere seguito dal nome di alcuni algoritmi per l'implementazione della modalità e il padding. Quindi lo schema di uno dei parametri del metodo getInstance()
"algoritmo/modalità/padding" oppure
"algoritmo"
A volte può essere anche utile specificare la dimensione della modalità, nel caso in cui si usino cifrari a blocchi come CFB e OFB. Per tutte le specifiche sulle possibili stringhe determinabili per questo parametro si rimanda il lettore alla consultazione del manuale della Sun Microsystem.
Di seguito riportiamo un semplice esempio di cifratura in cui si fa riferimento ad un cifrario con modalità a catena di blocchi e padding PKCS con provider Bouncy Castle:
Cipher cifrario =
Cipher.getInstance('DES/CBC/PKCS5Padding','BC');
cifrario.init(Cipher.ENCRYPT_MODE, chiaveSegreta);
byte[] messaggioCifrato = cifrario.doFinal(messaggio.getBytes());
Quando si creano delle coppie di chiavi queste devono essere memorizzate in modo da garantirne la protezione da possibili attacchi, che ne metterebbero in pericolo l'affidabilità. Una tecnica molto utilizzata per la protezione delle chiavi è la cifratura con password.
Quando si generano coppie di chiavi asimmetriche, la protezione è indispensabile soprattutto per quanto riguarda quella privata. Solitamente si usa memorizzare la chiave privata su dispositivi fisici esterni come floppy o smartcard, ma questo implica di avere sempre un supporto esterno e di mantenerlo in un posto sicuro. Un altro metodo, molto più pratico è quello di memorizzare la chiave privata nel file system della macchina locale, all'interno di un file e di proteggere il suo accesso con una password. La presenza della password si rende necessaria perchè la semplice memorizzazione della chiave sul file system è riduttivo in termini di sicurezza, in quanto chiunque abbia accesso alla macchina può leggere il contenuto di quel file. Un metodo quindi è quello di utilizzare un algoritmo di cifratura con password, o PBE; inoltre sarebbe buona norma modificare i diritti di accesso a quel file.
La cifratura con password utilizza una combinazione tra cifrature hash e normale cifratura. Acquisita la password calcoliamo il suo digest con algoritmo del tipo SHA-1 o MD5, quindi utilizziamo il risultato di questa operazione come chiave con cui inizializzare un cifrario. Una volta inizializzato il cifrario è possibile andare a cifrare la chiave.
Figura : Processo di cifratura con password - tratto da [11]
Il problema dell'utilizzo di questa tecnica è che generalmente le password hanno una dimensione di 6 ÷ 8 caratteri, e per lo più sono parole di senso compiuto, in quanto devono essere facilmente ricordabili. Questa loro caratteristica li rende facilmente vulnerabili con i cosiddetti attacchi con dizionario[8]. Per questo motivo è un metodo meno sicuro rispetto ad algoritmi che utilizzano chiavi binarie come Blowfish o TripleDES
Una possibile modifica al PBE che lo rende molto più sicuro è quella di prevedere tecniche come:
Conteggio di ripetizioni;
Salt.
Il conteggio di ripetizioni consiste nel tentativo di aumentare il tempo che si impiegherebbe per applicare un attacco con dizionario. Quindi, operativamente parlando, si tratta di applicare un certo numero di volte l'algoritmo del calcolo del digest alla password. Generalmente tale numero è preso nell'ordine del migliaio.
La tecnica del salt, invece, consiste nell'aggiunta di una stringa casuale davanti alle password prima che si applichi il calcolo del digest, in modo da aumentare sensibilmente il numero di possibili hash generabili con quella password. Generalmente queste stringhe aggiuntive sono dell'ordine degli 8 o 16 byte. Se ad esempio aggiungiamo un salt di 8 byte, abbiamo un spazio dei possibili digest che è 264 volte più grande. Dato che la generazione di un digest è molto più complesso della generazione di una password, questa modifica rende praticamente impossibile riuscire a risalire alla password.
Di seguito è riportato lo schema della cifratura con password mediante tecnica del salt:
Figura : Processo di cifratura con password e salt - tratta da [11]
In Java, la cifratura basata su password si avvale dei servizi offerti dai package javax.crypto.spec, oltre ai servizi offerti da alcune classi del package javax.crypto
javax.crypto.spec.PBEKeySpec: questa classe viene utilizzata per creare una chiave basata su password, utilizzando un'istanza della classe SecretKeyFactory È una delle poche classi della JCE sulla quale l'istanza è ottenuta tramite il costruttore standard. La password da passare al costruttore deve essere in formato di array di char. Questa costrizione è dovuta la fatto che in Java le stringhe sono immutabili e invece può essere necessario eliminare la password dopo il suo utilizzo, per motivi di sicurezza. Un esempio di utilizzo di questa classe può essere il seguente:
char[] password = "password".toCharArray();
PBEKeySpec pbekeyspec = new PBEKeySpec(password);
javax.crypto.SecretKeyFactory: questa classe rappresenta un costruttore per chiavi segrete. Per poter utilizzare concretamente un oggetto javax.crypto.PBEKeySpec occorre farlo passare attraverso un oggetto SecretKeyFactory. Quest'ultima classe tramite una chiamata al metodo generateSecret(), genererà una chiave prendendo una specifica di essa dall'oggetto PBEKeySpec, ricevuto come argomento.
javax.crypto.spec.PBEParameterSpec: questa classe specifica un insieme di parametri usati con un PBE, come è definito nello standard PKCS#5. Un oggetto di questo tipo viene passato come parametro al cifrario in modo che questo venga inizializzato come un cifrario PBE con salt e conteggio di ripetizioni. Un oggetto PBEParameterSpec, infatti, è un contenitore per un salt e il conteggio di ripetizioni.
SecretKeyFactory skf = SecretKeyFactory.getInstance("nome algoritmo");
SecretKey chiave = KeyFactory.generateSecret(pbekeyspec);
PBEParameterSpec param = new PBEParameterSpec (salt, N_ripet);
Cipher cipher = Cipher.getInstance("nome algoritmo");
Cipher.init(Cipher.ENCRYPT_MODE, chiave,param);
Appunti su: |
|
Appunti internet | |
Tesine c | |
Lezioni database | |