A388

Fixing the "Keyset does not exist" exception with the Data Protection API in .NET Core

Looks like I'm going to be making a series of posts based on the project I'm currently working on. In this post, we're going explore an infuriating exception I was getting.

In the project, I'm using the Data Protection API for the first time. I settled on using it with a certificate for protecting the keys. On first run of any of the apps in the project, they would create a new starter key fine, but every run after that they would be unable to decrypt the key and would just keep making new ones. The exception was:

Key {020d5bfa-9105-4854-84ce-f097763d47d5} is ineligible to be the default key because its CreateEncryptor method failed.

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist

This was puzzling. Why would it fail to create the encryptor when it just did so on the previous run and generated the key?

After struggling with this for a while, it turned out to be just permissions. For whatever reason, when its time to decrypt the key, the app was unable to read the certificate out of the certificate store. It apparently works perfectly fine when writing new keys, but not for reading them.

Answers on StackOverflow suggested giving IIS_IUSER permissions on the certificate, and I'm sure that would probably work fine in a production environment, but I was not always testing the apps from IIS. In fact, in order to resolve some other even more infuriating issues, I had to manually run the apps through the console. At that point, they were inheriting my user.

In the end, I just gave Everyone permissions on the certificate and it worked out. I also did the same for the certificate on the production server (it's the same certificate). To do so:

  • Open the Certificates MMC
  • Find your certificate, probably in Personal > Certificates
  • Right-click on the certificate All Tasks > Manage Private Keys
  • Add Everyone as a new user with full permissions

Make sure the certificate is valid. You can use a self-signed certificate if you go through some annoying hoops and implement your own version of the class that finds the certificate by its thumbprint. You'll have to do that just so you can tell it to ignore invalid certificates. I did that in the beginning, but eventually just spent the $16 it was to get a valid certificate for two years over at Namecheap.