How to Implement Android Keystore Hardware-Backed Attestation for Secure Financial Applications

Developing financial applications, cryptocurrency wallets, or enterprise identity solutions on Android requires uncompromising cryptographic security. Storing sensitive private keys in the standard application SharedPreferences or even encrypted SQLite databases is fundamentally insecure, as these storage mediums are vulnerable to extraction on rooted devices or via sophisticated privilege escalation exploits. To protect cryptographic material from complete operating system compromise, Android provides the Hardware-Backed Keystore. By leveraging this system, developers can generate and utilize cryptographic keys that are securely generated and strictly confined within the device’s Trusted Execution Environment (TEE) or a dedicated Secure Element (SE). The key material itself never enters the Android OS’s main memory (RAM).

Understanding Hardware-Backed Attestation

While generating a key inside the TEE prevents extraction, how can a remote backend server definitively trust that the key was actually generated inside secure hardware and not emulated by a malicious client on a rooted emulator? This is the purpose of Keystore Attestation.

When you request key generation with attestation, the Android Keystore asks the TEE to cryptographically sign a certificate chain. The root of this certificate chain is signed by Google’s master attestation key, which is factory-injected into the device hardware by the OEM. By transmitting this certificate chain to your backend server, your backend can mathematically verify the hardware provenance of the key, the device’s boot state (Verified Boot), and the specific package name (APK signature) of the application that requested the key.

Generating an Attested Key

To implement hardware-backed attestation in Kotlin, you utilize the KeyGenParameterSpec builder provided by the Android framework. You must explicitly define an attestation challenge (a cryptographic nonce generated by your backend server to prevent replay attacks).

import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyPairGenerator
import java.security.KeyStore

fun generateAttestedKey(alias: String, challenge: ByteArray) {
    val keyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_EC,
        "AndroidKeyStore"
    )
    
    val parameterSpec = KeyGenParameterSpec.Builder(
        alias,
        KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
    )
        .setDigests(KeyProperties.DIGEST_SHA256)
        .setAttestationChallenge(challenge)
        // Enforce that the key can only be used if the user authenticates via Biometrics
        .setUserAuthenticationRequired(true)
        .setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG)
        .build()
        
    keyPairGenerator.initialize(parameterSpec)
    keyPairGenerator.generateKeyPair()
}

Retrieving the Attestation Certificate Chain

Once the key pair is generated, you must extract the resulting certificate chain from the Android Keystore. This chain contains the hardware-signed attestation data.

fun getAttestationChain(alias: String): Array<java.security.cert.Certificate>? {
    val keyStore = KeyStore.getInstance("AndroidKeyStore")
    keyStore.load(null)
    
    // Returns the certificate chain provided by the TEE/SE
    return keyStore.getCertificateChain(alias)
}

Backend Verification Process

The Android client must serialize this certificate chain (typically to PEM or Base64 format) and transmit it to your financial backend over a secure TLS connection.

The backend server must then perform strict validation:

  1. Verify the Signature Chain: Ensure the leaf certificate is signed by the intermediate certificate, and the intermediate is signed by the official Google Hardware Attestation Root certificate.
  2. Verify the Challenge: Extract the attestation extension data from the leaf certificate and confirm the challenge matches the nonce originally provided by the server.
  3. Verify the Verified Boot State: Inspect the RootOfTrust structure within the attestation data. Ensure the verifiedBootState is Verified (indicating the OS image has not been tampered with or rooted).
  4. Verify the Application Identity: Check the AttestationApplicationId to ensure the requesting application’s package name and APK signature hash exactly match your official release build.

Only if all these cryptographic checks pass should your backend register the public key for future transactional signing operations. By enforcing Hardware-Backed Attestation, financial institutions can guarantee that transactions are being signed by a legitimate, uncompromised hardware device running the genuine application.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.