Android Keystore and Cryptography: Finding and Exploiting Weak Implementations

Android keystore cryptography security — cyberpunk dark illustration

The Android Keystore system exists for one purpose: to keep cryptographic keys away from attackers. But weak key configurations, broken cipher choices, and flawed key derivation routinely undermine it. This post covers the most common cryptographic vulnerabilities in Android apps and how to identify and exploit them during a security assessment.

The Android Keystore — What It Actually Protects

The Android Keystore stores keys in hardware-backed secure storage (TEE or StrongBox) when available. Keys generated inside the Keystore never leave it in plaintext — cryptographic operations happen inside the secure enclave. The problem is almost never the Keystore itself. It is the configuration of keys stored in it, and the cryptographic operations performed with them.

Finding Keystore Misconfigurations

The most critical misconfigurations are: keys with no user authentication requirement, keys with overly broad purposes, and keys with insufficient key size. All are auditable at runtime with Frida.

bash — keystore-audit
# List all KeyStore aliases in the app via Frida
$ frida -U -l keystore_enum.js com.banking.app
 
[+] [*] KeyStore.load() called — type: AndroidKeyStore
[+] [*] Aliases: [“user_signing_key”, “session_token_key”, “backup_key”]
 
# Check if key requires user authentication
$ run app.provider.query content://com.banking.app/keys
[-] user_signing_key: userAuthRequired=false
[-] session_token_key: userAuthRequired=false
>>> Keys accessible without screen lock / biometrics <<<

A key with userAuthRequired=false is accessible to any process running as the app — including a compromised process or a malicious library loaded into the app. On rooted devices this means the key can be used to sign arbitrary data or decrypt sensitive content without any user awareness.

Broken Cipher Implementations

Even when the Keystore is used correctly, the cipher operation wrapping it can be fatally flawed. The classic failures are ECB mode (leaks data patterns, deterministic), static IVs (makes AES-CBC equivalent to ECB), and weak key derivation (passwords hashed with MD5, no salt).

bash — crypto-weakness
# Extract encrypted SharedPreferences data
$ adb pull /data/data/com.app/shared_prefs/secrets.xml /tmp/secrets.xml
/data/data/…/secrets.xml: 1 file pulled
 
# Check encryption mode used
$ cat /tmp/secrets.xml | grep -i “iv\|cipher\|aes”
[-] AES/ECB/PKCS5Padding
[-] AAAAAAAAAAAAAAAAAAAAAA==
>>> ECB mode + static IV = deterministic encryption <<<
 
# Decrypt with static key recovered from decompiled code
$ python3 decrypt.py –mode ECB –key “hardcoded123!” –data secrets.xml
[+] Decrypted: {“auth_token”: “Bearer eyJhbGciOiJIUzI1NiJ9…”}

Runtime Crypto Interception with Frida

When source code is unavailable, hook the Java crypto primitives directly. The Cipher.doFinal() method is the universal intercept point — every AES/RSA/DES operation passes through it.

javascript — frida-crypto-hook
# Hook Cipher.doFinal to intercept plaintext
Java.perform(function() {
var Cipher = Java.use(“javax.crypto.Cipher”);
Cipher.doFinal.overload(“[B”).implementation = function(input) {
var result = this.doFinal(input);
console.log(“[*] Plaintext: ” + Java.array(“byte”, result));
return result;
};
});
 
[+] [*] Algorithm: AES/ECB/PKCS5Padding
[+] [*] Plaintext: {“user_id”: 1337, “role”: “admin”, “exp”: 9999999999}

Hardcoded Keys and Secrets

A large proportion of Android crypto vulnerabilities come from hardcoded keys — baked into the APK in BuildConfig, string resources, or native libraries. Static analysis finds most of them in minutes.

# Scan decompiled APK for crypto material
grep -rn "SecretKeySpec\|AES\|HMAC\|hardcoded\|password\|secret" output/ --include="*.java" -i
grep -rn "[A-Za-z0-9+/](32,)=(0, 2)" output/ --include="*.java"  # Base64 blobs
strings lib/arm64-v8a/libnative.so | grep -E "[A-F0-9](32,)"  # Hex keys in .so

Remediation Priorities

  • Always use AES-GCM (authenticated encryption) — never ECB, never static IV with CBC
  • Set setUserAuthenticationRequired(true) on all Keystore keys that protect sensitive operations
  • Use setUserAuthenticationValidityDurationSeconds(-1) to require biometric per-operation
  • Derive keys from passwords using PBKDF2WithHmacSHA256 with >= 100,000 iterations and a random 128-bit salt
  • Never store raw keys in SharedPreferences, files, or SQLite — use the Keystore for all key material
  • Enable StrongBox where available (setIsStrongBoxBacked(true)) for highest-value keys

Cryptographic vulnerabilities in Android apps are some of the most impactful bugs in mobile security — they often lead directly to credential theft, session hijacking, or complete data compromise. For hands-on labs covering Android crypto exploitation, Mobile Hacking Lab includes dedicated cryptography challenges in the CAED certification track.