MuntashirAkon/AppManager · critical · KeyStoreException
Could not load AES local protection key from keystore
Error message
Could not load AES local protection key from keystore
What it means
getAesGcmLocalProtectionKey loads the app's AES-GCM local protection key from the Android keystore under a fixed alias. If the keystore reports the alias exists but KeyStore.getKey returns null, the key cannot be recovered and a KeyStoreException is thrown. This usually indicates keystore corruption or an entry the app cannot access.
Solutions
- Delete the stale keystore alias (keyStore.deleteEntry) and regenerate the key via the createKey path, accepting that previously protected data cannot be decrypted
- Clear the app's keystore-backed data / reinstall the app to reset keystore state
- Check for device credential changes (settings reset) and inform the user data is unrecoverable
Example fix
// before
SecretKey secretKey = (SecretKey) keyStore.getKey(AES_LOCAL_PROTECTION_KEY_ALIAS, null);
if (secretKey == null) {
throw new KeyStoreException("Could not load AES local protection key from keystore");
}
// after
SecretKey secretKey = (SecretKey) keyStore.getKey(AES_LOCAL_PROTECTION_KEY_ALIAS, null);
if (secretKey == null) {
Log.w(TAG, "Stale keystore alias; regenerating key");
keyStore.deleteEntry(AES_LOCAL_PROTECTION_KEY_ALIAS);
return generateAndStoreNewKey(); // falls through to creation path
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!keyStore.containsAlias(AES_LOCAL_PROTECTION_KEY_ALIAS)) {
// key missing entirely: go to creation path
return createNewKey();
} Try / catch
try {
SecretKey key = CompatUtil.getAesGcmLocalProtectionKey();
} catch (KeyStoreException e) {
// keystore entry unrecoverable; reset crypto state or inform user data is lost
resetCryptoState();
} Prevention
- Never assume hardware-backed keys survive OS upgrades or credential resets
- Design for key loss: keep an explicit re-key/reset path
- Test on devices with keystore-destroying updates and backup/restore flows
When it happens
Trigger: keyStore.containsAlias(AES_LOCAL_PROTECTION_KEY_ALIAS) is true but keyStore.getKey(alias, null) returns null — e.g. hardware-backed key destroyed by system update, keystore entry corrupted, or key generated with different user credentials.
Common situations: OS upgrade or device restore that invalidated hardware-backed keys; keystore database corruption; work-profile/unlock-credential changes invalidating keys.
Related errors
- Could not decrypt encrypted password.
- Decrypted pass is empty for alias
- Failed to setup metadata.
- No KeyPair with alias
- No KeyPair with alias
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/53f44abb776ac903.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/io/github/muntashirakon/AppManager/crypto/ks/CompatUtil.java:98
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException,
NoSuchProviderException, InvalidAlgorithmParameterException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, UnrecoverableKeyException {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
keyStore.load(null);
Log.i(TAG, "Loading local protection key");
SharedPreferences sharedPreferences = context.getSharedPreferences("keystore", Context.MODE_PRIVATE);
// Get the version of Android when the key has been generated, default to the current version of the system.
// In the latter case, the key will be generated.
int androidVersionWhenTheKeyHasBeenGenerated = sharedPreferences.getInt(
SHARED_KEY_ANDROID_VERSION_WHEN_KEY_HAS_BEEN_GENERATED, Build.VERSION.SDK_INT);
// Check if there's a key in the Android keystore (M and later)
if (keyStore.containsAlias(AES_LOCAL_PROTECTION_KEY_ALIAS)) {
Log.i(TAG, "AES local protection key found in keystore");
SecretKey secretKey = (SecretKey) keyStore.getKey(AES_LOCAL_PROTECTION_KEY_ALIAS, null);
if (secretKey == null) {
throw new KeyStoreException("Could not load AES local protection key from keystore");
}
return new SecretKeyAndVersion(secretKey, androidVersionWhenTheKeyHasBeenGenerated);
}
// Check if a key has been created on version < M (such as, in case of an OS upgrade)
SecretKey secretKey = readKeyApiL(sharedPreferences, keyStore);
if (secretKey != null) {
return new SecretKeyAndVersion(secretKey, androidVersionWhenTheKeyHasBeenGenerated);
}
// Otherwise generate key
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.i(TAG, "Generating AES key with keystore");
KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
ANDROID_KEY_STORE_PROVIDER);
generator.init(new KeyGenParameterSpec.Builder(AES_LOCAL_PROTECTION_KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)View on GitHub (pinned to 0152f468fc)