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
- Fix the underlying crypto failure: verify the AES local-protection key loads (see getAesGcmLocalProtectionKey) before retrying
- Retry addKeyPair after keystore access is restored
- 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
- Confirm the AES local-protection key loads before keystore writes
- Handle device-credential resets that invalidate keystore keys
- Treat keystore write failures as atomic: alias is rolled back on password-save failure
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
- Could not decrypt encrypted password.
- Could not load AES local protection key from keystore
- Decrypted pass is empty for alias
- Failed to setup metadata.
- No KeyPair with alias
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)