Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

In this tutorial, we’ll examine how to deal with java.security.UnrecoverableKeyException. We’ll also explore the details of what this exception actually means and what causes it. Finally, we’ll review possible solutions to this problem.

2. Theory Background

In Java, we have a notion of a keystore. It is essentially a file that contains some secrets. What it can contain, in particular, is certificate chains along with private keys for them. Since the certificate is just a fancy public-key wrapper, we can essentially state that the keystore contains an asymmetric key pair.

Usually, it is a good practice to protect our private keys with a password (‘password’ also commonly referred to as ‘passphrase’). It is a good practice not only in Java keystores but also in cybersecurity in general. The way this protection is usually implemented is by encrypting the private key with the password using a symmetric key encryption algorithm, such as various flavors of AES instances.

What is important for us here is that private keys in the keystore can be encrypted with the password, just like we described. This feature is available not in all keystore types, for instance JKS keystores support private key password protection, but PKCS12 keystores does not. In our example, we’ll need a password protection feature, so we’re going to work with JKS keystores from now on.

3. UnrecoverableKeyException Origins

A java.security.UnrecoverableKeyException typically occurs when we’re working with KeyManagerFactory, specifically when we invoke init() method on it. This is the class from JSSE that allows us to retrieve KeyManager instances. KeyManager is basically the interface that represents the abstraction responsible to authenticate us as clients to our peers.

The init() method takes two arguments – the source keystore to get credentials for authentication from and the password for private key decryption. The java.security.UnrecoverableKeyException occurs when KeyManagerFactory cannot recover the certificate chain’s private key. Here comes the question – what does ‘recover’ actually mean? Well, it means that the private key for the certificate chain cannot be decrypted with the given password. So, therefore, the most common by far source of java.security.UnrecoverableKeyException is the wrong password for the private key in the keystore.

To sum up, if the password/passphrase we provided for KeyManagerFactory for private keys is incorrect, then KeyManagerFactory won’t be able to decrypt the keys, and therefore, we have this error.

4. Simulating The Exception

Let’s get our hands dirty and try to simulate this error. For this, we’ll need a JKS keystore with a private key and corresponding certificate pair. We can achieve this by using the keytool:

$ keytool -genkey -alias single-entry -storetype JKS -keyalg RSA -validity 365 -keystore single_entry_keystore.jks

Once we run this command, keytool will prompt us for a couple of additional information. In our case, it would be some additional info to generate a certificate (CN, expiration, etc.) and the password for both the keystore and the private key. Let’s assume that we’ve chosen an ‘admin123’ password for the keystore and a ‘privateKeyPassword’ passphrase for the private key.

In order to load this keystore in Java, we would do this:

public static X509ExtendedKeyManager initializeKeyManager() 
  throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, URISyntaxException {
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    KeyStore instance = KeyStore.getInstance(KeyStore.getDefaultType());
    InputStream resourceAsStream = Files.newInputStream(Paths.get(ClassLoader.getSystemResource("single_entry_keystore.jks").toURI()));
    instance.load(resourceAsStream, "admin123".toCharArray());
    kmf.init(instance, "privateKeyPassword".toCharArray());
    return (X509ExtendedKeyManager) kmf.getKeyManagers()[0];
}

We obtained the instance of KeyManager from the Keysotre we’ve just created. This code works fine since both passwords are correct. If we changed the password for the private key in the example above, we would get a java.security.UnrecoverableKeyException. So, just using the correct password would solve this problem.

5. UnrecoverableKeyException Edge Cases

There are some corner cases, that can cause the java.security.UnrecoverableKeyException that most people are unaware of. Let’s discuss them one by one.

5.1. Multiple Private Key Entries

For instance, let’s imagine a scenario where we do not have a single private key/certificate chain in the keystore but multiple of them. Let’s create a new keystore with two keys in it:

$ keytool -genkey -alias entry-1 -storetype JKS -keyalg RSA -validity 365 -keystore multi_entry_keystore.jks
$ keytool -genkey -alias entry-2 -storetype JKS -keyalg RSA -validity 365 -keystore multi_entry_keystore.jks

So here, we’ve added two private keys with certificate entries to the keystore. Let’s assume that we’ve added the first private key with password ‘abc123’, and the second one with the password ‘bcd456’. That should be perfectly fine, after all, keystore can have multiple keys encrypted with different passwords, no problem here so far.

Now, the code for building KeyManager for the given keystore would not change – it would look exactly like we already did above. The only problem here is that the KeyManagerFactory.init() method only accepts one password for the private key decryption.

That seems strange – what password exactly are we supposed to provide here – ‘abc123’, or ‘bcd456’? Well, it turned out that Sun JSSE implementation of KeyManagerFactory, which is used in an overwhelming amount of JDKs, is, by default, expecting a single password for each private key in the keystore. That’s right, even though having two private keys in the keystore encrypted with different passwords is technically not a problem. There is no restriction on this from a theoretical standpoint; however, there is one from the API standpoint.

We cannot have different passwords for any two given keys in the keystore. If we violate this rule, the KeyManagerFactory implementation would try to decrypt all keys with the provided password, and of course, it would fail for at least one key. Therefore, KeyManagerFactory.init() would throw an exception because it could not decrypt the key – and that’s right, it was not able indeed. So we absolutely need to keep this in mind.

5.2. External Libraries Restrictions

As Software Engineers, we don’t often interact with JSSE directly. Frameworks usually hide this by creating multiple layers of abstraction for us. However, we should understand that we still interact with KeyManager and other JSSE classes indirectly by using various clients, such as Apache HTTP client or Apache Tomcat.

And these frameworks can and often do impose various constraints on what passwords they expect. For instance, the current Apache Tomcat implementation relies on the fact that the keystore password is equal to the password of the private keys in the keystore. These restrictions can differ from one library to another, but now, as we understand the cause of java.security.UnrecoverableKeyException, we should know where to dig. So, be aware of the frameworks used in the project and their implementation limitations.

6. Conclusion

In this article, we’ve explored everything we need to know about java.security.UnrecoverableKeyException – what causes it, and ways to fix it. We’ve understood that java.security.UnrecoverableKeyException is thrown by KeyManagerFactory to signal the inability to decrypt the private keys inside the keystore. That, as mentioned, happens primarily due to an incorrect decryption key (the password).

There are some nuances to know about as well. For instance, we cannot have multiple private keys within the keystore with different passwords. This is not acceptable from a JSSE standpoint. We should also beware of the constraints of different frameworks for dealing with keystores and private keys.

As always, the source code for this article will be available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Subscribe
Notify of
guest
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments