Panduan ke Kelas Sandi

1. Ikhtisar

Sederhananya, enkripsi adalah proses pengkodean pesan sedemikian rupa sehingga hanya pengguna yang berwenang yang dapat memahami atau mengaksesnya.

Pesan tersebut, disebut sebagai teks biasa , dienkripsi menggunakan algoritme enkripsi - teks sandi yang menghasilkan sandi yang hanya dapat dibaca oleh pengguna yang berwenang melalui dekripsi.

Pada artikel ini, kami menjelaskan secara rinci kelas Cipher inti , yang menyediakan fungsionalitas enkripsi dan dekripsi kriptografi di Java.

2. Kelas Cipher

Java Cryptography Extension (JCE) adalah bagian dari Java Cryptography Architecture (JCA) yang menyediakan aplikasi dengan cipher kriptografi untuk enkripsi dan dekripsi data serta hashing data pribadi.

Kelas Cipher - terletak di paket javax.crypto - membentuk inti dari kerangka JCE, menyediakan fungsionalitas untuk enkripsi dan dekripsi.

2.1. Instansiasi Cipher

Untuk membuat instance objek Cipher , kita memanggil metode getInstance statis , dengan meneruskan nama transformasi yang diminta . Secara opsional, nama penyedia dapat ditentukan.

Mari kita tulis kelas contoh yang menggambarkan instansiasi sebuah Cipher :

public class Encryptor { public byte[] encryptMessage(byte[] message, byte[] keyBytes) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); //... } }

Transformasi AES / ECB / PKCS5Padding memberi tahu metode getInstance untuk membuat instance objek Cipher sebagai cipher AES dengan mode operasi ECB dan skema padding PKCS5.

Kita juga dapat membuat instance objek Cipher dengan hanya menentukan algoritme dalam transformasi:

Cipher cipher = Cipher.getInstance("AES");

Dalam kasus ini, Java akan menggunakan nilai default khusus penyedia untuk mode dan skema padding.

Perhatikan bahwa getInstance akan menampilkan NoSuchAlgorithmException jika transformasinya null , kosong, atau dalam format yang tidak valid, atau jika penyedia tidak mendukungnya.

Ini akan memunculkan NoSuchPaddingException jika transformasi berisi skema padding yang tidak didukung.

2.2. Keamanan Benang

Kelas Cipher adalah kelas stateful tanpa bentuk sinkronisasi internal apa pun. Faktanya, metode seperti init () atau update () akan mengubah status internal instance Cipher tertentu .

Oleh karena itu, kelas Cipher tidak aman untuk thread. Jadi kita harus membuat satu contoh Cipher per kebutuhan enkripsi / dekripsi.

2.3. Kunci

The Key antarmuka merupakan kunci untuk operasi kriptografi. Kunci adalah wadah buram yang menyimpan kunci yang dienkode, format enkode kunci, dan algoritme kriptografinya.

Kunci umumnya diperoleh melalui pembuat kunci, sertifikat, atau spesifikasi kunci menggunakan pabrik kunci.

Mari buat Kunci simetris dari byte kunci yang disediakan:

SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");

2.4. Inisialisasi Cipher

Kita sebut init () metode untuk menginisialisasi C ipher objek dengan Key atau Sertifikat dan opmode menunjukkan mode operasi dari cipher.

Secara opsional, kita bisa memasukkan sumber keacakan . Secara default, implementasi SecureRandom dari penyedia terinstal dengan prioritas tertinggi digunakan. Jika tidak, itu akan menggunakan sumber yang disediakan sistem.

Kami dapat menentukan sekumpulan parameter khusus algoritme secara opsional. Misalnya, kita bisa meneruskan IvParameterSpec untuk menentukan vektor inisialisasi .

Berikut adalah mode operasi penyandian yang tersedia:

  • ENCRYPT_MODE : initialize cipher objek untuk mode enkripsi
  • DECRYPT_MODE : menginisialisasi objek penyandian ke mode dekripsi
  • WRAP_MODE : menginisialisasi objek penyandian ke mode pembungkusan kunci
  • UNWRAP_MODE : menginisialisasi objek penyandian ke mode pembukaan kunci

Mari kita inisialisasi objek Cipher :

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); // ...

Sekarang, metode init memunculkan InvalidKeyException jika kunci yang diberikan tidak sesuai untuk menginisialisasi cipher, seperti ketika panjang kunci / encoding tidak valid.

Ini juga dilemparkan saat sandi memerlukan parameter algoritme tertentu yang tidak dapat ditentukan dari kunci, atau jika kunci memiliki ukuran kunci yang melebihi ukuran kunci maksimum yang diizinkan (ditentukan dari file kebijakan yurisdiksi JCE yang dikonfigurasi).

Mari kita lihat contoh menggunakan Sertifikat :

public byte[] encryptMessage(byte[] message, Certificate certificate) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, certificate); // ... }

The Cipher objek mendapatkan kunci publik untuk enkripsi data dari sertifikat dengan memanggil getPublicKey metode.

2.5. Enkripsi dan Dekripsi

Setelah menginisialisasi objek Cipher , kita memanggil metode doFinal () untuk melakukan operasi enkripsi atau dekripsi. Metode ini mengembalikan array byte yang berisi pesan yang dienkripsi atau didekripsi.

Metode doFinal () juga menyetel ulang objek Cipher ke keadaan semula saat diinisialisasi sebelumnya melalui panggilan ke metode init () , membuat objek Cipher tersedia untuk mengenkripsi atau mendekripsi pesan tambahan.

Mari panggil doFinal dalam metode encryptMessage kami :

public byte[] encryptMessage(byte[] message, byte[] keyBytes) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(message); }

Untuk melakukan operasi dekripsi, kami mengubah opmode menjadi DECRYPT_MODE :

public byte[] decryptMessage(byte[] encryptedMessage, byte[] keyBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(encryptedMessage); }

2.6. Penyedia

Didesain untuk menggunakan arsitektur berbasis penyedia, JCE memungkinkan pustaka kriptografi yang memenuhi syarat seperti BouncyCastle untuk dicolokkan sebagai penyedia keamanan dan algoritme baru ditambahkan dengan mulus .

Now let's add BouncyCastle as a security provider. We can add a security provider either statically or dynamically.

To add BouncyCastle statically, we modify the java.security file located in /jre/lib/security folder.

We add the line at the end of the list:

... security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider

When adding the provider property, the property key is in the format security.provider.N where the number N is one more than the last one on the list.

We can also add the BouncyCastle security provider dynamically without having to modify the security file:

Security.addProvider(new BouncyCastleProvider());

We can now specify the provider during cipher initialization:

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");

BC specifies BouncyCastle as the provider. We can get the list of registered providers via the Security.getProviders() method.

3. Testing Encryption and Decryption

Let's write an example test to illustrate message encryption and decryption.

In this test, we use AES encryption algorithm with a 128-bit key and assert that the decrypted result is equal to the original message text:

@Test public void whenIsEncryptedAndDecrypted_thenDecryptedEqualsOriginal() throws Exception { String encryptionKeyString = "thisisa128bitkey"; String originalMessage = "This is a secret message"; byte[] encryptionKeyBytes = encryptionKeyString.getBytes(); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(encryptionKeyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedMessageBytes = cipher.doFinal(message.getBytes()); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedMessageBytes = cipher.doFinal(encryptedMessageBytes); assertThat(originalMessage).isEqualTo(new String(decryptedMessageBytes)); }

4. Conclusion

In this article, we discussed the Cipher class and presented usage examples. More details on the Cipher class and the JCE Framework can be found in the class documentation and the Java Cryptography Architecture (JCA) Reference Guide.

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di GitHub . Ini adalah proyek berbasis Maven, jadi semestinya mudah untuk mengimpor dan menjalankan apa adanya.