Programmazione avanzata Java e C Crittografia
ü JCA o Java Cryptography Architecture è il framework java per la crittografia che fa parte della API di sicurezza di Java, ed è stato introdotto nei pacchetti java.security e java.crypto. JCA fornisce le principali funzionalità di crittografia quali: ü firma digitale; ü message digest (hash); ü certificati e convalida dei certificati; ü crittografia (simmetrica/blocco asimmetrico/cifrari a flusso); ü generazione e gestione di chiavi; ü generazione di numeri casuali e molto altro.
ü La chiave crittografica è una informazione che è utilizzata in un algoritmo di criptazione e decriptazione per generare un testo cifrato da uno in chiaro. ü Possono avere lunghezza variabile in funzione dell algoritmo utilizzato ed in generale si parla di chiavi simmetriche o asimmetriche. JCA offre le funzionalità per la generazione di entrambe le tipologie di chiavi. ü La classe KeyGenerator espone le funzionalità per la generazione di chiavi simmetriche. Per istanziarla possiamo invocare il metodo getinstance() specificando l algoritmo che si intende utilizzare.
Si noti che questa modalità di istanziare gli di oggetti in JCA è molto comune perché il framework è progettato in modo da demandare l implementazione degli algoritmi a provider esterni registrati. La stringa di input specifica l implementazione (ovvero algoritmo) richiesta e se non presente il metodo restituisce l eccezione NoSuchAlgorithmException.
Il codice seguente generare una chiave simmetrica. private static final String SYM_ALGORITHM = "AES"; private static final Integer SYM_KEY_SIZE = 128; public static Key generatesymmetrickey() throws NoSuchAlgorithmException { KeyGenerator generator = KeyGenerator.getInstance( SYM_ALGORITHM ); generator.init( SYM_KEY_SIZE ); SecretKey key = generator.generatekey(); return key; }
Gli algoritmi disponibili nel JCA, con le rispettive lunghezze in bit delle chiavi, sono i seguenti: ü AES (128) ü 1/7DES (56) ü DESede (168) ü HmacSHA1 ü HmacSHA256
Per la generazione di una coppia di chiavi asimmetriche (pubblica e privata) la classe del framework da utilizzare è KeyPairGenerator. La coppia di chiavi è restituita nel bean KeyPair caratterizzata da due sole proprietà in sola lettura: publickey e privatekey.
private static final String ASYM_ALGORITHM = "RSA"; private static final Integer ASYM_KEY_SIZE = 1024; public static KeyPair generateasymmetrickey() throws NoSuchAlgorithmException { KeyPairGenerator generator = KeyPairGenerator.getInstance( ASYM_ALGORITHM ); generator.initialize( ASYM_KEY_SIZE ); return generator.generatekeypair(); }.
Gli algoritmi supportati dal framework sono: ü DiffieHellman (1024) ü DSA (1024) ü RSA (1024, 2048)
La crittografia simmetrica o crittografia a chiave privata indica un metodo di cifratura molto semplice in cui la chiave utilizzata per cifrare il testo in chiaro è la stessa utilizzata per decifrarlo. La figura seguente mostra il processo appena descritto.
La classe che espone i servizi per la crittazione e decrittazione è java.crypto.cipher. Come già visto per ottenerne una istanza è necessario invocare il metodo getinstance() passando una stringa (transformation) cheindica il set di operazioni che devono essere eseguite sull input per ottenere l output richiesto. Tale stringa ha il formato algorithm/mode/padding dove: ü algorithm indica l algoritmo di crittazione desiderato; ü mode indica il metodo di crittazione che può essere a Blocchi di Cifre o a Flusso di Cifre; ü padding nel caso di cifratura a blocchi indica l algoritmo da utilizzare per riempire il blocco finale.
Il codice seguente mostra un esempio di cifratura simmetrica con algoritmo di cifratura a blocchi AES in modalità CBC. Il metodo CBC combina il blocco corrente con l esito della crittazione del blocco precedente, quindi necessita di un vettore di inizializzazione per cifrare il primo blocco.
public static byte [] encrypt( Key key, byte[] iv, byte[] plaintext ) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" ); cipher.init( Cipher.ENCRYPT_MODE, key, new IvParameterSpec( iv ) ); return cipher.dofinal( plaintext ); }
L oggetto Cipher può essere utilizzato sia per criptare che per decriptare. La selezione della modalità operativa si effettua passando il parametro mode al metodo init(), che nel nostro esempio assume il valore ENCRYPT_MODE. A tale metodo passiamo anche la chiave da utilizzare per l operazione ed il vettore di inizializzazione.
Per eseguire l operazione inversa di decriptazione il codice java sarà simile al precedente ma l operazione specificata nel metodo init() dovrà essere DECRYPT_MODE.
public static byte [] decrypt( Key key, byte[] iv, byte[] ciphertext ) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" ); cipher.init( Cipher.DECRYPT_MODE, key, new IvParameterSpec( iv ) ); return cipher.dofinal( ciphertext ); }
Si utilizza la classe SecureRandom che utilizza algoritmi per la generazione di numeri casuali non facilmente predicibile. public static byte [] generateinitvector() { SecureRandom random = new SecureRandom(); byte [] iv = new byte [ SYM_KEY_SIZE / 8 ]; random.nextbytes( iv ); return iv; }
La crittografia asimmetrica, conosciuta anche come crittografia a chiave pubblica/privata o solamente crittografia a chiave pubblica, è un metodo di criptazione in cui si utilizza una coppia di chiavi diverse, una pubblica ed una privata, in grado di funzionare solo l una con l altra. La chiave privata è utilizzabile solo dal legittimo possessore, mentre quella pubblica è resa nota a chiunque.
In pratica, ciò che viene cifrato da una chiave può essere decifrato solo con l altra chiave della coppia e viceversa. Sebbene più sicura, in quanto elimina il problema della condivisione della chiave tra mittente e destinatario, risulta computazionalmente più onerosa rispetto ad un algoritmo a chiave simmetrica.
Per questo motivo è generalmente utilizzato per cifrare solamente pochi blocchi di dati, come ad esempio la una chiave simmetrica. E poi la chiave simmetrica(detta anche chiave di sessione) che viene utilizzata per la criptazione del testo, come mostrato nell immagine seguente.
public static byte [] encrypt( PublicKey key, byte[] plaintext ) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { Cipher cipher = Cipher.getInstance( "RSA/ECB/NoPadding" ); cipher.init( Cipher.ENCRYPT_MODE, key ); return cipher.dofinal( plaintext ); }
public static byte [] decrypt( PrivateKey key, byte[] ciphertext ) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { Cipher cipher = Cipher.getInstance( key.getalgorithm() + "/ECB/NoPadding" ); cipher.init( Cipher.DECRYPT_MODE, key ); return cipher.dofinal( ciphertext ); }
Tutti i metodi di cifratura e decifratura che abbiamo visto ricevono in input e restituiscono in output un array di byte. Per poter persistere o trasferire tale array attraverso un media che tratta dati testuali è necessario convertirlo in Base 64. Questo per garantire che il dato rimanga intatto durante il trasporto.
private static final BASE64Encoder encoder = new BASE64Encoder(); private static final BASE64Decoder decoder = new BASE64Decoder(); public static String base64encode( byte[] array ) { return encoder.encode( array ); } public static byte[] base64decode( String buffer ) throws Exception { return decoder.decodebuffer( buffer ); }
In conclusione inserendo i metodi descritti sopra in una classe CryptoHelper le operazioni di criptazione e decriptazione avvengono nel modo seguente:
public static void main(string[] args) throws Exception { String msg = "Questo è il testo da proteggere"; // Cifratura con chiave simmetrica Key key = CryptoHelper.generateSymmetricKey(); byte[] iv = CryptoHelper.generateInitVector(); String encripted = CryptoHelper.base64Encode( CryptoHelper.encrypt( key, iv, msg.getbytes() ) ); System.out.println( encripted ); // ----------------------- // Invio messaggio cifrato // ----------------------- // Decifratura con chiave simmetrica String decripted = new String( CryptoHelper.decrypt( key, iv, CryptoHelper.base64Decode( encripted ) ) );
System.out.println( decripted ); // Cifratura con chiave simmetrica KeyPair keypair = CryptoHelper.generateAsymmetricKey(); encripted = CryptoHelper.base64Encode( CryptoHelper.encrypt( keypair.getpublic(), iv, msg.getbytes() ) ); System.out.println( encripted ); // ----------------------- // Invio messaggio cifrato // ----------------------- // Decifratura con chiave asimmetrica decripted = new String( CryptoHelper.decrypt( keypair.getprivate(), iv, CryptoHelper.base64Decode( encripted ) ) ); System.out.println( decripted ); }