Android Keystore is a cryptographic provider that generates and stores encryption keys in an isolated execution environment (TEE), inaccessible even to the operating system. According to AOSP Security Documentation (2025), Keystore is used in more than 80% of Android apps from the Google Play top 100 for protecting tokens and encrypting data. Understanding Android Keystore is critically important for secure key storage on Android.
Key Takeaways
Android Keystore — a system component of the Android platform that provides an API for generating, storing, and using cryptographic keys in a protected environment. Unlike software cryptographic libraries (Bouncy Castle, Conscrypt), Keystore ensures that private keys never leave the isolated execution area.
Keystore first appeared in Android 4.3 (API 18) as a software provider with RSA support. Starting with Android 6.0 (API 23), Keystore received hardware support through the Keymaster Hardware Abstraction Layer (HAL), which delegates cryptographic operations to the Trusted Execution Environment (TEE) on compatible devices. According to Android Compatibility Definition Document (2025), all devices with Android 9+ are required to support hardware-backed Keystore via TEE or StrongBox.
Keys in Keystore are identified by an alias — a string that is passed when creating or loading a key. Keystore does not allow access to the raw key material: getEncoded() methods return null for keys created in Keystore. This is a fundamental difference from software keys — an attacker cannot extract the private key even with full control over the device.
Keystore is integrated with other Android security mechanisms: biometric authentication (BiometricPrompt), File-Based Encryption, and SafetyNet / Play Integrity verification functions. Keys can be configured for automatic deletion under certain conditions: when the passcode is removed, when a new fingerprint is added, or upon expiration.
The Android Keystore architecture includes three implementation levels that differ in the degree of hardware protection. The level depends on the capabilities of the device’s hardware.
TEE (Trusted Execution Environment) — an isolated area running in parallel with the main OS on the same processor. TEE uses ARM TrustZone technology, which splits the physical processor core into two virtual ones: Normal World (Android) and Secure World (TEE). Code in Secure World has access to memory and peripherals that are inaccessible from Normal World.
When an application calls a cryptographic operation through Keystore, the request is passed through Keymaster HAL to TEE, where the operation is performed in hardware. The result is returned to the application, but the private key remains in TEE’s protected memory. TEE is certified for compliance with the GlobalPlatform TEE Protection Profile and is a mandatory requirement for Android 9+ on devices with processors that support TrustZone.
TEE supports AES/GCM (128, 256 bit), RSA (2048, 4096 bit), EC (P-256, P-384, P-521) and HMAC-SHA256 algorithms. TEE performance is lower than software cryptography (by 20–40%), but for typical operations (JWT signing, session key decryption), the latency does not exceed 10–50 ms.
StrongBox — a dedicated security chip, physically separate from the main processor. Unlike TEE, which shares processor time with Android, StrongBox has its own CPU, RAM, True Random Number Generator (TRNG), and secure storage (One-Time Programmable memory). StrongBox is certified for Common Criteria EAL 4+ and Secure IC Protection Profile.
StrongBox is available on devices with Android 9+ provided the corresponding chip is present (e.g., Titan M on Google Pixel, Knox on Samsung Galaxy). The developer enables StrongBox via the setIsStrongBoxBacked(true) flag in KeyGenParameterSpec. If hardware support is not available, the flag is ignored and Keystore falls back to TEE.
StrongBox limitations: supports a limited set of algorithms (AES-256, EC P-256, HMAC-SHA256), operation queue — no more than one at a time, operation count — limited by chip resources. StrongBox is not designed for high-load scenarios — use TEE for frequent operations and StrongBox only for critical keys (master encryption keys, signing keys).
Software-based Keystore is a software implementation used on devices without hardware support for TEE or StrongBox. Keys are stored in encrypted form in the file system, but the private key may be temporarily decrypted in RAM. Software Keystore is less secure — an attacker with root access can intercept the key in memory.
Starting with Android 12 (API 31), Google requires hardware-backed Keystore for all new devices. Devices with Android 9–11 may have software Keystore on budget models. The developer can check the protection level via KeyStore.getKeyCharacteristics() — the SECURITY_LEVEL_TRUSTED_ENVIRONMENT or SECURITY_LEVEL_STRONGBOX attribute confirms hardware protection.
Android Keystore supports a wide range of cryptographic algorithms, divided into categories depending on the key type. The choice of algorithm affects performance, compatibility, and security level.
AES (Advanced Encryption Standard) — symmetric encryption for protecting data on the device. Recommended mode: AES/GCM/NoPadding (256 bit). GCM provides authenticated encryption (AEAD) — integrity checking of encrypted data. IV (Initialization Vector) size: 12 bytes for GCM. Do not use AES/ECB — it does not provide adequate protection.
RSA (Rivest–Shamir–Adleman) — asymmetric encryption for protecting session keys and digital signatures. Recommended size: 2048 or 4096 bits. Modes: RSA/ECB/PKCS1Padding (encryption) and RSA/ECB/PKCS1Sign (signature). RSA 1024 is considered deprecated and is not recommended for new applications (NIST SP 800-131A Rev. 2).
EC (Elliptic Curve) — asymmetric cryptography on elliptic curves for signing and key exchange. Supported curves: secp256r1 (P-256, mandatory), secp384r1 (P-384) and secp521r1 (P-521). EC provides comparable security to RSA with significantly smaller key size. P-256 is recommended for most scenarios: it is supported by all devices and provides 128-bit security level.
HMAC (Hash-based Message Authentication Code) — symmetric message authentication. Supported hash functions: SHA-256, SHA-384, SHA-512. HMAC is used for verifying data integrity and authenticity, for example, for verifying webhook requests or checking configuration integrity.
All algorithms can be bound to biometric authentication via KeyGenParameterSpec.Builder.setUserAuthenticationRequired(true). On Android 11+ the setUserAuthenticationParameters() flag is available with a timeout (in seconds) during which the key is available after biometric authentication, without a repeated request.
Let’s look at practical examples of working with Android Keystore in Kotlin: generating an AES key, encrypting data, and creating an asymmetric pair for signing.
The example creates a 256-bit AES/GCM key with biometric authentication binding. The key is not exportable via getEncoded().
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyStore
private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
fun generateAesKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT or
KeyProperties.PURPOSE_DECRYPT
)
.setKeySize(256)
.setBlockModes(KeyProperties.KEY_BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build()
val generator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore"
)
generator.init(spec)
generator.generateKey()
}
The example encrypts data using a key from Android Keystore. Cipher gets the key by alias, initializes AES/GCM encryption, and returns the encrypted data along with the IV.
fun encryptData(alias: String, plaintext: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val secretKey = keyStore.getKey(alias, null) as SecretKey
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.getIV()
val encrypted = cipher.doFinal(plaintext)
// IV + encrypted data
return iv + encrypted
}
fun decryptData(alias: String, ciphertextWithIv: ByteArray): ByteArray {
val iv = ciphertextWithIv.copyOfRange(0, 12)
val encrypted = ciphertextWithIv.copyOfRange(12, ciphertextWithIv.size)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val secretKey = keyStore.getKey(alias, null) as SecretKey
val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
return cipher.doFinal(encrypted)
}
The example creates an RSA-2048 key pair in Keystore with StrongBox binding. The private key is used for signing, the public key can be exported via getEncoded().
fun generateRsaKeyPair(alias: String) {
val spec = KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN or
KeyProperties.PURPOSE_VERIFY
)
.setKeySize(2048)
.setSignaturePaddings(
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1
)
.setDigests(KeyProperties.DIGEST_SHA256)
.setIsStrongBoxBacked(true)
.build()
val pair = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
).apply { init(spec) }
.generateKeyPair()
// Public key can be exported
val publicKey = pair.public // X509EncodedKeySpec
}
Effective use of Android Keystore requires following rules that ensure maximum protection while maintaining performance.
Use KeyGenParameterSpec with the minimum necessary parameters: specify only those purpose, block modes, and paddings that are actually used. Redundant parameters (e.g., PURPOSE_ENCRYPT for a key that is only used for signing) create unnecessary attack vectors. Android recommends explicitly specifying digest for signing — SHA256 is the minimum acceptable level (SHA1 is deprecated).
Bind keys to biometrics for critical operations: setUserAuthenticationRequired(true) guarantees that the key can only be used after biometric authentication. On Android 11+ use setUserAuthenticationParameters() with a timeout (recommended 30–60 seconds) to avoid requesting biometrics for every operation within a single session. setInvalidatedByBiometricEnrollment(true) automatically deletes the key when a new fingerprint or face is enrolled — this prevents access with old biometric data.
Check the security level at initialization: use KeyStore.getKeyCharacteristics() to determine SECURITY_LEVEL. If the device only supports software Keystore (SECURITY_LEVEL_SOFTWARE), make a decision: either decline the functionality or use additional encryption (e.g., key wrapping via user password). Do not rely on StrongBox if it is not guaranteed — always specify the setIsStrongBoxBacked(true) flag and verify the result via getKeyCharacteristics.
Rotate keys on a schedule: cryptographic keys have a recommended lifetime. NIST SP 800-57 recommends changing AES keys every 1–2 years, RSA/EC pairs every 2–3 years. Implement a key rotation mechanism: check the key creation date at app launch (KeyGenParameterSpec.Builder.setKeyValidityStart/End) and generate a new key when it expires. Old data encrypted with the old key should be decrypted and re-encrypted with the new one.
Do not use Keystore for large data: Keystore is designed for storing keys (a few hundred bytes), not for encrypting large files. For data encryption, use the scheme: generate a random AES key (DEK — Data Encryption Key), encrypt the data with this key, and encrypt the DEK with a Keystore key (KEK — Key Encryption Key). Android EncryptedSharedPreferences uses exactly this scheme: master key in Keystore, data — AES-256 GCM.
Frequently Asked Questions
No, Android Keystore is designed so that the private key never leaves TEE or StrongBox. The getEncoded() method returns null for keys created in Keystore. The key can only be used through Cipher, Signature, or Mac API — the raw material is inaccessible.
TEE (TrustZone) — virtual isolation on the same processor, uses time-sharing. StrongBox — a separate chip with its own CPU and memory. StrongBox is more secure (Common Criteria EAL 4+), but slower and supports fewer algorithms. TEE is suitable for frequent operations, StrongBox for critical keys.
Use KeyStore.getKeyCharacteristics() after generating a key with the setIsStrongBoxBacked(true) flag. The SECURITY_LEVEL_STRONGBOX attribute confirms hardware support. If the device does not support StrongBox, Keystore falls back to TEE without error — you must explicitly check the security level.
Keys in Keystore are automatically deleted when the app is uninstalled from the device. On Android 10+ keys may persist if the app has the allowBackup=true flag in its manifest, but they will be unavailable after reinstalling. It is recommended to generate keys again on a clean install.
No, Android Keystore is tied to the hardware of a specific device. A key generated in the TEE of one device cannot be transferred to another. For cross-platform encryption, use the scheme: Keystore protects the key on the device, and session keys are transmitted through a secure API using asymmetric encryption.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also