Search code examples
c#asp.netasp.net-coredata-protection

ASP.Net Core Data Protection API in a Clustered Environment


I'm having difficulty understanding the Data Protection API.

I'm wanting to set up some net core web applications in a clustered environment (service fabric). Previously what you'd do is just ensure that each machine has the same key in its web.config. Simple. With the new data protection API it seems a little (lottle!) bit more involved.

From the documentation here it appears that it should be as simple as setting up the Data Protection service with the appropriate certificate.

However I tried this:

    public static void Main(string[] args)
    {
        // add data protection services
        var serviceCollection = new ServiceCollection();
        string thumbPrint = "XXXXXXXXXXXX";
        serviceCollection.AddDataProtection()
            .ProtectKeysWithDpapiNG($"CERTIFICATE=HashId:{thumbPrint}", flags: Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiNGProtectionDescriptorFlags.None);
        var services = serviceCollection.BuildServiceProvider();

        // create an instance of MyClass using the service provider
        var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
        instance.RunSample();
    }

    public class MyClass
    {
        IDataProtector _protector;

        // the 'provider' parameter is provided by DI
        public MyClass(IDataProtectionProvider provider)
        {
            _protector = provider.CreateProtector("Contoso.MyClass.v1");
        }

        public void RunSample()
        {
            Console.Write("Enter input: ");
            string input = Console.ReadLine();

            // protect the payload
            string protectedPayload = _protector.Protect(input);
            Console.WriteLine($"Protect returned: {protectedPayload}");

            // unprotect the payload
            string unprotectedPayload = _protector.Unprotect(protectedPayload);
            Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

            Console.ReadLine();
        }
    }

And I just get an exception of

System.InvalidOperationException occurred
  HResult=0x80131509
  Message=No service for type 'Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository' has been registered.

Which after some digging around turns out that it's because there is no persisted store specified for the keys.

What is advised here? Should I be persisting my keys to some central location (i.e. a share that is available to all my applications). If so, what is the reason why?


Solution

  • You have to supply an implementation of IXmlRepository which provides the data protection API with a place to store the keys. The ProtectKeysWith*() directives protect the keys at rest (in basic terms, encrypts the keys before saving them!). Additional info here.

    I ended up persisting my keys to AzureStorage. More info here.

    serviceCollection.AddDataProtection()
        .ProtectKeysWithDpapiNG($"CERTIFICATE=HashId:{thumbPrint}", flags: Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiNGProtectionDescriptorFlags.None)
        .PersistKeysToAzureBlobStorage(/* params */);
    

    It is also worth noting that the certificate used to protect the keys must be stored in a certificate store and the account which the application is running under must have read access. See here.