Pada tulisan kali ini kita akan membahas tentang implementasi algoritma kriptografi AES (Advanced Encryption Standard) dengan menggunakan bahasa pemrograman Java. AES adalah algoritma kriptografi kunci simetris yang mengenkripsi dan mendekripsi pesan dengan menggunakan satu kunci yang sama. Pihak yang membutuhkan akses terhadap pesan harus memiliki kunci tersebut.
Proses kriptografi secara umum dalam pemrograman java diimplementasikan dalam paket java javax.crypto.*<sup>[doc]</sup> dan javax.crypto.spec.*<sup>[doc]</sup>. Pada tulisan kali ini kita akan mencoba membuat sebuah program java yang akan mengenkripsi dan mendekripsi pesan dengan algoritma AES dengan menggunakan beberapa class pada kedua paket tersebut.
Fungsi Enkripsi
Disini kita akan membuat sebuah fungsi statik yang akan mengembalikan ciphertext dari sebuah pesan. Fungsi ini menerima dua parameter String yaitu pesan asli (yang akan dienkripsi) dan kunci yang akan digunakan
private static String doEncrypt(String pesan, String kunci) throws Exception {
// code goes here
}
Langkah pertama yang harus dilakukan adalah membuat objek SecretKeySpec<sup>[doc]</sup> dari parameter kunci. Sebagai catatan panjang dari kunci harus 16, 24, atau 32 byte.
SecretKeySpec keySpec = new SecretKeySpec(kunci.getBytes(), "AES");
Selanjutnya kita akan membutuhkan Initialization Vector (IV) yaitu nilai byte acak dengan panjang 16, 24, atau 32 byte. Nilai ini akan di-generate menggunakan objek SecureRandom<sup>[doc]</sup>, dari nilai acak tersebut kita bentuk objek IvParameterSpec<sup>[doc]</sup>.
byte[] bIv = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(bIv);
IvParameterSpec ivSpec = new IvParameterSpec(bIv);
Sampai tahap ini kita sudah mulai bisa mengenkripsi pesan dengan menggunakan objek Cipher<sup>[doc]</sup>. Tahapan enkripsi adalah sebagai berikut :
- Membentuk objek
Ciphermenggunakan metode staticgetInstance()<sup>[doc]</sup> - Inisialisasi objek menggunakan metode
init()<sup>[doc]</sup>. Dalam metode tersebut kita menyebutkan beberapa parameter yaituCipher.ENCRYPT_MODE<sup>[doc]</sup>,keySpecdanivSpec - Untuk mendapatkan ciphertext dari pesan asli kita gunakan metode
doFinal()<sup>[doc]</sup>. Dalam pemanggilannya kita gunakan satu parameter input yaitu bentuk array byte dari parameterpesan
Kode program untuk tahap ini adalah sebagai berikut :
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] bEnc = c.doFinal(pesan.getBytes());
Sampai tahap ini, kita sudah memiliki cipher text dari pesan asli. Disini ada dua hal yang perlu diperhatikan :
- Cipher text yang kita miliki menggunakan tipe array byte (
bEnc), yang jika dikonversi menjadi String akan berpeluang menghasilkan karakter non-printable yang bisa mempengaruhi hasil dekripsi nantinya. Solusi yang bisa digunakan adalah dengan meng-encode array byte tersebut menjadi string dengan skema Base64. Untuk proses ini kita bisa gunakan metodeencodeToString()<sup>[doc]</sup> dari objekBase64.Encoder<sup>[doc]</sup> - Nilai array byte
bIvyang digunakan pada proses enkripsi akan diperlukan pada proses dekripsi nantinya. Jadi selain cipher text, kita juga perlu mengembalikan nilaibIvtersebut. Yang bisa dilakukan disini adalah meng-encodebIvmenjadi String dengan skema Base64 dan menggabungkannya dengan cipher text.
Perhatikan kode berikut :
String strEnc = Base64.getEncoder().encodeToString(bEnc);
String strIv = Base64.getEncoder().encodeToString(bIv);
return strIv + ":" + strEnc;
Kode lengkap untuk fungsi enkripsi adalah sebagai berikut :
private static String doEncrypt(String pesan, String kunci) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(kunci.getBytes(), "AES");
byte[] bIv = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(bIv);
IvParameterSpec ivSpec = new IvParameterSpec(bIv);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] bEnc = c.doFinal(pesan.getBytes());
String strEnc = Base64.getEncoder().encodeToString(bEnc);
String strIv = Base64.getEncoder().encodeToString(bIv);
return strIv + ":" + strEnc;
}
Fungsi diatas sudah bisa digunakan untuk mengenkripsi pesan. Yang perlu diperhatikan, fungsi tersebut akan menghasilkan cipher text yang berbeda tiap kali dipanggil walaupun dengan menggunakan kunci dan pesan yang sama.
Fungsi Dekripsi
Seperti sebelumnya kita perlu membuat fungsi statik untuk mendapatkan pesan asli dari cipher text
private static String doDecrypt(String cipher, String kunci) throws Exception {
// code goes here
}
Kemudian kita perlu membuat objek SecretKeySpec dari parameter kunci
SecretKeySpec keySpec = new SecretKeySpec(kunci.getBytes(), "AES");
Dari fungsi enkripsi yang sudah dikerjakan diatas kita ketahui bahwa nilai yang akan kita dapatkan adalah nilai String gabungan antara strIv dan strEnc yang dipisahkan dengan simbol titik dua ( : ). Yang perlu kita lakukan pertama adalah memisahkan keduanya menggunakan metode split(), dan karena keduanya menggunakan skema Base64, keduanya perlu di-decode terlebih dahulu.
String[] pair = cipher.split(":");
byte[] bIv = Base64.getDecoder().decode(pair[0]);
byte[] bEnc = Base64.getDecoder().decode(pair[1]);
Selanjutkan kita membentuk objek IvParameterSpec menggunakan variabel bIv
IvParameterSpec ivSpec = new IvParameterSpec(bIv);
Dari sini kita mulai bisa melakukan proses dekripsi, tahapan yang dilakukan adalah sebagai berikut :
- Membentuk objek
Ciphermenggunakan metode statikgetInstance() - Inisialisasi objek menggunakan metode
init(). Parameter yang kita gunakan adalahCipher.DECRYPT_MODE<sup>[doc]</sup>,keySpec, danivSpec - Proses dekripsi dilakukan dengan metode
doFinal()dengan satu parameter yaitubEnc - Kita bisa dapatkan pesan asli dengan membentuk objek String dari hasil dekripsi
Kode program untuk tahap ini adalah sebagai berikut :
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] bDec = c.doFinal(bEnc);
return new String(bDec);
Kode program selengkapnya untuk fungsi dekripsi adalah sebagai berikut :
private static String doDecrypt(String cipher, String kunci) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(kunci.getBytes(), "AES");
String[] pair = cipher.split(":");
byte[] bIv = Base64.getDecoder().decode(pair[0]);
byte[] bEnc = Base64.getDecoder().decode(pair[1]);
IvParameterSpec ivSpec = new IvParameterSpec(bIv);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] bDec = c.doFinal(bEnc);
return new String(bDec);
}
Kode program selengkapnya adalah sebagai berikut :
package com.sad301;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Main {
public static void main(String[] args) {
String key = "1n1p455w0rdk03+_"; // 16 bit
String plain = "Anak ayam turun sepuluh, mati satu tinggal sembilan";
try {
String cipher = doEncrypt(plain, key);
System.out.println(cipher);
plain = doDecrypt(cipher, key);
System.out.println(plain);
}
catch(Exception e) {
System.err.println(e.toString());
}
}
private static String doEncrypt(String pesan, String kunci) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(kunci.getBytes(), "AES");
byte[] bIv = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(bIv);
IvParameterSpec ivSpec = new IvParameterSpec(bIv);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] bEnc = c.doFinal(pesan.getBytes());
String strEnc = Base64.getEncoder().encodeToString(bEnc);
String strIv = Base64.getEncoder().encodeToString(bIv);
return strIv + ":" + strEnc;
}
private static String doDecrypt(String cipher, String kunci) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(kunci.getBytes(), "AES");
String[] pair = cipher.split(":");
byte[] bIv = Base64.getDecoder().decode(pair[0]);
byte[] bEnc = Base64.getDecoder().decode(pair[1]);
IvParameterSpec ivSpec = new IvParameterSpec(bIv);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] bDec = c.doFinal(bEnc);
return new String(bDec);
}
}