Android Keystore

Android Keystore

1. What Is Android Keystore?

Android Keystore is a system service used to generate and securely store cryptographic keys.

The application can use a stored key for encryption, decryption or signing, but the secret/private key material remains non-exportable.

Android Application
        |
        | Requests cryptographic operation
        v
Android Keystore
        |
        +-- Software-backed storage
        |
        +-- Hardware-backed TEE
        |
        +-- StrongBox security hardware

---

2. Why Use Android Keystore?

It helps protect cryptographic keys from:

* Extraction from application files

* APK reverse engineering

* Accidental key exposure

* Unauthorized cryptographic operations

* Some attacks on the Android operating system

The key can also be configured so that it works only after biometric or device-credential authentication.

---

3. Classification of Keys

Symmetric key

The same key performs encryption and decryption.

Plain text
    |
    | AES secret key
    v
Encrypted data
    |
    | Same AES key
    v
Plain text

Common algorithm:

AES

Asymmetric key pair

It contains two keys:

Public key  -> Can be shared
Private key -> Remains protected in Keystore

Common algorithms:

RSA
EC
Symmetric keyAsymmetric key
One secret keyPublic and private key pair
FasterComparatively slower
Used for data encryptionUsed for signing and key exchange
Example: AESExamples: RSA and EC

---

4. Key Generation

Keys are generated using:

* KeyGenerator for symmetric keys

* KeyPairGenerator for asymmetric keys

* KeyGenParameterSpec to define security rules

AES key generation

private const val KEY_ALIAS = "secure_data_key"

fun generateAesKey() {
    val keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES,
        "AndroidKeyStore"
    )

    val keySpec = KeyGenParameterSpec.Builder(
        KEY_ALIAS,
        KeyProperties.PURPOSE_ENCRYPT or
            KeyProperties.PURPOSE_DECRYPT
    )
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(
            KeyProperties.ENCRYPTION_PADDING_NONE
        )
        .setKeySize(256)
        .build()

    keyGenerator.init(keySpec)
    keyGenerator.generateKey()
}

Getting the generated key

fun getAesKey(): SecretKey? {
    val keyStore = KeyStore.getInstance(
        "AndroidKeyStore"
    ).apply {
        load(null)
    }

    return keyStore.getKey(
        KEY_ALIAS,
        null
    ) as? SecretKey
}

The alias is the name used to find the key later.

---

5. Non-Exportable Keys

A non-exportable key cannot be extracted as raw key bytes from Android Keystore.

Application asks:
"Decrypt this data"

Application does not receive:
"Here are the secret-key bytes"

Instead, the app receives a key reference and asks Android Keystore to perform the cryptographic operation.

Application
    |
    | Cipher operation using key alias
    v
Android Keystore
    |
    | Uses protected key internally
    v
Encrypted or decrypted result

Important clarification

Android Keystore protects the key, not automatically all application data.

The app must still:

1. Encrypt sensitive data with the key.

2. Store the encrypted data.

3. Protect the IV and authentication tag correctly.

4. Avoid logging plaintext data.

---

6. Normal Key vs Keystore Key

Normal application keyAndroid Keystore key
May be stored as a string or byte arrayStored through the Keystore service
Can accidentally appear in memory or logsKey material remains non-exportable
Can be extracted from application storageHarder to extract
No built-in usage restrictionsCan require biometric authentication
Usually software-managedMay be hardware-backed

---

7. Hardware-Backed Security

Android Keystore keys may be protected by secure hardware.

Main hardware options are:

Trusted Execution Environment

TEE is an isolated execution environment separate from the normal Android operating system.

Normal Android OS
       |
       | Sends operation
       v
Trusted Execution Environment
       |
       | Uses protected key
       v
Cryptographic result

StrongBox

StrongBox provides stronger isolation through dedicated secure hardware when supported by the device.

A hardware-backed key’s plaintext key material remains available only within secure hardware.

Comparison

Software-backedTEE-backedStrongBox-backed
Protected by Android softwareProtected inside secure execution environmentProtected by dedicated security hardware
Widest availabilityAvailable on many devicesNot supported on every device
Lower hardware isolationStrong isolationStrongest isolation
Faster in some casesSecure and practicalMay be slower

---

8. Requesting a StrongBox-Backed Key

fun generateStrongBoxKey() {
    val keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES,
        "AndroidKeyStore"
    )

    val keySpec = KeyGenParameterSpec.Builder(
        "strongbox_key",
        KeyProperties.PURPOSE_ENCRYPT or
            KeyProperties.PURPOSE_DECRYPT
    )
        .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
        .setEncryptionPaddings(
            KeyProperties.ENCRYPTION_PADDING_NONE
        )
        .setIsStrongBoxBacked(true)
        .build()

    try {
        keyGenerator.init(keySpec)
        keyGenerator.generateKey()
    } catch (exception: StrongBoxUnavailableException) {
        // Generate a normal Keystore key as fallback.
    }
}

StrongBox support must not be assumed. The app should provide a secure fallback when it is unavailable. Android provides key attestation when stronger verification of hardware-backed key properties is required.

---

9. Checking Whether a Key Is Hardware-Backed

For an asymmetric private key:

fun getKeySecurityLevel(
    privateKey: PrivateKey
): Int {
    val keyFactory = KeyFactory.getInstance(
        privateKey.algorithm,
        "AndroidKeyStore"
    )

    val keyInfo = keyFactory.getKeySpec(
        privateKey,
        KeyInfo::class.java
    )

    return keyInfo.securityLevel
}

Possible security levels include:

SOFTWARE
TRUSTED_ENVIRONMENT
STRONGBOX

Do not assume that every Android Keystore key is hardware-backed.

---

10. Biometric-Protected Keys

A biometric-protected key can be used only after the user successfully authenticates.

User requests secure operation
          |
          v
BiometricPrompt
          |
      Success?
       /    \
     No      Yes
     |        |
   Reject   Unlock key operation

Android supports connecting a Keystore cryptographic operation to BiometricPrompt through CryptoObject.

---

11. Generating a Biometric-Protected Key

private const val BIOMETRIC_KEY =
    "biometric_secure_key"

fun generateBiometricKey() {
    val keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES,
        "AndroidKeyStore"
    )

    val keySpec = KeyGenParameterSpec.Builder(
        BIOMETRIC_KEY,
        KeyProperties.PURPOSE_ENCRYPT or
            KeyProperties.PURPOSE_DECRYPT
    )
        .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
        .setEncryptionPaddings(
            KeyProperties.ENCRYPTION_PADDING_PKCS7
        )
        .setUserAuthenticationRequired(true)
        .setUserAuthenticationParameters(
            0,
            KeyProperties.AUTH_BIOMETRIC_STRONG
        )
        .setInvalidatedByBiometricEnrollment(true)
        .build()

    keyGenerator.init(keySpec)
    keyGenerator.generateKey()
}

Here:

Authentication duration = 0

means authentication is required for every key use.

The key can also be configured for time-based authorization, where it remains usable for a limited period after authentication.

---

12. Using the Key with BiometricPrompt

Prepare the cipher

fun createCipher(): Cipher {
    val keyStore = KeyStore.getInstance(
        "AndroidKeyStore"
    ).apply {
        load(null)
    }

    val key = keyStore.getKey(
        BIOMETRIC_KEY,
        null
    ) as SecretKey

    return Cipher.getInstance(
        "AES/CBC/PKCS7Padding"
    ).apply {
        init(Cipher.ENCRYPT_MODE, key)
    }
}

Pass it to BiometricPrompt

val cipher = createCipher()

val cryptoObject =
    BiometricPrompt.CryptoObject(cipher)

biometricPrompt.authenticate(
    promptInfo,
    cryptoObject
)

Use it after successful authentication

override fun onAuthenticationSucceeded(
    result: BiometricPrompt.AuthenticationResult
) {
    val authenticatedCipher =
        result.cryptoObject?.cipher
            ?: return

    val encryptedData =
        authenticatedCipher.doFinal(
            sensitiveData.toByteArray()
        )
}
The biometric result authorizes the cryptographic operation; the app does not receive the user’s fingerprint or face data.

---

13. Authentication Per Use vs Time-Based Authentication

Authentication per useTime-based authentication
User authenticates for every operationKey remains available for a limited period
Used with CryptoObjectMay be unlocked after device authentication
Stronger controlMore convenient
Suitable for payments and sensitive actionsSuitable for repeated low-risk operations
Per-use:
Authenticate -> One operation -> Locked

Time-based:
Authenticate -> Key available for N seconds

---

14. Biometric Enrollment and Key Invalidation

A biometric-protected key can be configured to become invalid when the user adds or changes biometric credentials.

Key generated
     |
     v
User adds new fingerprint
     |
     v
Key invalidated
     |
     v
App generates a new key

The app should handle:

KeyPermanentlyInvalidatedException

and require the user to authenticate again or recreate the key.

---

15. Common Mistakes

1. Assuming every key is hardware-backed

Android Keystore may use software, TEE or StrongBox depending on the device and configuration.

2. Hardcoding encryption keys

Avoid:

const val KEY = "my-secret-key"

Generate keys through Android Keystore.

3. Trying to export a private or secret key

Keystore keys are designed to be used through cryptographic operations, not extracted as raw bytes.

4. Encrypting without storing the IV

The IV is required for decryption.

Store:
Encrypted data + IV

Do not store:
Raw secret key

5. Reusing an IV with AES-GCM

A new unpredictable IV must be used for each encryption operation with the same key.

6. Assuming StrongBox is always available

Handle StrongBoxUnavailableException and provide an appropriate fallback.

7. Using biometric authentication without CryptoObject

A normal biometric success only confirms authentication. For protecting a cryptographic operation, bind the Keystore cipher, signature or MAC through CryptoObject.

8. Not handling key invalidation

Keys may become unusable after biometric enrollment changes, lock-screen changes or security-policy changes.

9. Storing plaintext after encryption

Clear or avoid unnecessary plaintext copies after the secure operation.

---

16. Interview Questions and Answers

Q1. What is Android Keystore?

Android Keystore is a system service that generates and stores cryptographic keys while keeping secret and private key material non-exportable.

Q2. What types of keys can be generated?

It can generate symmetric keys such as AES and asymmetric key pairs such as RSA or EC.

Q3. What does non-exportable mean?

It means the application can use the key for cryptographic operations but cannot obtain its raw secret or private key bytes.

Q4. Is every Android Keystore key hardware-backed?

No. A key may be software-backed, TEE-backed or StrongBox-backed depending on the device and configuration.

Q5. What is hardware-backed security?

The key and cryptographic operations are protected inside secure hardware, reducing exposure to the normal Android operating system.

Q6. TEE vs StrongBox?

TEE is an isolated secure environment within the main processor. StrongBox uses dedicated secure hardware and provides stronger isolation but is not available on every device.

Q7. How do you check whether a key is hardware-backed?

Read its KeyInfo.securityLevel or verify its properties using key attestation when server-side assurance is required.

Q8. What is a biometric-protected key?

It is a Keystore key configured so that cryptographic operations require successful biometric or device authentication.

Q9. What is BiometricPrompt.CryptoObject?

It wraps a Cipher, Signature or Mac and binds that cryptographic operation to successful biometric authentication.

Q10. What happens when biometric enrollment changes?

The key may be permanently invalidated if it was configured with biometric-enrollment invalidation enabled.

Q11. Can Android Keystore encrypt complete files directly?

Keystore protects and operates with cryptographic keys. The application still performs encryption and stores the resulting ciphertext and required metadata.

Q12. What should happen when StrongBox is unavailable?

The app should catch StrongBoxUnavailableException and use a suitable fallback, such as a normal hardware-backed or Android Keystore key.

---

17. Final Interview Answer

> Android Keystore securely generates and stores cryptographic keys. Secret and private keys remain non-exportable, so the application uses them through encryption, decryption or signing operations without receiving the raw key material. Keys can be software-backed, TEE-backed or StrongBox-backed, depending on the device. For sensitive operations, a key can require biometric authentication by enabling user authentication during key generation and passing its cipher through BiometricPrompt.CryptoObject. The application should not assume hardware or StrongBox support and must handle unavailable hardware and key invalidation correctly.