Enkripsi dan Dekripsi AES Java

Java Top

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya

1. Ikhtisar

Sandi blok kunci simetris memainkan peran penting dalam enkripsi data. Ini berarti bahwa kunci yang sama digunakan untuk enkripsi dan dekripsi. Advanced Encryption Standard (AES) adalah algoritme enkripsi kunci simetris yang banyak digunakan.

Dalam tutorial ini, kita akan melihat bagaimana menerapkan enkripsi dan dekripsi AES menggunakan Java Cryptography Architecture (JCA) dalam JDK.

2. Algoritma AES

Algoritma AES adalah cipher blok kunci simetris berulang yang mendukung kunci kriptografi (kunci rahasia) 128, 192, dan 256 bit untuk mengenkripsi dan mendekripsi data dalam blok 128 bit . Gambar di bawah ini menunjukkan algoritma AES tingkat tinggi:

Jika data yang akan dienkripsi tidak memenuhi persyaratan ukuran blok 128 bit, itu harus di-padded. Padding adalah proses pengisian blok terakhir menjadi 128 bit.

3. Variasi AES

Algoritme AES memiliki enam mode operasi:

  1. ECB (Buku Kode Elektronik)
  2. CBC (Rantai Blok Cipher)
  3. CFB (Umpan Balik Cipher)
  4. OFB (Umpan Balik Keluaran)
  5. RKT (Penghitung)
  6. GCM (Galois / Counter Mode)

Mode operasi dapat diterapkan untuk memperkuat efek algoritma enkripsi. Selain itu, mode operasi dapat mengubah sandi blok menjadi sandi aliran. Setiap mode memiliki kekuatan dan kelemahannya masing-masing. Mari kita tinjau sebentar.

3.1. ECB

Mode operasi ini adalah yang paling sederhana dari semuanya. Plaintext dibagi menjadi beberapa blok dengan ukuran 128 bit. Kemudian setiap blok akan dienkripsi dengan kunci dan algoritma yang sama. Oleh karena itu, menghasilkan hasil yang sama untuk blok yang sama. Ini adalah kelemahan utama mode ini dan tidak disarankan untuk enkripsi . Ini membutuhkan data padding.

3.2. CBC

Untuk mengatasi kelemahan ECB, mode CBC menggunakan Inisialisasi Vektor (IV) untuk menambah enkripsi. Pertama, KBK menggunakan blok teks biasa xor dengan IV. Kemudian mengenkripsi hasilnya ke blok ciphertext. Blok berikutnya menggunakan hasil enkripsi untuk xor dengan blok teks biasa sampai blok terakhir.

Dalam mode ini, enkripsi tidak dapat diparalelkan, tetapi dekripsi dapat diparalelkan. Ini juga membutuhkan data padding.

3.3. CFB

Mode ini dapat digunakan sebagai stream cipher. Pertama, mengenkripsi IV, kemudian akan xor dengan blok teks biasa untuk mendapatkan ciphertext. Kemudian CFB mengenkripsi hasil enkripsi ke xatau teks biasa. Itu membutuhkan infus.

Dalam mode ini, dekripsi dapat diparalelkan tetapi enkripsi tidak dapat diparalelkan.

3.4. OFB

Mode ini dapat digunakan sebagai stream cipher. Pertama, mengenkripsi IV. Kemudian menggunakan hasil enkripsi untuk xor plaintext untuk mendapatkan ciphertext.

Itu tidak memerlukan data padding dan tidak akan terpengaruh oleh blok berisik.

3.5. RKT

Mode ini menggunakan nilai penghitung sebagai IV. Ini sangat mirip dengan OFB, tetapi menggunakan penghitung untuk dienkripsi setiap saat, bukan IV.

Mode ini memiliki dua kekuatan, termasuk enkripsi / dekripsi paralelisasi, dan noise dalam satu blok tidak memengaruhi blok lainnya.

3.6. GCM

Mode ini merupakan perpanjangan dari mode CTR. GCM telah menerima perhatian yang signifikan dan direkomendasikan oleh NIST. Model GCM mengeluarkan teks sandi dan tag otentikasi. Keuntungan utama mode ini, dibandingkan mode operasi algoritme lainnya, adalah efisiensinya.

Dalam tutorial ini, kita akan menggunakan algoritma AES / CBC / PKCS5Padding karena banyak digunakan di banyak proyek.

3.7. Ukuran Data Setelah Enkripsi

Seperti disebutkan sebelumnya, AES memiliki ukuran blok 128 bit atau 16 byte. AES tidak mengubah ukuran, dan ukuran ciphertext sama dengan ukuran cleartext. Selain itu, dalam mode ECB dan CBC, kita harus menggunakan algoritma padding seperti PKCS 5. Jadi, ukuran data setelah enkripsi adalah:

ciphertext_size (bytes) = cleartext_size + (16 - (cleartext_size % 16))

Untuk menyimpan IV dengan ciphertext, kita perlu menambahkan 16 byte lagi.

4. Parameter AES

Dalam algoritma AES, kita membutuhkan tiga parameter: data masukan, kunci rahasia, dan IV. IV tidak digunakan dalam mode ECB.

4.1. Memasukan data

Data masukan ke AES dapat berupa string, file, objek, dan kata sandi.

4.2. Kunci rahasia

There are two ways for generating a secret key in the AES: generating from a random number or deriving from a given password.

In the first approach, the secret key should be generated from a Cryptographically Secure (Pseudo-)Random Number Generator like the SecureRandom class.

For generating a secret key, we can use the KeyGenerator class. Let’s define a method for generating the AES key with the size of n (128, 192, and 256) bits:

public static SecretKey generateKey(int n) throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(n); SecretKey key = keyGenerator.generateKey(); return key; }

In the second approach, the AES secret key can be derived from a given password using a password-based key derivation function like PBKDF2. We also need a salt value for turning a password into a secret key. The salt is also a random value.

We can use the SecretKeyFactory class with the PBKDF2WithHmacSHA256 algorithm for generating a key from a given password.

Let’s define a method for generating the AES key from a given password with 65,536 iterations and a key length of 256 bits:

public static SecretKey getKeyFromPassword(String password, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256); SecretKey secret = new SecretKeySpec(factory.generateSecret(spec) .getEncoded(), "AES"); return secret; }

4.3. Initialization Vector (IV)

IV is a pseudo-random value and has the same size as the block that is encrypted. We can use the SecureRandom class to generate a random IV.

Let’s define a method for generating an IV:

public static IvParameterSpec generateIv() { byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); return new IvParameterSpec(iv); }

5. Encryption and Decryption

5.1. String

To implement input string encryption, we first need to generate the secret key and IV according to the previous section. As the next step, we create an instance from the Cipher class by using the getInstance() method.

Additionally, we configure a cipher instance using the init() method with a secret key, IV, and encryption mode. Finally, we encrypt the input string by invoking the doFinal() method. This method gets bytes of input and returns ciphertext in bytes:

public static String encrypt(String algorithm, String input, SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, key, iv); byte[] cipherText = cipher.doFinal(input.getBytes()); return Base64.getEncoder() .encodeToString(cipherText); }

For decrypting an input string, we can initialize our cipher using the DECRYPT_MODE to decrypt the content:

public static String decrypt(String algorithm, String cipherText, SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, key, iv); byte[] plainText = cipher.doFinal(Base64.getDecoder() .decode(cipherText)); return new String(plainText); }

Let's write a test method for encrypting and decrypting a string input:

@Test void givenString_whenEncrypt_thenSuccess() throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { String input = "baeldung"; SecretKey key = AESUtil.generateKey(128); IvParameterSpec ivParameterSpec = AESUtil.generateIv(); String algorithm = "AES/CBC/PKCS5Padding"; String cipherText = AESUtil.encrypt(algorithm, input, key, ivParameterSpec); String plainText = AESUtil.decrypt(algorithm, cipherText, key, ivParameterSpec); Assertions.assertEquals(input, plainText); }

5.2. File

Now let's encrypt a file using the AES algorithm. The steps are the same, but we need some IO classes to work with the files. Let's encrypt a text file:

public static void encryptFile(String algorithm, SecretKey key, IvParameterSpec iv, File inputFile, File outputFile) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, key, iv); FileInputStream inputStream = new FileInputStream(inputFile); FileOutputStream outputStream = new FileOutputStream(outputFile); byte[] buffer = new byte[64]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { byte[] output = cipher.update(buffer, 0, bytesRead); if (output != null) { outputStream.write(output); } } byte[] outputBytes = cipher.doFinal(); if (outputBytes != null) { outputStream.write(outputBytes); } inputStream.close(); outputStream.close(); }

Please note that trying to read the entire file – particularly if it is large – into memory is not recommended. Instead, we encrypt a buffer at a time.

For decrypting a file, we use similar steps and initialize our cipher using DECRYPT_MODE as we saw before.

Again, let's define a test method for encrypting and decrypting a text file. In this method, we read the baeldung.txt file from the test resource directory, encrypt it into a file called baeldung.encrypted, and then decrypt the file into a new file:

@Test void givenFile_whenEncrypt_thenSuccess() throws NoSuchAlgorithmException, IOException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { SecretKey key = AESUtil.generateKey(128); String algorithm = "AES/CBC/PKCS5Padding"; IvParameterSpec ivParameterSpec = AESUtil.generateIv(); Resource resource = new ClassPathResource("inputFile/baeldung.txt"); File inputFile = resource.getFile(); File encryptedFile = new File("classpath:baeldung.encrypted"); File decryptedFile = new File("document.decrypted"); AESUtil.encryptFile(algorithm, key, ivParameterSpec, inputFile, encryptedFile); AESUtil.decryptFile( algorithm, key, ivParameterSpec, encryptedFile, decryptedFile); assertThat(inputFile).hasSameTextualContentAs(decryptedFile); }

5.3. Password-Based

We can do the AES encryption and decryption using the secret key that is derived from a given password.

For generating a secret key, we use the getKeyFromPassword() method. The encryption and decryption steps are the same as those shown in the string input section. We can then use the instantiated cipher and the provided secret key to perform the encryption.

Let's write a test method:

@Test void givenPassword_whenEncrypt_thenSuccess() throws InvalidKeySpecException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { String plainText = "www.baeldung.com"; String password = "baeldung"; String salt = "12345678"; IvParameterSpec ivParameterSpec = AESUtil.generateIv(); SecretKey key = AESUtil.getKeyFromPassword(password,salt); String cipherText = AESUtil.encryptPasswordBased(plainText, key, ivParameterSpec); String decryptedCipherText = AESUtil.decryptPasswordBased( cipherText, key, ivParameterSpec); Assertions.assertEquals(plainText, decryptedCipherText); }

5.4. Object

For encrypting a Java object, we need to use the SealedObject class. The object should be Serializable. Let's begin by defining a Student class:

public class Student implements Serializable { private String name; private int age; // standard setters and getters } 

Next, let's encrypt the Student object :

public static SealedObject encryptObject(String algorithm, Serializable object, SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, key, iv); SealedObject sealedObject = new SealedObject(object, cipher); return sealedObject; }

The encrypted object can later be decrypted using the correct cipher:

public static Serializable decryptObject(String algorithm, SealedObject sealedObject, SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, ClassNotFoundException, BadPaddingException, IllegalBlockSizeException, IOException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, key, iv); Serializable unsealObject = (Serializable) sealedObject.getObject(cipher); return unsealObject; }

Let's write a test case:

@Test void givenObject_whenEncrypt_thenSuccess() throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, IOException, BadPaddingException, ClassNotFoundException { Student student = new Student("Baeldung", 20); SecretKey key = AESUtil.generateKey(128); IvParameterSpec ivParameterSpec = AESUtil.generateIv(); String algorithm = "AES/CBC/PKCS5Padding"; SealedObject sealedObject = AESUtil.encryptObject( algorithm, student, key, ivParameterSpec); Student object = (Student) AESUtil.decryptObject( algorithm, sealedObject, key, ivParameterSpec); assertThat(student).isEqualToComparingFieldByField(object); }

6. Conclusion

Singkatnya, kita telah mempelajari cara mengenkripsi dan mendekripsi data input seperti string, file, objek, dan data berbasis kata sandi, menggunakan algoritma AES di Java. Selain itu, kami telah membahas variasi AES dan ukuran data setelah enkripsi.

Seperti biasa, kode sumber lengkap artikel tersedia di GitHub.

Jawa bawah

Saya baru saja mengumumkan kursus Learn Spring baru , yang berfokus pada dasar-dasar Spring 5 dan Spring Boot 2:

>> LIHAT KURSUSnya