02 Januari 2018
Algoritma Kriptografi AES dengan Java

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.*[doc] dan javax.crypto.spec.*[doc]. 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[doc] 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[doc], dari nilai acak tersebut kita bentuk objek IvParameterSpec[doc].

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[doc]. Tahapan enkripsi adalah sebagai berikut :

  1. Membentuk objek Cipher menggunakan metode static getInstance()[doc]
  2. Inisialisasi objek menggunakan metode init()[doc]. Dalam metode tersebut kita menyebutkan beberapa parameter yaitu Cipher.ENCRYPT_MODE[doc], keySpec dan ivSpec
  3. Untuk mendapatkan ciphertext dari pesan asli kita gunakan metode doFinal()[doc]. Dalam pemanggilannya kita gunakan satu parameter input yaitu bentuk array byte dari parameter pesan

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 :

  1. 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 metode encodeToString()[doc] dari objek Base64.Encoder[doc]
  2. Nilai array byte bIv yang digunakan pada proses enkripsi akan diperlukan pada proses dekripsi nantinya. Jadi selain cipher text, kita juga perlu mengembalikan nilai bIv tersebut. Yang bisa dilakukan disini adalah meng-encode bIv menjadi 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 :

  1. Membentuk objek Cipher menggunakan metode statik getInstance()
  2. Inisialisasi objek menggunakan metode init(). Parameter yang kita gunakan adalah Cipher.DECRYPT_MODE[doc], keySpec, dan ivSpec
  3. Proses dekripsi dilakukan dengan metode doFinal() dengan satu parameter yaitu bEnc
  4. 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);
  }

}