Sistem keystore Android

Sistem Keystore Android memungkinkan Anda menyimpan kunci kriptografis 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 mengetahui informasi selengkapnya.

Sistem Keystore digunakan oleh API KeyChain, diperkenalkan di Android 4.0 (API level 14); fitur penyedia Keystore Android, diperkenalkan di Android 4.3 (API level 18); dan Library keamanan, tersedia sebagai bagian dari Jetpack. Dokumen ini menjelaskan kapan dan bagaimana Anda dapat menggunakan penyedia Keystore Android.

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 mengekstrak materi kuncinya (misalnya, untuk digunakan di luar perangkat Android).
  • Materi kunci mungkin terikat dengan 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 hanya akan aktif jika hardware aman perangkat mendukung kombinasi algoritme kunci tertentu, mode pemblokiran, skema padding, dan digest yang mengizinkan kunci untuk digunakan. Untuk memeriksa apakah fitur ini diaktifkan bagi suatu kunci, dapatkan KeyInfo untuk kunci tersebut dan periksa nilai hasil 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, indikasikan preferensi untuk menyimpan kunci di StrongBox Keymaster dengan meneruskan true ke metode setIsStrongBoxBacked().

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 diautentikasi baru-baru ini. Baca Mewajibkan Autentikasi Pengguna untuk Penggunaan Kunci.

Sebagai langkah keamanan 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 yang digunakan. Otorisasi kriptografis dan autentikasi pengguna kemungkinan 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 dan independen.

Untuk mencari tahu apakah otorisasi autentikasi pengguna kunci diberlakukan oleh hardware aman, buat kueri menggunakan KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware().

Memilih antara keychain atau penyedia Keystore Android

Gunakan API KeyChain jika Anda menginginkan kredensial berskala sistem. Ketika suatu aplikasi meminta penggunaan kredensial apa pun melalui API KeyChain, pengguna dapat memilih kredensial terinstal mana yang dapat diakses oleh suatu aplikasi, melalui UI yang disediakan oleh sistem. Hal ini memungkinkan beberapa aplikasi menggunakan kumpulan kredensial yang sama atas izin pengguna.

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

Menggunakan penyedia keystore Android

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

AndroidKeyStore didaftarkan 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

Membuat PrivateKey baru juga mengharuskan Anda menentukan atribut X.509 awal yang akan dimiliki oleh sertifikat yang ditandatangani sendiri.

Library keamanan menyediakan implementasi default untuk menghasilkan kunci simetris yang valid, seperti yang ditunjukkan dalam cuplikan berikut:

Kotlin

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
    

Java

    // Although you can define your own key generation parameter specification, it's
    // recommended that you use the value specified here.
    KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC;
    String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);
    

Atau, Anda dapat menggunakan KeyStore.setKeyEntry untuk mengganti sertifikat di lain waktu dengan sertifikat yang ditandatangani oleh Certificate Authority (CA).

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 ini, ikuti proses yang sama seperti saat membuat kunci pribadi baru. Gunakan library Keamanan dalam setiap kasus.

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 pengimporan kunci terenkripsi yang aman ke 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 perangkat yang Anda percaya, 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, lalu teruskan pesan ASN.1 sebagai array byte.

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

Menggunakan entri keystore

Penggunaan penyedia AndroidKeyStore berlangsung di seluruh API KeyStore 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();
    

Demikian pula, verifikasikan 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 AndroidKeyStore, Anda dapat menetapkan agar kunci hanya boleh digunakan jika pengguna telah diautentikasi. Pengguna diautentikasi menggunakan subset 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 diizinkan untuk digunakan begitu pengguna membuka layar kunci aman atau mengonfirmasi kredensial layar kunci aman mereka menggunakan alur KeyguardManager.createConfirmDeviceCredentialIntent. Masa berlaku otorisasi tersebut bervariasi untuk setiap kunci, seperti yang ditetapkan menggunakan setUserAuthenticationValidityDurationSeconds saat membuat atau mengimpor kunci. Kunci tersebut hanya dapat dibuat atau diimpor jika layar kunci aman diaktifkan (lihat KeyguardManager.isDeviceSecure()). Kunci ini akan menjadi tidak valid secara permanen jika layar kunci aman dinonaktifkan (dikonfigurasi ulang ke Tidak Ada, Geser, atau mode lain yang tidak mengautentikasi pengguna) atau direset paksa (misalnya oleh Administrator Perangkat).
  • Autentikasi pengguna mengizinkan operasi kriptografis tertentu yang terkait dengan sebuah kunci. Dalam mode ini, setiap operasi yang melibatkan kunci tersebut harus diizinkan satu per satu oleh pengguna. Saat ini, satu-satunya alat otorisasi tersebut adalah autentikasi sidik jari: FingerprintManager.authenticate. Kunci tersebut hanya dapat dibuat atau diimpor jika setidaknya satu sidik jari sudah didaftarkan (lihat FingerprintManager.hasEnrolledFingerprints). Kunci ini akan menjadi tidak valid secara permanen jika sidik jari baru didaftarkan atau semua sidik jari dibatalkan 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 privat), ECPublicKeySpec (hanya kunci publik), X509EncodedKeySpec (hanya kunci publik)
RSA 23+ Spesifikasi kunci yang didukung: KeyInfo (hanya kunci privat), RSAPublicKeySpec (hanya kunci publik), X509EncodedKeySpec (hanya kunci publik)

KeyStore

KeyStore mendukung jenis kunci yang sama seperti 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 KeyPairGeneratorSpec yang diinisialisasi algoritme "RSA", yang jenisnya ditetapkan ke "EC" menggunakan setKeyType(String). Nama kurva EC tidak dapat ditetapkan menggunakan metode ini; 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

Artikel blog

Lihat entri blog Unifying Key Store Access in ICS.