Introduzione
Nell’articolo Crittografia in Java abbiamo introdotto alcuni concetti base per la gestione della crittografia in java. Vediamo ora come sia possibile con il framework JCA gestire ed utilizzare i certificati digitali.
Keystore e Trustore
Un keystore è un repository di certificati digitali utilizzato in java genericamente indicato come JKS (Java Key Store). Da un punto di vista tecnologico non c’è differenza tra un keystore ed un truststore, entrambe sono repository. Ciò che li differenzia è il contenuto e soprattutto l’utilizzo. In generale il keystore contiene un certificato digitale in cui è inclusa una coppia di chiavi pubblica/privata. Diversamente il truststore include i certificati delle entità di cui ci fidiamo e, ovviamente, tali certificati comprendono esclusivamente la chiave pubblica di tali entità. Ovviamente nulla impedisce di utilizzare lo stesso repository sia come keystore che come truststore.
Per i nostri scopi abbiamo bisogno sia di un keystore che di un truststore. Fortunatamente il jdk ci mette a disposizione un keytool
per la gestione dei keystore e la generazione di certificati digitali. Per generare i due repository seguiamo i seguenti passi.
L’istruzione seguente consente di generare un keystore e salvarlo nel file javaboss-keystore.jks
. Per il certificato, identificato con l’alias javaboss.it
, è utilizzato l’algoritmo RSA con chiavi di lunghezza 2048. Sia il certificato che il keystore sono protetti con la password javaboss
.
1 |
keytool -genkey -alias javaboss.it -keyalg RSA -keystore javaboss-keystore.jks -keysize 2048 |
1 |
keytool -export -alias javaboss.it -file javaboss.crt -keystore javaboss-keystore.jks -storepass javaboss |
A questo punto dobbiamo generare un truststore dove inserire il certificato appena esportato. Per farlo dobbiamo prima generare un keystore come fatto prima e poi eliminare il certificato che il keytool ha inserito nel repository. Otteniamo così un keystore vuoto conservato nel file javaboss-truststore.jks.
1 2 |
keytool -genkey -alias javaboss-alias -keystore javaboss-truststore.jks keytool -delete -alias javaboss-alias -keystore javaboss-truststore.jks |
1 |
keytool -import -trustcacerts -alias javaboss.it -file javaboss.crt -keystore javaboss-truststore.jks |
Gestione del Keystore
Dopo aver creato il keystore ed il truststore vediamo come accedervi. Per farlo è sufficiente caricare il keystore dal filesystem ma è necessario conoscerne la password di protezione:
1 2 3 4 5 6 7 |
public static KeyStore loadKeyStore( File file, String password ) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { FileInputStream is = new FileInputStream( file ); KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); keystore.load(is, password.toCharArray() ); return keystore; } |
Supponendo di voler inviare un messaggio ad un destinatario del quale abbiamo il certificato nel truststore, dobbiamo innanzitutto estrarne la chiave pubblica conoscendo l’alias del certificato:
1 2 3 4 5 6 |
public static PublicKey getPublicKey( KeyStore keystore, String alias ) throws KeyStoreException { if ( keystore.containsAlias( alias ) ) { return keystore.getCertificate( alias ).getPublicKey(); } return null; } |
Analogamente il destinatario dovrà estrarre la propria chiave privata dal certificato contenuto nel keystore. Per farlo deve ovviamente conoscerne la password:
1 2 3 4 5 6 7 |
public static PrivateKey getPrivateKey( KeyStore keystore, String alias, String password ) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException { if ( keystore.containsAlias( alias ) ) { return (PrivateKey) keystore.getKey(alias, password.toCharArray()); } return null; } |
Utilizzando la classe CryptoHelper
introdotta nel post Crittografia in Java implementiamo una classe KeystoreManager per gestire un keystore (o truststore) ed eseguire le operazioni di cifratura e decifratura. Riportiamo uno stralcio della classe che sarà comunque disponibile per il download.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
public class KeystoreManager { // Keystore gestito private KeyStore keystore; public KeystoreManager( String path, String password ) { try { this.keystore = CryptoHelper.loadKeyStore( new File( path ), password ); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { throw new RuntimeException( "Impossibile caricare il certificato dal file " + path , e ); } } public byte[] encryptWithPublicKey( String alias, byte[] plaintext ) { PublicKey pk = null; try { pk = CryptoHelper.getPublicKey( keystore, alias ); } catch (KeyStoreException e) { throw new RuntimeException( "Eccezione nell'accesso al keystore", e ); } if ( pk == null ) { throw new RuntimeException( "Public key per l'alias " + alias + " non trovata" ); } try { return CryptoHelper.encrypt( pk, plaintext ); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) { throw new RuntimeException( "Eccezione durante la criptazione", e ); } } public byte[] decryptWithPrivateKey( String alias, String password, byte[] ciphertext ) { PrivateKey pk = null; try { pk = CryptoHelper.getPrivateKey( keystore, alias, password ); } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) { throw new RuntimeException( "Eccezione nell'accesso al keystore", e ); } if ( pk == null ) { throw new RuntimeException( "Private key per l'alias " + alias + " non trovata" ); } try { return CryptoHelper.decrypt( pk, ciphertext ); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) { throw new RuntimeException( "Eccezione durante la criptazione", e ); } } } |
In conclusione utilizzando la classe KeystoreManager
l’invio del messaggio cifrato avverrà secondo lo schema mostrato dal seguente codice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public static void main(String[] args) throws Exception { String msg = "Questo è il testo da proteggere"; // Criptazione del messaggio utilizzando la chiave pubblica nel truststore KeystoreManager truststore = new KeystoreManager( "res/javaboss-truststore.jks", "javaboss" ); String encripted = CryptoHelper.base64Encode( truststore.encryptWithPublicKey( "javaboss.it", msg.getBytes() ) ); System.out.println( encripted ); // ----------------------- // Invio messaggio cifrato // ----------------------- // Decriptazione del messaggio utilizzando la chiave private nel keystore KeystoreManager keystore = new KeystoreManager( "res/javaboss-keystore.jks", "javaboss" ); String decripted = new String( keystore.decryptWithPrivateKey( "javaboss.it", "javaboss", CryptoHelper.base64Decode( encripted ) ) ); System.out.println( decripted ); } |
1 2 3 4 5 6 |
RM2Nxtb9tRurL0Ds/84xb/qdjgi1Jw4xu86U5IwcivTfVCPFY4/XJgwYkNfQrSPAFkxhUdeeaeSN 8OBtfZJi1ODzbg4T2ObupqrySdEoz9oVL8jMZp0jU5noBZirSJ/cDHv1Yb63PpSR4cqO1zykXfbW OP62oY9I5/cCzLPBlK5i8+Rb6jNqPbLvhVeCrA4TF2HSAsmskDAmbiY5heja40RIi6Hwx69flMzB neyLnp9wF46GOKM7mfnucyOrrnWB+BT3H70kG6V8uL+Wu098Zu5Hvjsf6bg7xB43YOmCSQUlMfgX TET5Hu5u86gwMRUIZd7lSIC6Wx+w50t4pPPsyw== Questo è il testo da proteggere |
Codice Sorgente
Il codice sorgente è scaricabile qui Cryptography.