๐Ÿ” Android Security and Privacy

Android Keystore Explained

The Android Keystore lets apps use cryptographic keys without ever exposing the key material, not even to the app itself. Keys can live in secure hardware, be bound to biometric authentication, and be attested by the device. Here's how it works and how to use it correctly.

โœ๏ธ Yunok Tech ๐Ÿ“… Updated โฑ 3 min read ๐ŸŽฏ Intermediate
On this page
  1. How the Keystore works
  2. Generate an AES key and encrypt data
  3. Keys that require user authentication
  4. StrongBox and attestation
  5. Pitfalls
  6. Use cases
  7. Frequently asked questions
  8. Conclusion

Encryption is only as strong as the protection of its key. Store an AES key in a file next to the encrypted data, and anyone who copies both has everything. The Android Keystore solves key storage: your app gets a handle to perform operations, while the key itself stays in a protected environment.

How the Keystore works

Android Keystore architecture The app calls Keystore APIs with a key alias and data. The Keystore system service forwards operations to secure hardware, a Trusted Execution Environment or a StrongBox secure element, where keys are generated and stored. Plaintext data goes in and ciphertext comes out; the key material never leaves the secure hardware. Your appalias "notes_key" plaintext ciphertext Keystore serviceaccess control Secure hardwareTEE or StrongBox๐Ÿ”‘ key materialnever leaves
The app never sees the key bytes, so a stolen data file is useless without the device's secure hardware.
  • Non-exportable keys: key material can't be extracted from the Keystore.
  • Hardware-backed on most modern devices, via a Trusted Execution Environment (TEE). Some devices also have StrongBox, a dedicated secure element.
  • Key restrictions: algorithms, purposes, block modes, validity, and user-authentication requirements are enforced by the Keystore.

Generate an AES key and encrypt data

KeystoreCrypto.ktKotlin
object KeystoreCrypto {
    private const val KEYSTORE = "AndroidKeyStore"
    private const val ALIAS = "notes_key"
    private const val TRANSFORMATION = "AES/GCM/NoPadding"

    private fun getOrCreateKey(): SecretKey {
        val ks = KeyStore.getInstance(KEYSTORE).apply { load(null) }
        (ks.getEntry(ALIAS, null) as? KeyStore.SecretKeyEntry)?.let { return it.secretKey }

        val spec = KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .setKeySize(256)
            .build()
        return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE)
            .apply { init(spec) }
            .generateKey()
    }

    /** Returns IV + ciphertext. A fresh random IV is generated for every encryption. */
    fun encrypt(plain: ByteArray): ByteArray {
        val cipher = Cipher.getInstance(TRANSFORMATION).apply { init(Cipher.ENCRYPT_MODE, getOrCreateKey()) }
        return cipher.iv + cipher.doFinal(plain)          // GCM IV is 12 bytes
    }

    fun decrypt(data: ByteArray): ByteArray {
        val iv = data.copyOfRange(0, 12)
        val cipher = Cipher.getInstance(TRANSFORMATION).apply {
            init(Cipher.DECRYPT_MODE, getOrCreateKey(), GCMParameterSpec(128, iv))
        }
        return cipher.doFinal(data, 12, data.size - 12)
    }
}

AES-GCM provides confidentiality and integrity: tampered ciphertext fails to decrypt. Never reuse an IV with the same key. Let the cipher generate it, as above. For most apps, a higher-level library such as Tink, with a Keystore-wrapped master key, is easier to use correctly. See Encrypted Data Storage.

Keys that require user authentication

Biometric-bound keyKotlin
val spec = KeyGenParameterSpec.Builder("payment_auth_key", KeyProperties.PURPOSE_SIGN)
    .setAlgorithmParameterSpec(ECGenParameterSpec("secp256r1"))
    .setDigests(KeyProperties.DIGEST_SHA256)
    .setUserAuthenticationRequired(true)
    .setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG)   // auth for every use
    .setInvalidatedByBiometricEnrollment(true)                                // new fingerprint โ†’ key invalid
    .build()

Using this key requires a successful BiometricPrompt with a CryptoObject. That's much stronger than "biometric check passed, so continue". See Biometric Authentication.

StrongBox and attestation

  • setIsStrongBoxBacked(true) requests a StrongBox key on supported devices. Handle StrongBoxUnavailableException and fall back to a TEE key.
  • Key attestation lets your server verify, via a certificate chain, that a key was generated in secure hardware on a genuine device. It's useful for high-assurance scenarios, and verification belongs on the server.

Pitfalls

PitfallConsequenceGuard
Keys can be invalidated (lock screen removed, biometric changes)KeyPermanentlyInvalidatedExceptionCatch, delete the key, re-create it, and re-authenticate or re-fetch data
Keys aren't backed upRestored encrypted data can't be decryptedExclude encrypted data from backup, or treat it as re-fetchable
Uninstall deletes keysData unrecoverableDesign encrypted local data as a cache, or sync to the server
Rolling your own crypto modesWeak securityUse AES-GCM via the Keystore or Tink; never ECB

Use cases

Encrypting tokens and local secrets

A master key in the Keystore wraps the data encryption keys.

Biometric-confirmed transactions

An auth-bound signing key signs a server challenge, and the server verifies it with the public key.

Device binding

An attested key pair proves requests come from the enrolled device.

Frequently asked questions

Can I store a password or token directly in the Keystore?

The Keystore stores keys, not arbitrary values. Encrypt the token with a Keystore key and store the ciphertext.

Is the Keystore safe on rooted devices?

Hardware-backed keys resist extraction even on rooted devices, but an attacker with root can still use the key through your app while it runs. Combine with server-side checks.

Does every device have StrongBox?

No. It's optional hardware. Always handle its absence.

Conclusion

The Android Keystore keeps key material out of your app's reach, often in secure hardware, with enforced restrictions and optional biometric binding and attestation. Use it (directly or via Tink) for every key that protects sensitive data, and handle invalidation gracefully.

Next step: decide what never belongs in the app at all with Secure API Keys and Secrets.

Official references

๐Ÿ“ฑ Apps built by Yunok Tech

Real Android apps on Google Play, free to download.

See all our apps โ†’

Ready to build real Android applications?

Security is easiest to get right when it is part of how you build from the first screen. Strengthen the Android fundamentals that secure, privacy-respecting apps are built on.

Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.

Explore the Android course and take your next step as an Android developer.

Share: LinkedIn X / Twitter WhatsApp