MuntashirAkon/AppManager · error · KeyStoreException

Could not decrypt encrypted password.

Error message

Could not decrypt encrypted password.

What it means

getAmKeyStorePassword loads the App Manager keystore password from SharedPreferences, where it is stored encrypted. It throws KeyStoreException when the encrypted password exists but getDecryptedPassword returns null, meaning decryption failed (wrong/corrupted Crypto trapdoor or broken saved blob).

Solutions

  1. Re-set the App Manager keystore password so a fresh encrypted value is saved under PREF_AM_KEYSTORE_PASS.
  2. Clear the stale preference entry (remove PREF_AM_KEYSTORE_PASS) and prompt the user to re-enter credentials.
  3. Check that the crypto primitive used by getDecryptedPassword (Android Keystore key) is valid and re-initialize it if invalidated.
  4. If prefs were restored from a backup, restore app data from the same device instead, or reconfigure the keystore.

Example fix

// before
char[] realPassword = getDecryptedPassword(mContext, encryptedPass);
if (realPassword == null) {
    throw new KeyStoreException("Could not decrypt encrypted password.");
}
// after
char[] realPassword = getDecryptedPassword(mContext, encryptedPass);
if (realPassword == null) {
    sSharedPreferences.edit().remove(PREF_AM_KEYSTORE_PASS).apply();
    realPassword = promptUserForKeyStorePassword(); // re-encrypt and save
    if (realPassword == null) throw new KeyStoreException("Could not decrypt encrypted password.");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check before requesting the AM keystore password
if (!sSharedPreferences.contains(PREF_AM_KEYSTORE_PASS)) {
    throw new KeyStoreException("No saved password for KeyStore."); // prompt user to set one first
}

Try / catch

// try
try {
    char[] pass = keyStoreManager.getAmKeyStorePassword();
} catch (KeyStoreException e) {
    // password blob undecryptable: reset saved password and re-prompt
    resetSavedKeyStorePassword();
    promptUserForKeyStorePassword();
}

Prevention

When it happens

Trigger: Calling getAmKeyStorePassword() when sSharedPreferences contains PREF_AM_KEYSTORE_PASS but getDecryptedPassword(mContext, encryptedPass) returns null — e.g. crypto key material unavailable or the stored ciphertext is corrupt.

Common situations: Device crypto (e.g. Android Keystore-backed) reset or wiped after OS update/biometric change; app data partially restored from backup so the ciphertext no longer matches the decryption key; corruption of the prefs XML.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/cde255c64e374db5. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/crypto/ks/KeyStoreManager.java:481

    /**
     * Get App Manager's KeyStore password. The password is stored in the shared preferences in an
     * encrypted format (the encryption/decryption is performed via AndroidKeyStore). In case the
     * user restores from the cache or accidentally deletes all entries from the shared pref, App
     * Manager will ask for KeyStore password again.
     *
     * @return KeyStore password in decrypted format. {@link Utils#clearChars(char[])} must be called when done.
     */
    @CheckResult
    @NonNull
    public char[] getAmKeyStorePassword() throws KeyStoreException {
        String encryptedPass = sSharedPreferences.getString(PREF_AM_KEYSTORE_PASS, null);
        if (encryptedPass == null) {
            throw new KeyStoreException("No saved password for KeyStore.");
        }
        char[] realPassword = getDecryptedPassword(mContext, encryptedPass);
        if (realPassword == null) {
            throw new KeyStoreException("Could not decrypt encrypted password.");
        }
        return realPassword;
    }

    /**
     * @return Password for the given alias. {@link Utils#clearChars(char[])} must be called when done.
     * @deprecated Kept for migratory purposes only, deprecated since v2.6.3. To be removed in v3.0.0.
     */
    @Deprecated
    @CheckResult
    @NonNull
    private char[] getAliasPassword(@NonNull String alias) throws KeyStoreException {
        char[] password;
        String prefAlias = getPrefAlias(alias);
        if (sSharedPreferences.contains(prefAlias)) {
            String encryptedPass = sSharedPreferences.getString(prefAlias, null);
            if (encryptedPass == null) {
                throw new KeyStoreException("Stored pass is empty for alias " + alias);

View on GitHub (pinned to 0152f468fc)