Kriptografi

Dokumen ini menjelaskan cara yang tepat untuk menggunakan fasilitas kriptografi Android dan menyertakan beberapa contoh penggunaannya. Jika aplikasi Anda membutuhkan keamanan kunci yang lebih ketat, gunakan sistem Android Keystore.

Menetapkan penyedia hanya dengan sistem Android Keystore

Jika menggunakan sistem Android Keystore, Anda harus menetapkan satu penyedia.

Namun, dalam situasi lain, Android tidak menjamin penyedia khusus untuk algoritma tertentu. Menetapkan penyedia tanpa menggunakan sistem Android Keystore dapat menyebabkan masalah kompatibilitas pada rilis mendatang.

Memilih algoritma yang direkomendasikan

Jika Anda boleh memilih algoritma apa pun yang akan digunakan (misalnya jika Anda tidak memerlukan kompatibilitas dengan sistem pihak ketiga), sebaiknya gunakan algoritma berikut:

Class Rekomendasi
Cipher AES dalam mode CBC atau GCM dengan kunci 256-bit (seperti AES/GCM/NoPadding)
MessageDigest Rangkaian SHA-2 (seperti SHA-256)
Mac HMAC rangkaian SHA-2 (seperti HMACSHA256)
Tanda Tangan Rangkaian SHA-2 dengan ECDSA (seperti SHA256withECDSA)

Melakukan operasi kriptografi umum

Bagian berikut menyertakan cuplikan yang menunjukkan bagaimana Anda dapat melakukan operasi kriptografi umum dalam aplikasi.

Mengenkripsi pesan

Kotlin

val plaintext: ByteArray = ...
val keygen = KeyGenerator.getInstance("AES")
keygen.init(256)
val key: SecretKey = keygen.generateKey()
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, key)
val ciphertext: ByteArray = cipher.doFinal(plaintext)
val iv: ByteArray = cipher.iv

Java

byte[] plaintext = ...;
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(256);
SecretKey key = keygen.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] iv = cipher.getIV();

Membuat ringkasan pesan

Kotlin

val message: ByteArray = ...
val md = MessageDigest.getInstance("SHA-256")
val digest: ByteArray = md.digest(message)

Java

byte[] message = ...;
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(message);

Membuat tanda tangan digital

Anda harus memiliki objek PrivateKey yang berisi kunci penandatanganan, yang dapat dibuat pada runtime, dibaca dari file yang dipaketkan dengan aplikasi Anda, atau didapatkan dari beberapa sumber lain menurut kebutuhan Anda.

Kotlin

val message: ByteArray = ...
val key: PrivateKey = ...
val s = Signature.getInstance("SHA256withECDSA")
        .apply {
            initSign(key)
            update(message)
        }
val signature: ByteArray = s.sign()

Java

byte[] message = ...;
PrivateKey key = ...;
Signature s = Signature.getInstance("SHA256withECDSA");
s.initSign(key);
s.update(message);
byte[] signature = s.sign();

Memverifikasi tanda tangan digital

Anda harus memiliki objek PublicKey yang berisi kunci publik penanda tangan, yang dapat dibaca dari file yang dipaketkan dengan aplikasi Anda, diekstrak dari sertifikat, atau diperoleh dari beberapa sumber lain menurut kebutuhan Anda.

Kotlin

val message: ByteArray = ...
val signature: ByteArray = ...
val key: PublicKey = ...
val s = Signature.getInstance("SHA256withECDSA")
        .apply {
            initVerify(key)
            update(message)
        }
val valid: Boolean = s.verify(signature)

Java

byte[] message = ...;
byte[] signature = ...;
PublicKey key = ...;
Signature s = Signature.getInstance("SHA256withECDSA");
s.initVerify(key);
s.update(message);
boolean valid = s.verify(signature);

Kompleksitas penerapan

Terdapat beberapa detail penerapan kriptografi Android yang tampak tidak biasa namun tetap disediakan karena pertimbangan kompatibilitas. Bagian ini membahas beberapa detail yang kemungkinan besar akan Anda jumpai.

Ringkasan pesan MGF1 OAEP

Cipher OAEP RSA dibuat menjadi parameter oleh dua ringkasan pesan yang berbeda: ringkasan “utama” dan ringkasan MGF1. Ada ID Cipher yang menyertakan nama ringkasan, seperti Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding"), yang menentukan ringkasan utama dan membiarkan ringkasan MGF1 tidak ditentukan. Untuk Android Keystore, SHA-1 digunakan untuk ringkasan MGF1, sedangkan untuk penyedia kriptografi Android lainnya, kedua ringkasan tersebut sama.

Untuk kontrol yang lebih menyeluruh atas ringkasan yang digunakan oleh aplikasi, minta cipher dengan OAEPPadding, seperti dalam Cipher.getInstance("RSA/ECB/OAEPPadding"), dan berikan OAEPParameterSpec ke init() untuk memilih kedua ringkasan secara eksplisit. Hal ini ditunjukkan dalam kode berikut:

Kotlin

val key: Key = ...
val cipher = Cipher.getInstance("RSA/ECB/OAEPPadding")
        .apply {
            // To use SHA-256 the main digest and SHA-1 as the MGF1 digest
            init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT))
            // To use SHA-256 for both digests
            init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT))
        }

Java

Key key = ...;
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
// To use SHA-256 the main digest and SHA-1 as the MGF1 digest
cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT));
// To use SHA-256 for both digests
cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));

Fungsi yang tidak digunakan lagi

Bagian berikut menjelaskan fungsi yang tidak digunakan lagi. Jangan gunakan di aplikasi Anda.

Algoritma Bouncy Castle

Penerapan Bouncy Castle untuk banyak algoritma kini sudah tidak digunakan lagi. Penerapan ini hanya efektif pada situasi ketika Anda meminta penyedia Bouncy Castle secara eksplisit, seperti dalam contoh berikut:

Kotlin

Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC")
// OR
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))

Java

Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC");
// OR
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"));

Seperti yang tercantum pada bagian tentang menentukan penyedia hanya dengan sistem Android Keystore, meminta penyedia tertentu tidak disarankan. Jika Anda mengikuti pedoman tersebut, penghentian ini tidak akan memengaruhi Anda.

Cipher enkripsi berbasis sandi tanpa vektor inisialisasi

Cipher enkripsi berbasis sandi (PBE) yang memerlukan vektor inisialisasi (IV) dapat memperolehnya dari kunci tersebut, jika disusun secara tepat, atau dari IV yang diteruskan secara eksplisit. Jika Anda meneruskan kunci PBE yang tidak berisi IV dan tidak meneruskan IV eksplisit, cipher PBE di Android saat ini mengasumsikan IV dengan nilai nol.

Saat menggunakan cipher PBE, selalu teruskan IV eksplisit seperti dalam cuplikan kode berikut:

Kotlin

val key: SecretKey = ...
val cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC")
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))

Java

SecretKey key = ...;
Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));

Penyedia kripto

Mulai dari Android 9 (API level 28), penyedia Java Cryptography Architecture (JCA) Kripto telah dihapus. Jika aplikasi Anda meminta instance penyedia Kripto tersebut, misalnya dengan memanggil metode berikut, NoSuchProviderException akan terjadi.

Kotlin

SecureRandom.getInstance("SHA1PRNG", "Crypto")

Java

SecureRandom.getInstance("SHA1PRNG", "Crypto");

Library kripto keamanan Jetpack

Library kripto keamanan Jetpack tidak digunakan lagi. Hal ini hanya memengaruhi kasus saat Anda memiliki dependensi berikut dalam file build.gradle modul aplikasi:

Groovy

dependencies {
    implementation "androidx.security:security-crypto:1.0.0"
}

Kotlin

dependencies {
    implementation("androidx.security:security-crypto:1.0.0")
}

Algoritma yang didukung

Ini adalah ID algoritma JCA yang didukung di Android: