MuntashirAkon/AppManager · critical · KeyStoreException

Password for could not be saved.

Error message

Password for ${alias} could not be saved.

What it means

addKeyPair stores a KeyPair in the AM keystore and then encrypts its keystore password for persistence. If getEncryptedPassword returns null (encryption of the password failed), the just-added entry is rolled back (deleteEntry) and a KeyStoreException is thrown, leaving the keystore unchanged.

Solutions

  1. Fix the underlying crypto failure: verify the AES local-protection key loads (see getAesGcmLocalProtectionKey) before retrying
  2. Retry addKeyPair after keystore access is restored
  3. Clear/reset keystore state and re-add keys, accepting loss of previously protected data

Example fix

// before
String encryptedPass = getEncryptedPassword(mContext, password);
if (encryptedPass == null) {
    mAmKeyStore.deleteEntry(alias);
    throw new KeyStoreException("Password for " + alias + " could not be saved.");
}
// after
String encryptedPass = getEncryptedPassword(mContext, password);
if (encryptedPass == null) {
    mAmKeyStore.deleteEntry(alias);
    throw new KeyStoreException("Password for " + alias + " could not be saved.",
            new KeyStoreException("Password encryption unavailable; check local protection key"));
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    ksManager.addKeyPair(alias, keyPair, password, false);
} catch (KeyStoreException e) {
    // password encryption failed; keystore state rolled back
    showKeystoreUnavailableError();
}

Prevention

When it happens

Trigger: getEncryptedPassword(mContext, password) returns null because the AES local-protection key used for password encryption is unavailable or encryption fails while adding a key pair (also reached from AdbConnectionManager and migrateKeyStore).

Common situations: Keystore corruption so the protection key cannot be used; device-credential reset invalidating keys; migration from an old keystore on a broken keystore state.

Related errors


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

Appendix: source

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

    }

    public void addKeyPair(String alias, @NonNull KeyPair keyPair, boolean isOverride)
            throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
        // Check existence of this alias in system preferences, this should be unique
        String prefAlias = getPrefAlias(alias);
        if (sSharedPreferences.contains(prefAlias) && mAmKeyStore.containsAlias(alias)) {
            Log.w(TAG, "Alias %s exists.", alias);
            if (isOverride) removeItemInternal(alias);
            else return;
        }
        char[] password = getAmKeyStorePassword();
        PrivateKey privateKey = keyPair.getPrivateKey();
        Certificate certificate = keyPair.getCertificate();
        mAmKeyStore.setKeyEntry(alias, privateKey, password, new Certificate[]{certificate});
        String encryptedPass = getEncryptedPassword(mContext, password);
        if (encryptedPass == null) {
            mAmKeyStore.deleteEntry(alias);
            throw new KeyStoreException("Password for " + alias + " could not be saved.");
        }
        sSharedPreferences.edit().putString(prefAlias, encryptedPass).apply();
        try (OutputStream is = new FileOutputStream(AM_KEYSTORE_FILE)) {
            mAmKeyStore.store(is, password);
            Utils.clearChars(password);
            Utils.clearChars(password);
        }
    }

    public void addSecretKey(String alias, @NonNull SecretKey secretKey, boolean isOverride)
            throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
        // Check existence of this alias in system preferences, this should be unique
        String prefAlias = getPrefAlias(alias);
        if (sSharedPreferences.contains(prefAlias) && mAmKeyStore.containsAlias(alias)) {
            if (!isOverride) throw new KeyStoreException("Alias " + alias + " exists.");
            else Log.w(TAG, "Alias %s exists.", alias);
        }
        char[] password = getAmKeyStorePassword();

View on GitHub (pinned to 0152f468fc)