Data protection in ASP.NET Core 2.1 only works on one machine

Coder6841 picture Coder6841 · Jun 13, 2018 · Viewed 14.7k times · Source

I'm using the ASP.NET Core Data Protection system to encrypt data with Application A and decrypt it with Application B.

Encryption and decryption both work when run on the development machine, but when Application B is moved to the production machine it's no longer able to decrypt because the IDataProtector.Unprotect method throws an exception:

System.InvalidOperationException: The key ring does not contain a valid default protection key. The data protection system cannot create a new key because auto-generation of keys is disabled.

Here's the code I'm using to configure decryption in Application B:

sKeysPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Keys");

services.AddDataProtection()
    .SetApplicationName("My Application") // Application A sets this same name
    .PersistKeysToFileSystem(new DirectoryInfo(sKeysPath))
    .ProtectKeysWithCertificate("634D3F23...")
    //.ProtectKeysWithCertificate(x509Certificate2) // I've tried using an X509 certificate parameter but it gives the same result as providing the thumbprint of the one in the certificate store
    .DisableAutomaticKeyGeneration(); // Application A is the master key generator so do not generate keys

The production machine does contain the same Keys folder (with .pfx and .xml files) and same keys installed in the Windows certificate store.

As I understand it, by providing the certificate file to the Data Protection system, it should work on any machine and not be binded to a specific machine or Windows user. Is that assumption incorrect or is there an issue with the way I'm performing decryption?

Here are some more detailed logging messages:

2018-06-13 16:32:32.6750 | TRACE | Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector | 5 | Performing unprotect operation to key {846541...} with purposes ('My Application', 'My Purpose').

2018-06-13 16:32:32.6750 | DEBUG | Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository | 37 | Reading data from file 'C:\inetpub\wwwroot\My Website\Keys\key-846541....xml'.

2018-06-13 16:32:32.6750 | DEBUG | Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager | 18 | Found key {846541...}.

2018-06-13 16:32:32.6750 | DEBUG | Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver | 13 | Considering key {846541...} with expiration date 2038-01-18 20:54:13Z as default key.

2018-06-13 16:32:32.6750 | DEBUG | Microsoft.AspNetCore.DataProtection.TypeForwardingActivator | Forwarded activator type request from Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor, Microsoft.AspNetCore.DataProtection, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 to Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60

2018-06-13 16:32:32.7051 | ERROR | Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager | 24 | An exception occurred while processing the key element '<key id="846541..." version="1" />'. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist

2018-06-13 16:32:32.7051 | TRACE | Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager | 25 | An exception occurred while processing the key element '<key id="846541..." version="1" />...

2018-06-13 16:32:32.7051 | WARN | Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver | 12 | Key {846541...} is ineligible to be the default key because its CreateEncryptor method failed. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist

2018-06-13 16:32:32.7051 | DEBUG | Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver | 14 | Key {846541...} is no longer under consideration as default key because it is expired, revoked, or cannot be deciphered.

2018-06-13 16:32:32.7051 | DEBUG | Microsoft.AspNetCore.DataProtection.KeyManagement.DefaultKeyResolver | 53 | Repository contains no viable default key. Caller should generate a key with immediate activation.

2018-06-13 16:32:32.7051 | DEBUG | Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider | 57 | Policy resolution states that a new key should be added to the key ring.

2018-06-13 16:32:32.7051 | ERROR | Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider | 49 | The key ring does not contain a valid default key, and the key manager is configured with auto-generation of keys disabled.

2018-06-13 16:32:32.7051 | ERROR | Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider | 48 | An error occurred while reading the key ring. System.InvalidOperationException: The key ring does not contain a valid default protection key. The data protection system cannot create a new key because auto-generation of keys is disabled.

Answer

Coder6841 picture Coder6841 · Jun 13, 2018

Thanks to Joe Audette's suggestion I checked out the detailed logging and found a more specific error which pointed me to this answer which had the solution.

The problem was that the permissions for the certificate in the Windows certificate store did not have the IIS_IUSRS group set to allow read access (Right click certificate → All Tasks → Manage Private Keys…). This issue didn't appear on the development machine because there it was running under Visual Studio's user context instead.