Crittografia e decrittografia di file in Java

Java Top

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO

1. Panoramica

In questo tutorial, daremo un'occhiata a come crittografare e decrittografare un file utilizzando le API JDK esistenti.

2. Scrivere prima un test

Inizieremo scrivendo il nostro test, in stile TDD. Poiché qui lavoreremo con i file, un test di integrazione sembra essere appropriato.

Poiché stiamo usando solo la funzionalità JDK esistente, non sono necessarie dipendenze esterne.

Innanzitutto, crittograferemo il contenuto utilizzando una chiave segreta appena generata (in questo esempio stiamo utilizzando AES, Advanced Encryption Standard, come algoritmo di crittografia simmetrica).

Si noti inoltre che stiamo definendo la stringa di trasformazione completa nel costruttore ( AES / CBC / PKCS5Padding ), che è una concatenazione di crittografia utilizzata, modalità di cifratura a blocchi e riempimento ( algoritmo / modalità / riempimento ). Le implementazioni JDK supportano una serie di trasformazioni differenti per impostazione predefinita, ma si noti che non tutte le combinazioni possono ancora essere considerate crittograficamente sicure per gli standard odierni.

Assumeremo che la nostra classe FileEncrypterDecrypter scriverà l'output in un file chiamato baz.enc . Successivamente, decifriamo questo file utilizzando la stessa chiave segreta e controlliamo che il contenuto decrittografato sia uguale al contenuto originale:

@Test public void whenEncryptingIntoFile_andDecryptingFileAgain_thenOriginalStringIsReturned() { String originalContent = "foobar"; SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey(); FileEncrypterDecrypter fileEncrypterDecrypter = new FileEncrypterDecrypter(secretKey, "AES/CBC/PKCS5Padding"); fileEncrypterDecrypter.encrypt(originalContent, "baz.enc"); String decryptedContent = fileEncrypterDecrypter.decrypt("baz.enc"); assertThat(decryptedContent, is(originalContent)); new File("baz.enc").delete(); // cleanup }

3. Crittografia

Inizializzeremo il cifrario nel costruttore della nostra classe FileEncrypterDecrypter usando la stringa di trasformazione specificata .

Questo ci consente di fallire presto nel caso in cui sia stata specificata una trasformazione sbagliata:

FileEncrypterDecrypter(SecretKey secretKey, String transformation) { this.secretKey = secretKey; this.cipher = Cipher.getInstance(transformation); }

Possiamo quindi utilizzare la crittografia istanziata e la chiave segreta fornita per eseguire la crittografia:

void encrypt(String content, String fileName) { cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] iv = cipher.getIV(); try (FileOutputStream fileOut = new FileOutputStream(fileName); CipherOutputStream cipherOut = new CipherOutputStream(fileOut, cipher)) { fileOut.write(iv); cipherOut.write(content.getBytes()); } }

Java ci consente di sfruttare la comoda classe CipherOutputStream per scrivere il contenuto crittografato in un altro OutputStream .

Tieni presente che stiamo scrivendo IV (vettore di inizializzazione) all'inizio del file di output. In questo esempio, l'IV viene generato automaticamente durante l'inizializzazione del Cipher .

L'uso di un IV è obbligatorio quando si utilizza la modalità CBC, al fine di randomizzare l'output crittografato. L'IV tuttavia non è considerato un segreto, quindi va bene scriverlo all'inizio del file.

4. Decrittazione

Allo stesso modo per decifrare dobbiamo leggere prima l'IV. Successivamente, possiamo inizializzare il nostro codice e decrittografare il contenuto.

Ancora una volta possiamo fare uso di una speciale classe Java, CipherInputStream , che si occupa in modo trasparente della decrittazione effettiva :

String decrypt(String fileName) { String content; try (FileInputStream fileIn = new FileInputStream(fileName)) { byte[] fileIv = new byte[16]; fileIn.read(fileIv); cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(fileIv)); try ( CipherInputStream cipherIn = new CipherInputStream(fileIn, cipher); InputStreamReader inputReader = new InputStreamReader(cipherIn); BufferedReader reader = new BufferedReader(inputReader) ) { StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } content = sb.toString(); } } return content; }

5. conclusione

Abbiamo visto che possiamo eseguire la crittografia e la decrittografia di base utilizzando classi JDK standard, come Cipher , CipherOutputStream e CipherInputStream .

Come al solito, il codice completo per questo articolo è disponibile nel nostro repository GitHub.

Inoltre, puoi trovare un elenco dei cifrari disponibili nel JDK qui.

Infine, tieni presente che gli esempi di codice qui non sono intesi come codice di livello di produzione e le specifiche del tuo sistema devono essere considerate accuratamente quando vengono utilizzate.

Fondo Java

Ho appena annunciato il nuovo corso Learn Spring , incentrato sui fondamenti di Spring 5 e Spring Boot 2:

>> SCOPRI IL CORSO