JCA (Java Cryptography Architecture) mette a disposizione un set di API per implementare logiche di sicurezza basate su firme digitali, gestione di message digest, creazione e validazione di certificati, meccanismi di cifratura, generazione e gestione di chiavi e così via. Vediamo come realizzare una semplice implementazione di cifratura a chiave asimmetrica, conosciuta anche come Public Key Cryptography. In questo esempio utilizzerò un file di origine scritto in chiaro. Dopo aver generato una coppia di chiavi RSA a 2048 bit, utilizzerò la chiave pubblica per crittografare il file e quella privata per decriptarlo.
Per generare la coppia di chiavi ho utilizzato l’oggetto KeyPairGenerator di JCA che supporta l’algoritmo RSA fino a 2048 bits. Per crittografare i dati in chiaro sul file di origine ho utilizzato l’oggetto Cipher con la stringa che definisce le operazioni di transformation “RSA/ECB/PKCS1Padding” da eseguire durante il processo di encryption/decryption. JCA definisce questi oggetti “engine classes“, di seguito una visione ad alto livello.
ROADMAP
STEP 1. Simple Maven project
STEP 2. Asymmetric Cipher Implementation
STEP 3. Test 2048-RSA file encryption/decryption
STEP 1. Simple Maven Project
Creo un progetto Maven generico utilizzando l’archetipo “quickstart” e aggiungo le dipendenze da junit e commons-io di Apache per implementare dei test e alcune operazione sui file.
mvn archetype:generate \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DgroupId=eu.giuseppeurso.sample.security \ -DartifactId=sample-security -Dversion=1.0-SNAPSHOT \
STEP 2. Asymmetric Cipher Implementation
La classe AsymmetricCipher implementa i metodi per generare le chiavi RSA e per cifrare/decifrare i dati. Per generare la coppia di chiavi pubblica/privata utilizzo java.security.KeyPairGenerator mentre per le operazioni di encryption/decryption utilizzo l’oggetto javax.crypto.Cipher.
public static KeyPair keyPairGenerator (String algorithm, int keysize) throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);
kpg.initialize(keysize);
KeyPair kp = kpg.generateKeyPair();
return kp;
}
public static byte[] encrypt (byte[] inputBytes, PublicKey key, String xform) throws Exception {
Cipher cipher = Cipher.getInstance(xform);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(inputBytes);
}
public static byte[] decrypt (byte[] inputBytes, PrivateKey key, String xform) throws Exception{
Cipher cipher = Cipher.getInstance(xform);
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(inputBytes);
}
STEP 3. Test 2048-RSA file encryption/decryption
Creo un classe di Test su cui lanciare alcuni test di unità sui metodi implementati. Per effettuare un test completo sul processo di encription/decryption dei dati, creo un semplice file con del testo in chiaro. Su tale file, dopo aver creato una coppia di chiavi RSA a 2048 bit, eseguo il metodo AsymmetricCipher.encrypt e salvo i byte in uscita su un nuovo file (FileUtils.writeByteArrayToFile di commons-io). Questo file non sarà “human-redable” in quanto criptato. Per testarne la consistenza e l’integrità rispetto all’originale, effettuo l’operazione inversa di decrypting. Confronto e verifico che i byte del file originale siano identici a quelli del file decriptato.
/**
* Test a file encyption
*
*/
public void testFileEncyption() throws Exception {
boolean actual=false;
// Generate a key-pair
KeyPair kp = AsymmetricCipher.keyPairGenerator("RSA", 2048);
PublicKey pubk = kp.getPublic();
PrivateKey prvk = kp.getPrivate();
File file = new File(fileToEncrypt);
byte[] dataBytes = FileUtils.readFileToByteArray(file);
System.out.println("Source file size is: " + dataBytes.length * 8+ " bits (=" + dataBytes.length + " bytes)");
System.out.println("RSA key size is: " + 2048 + " bits (= "+ 2048 / 8 + " bytes)");
// Now start with the file encryption
String xform = "RSA/ECB/PKCS1Padding";
byte[] encBytes = AsymmetricCipher.encrypt(dataBytes, pubk, xform);
file = new File(encryptedFile);
FileUtils.writeByteArrayToFile(file, encBytes);
System.out.println("Encrypted file at: " + encryptedFile);
// Decrypt the generated file
byte[] decBytes = AsymmetricCipher.decrypt(encBytes, prvk, xform);
file = new File(unencryptedFile);
FileUtils.writeByteArrayToFile(file, decBytes);
System.out.println("Unencrypted file at: " + unencryptedFile);
// Comparing the encrypted/decrypted bytes
actual = java.util.Arrays.equals(dataBytes, decBytes);
assertEquals("Invalid decryption.", true, actual);
}
Con Maven lancio il build sul progetto e verifico l’esito dei test. Se non ci sono errori viene generato un file criptato e un nuovo file decriptato consistente rispetto all’originale.
$ cd sample-security $ mvn clean install [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Java Security sample 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ sample-security --- [.......]
Italiano
Inglese




