You already see it in Tomcat, Confluence, Spring Boot, or a plain Java SSL stack trace: java.security.UnrecoverableKeyException: Cannot recover key. The keystore file usually opens fine — the failure happens one step later when Java tries to decrypt the private key for your TLS alias.
Use the table below to jump to the cause that matches your setup. Each section explains why it happens and what to change. If keytool -list fails before you reach this error, start with Fix keystore tampered or password incorrect instead.
Tested on: Ubuntu 26.04 LTS; OpenJDK 25.0.3; kernel 7.0.0-27-generic.
What the error actually means
KeyManagerFactory.init(keystore, password) needs a password to decrypt each PrivateKeyEntry. If that password does not match the key password set when the entry was created, the JVM throws:
java.security.UnrecoverableKeyException: Cannot recover keyIn Tomcat logs the same root exception is often wrapped:
Caused by: java.lang.IllegalArgumentException: Cannot recover key
...
Caused by: java.security.UnrecoverableKeyException: Cannot recover keyThis is not a corrupt certificate chain problem and not a truststore issue. Java recovered the keystore container but not the private key inside the alias your connector uses.
Find your cause
| Likely cause | Clues | Go to |
|---|---|---|
| Store password ≠ key password | keytool -list works; Tomcat or KeyManagerFactory.init fails; JKS created with different -keypass |
Key password mismatch |
Tomcat certificateKeyPassword missing or wrong |
certificateKeystorePassword is correct; certificateKeyPassword is omitted or stale in server.xml |
Tomcat connector passwords |
| Spring Boot key password wrong | server.ssl.key-store-password correct; server.ssl.key-password missing or wrong |
Spring Boot SSL passwords |
| Wrong alias | Multiple PrivateKeyEntry rows; connector points at the wrong alias |
Wrong alias or multiple keys |
| Post-renewal key password drift | New cert imported; old key password still in app config | After certificate renewal |
Key password does not match store password
JKS allows a store password (-storepass) and a per-alias key password (-keypass) to differ. keytool -genkeypair accepts both:
keytool -genkeypair -alias tomcat -keyalg RSA -keystore server.jks -storetype JKS \
-storepass storepass -keypass keypass123 \
-dname "CN=localhost" -nopromptListing the keystore only checks the store password — it still succeeds:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entryTomcat-style loading uses KeyManagerFactory.init with a single password for private keys. If the app passes the store password while the entry was protected with keypass123, initialization fails:
Keystore loaded OK
Exception in thread "main" java.security.UnrecoverableKeyException: Cannot recover key
at java.base/sun.security.provider.KeyProtector.recover(KeyProtector.java:293)
at java.base/sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:162)
...
at java.base/javax.net.ssl.KeyManagerFactory.init(KeyManagerFactory.java:269)Passing the correct key password succeeds:
Keystore loaded OK
KeyManagerFactory initialized OKFix by aligning passwords. Change the key password to match the store password (typical for Tomcat):
keytool -keypasswd -alias tomcat -keystore server.jks \
-storepass storepass -keypass keypass123 -new storepass -nopromptkeytool -keypasswd mainly for JKS keystores. For PKCS12, modern keytool commonly expects the key password and store password to stay aligned, and may warn that a separate -keypass is ignored. If a PKCS12 file still fails with Cannot recover key, recreate or re-export the .p12 with a known password and update the app config.
After alignment, KeyManagerFactory.init with the store password works again. More rotation patterns in Change alias, password, and delete entries.
In Java/keytool workflows, PKCS12 is normally configured with the same store and key password — mismatches are less common but the same Cannot recover key message appears if the app supplies the wrong decryption password for the alias.
Tomcat connector passwords
Tomcat reads the identity keystore through JSSE. On the Certificate element in server.xml (or equivalent), these attributes matter:
| Attribute | Role |
|---|---|
certificateKeystoreFile |
Path to the keystore |
certificateKeystorePassword |
Store password (legacy docs: keystorePass) |
certificateKeyPassword |
Key password for the alias (legacy: keyPass) |
certificateKeyAlias |
Alias to load (recommended when several keys exist) |
You hit Cannot recover key when:
certificateKeyPasswordis wrong while the store password is rightcertificateKeyPasswordis omitted and Tomcat reuses the store password, but the alias was created with a different-keypass- The connector targets the wrong alias and the reused password does not fit that entry
Fix options:
- Set
certificateKeyPasswordto the real key password for the alias, or - Run
keytool -keypasswdso the alias key password matches what Tomcat passes (often the same as the store password), or - Set
certificateKeyAliasexplicitly and match passwords for that alias only
See Configure Tomcat SSL with keytool for a clean PKCS12 connector layout.
Spring Boot SSL passwords
Spring Boot separates store and key passwords:
server.ssl.key-store=/path/to/keystore.p12
server.ssl.key-store-password=store-secret
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat
server.ssl.key-password=key-secretIf key-password is omitted, Spring may reuse the store password depending on version and keystore type. If you generated the PKCS12 with different store and key passwords (unusual but possible in JKS conversion paths), startup fails with UnrecoverableKeyException.
Canonical environment mapping for the store password is SERVER_SSL_KEYSTOREPASSWORD; verify resolved properties map to server.ssl.key-store-password and server.ssl.key-password in your deployment layer.
Wrong alias or multiple private keys
KeyManagerFactory.init walks private-key entries. If the keystore holds several aliases with different key passwords and the factory is initialized with one password, decryption fails for at least one entry.
Check aliases:
keytool -list -v -keystore server.jks -storepass storepasstomcat, Jul 3, 2026, PrivateKeyEntry,
oldhost, Jan 1, 2025, PrivateKeyEntry,Only PrivateKeyEntry aliases can be used as a TLS server identity. A trustedCertEntry is for truststore CA certificates and cannot provide the server private key.
After a hostname or cert renewal, an old PrivateKeyEntry may remain. Either:
- Point the connector at the correct alias (
certificateKeyAlias/server.ssl.key-alias), or - Delete obsolete aliases with
keytool -delete, or - Align every remaining private key to the password your app uses
After certificate renewal
Renewal workflows often import a new certificate onto an existing alias or add a new alias with a fresh -keypass. The store password in server.xml or Spring config is updated, but the key password for the alias is not.
Symptoms match Confluence and Tomcat KB patterns: keystore opens, connector password was rotated, startup still reports Cannot recover key.
Before changing passwords, confirm the active alias with keytool -list -v and match it with certificateKeyAlias or server.ssl.key-alias.
Fix:
keytool -keypasswd -alias confluence -keystore confluence.jks \
-storepass NEW_STORE -keypass OLD_KEY -new NEW_STORE -noprompt
keytool -storepasswd -keystore confluence.jks \
-storepass OLD_STORE -new NEW_STORE -nopromptUpdate certificateKeystorePassword and certificateKeyPassword (or Spring equivalents) to NEW_STORE before restart.
Quick lab reproduction
To see the Tomcat-style failure in isolation, create a JKS keystore with different store and key passwords, then initialize KeyManagerFactory with only the store password.
Create the keystore:
mkdir -p ~/unrecoverable-key-lab && cd ~/unrecoverable-key-lab
keytool -genkeypair -alias tomcat -keyalg RSA -keystore mismatch.jks -storetype JKS \
-storepass storepass -keypass keypass123 \
-dname "CN=localhost" -nopromptSave this program as LoadKeystore.java:
import java.io.FileInputStream;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
public class LoadKeystore {
public static void main(String[] args) throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
try (FileInputStream in = new FileInputStream("mismatch.jks")) {
ks.load(in, "storepass".toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "storepass".toCharArray());
System.out.println("KeyManagerFactory initialized OK");
}
}Compile and run:
javac LoadKeystore.java
java LoadKeystoreExpected failure when store and key passwords differ:
Exception in thread "main" java.security.UnrecoverableKeyException: Cannot recover keyAlign the key password, then rerun:
keytool -keypasswd -alias tomcat -keystore mismatch.jks \
-storepass storepass -keypass keypass123 -new storepass -noprompt
java LoadKeystoreKeyManagerFactory initialized OKRun only in a throwaway directory — not on production keystores.
Distinguish from other keystore errors
| Error | When it fails | Guide |
|---|---|---|
UnrecoverableKeyException: Cannot recover key |
Private key decrypt with wrong key password | This page |
Public keys in reply and keystore don't match |
CSR alias ≠ import alias | Public keys mismatch |
PKIX path building failed |
Trust chain missing at TLS handshake | PKIX troubleshooting |
References
- KeyManagerFactory (Java SE)
- KeyStore (Java SE)
- Apache Tomcat HTTP Connector — SSL
- keytool documentation (Oracle)
Summary
java.security.UnrecoverableKeyException: Cannot recover key means the keystore opened but Java could not decrypt the private key for your TLS alias. Start with the cause table: mismatched store and key passwords, missing Tomcat certificateKeyPassword, wrong Spring server.ssl.key-password, a bad alias, or stale passwords after renewal. Use keytool -keypasswd to align the alias, update connector or Spring config, and distinguish this from store-password errors that fail earlier at KeyStore.load.

