Kriptografi di Jawa

Halo, Habr! Saya hadir untuk Anda terjemahan artikel "Java Cryptography" oleh Jakob Jenkov.


Publikasi ini adalah terjemahan dari artikel Kriptografi Java pertama dari serangkaian artikel untuk pemula yang ingin mempelajari dasar-dasar kriptografi di Jawa.


Daftar isi:


  1. Kriptografi Jawa
  2. Sandi
  3. Pesan pesan
  4. Mac
  5. Tanda tangan
  6. Keypair
  7. Keygenerator
  8. KeyPairGenerator
  9. Keystore
  10. Keytool
  11. Sertifikat
  12. Pabrik Sertifikat
  13. CertPath

Kriptografi Jawa


Java Cryptography API menyediakan kemampuan untuk mengenkripsi dan mendekripsi data dalam java, serta mengelola kunci, tanda tangan dan mengotentikasi (mengotentikasi) pesan, menghitung hash kriptografi dan banyak lagi.


Artikel ini menjelaskan dasar-dasar cara menggunakan Java Cryptography API untuk melakukan berbagai tugas yang memerlukan enkripsi aman.


Artikel ini tidak menjelaskan dasar-dasar teori kriptografi. Anda harus melihat informasi ini di tempat lain.


Ekstensi Kriptografi Java


API kriptografi Java disediakan oleh apa yang disebut Java Cryptography Extension (JCE). JCE telah lama menjadi bagian dari platform Java. Awalnya, JCE dipisahkan dari Jawa karena pembatasan ekspor pada teknologi enkripsi di Amerika Serikat. Oleh karena itu, algoritma enkripsi terkuat tidak termasuk dalam platform Java standar. Algoritme enkripsi yang lebih kuat ini dapat diterapkan jika perusahaan Anda berlokasi di AS, tetapi dalam kasus lain Anda harus menggunakan algoritma yang lebih lemah atau menerapkan algoritma enkripsi Anda sendiri dan menghubungkannya ke JCE.


Sejak 2017, aturan untuk mengekspor algoritma enkripsi di Amerika Serikat telah dilonggarkan secara signifikan, dan di sebagian besar dunia Anda dapat menggunakan standar enkripsi internasional melalui Java JCE.


Arsitektur Kriptografi Jawa


Java Cryptography Architecture (JCA) adalah nama dari desain API kriptografi internal di Jawa. JCA terstruktur di sekitar beberapa kelas inti dan antarmuka tujuan umum. Fungsi sebenarnya dari antarmuka ini disediakan oleh pemasok. Dengan demikian, Anda dapat menggunakan kelas Cipher untuk mengenkripsi dan mendekripsi beberapa data, tetapi implementasi spesifik dari cipher (algoritma enkripsi) tergantung pada penyedia tertentu yang digunakan.


Anda juga dapat menerapkan dan menghubungkan penyedia Anda sendiri, tetapi Anda harus berhati-hati dengan ini. Mengimplementasikan enkripsi dengan benar tanpa lubang keamanan itu sulit! Jika Anda tidak tahu apa yang Anda lakukan, Anda mungkin lebih baik menggunakan penyedia Java bawaan atau menggunakan penyedia tepercaya seperti Bouncy Castle.


Kelas dan antarmuka utama


API Java Cryptography terdiri dari paket-paket Java berikut:


  • keamanan java
  • java.security.cert
  • java.security.spec
  • java.security.interfaces
  • javax.crypto
  • javax.crypto.spec
  • javax.crypto.interfaces

Kelas utama dan antarmuka dari paket ini:


  • Penyedia
  • SecureRandom
  • Sandi
  • Pesan pesan
  • Tanda tangan
  • Mac
  • Parameter Algoritma
  • AlgorithmParameterGenerator
  • Pabrik kunci
  • SecretKeyFactory
  • KeyPairGenerator
  • Keygenerator
  • Kesepakatan kunci
  • Keystore
  • Pabrik Sertifikat
  • CertPathBuilder
  • CertPathValidator
  • CertStore

Penyedia


Kelas Provider (java.security.Provider) adalah kelas pusat di Java crypto API. Untuk menggunakan Java crypto API, Anda harus menginstal penyedia kriptografi. Java SDK hadir dengan penyedia kriptografinya sendiri. Kecuali Anda secara eksplisit mengatur penyedia kriptografi, penyedia default akan digunakan. Namun, penyedia kriptografi ini mungkin tidak mendukung algoritma enkripsi yang ingin Anda gunakan. Karena itu, Anda mungkin harus menginstal penyedia kriptografi Anda sendiri.


Salah satu penyedia kriptografi paling populer untuk Java crypto API disebut Bouncy Castle. Berikut adalah contoh di mana BouncyCastleProvider ditetapkan sebagai penyedia kriptografi:


import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.Security; public class ProviderExample { public static void main(String[] args) { Security.addProvider(new BouncyCastleProvider()); } } 

Sandi


Kelas Cipher (javax.crypto.Cipher) mewakili algoritma kriptografi. Sebuah cipher dapat digunakan baik untuk enkripsi maupun untuk mendekripsi data. Kelas Cipher dijelaskan secara lebih rinci di bagian berikut, dengan deskripsi singkat di bawah ini.


Membuat instance kelas cipher yang menggunakan algoritma enkripsi AES untuk penggunaan internal:


 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

Metode Cipher.getInstance (...) menerima string yang menentukan algoritma enkripsi mana yang akan digunakan, serta beberapa parameter algoritma lainnya.
Dalam contoh di atas:


  • AES - algoritma enkripsi
  • CBC adalah mode di mana algoritma AES dapat bekerja.
  • PKCS5Padding adalah bagaimana algoritma AES harus menangani byte data terakhir untuk enkripsi. Apa sebenarnya artinya ini, lihat di manual kriptografi secara keseluruhan, dan tidak di artikel ini.

Inisialisasi sandi


Sebelum menggunakan contoh sandi, Anda harus menginisialisasi. Cipher instance diinisialisasi dengan memanggil metode init () . Metode init () mengambil dua parameter:


  • Mode - Enkripsi / Dekripsi
  • Kunci

Parameter pertama menunjukkan mode operasi instance cipher: untuk mengenkripsi atau mendekripsi data. Parameter kedua menunjukkan kunci mana yang mereka gunakan untuk mengenkripsi atau mendekripsi data.


Contoh:


 byte[] keyBytes = new byte[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; String algorithm = "RawBytes"; SecretKeySpec key = new SecretKeySpec(keyBytes, algorithm); cipher.init(Cipher.ENCRYPT_MODE, key); 

Harap dicatat bahwa metode pembuatan kunci dalam contoh ini tidak aman dan tidak boleh digunakan dalam praktik. Artikel ini di bagian berikut akan menjelaskan cara membuat kunci lebih aman.


Untuk menginisialisasi instance cipher untuk mendekripsi data, Anda harus menggunakan Cipher.DECRYPT_MODE, misalnya:


 cipher.init(Cipher.DECRYPT_MODE, key); 

Enkripsi atau Dekripsi Data


Setelah menginisialisasi sandi, Anda dapat mulai mengenkripsi atau mendekripsi data dengan memanggil metode pembaruan () atau doFinal () . Metode pembaruan () digunakan jika Anda mengenkripsi atau mendekripsi data. Metode doFinal () dipanggil saat Anda mengenkripsi potongan data terakhir atau jika blok data yang Anda lewati ke doFinal () adalah satu set data untuk enkripsi.


Contoh enkripsi data menggunakan metode doFinal () :


 byte[] plainText = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] cipherText = cipher.doFinal(plainText); 

Untuk mendekripsi data, Anda harus meneruskan ciphertext (data) ke metode doFinal () atau doUpdate () .


Kunci


Untuk mengenkripsi atau mendekripsi data, Anda memerlukan kunci. Ada dua jenis kunci, tergantung pada jenis algoritma enkripsi apa yang digunakan:


  • Kunci simetris
  • Kunci asimetris

Kunci simetris digunakan untuk algoritma enkripsi simetris. Algoritma enkripsi simetris menggunakan kunci yang sama untuk enkripsi dan dekripsi.
Kunci asimetris digunakan untuk algoritma enkripsi asimetris. Algoritma enkripsi asimetris menggunakan satu kunci untuk enkripsi dan yang lainnya untuk dekripsi. Algoritma enkripsi kunci publik dan pribadi adalah contoh dari algoritma enkripsi asimetris.


Entah bagaimana, pihak yang perlu mendekripsi data harus mengetahui kunci yang diperlukan untuk mendekripsi data. Jika decryptor bukan merupakan pihak dalam enkripsi data, kedua pihak harus menyetujui kunci atau bertukar kunci. Ini disebut pertukaran kunci.


Keamanan Kunci


Kunci harus sulit ditebak sehingga penyerang tidak dapat dengan mudah mengambil kunci enkripsi. Dalam contoh dari bagian sebelumnya pada kelas Cipher, kunci yang sangat sederhana, kode keras digunakan. Dalam praktiknya, ini tidak layak dilakukan. Jika kunci para pihak mudah ditebak, penyerang akan dapat mendekripsi data yang dienkripsi dan, mungkin, membuat pesan palsu sendiri. Penting untuk membuat kunci yang sulit ditebak. Jadi, kuncinya harus terdiri dari byte acak. Semakin banyak byte acak, semakin sulit untuk menebak, karena ada lebih banyak kemungkinan kombinasi.


Generasi kunci


Untuk menghasilkan kunci enkripsi acak, Anda bisa menggunakan kelas Java KeyGenerator. KeyGenerator akan dijelaskan secara lebih rinci dalam bab-bab berikut, berikut adalah contoh kecil penggunaannya di sini:


 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); SecureRandom secureRandom = new SecureRandom(); int keyBitSize = 256; keyGenerator.init(keyBitSize, secureRandom); SecretKey secretKey = keyGenerator.generateKey(); 

Contoh SecretKey yang dihasilkan dapat diteruskan ke metode Cipher.init () , misalnya seperti ini:


 cipher.init(Cipher.ENCRYPT_MODE, secretKey); 

Generasi pasangan kunci


Algoritma enkripsi asimetris menggunakan pasangan kunci yang terdiri dari kunci publik dan kunci pribadi untuk mengenkripsi dan mendekripsi data. Untuk membuat pasangan kunci asimetris, Anda dapat menggunakan KeyPairGenerator (java.security.KeyPairGenerator). KeyPairGenerator akan dijelaskan secara lebih rinci dalam bab-bab berikut, di bawah ini adalah contoh sederhana menggunakan Java KeyPairGenerator:


 SecureRandom secureRandom = new SecureRandom(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); 

Toko Kunci


Java KeyStore adalah database yang dapat berisi kunci. Java KeyStore diwakili oleh kelas KeyStore (java.security.KeyStore). Keystore dapat berisi kunci dari jenis berikut:


  • Kunci pribadi
  • Kunci dan sertifikat publik (Kunci publik + sertifikat)
  • Kunci rahasia

Kunci privat dan publik digunakan dalam enkripsi asimetris. Kunci publik mungkin memiliki sertifikat terkait. Sertifikat adalah dokumen yang membuktikan identitas seseorang, organisasi, atau perangkat yang mengklaim memiliki kunci publik.


Sertifikat biasanya ditandatangani secara digital oleh pihak yang bersandar sebagai bukti.
Kunci privat digunakan dalam enkripsi simetris. Kelas KeyStore cukup kompleks, itulah sebabnya ia dijelaskan lebih rinci nanti dalam bab terpisah tentang Java KeyStore.


Alat Manajemen Kunci (Keytool)


Java Keytool adalah alat baris perintah yang dapat bekerja dengan file Java KeyStore. Keytool dapat menghasilkan pasangan kunci dalam file KeyStore, sertifikat ekspor dan sertifikat impor ke KeyStore dan beberapa fungsi lainnya. Keytool hadir dengan instalasi Java. Keytool dijelaskan secara lebih rinci nanti dalam bab terpisah tentang Java Keytool.


Pesan Intisari


Ketika Anda menerima data terenkripsi dari sisi lain, dapatkah Anda yakin bahwa tidak ada yang mengubah data terenkripsi dalam perjalanan ke Anda?


Biasanya, solusinya adalah menghitung intisari pesan dari data sebelum mengenkripsi, kemudian mengenkripsi data dan intisari pesan, dan mengirimkannya melalui jaringan. Intisari pesan adalah nilai hash yang dihitung berdasarkan data pesan. Jika setidaknya satu byte diubah dalam data terenkripsi, intisari pesan yang dihitung dari data juga akan berubah.


Saat Anda menerima data terenkripsi, Anda mendekripsi, menghitung intisari pesan darinya, dan membandingkan intisari pesan yang dihitung dengan intisari dari pesan yang dikirim bersama dengan data terenkripsi. Jika dua pesan dicerna sama, ada probabilitas tinggi (tetapi tidak 100%) bahwa data belum diubah.


Java MessageDigest (java.security.MessageDigest) dapat digunakan untuk menghitung intisari pesan. Untuk membuat instance MessageDigest, metode MessageDigest.getInstance () dipanggil. Ada beberapa algoritma intisari pesan yang berbeda. Anda perlu menentukan algoritma mana yang ingin Anda gunakan saat membuat instance MessageDigest. Bekerja dengan MessageDigest akan dijelaskan lebih terinci di bab Java MessageDigest.


Pengantar singkat ke kelas MessageDigest:


 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); 

Contoh ini membuat instance MessageDigest yang menggunakan algoritma hashing kriptografi internal SHA-256 untuk menghitung intisari pesan.


Untuk menghitung intisari pesan dari beberapa data, Anda memanggil metode update () atau digest () . Metode pembaruan () dapat dipanggil beberapa kali, dan intisari pesan diperbarui di dalam objek. Ketika Anda telah melewati semua data yang ingin Anda sertakan dalam intisari pesan, Anda memanggil intisari () dan mengambil ringkasan intisari pesan.


Contoh pembaruan panggilan () beberapa kali, diikuti dengan panggilan untuk dicerna () :


 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); byte[] data1 = "0123456789".getBytes("UTF-8"); byte[] data2 = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8"); messageDigest.update(data1); messageDigest.update(data2); byte[] digest = messageDigest.digest(); 

Anda juga dapat memanggil digest () sekali, meneruskan semua data untuk menghitung intisari pesan. Contoh:


 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); byte[] data1 = "0123456789".getBytes("UTF-8"); byte[] digest = messageDigest.digest(data1); 

Kode Otentikasi Pesan (MAC)


Kelas Java Mac digunakan untuk membuat MAC (Message Authentication Code) dari pesan. MAC mirip dengan intisari pesan, tetapi menggunakan kunci tambahan untuk mengenkripsi intisari pesan. Hanya memiliki data sumber dan kunci, Anda dapat memeriksa MAC. Jadi, MAC adalah cara yang lebih aman untuk melindungi blok data dari modifikasi daripada intisari pesan. Kelas Mac dijelaskan secara lebih rinci di bab Java Mac, diikuti oleh pengantar singkat.


Contoh Java Mac dibuat dengan memanggil metode Mac.getInstance () , dengan memasukkan nama algoritma yang digunakan sebagai parameter. Begini tampilannya:


 Mac mac = Mac.getInstance("HmacSHA256"); 

Sebelum Anda membuat MAC dari data, Anda harus menginisialisasi instance Mac dengan kunci. Berikut adalah contoh menginisialisasi instance Mac dengan kunci:


 byte[] keyBytes = new byte[]{0,1,2,3,4,5,6,7,8 ,9,10,11,12,13,14,15}; String algorithm = "RawBytes"; SecretKeySpec key = new SecretKeySpec(keyBytes, algorithm); mac.init(key); 

Setelah menginisialisasi instance Mac, Anda dapat menghitung MAC dari data dengan memanggil metode update () dan doFinal () . Jika Anda memiliki semua data untuk menghitung MAC, Anda dapat segera memanggil metode doFinal () . Begini tampilannya:


 byte[] data = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8"); byte[] data2 = "0123456789".getBytes("UTF-8"); mac.update(data); mac.update(data2); byte[] macBytes = mac.doFinal(); 

Tanda tangan


Kelas Signature (java.security.Signature) digunakan untuk menandatangani data secara digital. Ketika data ditandatangani, tanda tangan digital dibuat dari data ini. Dengan demikian, tanda tangan dipisahkan dari data.


Tanda tangan digital dibuat dengan membuat intisari pesan (hash) dari data dan mengenkripsi intisari pesan ini dengan kunci pribadi perangkat, orang atau organisasi yang harus menandatangani data. Intisari pesan terenkripsi disebut tanda tangan digital.


Untuk membuat turunan dari Signature, metode Signature.getInstance (...) dipanggil:


 Signature signature = Signature.getInstance("SHA256WithDSA"); 

Tanda tangan data


Untuk menandatangani data, Anda harus menginisialisasi instance tanda tangan dalam mode tanda tangan dengan memanggil metode initSign (...), meneruskan kunci pribadi untuk menandatangani data. Contoh menginisialisasi instance tanda tangan dalam mode tanda tangan:


 signature.initSign(keyPair.getPrivate(), secureRandom); 

Setelah menginisialisasi instance tanda tangan, ini dapat digunakan untuk menandatangani data. Ini dilakukan dengan memanggil metode update (), meneruskan data tanda tangan sebagai parameter. Anda dapat memanggil metode pembaruan () beberapa kali untuk melengkapi data untuk membuat tanda tangan. Setelah semua data diteruskan ke metode pembaruan (), metode tanda () dipanggil untuk mendapatkan tanda tangan digital. Begini tampilannya:


 byte[] data = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8"); signature.update(data); byte[] digitalSignature = signature.sign(); 

Verifikasi Tanda Tangan


Untuk memverifikasi tanda tangan, Anda perlu menginisialisasi instance tanda tangan dalam mode verifikasi dengan memanggil metode initVerify (...) , dengan memasukkan sebagai parameter kunci publik yang digunakan untuk memverifikasi tanda tangan. Contoh menginisialisasi instance tanda tangan dalam mode verifikasi terlihat seperti ini:


 Signature signature = Signature.getInstance("SHA256WithDSA"); signature.initVerify(keyPair.getPublic()); 

Setelah inisialisasi dalam mode verifikasi, data yang ditandatangani dikirim ke metode pembaruan () . Panggilan ke metode verifikasi () mengembalikan benar atau salah tergantung pada apakah tanda tangan dapat diverifikasi atau tidak. Berikut ini adalah verifikasi tanda tangan:


 byte[] data2 = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8"); signature2.update(data2); boolean verified = signature2.verify(digitalSignature); 

Contoh Tanda Tangan dan Verifikasi Lengkap


 SecureRandom secureRandom = new SecureRandom(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); KeyPair keyPair = keyPairGenerator.generateKeyPair(); Signature signature = Signature.getInstance("SHA256WithDSA"); signature.initSign(keyPair.getPrivate(), secureRandom); byte[] data = "abcdefghijklmnopqrstuvxyz".getBytes("UTF-8"); signature.update(data); byte[] digitalSignature = signature.sign(); Signature signature2 = Signature.getInstance("SHA256WithDSA"); signature2.initVerify(keyPair.getPublic()); signature2.update(data); boolean verified = signature2.verify(digitalSignature); System.out.println("verified = " + verified); 

Source: https://habr.com/ru/post/id444764/


All Articles