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

  1. Delete the stale keystore alias (keyStore.deleteEntry) and regenerate the key via the createKey path, accepting that previously protected data cannot be decrypted
  2. Clear the app's keystore-backed data / reinstall the app to reset keystore state
  3. 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

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


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)