August 11, 2026
Android Keystore Deep Dive: Where Your Keys Should Actually Live
Part 5 of our Android security series. Part 4 covered Secure Logging — this post covers the other half of CRYPTO: where your encryption…

By Khizar Khan
2 min read
Part 5 of our Android security series. Part 4 covered Secure Logging — this post covers the other half of CRYPTO: where your encryption keys actually live.
What it is, in one line
Android Keystore is a system that generates and stores cryptographic keys inside secure hardware on the device, so the key material itself never has to exist in your app's regular memory or storage.
Why this matters
A common (and risky) pattern: generate an AES key in code, then store it in SharedPreferences or a local file so you can reuse it later. The problem is that key is now just another file on the device — readable by anyone with root access or a rooted-device exploit, no different from any other app data.
Android Keystore solves this differently: the key is generated inside the device's TEE (Trusted Execution Environment) or StrongBox (a dedicated secure chip, where available). Your app never actually holds the raw key — it just asks the Keystore to encrypt or decrypt using a key it never sees directly.
Example 1: Generate a key that never leaves secure hardware
val keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"
)
val spec = KeyGenParameterSpec.Builder(
"app_data_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
setKeySize(256)
}.build()
keyGenerator.init(spec)
keyGenerator.generateKey()val keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"
)
val spec = KeyGenParameterSpec.Builder(
"app_data_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
setKeySize(256)
}.build()
keyGenerator.init(spec)
keyGenerator.generateKey()Notice what's missing: there's no key material to save anywhere. You just reference "app_data_key" by name whenever you need to encrypt or decrypt something — the Keystore handles the rest internally.
Example 2: Prefer StrongBox when the device supports it
Some devices have a dedicated secure chip (StrongBox) that's even more isolated than the standard TEE. Ask for it, but always have a fallback — not every device supports it:
val spec = KeyGenParameterSpec.Builder(
"app_data_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
setKeySize(256)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setIsStrongBoxBacked(true)
}
}.build()val spec = KeyGenParameterSpec.Builder(
"app_data_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
setKeySize(256)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setIsStrongBoxBacked(true)
}
}.build()If the device doesn't support StrongBox, this throws a StrongBoxUnavailableException — so wrap it and retry without setIsStrongBoxBacked(true) as a fallback rather than crashing.
Example 3: Require authentication before the key can be used
For genuinely sensitive operations — decrypting a saved payment method, unlocking a vault of stored credentials — you can tie key use directly to a fresh biometric or device-credential check:
val spec = KeyGenParameterSpec.Builder(
"payment_vault_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
setKeySize(256)
setUserAuthenticationRequired(true)
setUserAuthenticationParameters(30, KeyProperties.AUTH_BIOMETRIC_STRONG)
}.build()val spec = KeyGenParameterSpec.Builder(
"payment_vault_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).apply {
setBlockModes(KeyProperties.BLOCK_MODE_GCM)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
setKeySize(256)
setUserAuthenticationRequired(true)
setUserAuthenticationParameters(30, KeyProperties.AUTH_BIOMETRIC_STRONG)
}.build()This means the key can only be used within 30 seconds of a successful biometric check — not just "the app checked a fingerprint somewhere in the UI." The restriction is enforced by the Keystore itself, not by your app's logic, so it can't be bypassed just by patching the app.
A gotcha worth knowing: keys can be invalidated
If a user changes their screen lock or enrolls a new fingerprint, keys created with setUserAuthenticationRequired(true) can become permanently invalidated by design — that's the system protecting against a changed authentication factor being used to unlock old data. Handle this gracefully: catch KeyPermanentlyInvalidatedException and prompt the user to re-authenticate or regenerate the key, rather than letting the app crash.
Quick checklist
- [ ] Encryption keys generated inside Android Keystore — never hardcoded, never stored in
SharedPreferencesor plain files - [ ] StrongBox requested where available, with a graceful fallback if unsupported
- [ ] Sensitive operations (payments, stored credentials) gated behind
setUserAuthenticationRequired(true) - [ ]
KeyPermanentlyInvalidatedExceptionhandled explicitly, not left to crash - [ ]
getSecurityLevel()checked in testing to confirm keys are actually hardware-backed, not falling back silently to software
The one-line takeaway
If your app generates or stores its own key material anywhere outside Android Keystore, that's the finding — the whole point of Keystore is that your app never has to hold the key at all, so there's nothing on disk for an attacker to steal.
That wraps our 5-part Android security series: MASVS/MASTG checklist → Network Security Config → Certificate Pinning → Secure Logging → Keystore. Together, these cover the highest-impact findings we see across most mobile security reviews — a solid baseline to build the rest of your app's security program on.