Android Keystore is a system mechanism in Android for securely storing cryptographic keys in hardware isolation. The system uses Trusted Execution Environment (TEE) on devices with ARM TrustZone or a dedicated Secure Element to protect keys at the chip level. According to Android Open Source Project, Keystore supports RSA, EC, AES and HMAC algorithms with key generation directly in the secure environment.
Key Takeaways
Android Keystore is a cryptographic provider implemented in Android starting from API 1 (Android 1.0), but full hardware support appeared with Android 4.3 (API 18). Keystore solves the problem of secure storage of private keys so that even if the operating system is compromised, an attacker cannot extract keys in plaintext.
The Android Keystore architecture consists of three layers: the application API (java.security.KeyStore), the system service (keystore daemon), and the hardware level (Keymaster HAL). The application accesses via the standard Java Cryptography Architecture (JCA) API, and the system service routes requests to Keymaster running in TEE.
All cryptographic operations with keys (signing, decryption) are performed inside TEE or Secure Element. Keys never leave the secure environment — the application receives only a handle (alias) to reference the key. This is a fundamental difference from software KeyStores, where keys are potentially accessible in process memory.
Standard JKS (Java KeyStore) or BKS (Bouncy Castle) store keys in password-protected files. Android Keystore stores keys in hardware isolation, where they are protected even from the root user. JKS is vulnerable to direct file system access; Android Keystore is not.
Another difference: in Android Keystore, keys have strict usage parameters (purpose — only sign/verify/encrypt/decrypt) specified at generation time. They cannot be changed later, which prevents key misuse.
When creating a new key, the application calls KeyPairGenerator or KeyGenerator with KeyGenParameterSpec, which contains all parameters of the future key. The system passes the request to Keymaster HAL, which generates the key inside TEE and returns a handle.
The KeyGenParameterSpec.Builder method accepts mandatory parameters: key name in Keystore, purpose (PURPOSE_SIGN, PURPOSE_ENCRYPT), algorithm (RSA, EC, AES). Additional parameters: digest (SHA-256), padding (PKCS7), userAuthenticationRequired (biometrics), keyValidityStart/End (time constraints).
After setting the parameters, KeyPairGenerator.generateKeyPair() returns a KeyPair, where PrivateKey is an object delegating operations to Keymaster. The public key can be extracted, the private key cannot. It exists only inside TEE.
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
fun generateKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(true)
.build()
val kpGen = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpGen.initialize(spec)
kpGen.generateKeyPair()
}
Signature for ECDSA or RSA-PSS is created through the standard API: Signature.getInstance(algorithm).initSign(privateKey). The signing operation is performed in TEE: the application passes data, Keymaster signs it in hardware and returns the signature. The key and data do not mix in shared memory.
For biometric protection, the user must be authenticated via BiometricPrompt before signing. Without successful authentication, Keymaster does not perform the operation and returns CryptoAuthenticationException.
import java.security.KeyStore
import java.security.Signature
import androidx.biometric.BiometricPrompt
fun signWithBiometric(alias: String) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val entry = ks.getEntry(alias, null) as KeyStore.PrivateKeyEntry
val signature = Signature.getInstance("SHA256withRSA")
signature.initSign(entry.privateKey)
// BiometricPrompt with CryptoObject(signature) requests FaceID/PIN
}
Android supports two key storage modes: software (on devices without TEE) and hardware (on devices with TEE or Secure Element). The mode depends on SoC capabilities and Android version.
On devices without Trusted Execution Environment (pre-Android 4.3 or budget SoCs), keys are stored encrypted using a master key derived from the lock screen password. This mode is less secure — keys are accessible in process memory during cryptographic operations.
The protection level is based on KeyStore file encryption using AES-256-GCM. The encryption key is generated from the user's password or PIN via Scrypt (PBKDF2 with a high iteration count).
On modern devices, Keymaster 4.x in TEE (ARM TrustZone) is used. Keys are generated, stored, and used exclusively inside TrustZone. Even the Linux kernel does not have access to private keys — only Keymaster HAL can perform operations.
Secure Element (e.g., eSE in Samsung Knox or StrongBox in Google Pixel 3+) is a separate chip with its own processor and memory. It is certified Common Criteria EAL 4+ and provides the maximum level of protection, including protection against physical tampering.
| Type | Storage Location | Protection Level | Available Since API |
|---|---|---|---|
| Software | File /data/misc/keystore | Medium (AES-256) | API 1+ |
| Keymaster 3 | TEE (TrustZone) | High | API 23+ |
| Keymaster 4 | TEE + Secure I/O | Very High | API 28+ |
| StrongBox | Hardware Secure Element | Maximum | API 28+, optional |
Android Keystore is integrated into Java Cryptography Architecture (JCA). To access the provider, the standard KeyStore.getInstance("AndroidKeyStore") is used. The API is available from API 18.
The KeyStore.load(null) method loads the application's KeyStore container. No password is required — Android uses the application context and its UID for access control. Each application sees only its own entries unless a shared UID is used.
The setEntry and getEntry methods work with KeyStore.PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry. The ProtectionParameter is always null for Android KeyStore (protection is implemented at the system level).
import java.security.KeyStore
import java.security.cert.Certificate
import android.security.keystore.KeyProtection
fun storeSecretKey(alias: String, key: SecretKey) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val prot = KeyProtection.Builder(
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.build()
ks.setEntry(alias, KeyStore.SecretKeyEntry(key), prot)
}
Using KeyCharacteristics, you can determine in which environment the key is stored: software KeyStore, TEE, or StrongBox. The getKeyCharacteristics() method returns a set of flags: FLAG_HARDWARE (keymaster), FLAG_SECURE_ELEMENT (StrongBox), FLAG_TRUSTED_USER_PRESENCE_REQUIRED (biometrics).
Android Keystore supports a wide range of cryptographic algorithms divided into three categories: asymmetric, symmetric, and MAC. Support for specific algorithms depends on the Keymaster HAL version.
RSA (1024–4096 bits) — for signing (PKCS1, PSS) and encryption (OAEP, PKCS1). EC (P-224, P-256, P-384, P-521) — for ECDSA signing and ECDH key agreement. AES (128, 256 bits) — for symmetric encryption in CBC, CTR, GCM modes. HMAC (SHA1, SHA256, SHA512) — for message authentication.
For each key, setPurposes is specified to restrict possible operations. An RSA key with PURPOSE_SIGN cannot be used for encryption, even if an attacker has API access. This is hardware-level key usage enforcement.
Keymaster includes a counter for failed biometric authentication attempts. After a specified number of failures (configurable via setInvalidatedByBiometricEnrollment), the key becomes unavailable and requires deletion/regeneration. When all biometric templates are removed, all keys with userAuthenticationRequired=true are automatically invalidated.
Key Attestation (Android 8.1+) is also supported: at the application's request, Keymaster signs a certificate with information about the key's characteristics (hardware/software, algorithm, purposes). The server can verify this certificate to confirm that the key was created in a trusted environment.
Frequently Asked Questions
Java KeyStore stores keys in a password-protected file (JKS, BKS). Android Keystore uses hardware isolation via TEE or Secure Element. Java KeyStore is vulnerable to root access; Android Keystore is not, because private keys never leave the secure environment.
Yes, via KeyStore.setEntry with KeyProtection. However, the imported key will not have hardware protection — it will be stored in the software Keystore, encrypted with a master key. For maximum security, always generate keys inside Keystore.
Use KeyChain.isBoundKeyAlgorithm or check KeyCharacteristics after key generation. The presence of FLAG_HARDWARE in the characteristics means the key was created in TEE. You can also check android.security.keystore.isHardwareBacked().
When the application is uninstalled, Android removes all its keys from Keystore. The data is irreversibly lost. On reinstallation, the application must generate new keys. Key backup through TEE is architecturally impossible.
On a locked device, Keymaster does not perform any operations. Keys with userAuthenticationRequired=true require biometric confirmation each time. Even with root access, an attacker cannot call Keymaster directly — only through the Android Keystore service.
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