Register now for Android Dev Summit 2019!

Sistem Android Keystore

Sistem Android Keystore memungkinkan Anda menyimpan kunci kriptografi dalam container agar lebih sulit untuk diekstrak dari perangkat. Setelah berada di keystore, kunci tersebut dapat digunakan untuk operasi kriptografi tanpa mengimpor materi kunci lainnya. Selain itu, keystore menawarkan fasilitas untuk membatasi kapan dan bagaimana kunci dapat digunakan, seperti mengharuskan autentikasi pengguna untuk penggunaan kunci atau membatasi kunci agar hanya digunakan dalam mode kriptografi tertentu. Lihat bagian Fitur Keamanan untuk informasi selengkapnya.

Sistem Keystore digunakan oleh KeyChain API serta fitur penyedia Android Keystore yang diperkenalkan dalam Android 4.3 (API level 18). Dokumen ini menjelaskan tentang kapan dan bagaimana menggunakan penyedia Android Keystore.

Fitur keamanan

Sistem Android Keystore melindungi materi kunci dari penggunaan yang tidak sah. Pertama, Android Keystore mengurangi penggunaan materi kunci di luar perangkat Android secara tidak sah dengan mencegah ekstraksi materi kunci dari proses aplikasi dan dari perangkat Android secara keseluruhan. Kedua, Android KeyStore mengurangi penggunaan materi kunci pada perangkat Android secara tidak sah dengan mengharuskan aplikasi menetapkan penggunaan kunci yang diizinkan dan kemudian memberlakukan pembatasan tersebut di luar proses aplikasi.

Pencegahan ekstraksi

Materi kunci dari kunci Android Keystore dilindungi dari ekstraksi melalui dua langkah pengamanan:

  • Materi kunci tidak memasuki proses aplikasi. Saat sebuah aplikasi menjalankan operasi kriptografi menggunakan kunci Android Keystore, di belakang layar teks biasa, ciphertext, dan pesan yang perlu ditandatangani atau diverifikasi diteruskan ke proses sistem yang menjalankan operasi kriptografi tersebut. Jika proses aplikasi ini diretas, penyerang mungkin dapat menggunakan kunci aplikasi, tetapi tidak dapat mengekstraksi materi kuncinya (misalnya, untuk digunakan di luar perangkat Android).
  • Materi kunci mungkin terikat pada hardware aman (misalnya, Trusted Execution Environment (TEE), Secure Element (SE)) perangkat Android. Jika fitur ini diaktifkan untuk sebuah kunci, materi kuncinya tidak akan terekspos di luar hardware aman. Jika Android OS diretas atau penyerang dapat membaca penyimpanan internal perangkat, penyerang mungkin dapat menggunakan setiap kunci Android Keystore aplikasi pada perangkat Android, tetapi tidak dapat mengekstraknya dari perangkat. Fitur ini aktif hanya jika hardware aman perangkat mendukung kombinasi tertentu dari algoritme kunci, mode pemblokiran, skema padding, dan ringkasan yang diizinkan untuk digunakan dengan kunci. Untuk memeriksa apakah fitur ini aktif untuk kunci tertentu, dapatkan KeyInfo untuk kunci tersebut dan periksa nilai kembalian KeyInfo.isInsideSecurityHardware().

Modul keamanan hardware

Perangkat yang didukung yang menjalankan Android 9 (API level 28) atau lebih tinggi dapat memiliki StrongBox Keymaster, suatu implementasi Keymaster HAL yang berada dalam modul keamanan hardware. Modul ini berisi hal-hal berikut:

  • CPU-nya sendiri.
  • Penyimpanan aman.
  • Generator angka-acak sebenarnya.
  • Mekanisme tambahan untuk menahan modifikasi paket dan sideloading aplikasi tanpa izin.

Saat memeriksa kunci yang disimpan dalam StrongBox Keymaster, sistem akan menguatkan integritas kunci dengan Trusted Execution Environment (TEE).

Untuk mendukung implementasi StrongBox rendah daya, beberapa algoritme dan ukuran kunci didukung:

  • RSA 2048
  • AES 128 dan 256
  • ECDSA P-256
  • HMAC-SHA256 (mendukung ukuran kunci antara 8 dan 64 byte, inklusif)
  • Triple DES 168

Saat membuat atau mengimpor kunci menggunakan class KeyStore, Anda menyatakan preferensi penyimpanan kunci di StrongBox Keymaster dengan meneruskan true ke metode setIsStrongBoxBacked() dalam class KeyGenParameterSpec.Builder atau KeyProtection.Builder.

Otorisasi penggunaan kunci

Untuk meminimalkan penggunaan kunci pada perangkat Android secara tidak sah, Android Keystore mengizinkan aplikasi menentukan apa saja penggunaan yang sah atas kunci tersebut selama proses pembuatan atau impor kunci. Setelah kunci dibuat atau diimpor, otorisasinya tidak dapat diubah. Selanjutnya, otorisasi diberlakukan oleh Android Keystore setiap kali kunci digunakan. Ini merupakan fitur keamanan lanjutan yang umumnya hanya berguna jika Anda menetapkan bahwa peretasan proses aplikasi setelah kunci dibuat/diimpor (tetapi tidak sebelum atau selama dibuat/diimpor) tidak akan mengakibatkan penggunaan kunci tersebut secara tidak sah.

Otorisasi penggunaan kunci yang didukung dikelompokkan ke dalam kategori berikut:

  • kriptografi: algoritme kunci sah, operasi, atau fungsi (enkripsi, dekripsi, tanda tangan, verifikasi), skema padding, mode pemblokiran, dan ringkasan yang diizinkan untuk digunakan dengan kunci;
  • interval validitas sementara: interval waktu saat kunci sah untuk digunakan;
  • autentikasi pengguna: kunci hanya dapat digunakan jika pengguna telah mengautentikasinya baru-baru ini. Lihat Mewajibkan Autentikasi Pengguna untuk Penggunaan Kunci.

Sebagai langkah pengamanan tambahan, untuk kunci yang materi kuncinya berada dalam hardware aman (lihat KeyInfo.isInsideSecurityHardware()), beberapa otorisasi penggunaan kunci dapat diberlakukan oleh hardware aman, bergantung pada perangkat Android-nya. Otorisasi kriptografik dan autentikasi pengguna kemungkinan besar akan diberlakukan oleh hardware aman. Otorisasi interval validitas sementara sepertinya tidak akan diberlakukan oleh hardware aman karena biasanya tidak memiliki jam real-time yang aman serta independen.

Untuk mengkueri apakah otorisasi autentikasi pengguna kunci diberlakukan oleh hardware aman, gunakan KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware().

Memilih antara keychain atau penyedia Android Keystore

Gunakan KeyChain API jika Anda menginginkan kredensial untuk seluruh sistem. Jika sebuah aplikasi meminta penggunaan kredensial apa pun melalui KeyChain API, pengguna bisa memilih, melalui UI yang disediakan oleh sistem, kredensial terinstal mana yang dapat diakses aplikasi. Hal ini memungkinkan beberapa aplikasi untuk menggunakan set kredensial yang sama dengan seizin pengguna.

Gunakan penyedia Android Keystore untuk mengizinkan setiap aplikasi menyimpan kredensialnya sendiri yang hanya bisa diakses oleh aplikasi tersebut. Cara ini memungkinkan aplikasi untuk mengelola sendiri kredensial yang hanya dapat digunakan olehnya sendiri, sembari memberikan manfaat keamanan yang sama seperti yang diberikan KeyChain API untuk kredensial seluruh sistem. Metode ini tidak memerlukan interaksi pengguna untuk memilih kredensial.

Menggunakan penyedia Android Keystore

Untuk menggunakan fitur ini, gunakan class KeyStore dan KeyPairGenerator standar atau KeyGenerator bersama dengan penyedia AndroidKeyStore yang diperkenalkan pada Android 4.3 (API level 18).

AndroidKeyStore terdaftar sebagai jenis KeyStore untuk digunakan dengan metode KeyStore.getInstance(type) dan sebagai penyedia untuk digunakan dengan metode KeyPairGenerator.getInstance(algorithm, provider) dan KeyGenerator.getInstance(algorithm, provider).

Membuat kunci pribadi baru

Untuk membuat sebuah PrivateKey baru, Anda juga harus menentukan atribut X.509 awal yang akan dimiliki oleh sertifikat yang ditandatangani sendiri. Anda dapat menggunakan KeyStore.setKeyEntry untuk mengganti sertifikat tersebut dengan sertifikat yang ditandatangani oleh Certificate Authority (CA) di kemudian hari.

Untuk membuat kunci, gunakan KeyPairGenerator dengan KeyPairGeneratorSpec:

Kotlin

    /*
     * Generate a new EC key pair entry in the Android Keystore by
     * using the KeyPairGenerator API. The private key can only be
     * used for signing or verification and only with SHA-256 or
     * SHA-512 as the message digest.
     */
    val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_EC,
            "AndroidKeyStore"
    )
    val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
            alias,
            KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
    ).run {
        setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
        build()
    }

    kpg.initialize(parameterSpec)

    val kp = kpg.generateKeyPair()
    

Java

    /*
     * Generate a new EC key pair entry in the Android Keystore by
     * using the KeyPairGenerator API. The private key can only be
     * used for signing or verification and only with SHA-256 or
     * SHA-512 as the message digest.
     */
    KeyPairGenerator kpg = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
    kpg.initialize(new KeyGenParameterSpec.Builder(
            alias,
            KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
            .setDigests(KeyProperties.DIGEST_SHA256,
                KeyProperties.DIGEST_SHA512)
            .build());

    KeyPair kp = kpg.generateKeyPair();
    

Membuat kunci rahasia baru

Untuk membuat kunci, gunakan KeyGenerator dengan KeyGenParameterSpec.

Mengimpor kunci terenkripsi secara lebih aman

Android 9 (API level 28) dan yang lebih tinggi memungkinkan Anda untuk mengimpor kunci terenkripsi dengan aman ke dalam Keystore menggunakan format kunci yang berencode ASN.1. Keymaster kemudian akan mendekripsi kunci dalam Keystore, sehingga isi kunci tidak pernah muncul sebagai teks biasa dalam memori host perangkat. Proses ini memberikan keamanan dekripsi kunci tambahan.

Untuk mendukung impor kunci terenkripsi secara aman ke dalam Keystore, lakukan langkah-langkah berikut:

  1. Buat pasangan kunci yang menggunakan fungsi PURPOSE_WRAP_KEY. Sebaiknya Anda juga menambahkan pengesahan ke pasangan kunci ini.

  2. Pada server atau mesin yang Anda percayai, buat pesan ASN.1 yang harus dimuat oleh SecureKeyWrapper.

    Wrapper akan berisi skema berikut:

    KeyDescription ::= SEQUENCE {
            keyFormat INTEGER,
            authorizationList AuthorizationList
        }
    
        SecureKeyWrapper ::= SEQUENCE {
            wrapperFormatVersion INTEGER,
            encryptedTransportKey OCTET_STRING,
            initializationVector OCTET_STRING,
            keyDescription KeyDescription,
            secureKey OCTET_STRING,
            tag OCTET_STRING
        }
        
  3. Buat objek WrappedKeyEntry, dengan meneruskan pesan ASN.1 sebagai array byte.

  4. Teruskan objek WrappedKeyEntry ini ke dalam overload setEntry() yang menerima objek Keystore.Entry.

Menggunakan entri keystore

Penggunaan penyedia AndroidKeyStore berlangsung di seluruh KeyStore API standar.

Mencantumkan entri

Cantumkan entri di keystore dengan memanggil metode aliases():

Kotlin

    /*
     * Load the Android KeyStore instance using the
     * "AndroidKeyStore" provider to list out what entries are
     * currently stored.
     */
    val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val aliases: Enumeration<String> = ks.aliases()
    

Java

    /*
     * Load the Android KeyStore instance using the
     * "AndroidKeyStore" provider to list out what entries are
     * currently stored.
     */
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);
    Enumeration<String> aliases = ks.aliases();
    

Menandatangani dan memverifikasi data

Tanda tangani data dengan mengambil KeyStore.Entry dari keystore dan menggunakan API Signature, seperti sign():

Kotlin

    /*
     * Use a PrivateKey in the KeyStore to create a signature over
     * some data.
     */
    val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val entry: KeyStore.Entry = ks.getEntry(alias, null)
    if (entry !is KeyStore.PrivateKeyEntry) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry")
        return null
    }
    val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run {
        initSign(entry.privateKey)
        update(data)
        sign()
    }
    

Java

    /*
     * Use a PrivateKey in the KeyStore to create a signature over
     * some data.
     */
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);
    KeyStore.Entry entry = ks.getEntry(alias, null);
    if (!(entry instanceof PrivateKeyEntry)) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry");
        return null;
    }
    Signature s = Signature.getInstance("SHA256withECDSA");
    s.initSign(((PrivateKeyEntry) entry).getPrivateKey());
    s.update(data);
    byte[] signature = s.sign();
    

Dengan cara yang sama, verifikasi data dengan metode verify(byte[]):

Kotlin

    /*
     * Verify a signature previously made by a PrivateKey in our
     * KeyStore. This uses the X.509 certificate attached to our
     * private key in the KeyStore to validate a previously
     * generated signature.
     */
    val ks = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }
    val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry
    if (entry == null) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry")
        return false
    }
    val valid: Boolean = Signature.getInstance("SHA256withECDSA").run {
        initVerify(entry.certificate)
        update(data)
        verify(signature)
    }
    

Java

    /*
     * Verify a signature previously made by a PrivateKey in our
     * KeyStore. This uses the X.509 certificate attached to our
     * private key in the KeyStore to validate a previously
     * generated signature.
     */
    KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
    ks.load(null);
    KeyStore.Entry entry = ks.getEntry(alias, null);
    if (!(entry instanceof PrivateKeyEntry)) {
        Log.w(TAG, "Not an instance of a PrivateKeyEntry");
        return false;
    }
    Signature s = Signature.getInstance("SHA256withECDSA");
    s.initVerify(((PrivateKeyEntry) entry).getCertificate());
    s.update(data);
    boolean valid = s.verify(signature);
    

Mewajibkan autentikasi pengguna untuk penggunaan kunci

Saat membuat atau mengimpor kunci ke dalam AndroidKeyStore, Anda dapat menentukan bahwa kunci hanya sah untuk digunakan jika pengguna telah diautentikasi. Pengguna diautentikasi menggunakan salah satu kredensial layar kunci aman mereka (pola/PIN/sandi, sidik jari).

Ini merupakan fitur keamanan lanjutan yang umumnya hanya berguna jika Anda menetapkan bahwa peretasan proses aplikasi setelah kunci dibuat/diimpor (tetapi tidak sebelum atau selama dibuat/diimpor) tidak dapat mengabaikan keharusan pengguna untuk diautentikasi agar dapat menggunakan kunci.

Apabila kunci hanya sah untuk digunakan jika pengguna telah diautentikasi, maka kunci akan dikonfigurasi melalui salah satu dari dua mode berikut agar dapat difungsikan:

  • Autentikasi pengguna mengizinkan penggunaan kunci selama durasi waktu tertentu. Semua kunci dalam mode ini sah untuk digunakan begitu pengguna membuka layar kunci aman atau mengonfirmasi kredensial layar kunci aman mereka menggunakan alur KeyguardManager.createConfirmDeviceCredentialIntent. Masa berlakunya otorisasi berbeda-beda untuk setiap kunci, sesuai yang ditetapkan menggunakan setUserAuthenticationValidityDurationSeconds selama pembuatan atau impor kunci. Kunci ini hanya dapat dibuat atau diimpor jika layar kunci aman aktif (lihat KeyguardManager.isDeviceSecure()). Kunci menjadi tidak valid secara permanen begitu layar kunci aman dinonaktifkan (dikonfigurasi ulang ke Tidak Ada, Geser, atau mode lain yang tidak mengautentikasi pengguna), atau disetel ulang secara paksa (misalnya oleh Administrator Perangkat).
  • Autentikasi pengguna mengizinkan operasi kriptografik tertentu yang terkait dengan salah satu kunci. Dalam mode ini, setiap operasi yang melibatkan kunci tersebut harus diizinkan satu per satu oleh pengguna. Saat ini, satu-satunya cara untuk otorisasi ini adalah dengan autentikasi sidik jari: FingerprintManager.authenticate. Kunci ini hanya dapat dibuat atau diimpor jika setidaknya ada satu sidik jari yang terdaftar (lihat FingerprintManager.hasEnrolledFingerprints). Kunci menjadi tidak valid secara permanen begitu sidik jari baru didaftarkan atau semua sidik jari dihapus pendaftarannya.

Algoritme yang didukung

Cipher

Algoritme Didukung (API Level) Catatan
AES/CBC/NoPadding 23+
AES/CBC/PKCS7Padding 23+
AES/CTR/NoPadding 23+
AES/ECB/NoPadding 23+
AES/ECB/PKCS7Padding 23+
AES/GCM/NoPadding 23+ Hanya IV dengan panjang 12 byte yang didukung.
RSA/ECB/NoPadding 18+
RSA/ECB/PKCS1Padding 18+
RSA/ECB/OAEPWithSHA-1AndMGF1Padding 23+
RSA/ECB/OAEPWithSHA-224AndMGF1Padding 23+
RSA/ECB/OAEPWithSHA-256AndMGF1Padding 23+
RSA/ECB/OAEPWithSHA-384AndMGF1Padding 23+
RSA/ECB/OAEPWithSHA-512AndMGF1Padding 23+
RSA/ECB/OAEPPadding 23+

KeyGenerator

Algoritme Didukung (API Level) Catatan
AES 23+ Ukuran yang didukung: 128, 192, 256
HmacSHA1 23+
  • Ukuran yang didukung: 8--1024 (inklusif), harus kelipatan 8
  • Ukuran default: 160
HmacSHA224 23+
  • Ukuran yang didukung: 8--1024 (inklusif), harus kelipatan 8
  • Ukuran default: 224
HmacSHA256 23+
  • Ukuran yang didukung: 8--1024 (inklusif), harus kelipatan 8
  • Ukuran default: 256
HmacSHA384 23+
  • Ukuran yang didukung: 8--1024 (inklusif), harus kelipatan 8
  • Ukuran default: 384
HmacSHA512 23+
  • Ukuran yang didukung: 8--1024 (inklusif), harus kelipatan 8
  • Ukuran default: 512

KeyFactory

Algoritme Didukung (API Level) Catatan
EC 23+ Spesifikasi kunci yang didukung: KeyInfo (hanya kunci pribadi), ECPublicKeySpec (hanya kunci publik), X509EncodedKeySpec (hanya kunci publik)
RSA 23+ Spesifikasi kunci yang didukung: KeyInfo (hanya kunci pribadi), RSAPublicKeySpec (hanya kunci publik), X509EncodedKeySpec (hanya kunci publik)

KeyStore

KeyStore mendukung jenis kunci yang sama dengan KeyPairGenerator dan KeyGenerator.

KeyPairGenerator

Algoritme Didukung (API Level) Catatan
DSA 19–22
EC 23+
  • Ukuran yang didukung: 224, 256, 384, 521
  • Kurva bernama yang didukung: P-224 (secp224r1), P-256 (alias secp256r1 dan prime256v1), P-384 (alias secp384r1), P-521 (alias secp521r1)

Sebelum API Level 23, kunci EC dapat dibuat menggunakan KeyPairGenerator dari algoritma "RSA" yang diinisialisasi KeyPairGeneratorSpec yang jenis kuncinya ditetapkan ke "EC" menggunakan setKeyType(String). Nama kurva EC tidak dapat ditetapkan menggunakan metode ini -- sebuah kurva NIST P akan otomatis dipilih berdasarkan ukuran kunci yang diminta.

RSA 18+
  • Ukuran yang didukung: 512, 768, 1024, 2048, 3072, 4096
  • Eksponen publik yang didukung: 3, 65537
  • Eksponen publik default: 65537

Mac

Algoritme Didukung (API Level) Catatan
HmacSHA1 23+
HmacSHA224 23+
HmacSHA256 23+
HmacSHA384 23+
HmacSHA512 23+

Tanda Tangan

Algoritme Didukung (API Level) Catatan
MD5withRSA 18+
NONEwithECDSA 23+
NONEwithRSA 18+
SHA1withDSA 19–22
SHA1withECDSA 19+
SHA1withRSA 18+
SHA1withRSA/PSS 23+
SHA224withDSA 20–22
SHA224withECDSA 20+
SHA224withRSA 20+
SHA224withRSA/PSS 23+
SHA256withDSA 19–22
SHA256withECDSA 19+
SHA256withRSA 18+
SHA256withRSA/PSS 23+
SHA384withDSA 19–22
SHA384withECDSA 19+
SHA384withRSA 18+
SHA384withRSA/PSS 23+
SHA512withDSA 19–22
SHA512withECDSA 19+
SHA512withRSA 18+
SHA512withRSA/PSS 23+

SecretKeyFactory

Algoritme Didukung (API Level) Catatan
AES 23+ Spesifikasi kunci yang didukung: KeyInfo
HmacSHA1 23+ Spesifikasi kunci yang didukung: KeyInfo
HmacSHA224 23+ Spesifikasi kunci yang didukung: KeyInfo
HmacSHA256 23+ Spesifikasi kunci yang didukung: KeyInfo
HmacSHA384 23+ Spesifikasi kunci yang didukung: KeyInfo
HmacSHA512 23+ Spesifikasi kunci yang didukung: KeyInfo

Contoh kode lainnya

Contoh Android AsymmetricFingerprintDialog, Android BasicAndroidKeyStore, dan Android ConfirmCredential menunjukkan lebih lanjut penggunaan API yang dibahas pada halaman ini.

Artikel blog

Lihat entri blog Menyatukan Akses Keystore di ICS.