Шифрування та дешифрування файлів на Java

Java Top

Я щойно оголосив про новий курс Learn Spring , орієнтований на основи Spring 5 та Spring Boot 2:

>> ПЕРЕВІРИТИ КУРС

1. Огляд

У цьому підручнику ми розглянемо, як зашифрувати та розшифрувати файл за допомогою існуючих API JDK.

2. Написання тесту спочатку

Ми почнемо з написання нашого тесту в стилі TDD. Оскільки ми будемо працювати з файлами тут, інтеграційний тест здається доречним.

Оскільки ми просто використовуємо існуючу функціональність JDK, ніякі зовнішні залежності не потрібні.

По-перше, ми зашифруємо вміст, використовуючи нещодавно сформований секретний ключ (ми використовуємо AES, Advanced Encryption Standard, як алгоритм симетричного шифрування в цьому прикладі).

Також зауважте, що ми визначаємо повний рядок трансформації в конструкторі ( AES / CBC / PKCS5Padding ), який є конкатенацією використовуваного шифрування, режиму блочного шифру та відступів ( алгоритм / режим / відступ ). Реалізації JDK підтримують низку різних перетворень за замовчуванням, але зауважте, що не кожна комбінація все ще може вважатися криптографічно безпечною за сучасними стандартами.

Будемо вважати, що наш клас FileEncrypterDecrypter запише вихідні дані у файл, який називається baz.enc . Потім ми розшифровуємо цей файл за допомогою того самого секретного ключа і перевіряємо, чи розшифрований вміст дорівнює вихідному вмісту:

@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. Шифрування

Ми ініціалізуємо шифр у конструкторі нашого класу FileEncrypterDecrypter, використовуючи вказаний рядок перетворення .

Це дозволяє нам провалитися рано, якщо було вказано неправильне перетворення:

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

Потім ми можемо використовувати примірник шифру та наданий секретний ключ для виконання шифрування:

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 дозволяє використовувати зручний клас CipherOutputStream для запису зашифрованого вмісту в інший OutputStream .

Зверніть увагу, що ми пишемо IV (Вектор ініціалізації) на початок вихідного файлу. У цьому прикладі IV автоматично генерується під час ініціалізації шифру .

Використання IV є обов’язковим під час використання режиму CBC для рандомізації зашифрованого виводу. IV, однак, не вважається таємницею, тому нормально писати його на початку файлу.

4. Розшифровка

Для розшифрування ми також повинні спочатку прочитати IV. Після цього ми можемо ініціалізувати наш шифр і розшифрувати вміст.

Знову ж таки, ми можемо використовувати спеціальний клас Java, CipherInputStream , який прозоро піклується про фактичне розшифрування :

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. Висновок

Ми бачили, що ми можемо виконувати базове шифрування та дешифрування за допомогою стандартних класів JDK, таких як Cipher , CipherOutputStream та CipherInputStream .

Як завжди, повний код цієї статті доступний у нашому сховищі GitHub.

Крім того, ви можете знайти тут список шифрів, доступних у JDK.

Нарешті, зауважте, що приклади коду тут не призначені як виробничий код, а особливості вашої системи потрібно ретельно враховувати при їх використанні.

Знизу Java

Я щойно оголосив про новий курс Learn Spring , орієнтований на основи Spring 5 та Spring Boot 2:

>> ПЕРЕВІРИТИ КУРС