azure-webjobsdata-protectionazure-blob-storageasp.net-core-1.1cryptographicexception

System.Security.Cryptography.CryptographicException: 'The key {00000000-0000-0000-0000-000000000000} was not found in the key ring.'


I have deployed a webjob (.Net 4.6) which encrypts some url strings and sends it to customers via email. I used .NET Core's IDataProtector for the encrpytion and I had to manually reference the DLL since webjob .net 4.6 was not supporting its library.

For example in webjob:

IDataProtector protector = provider.CreateProtector("myProtector")
http://test.com/Email/Agreement?n=" + protector.Protect(name) + "&a=" + protector.Protect(address)

which becomes a link

http://test.com/Email/Agreement?n=CfDJ8AAAAAAAAAAAAAAAAAAAAAD96g09022UwEG-GyVftzkm-n2JuL1hmvL5HLyghXliIgJ_N014EBqBFfWjmdA&a=CfDJ8AAAAAAAAAAAAAAAAAAAAAALoq9IuMBZKozhzH3jRaHpyFE1wtXzQK3T_0RNuX9LsSVxPITWgU9ZR21jXLk3YGjt

in the email.

when the customers click on the url link in their email, it would go to my client application's (.Net Core 1.1) controller to decrypt the url string in order to pop up an agreement page.

Ex:

public EmailController(IDataProtectionProvider provider)
{         
    _protector = provider.CreateProtector("myProtector");
}
public IActionResult Agreement(string n, string a)
{     
    var decryptName = _protector.Unprotect(n);
    var decryptAddress = _protector.Unprotect(a);
}

However, I am getting a following error message when I tried to Unprotect them:

System.Security.Cryptography.CryptographicException: 'The key {00000000-0000-0000-0000-000000000000} was not found in the key ring.'

When I searched for an answer, I realized I can configure Data Protection to store the keys to Azure Blob Storage. This link shows me how to persist keys to azure blob storage.

Questions:

  1. What is the best approach other than storing a key to azure blob storage?
  2. If I am on the right track, how do I store it?
  3. How do I configure the same setting for the webjob project as shown in the link which does not have Startup.cs for configuration?

Help is much appreciated.


Solution

  • What is the best approach other than storing a key to azure blob storage?

    Based on the document provided by you, we could store the key in file system, Azure Redis, Azure Storage and Registry. Since Registry is not support by Web Job(Azure Web App). If choosing file system, we also need to transfer the keys between the Web Job and your web application. So Azure Redis and Azure Storage would be the good approaches.

    If I am on the right track, how do I store it?

    Here are the detail steps of how to store keys on Azure Storage.

    Step 1, you need to create an Azure Storage account if you doesn't have one.

    Step 2, install Microsoft.AspNetCore.DataProtection.AzureStorage package using NuGet.

    Step 3, configure DataProtection using following code. We need to invoke SetApplicationName method and use the same application name as your Web Job.

    var storageAccount = CloudStorageAccount.Parse("put your azure storage connection string here");
    var client = storageAccount.CreateCloudBlobClient();
    
    var container = client.GetContainerReference("key-container");
    container.CreateIfNotExistsAsync().GetAwaiter().GetResult();
    
    services.AddDataProtection().SetApplicationName("myapplication")
        .PersistKeysToAzureBlobStorage(container, "keys.xml");
    

    Step 4, in your controller, you could use IDataProtectionProvider as following.

    public class HomeController : Controller
    {
        private IDataProtector _protector;
        public HomeController(IDataProtectionProvider provider)
        {
            _protector = provider.CreateProtector("test-purpose");
        }
    
        public IActionResult Index()
        {
            string encryptedTExt = _protector.Protect("abcd");
            return Content(encryptedTExt);
        }
    }
    

    How do I configure the same setting for the webjob project as shown in the link which does not have Startup.cs for configuration?

    Step 1, you need to add reference to following DLLs.

    enter image description here

    Step 2, Add a wrapper class of IDataProtector as following.

    public class EncryptService
    {
        IDataProtector _protector;
    
        // the 'provider' parameter is provided by DI
        public EncryptService(IDataProtectionProvider provider)
        {
            _protector = provider.CreateProtector("test-purpose");
        }
    
        public string Protect(string text)
        {
            return _protector.Protect(text);
        }
    
        public string Unprotect(string encryptedText)
        {
            return _protector.Unprotect(encryptedText);
        }
    }
    

    Step 3, use ServiceCollection to configure the Data Protection service. Please note that we need to invoke SetApplicationName method and use the same application name as your web application.

    static void Main()
    {
        var storageAccount = CloudStorageAccount.Parse("put your azure storage connection string here");
        var client = storageAccount.CreateCloudBlobClient();
    
        var container = client.GetContainerReference("key-container");
        container.CreateIfNotExistsAsync().GetAwaiter().GetResult();
    
        var serviceCollection = new ServiceCollection();
    
        serviceCollection.AddDataProtection().SetApplicationName("myapplication")
            .PersistKeysToAzureBlobStorage(container, "keys.xml"); 
    
        var services = serviceCollection.BuildServiceProvider();
    }
    

    Step 4, after that, you could use following code to encrypt or decrypt your data.

    var encryptService = ActivatorUtilities.CreateInstance<EncryptService>(services);
    string text = encryptService.Protect("abcd");