aspnet/DataProtection

Configuring ProtectKeysWith*() doesn't register an IXmlRepository

Mardoxx opened this issue · 5 comments

Some background info: I'm looking at hosting several apps in a clustered environment (service fabric). So naturally one machine may need to decrypt information which was encrypted by another.

Example lifted from here and here

    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();
        }
    }

This fails with

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

How can I reinstate the default persistence store. I.e. the one outlined here.

Would it be ill-advised to do this? Is it possible to just have the keys stored per process in a clustered environment -- or is this a complete impossibility due to the mechanisms involved?

Should I be persisting my keys in some share that every machine has access to?

@Mardoxx, for security reasons you need to specify IXmlRepository when ever you specify an IXmlEncryptor. In other words, when you call ProtectKeysWith..., you should also call something like PersistKeysToFileSystem, PersistKeysToRegistry etc. Try doing

serviceCollection.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
    .ProtectKeysWithDpapiNG($"CERTIFICATE=HashId:{thumbPrint}", flags: Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiNGProtectionDescriptorFlags.None);

cc @blowdart

What do you mean by, "for security reasons"?

So providing any an implementation of IXmlEncryptor (and IXmlRepository) disables the default heuristics outlined previously?

Would it be possible to implement IXmlEncryptor which uses a data encipherment certificate to de/encrypt, and an IXmlRepository that just, I guess, stores documents in memory? Would that be advisable? Or have I completely misunderstood the API?

I think what I'm asking is, is there a mechanism by which you can just use certificates as the keys?

I apologise for my naivety. I'm not at all a security expert (yet!). It just seems rather longwinded, in a multi machine environment, to use azure storage persistence for keys, and then to encrypt these at rest using a certificate. Instead of, say just using the certificate as the key!

What do you mean by, "for security reasons"?

I am not sure exactly why that security restriction exists. @blowdart might have a better explanation.

If you don't want to persist keys to file system, you can write your own implementation of IXmlRepository that stores the keys in memory and add it to the DI.

You already have a repository for just in memory, it's the ephemeral repository

Specifying protection doesn't give you a keyring you need to specify that first. For example, if you protect the keyring with a certificate ... well, you still need to specify the keyring first :)

This issue was moved to dotnet/aspnetcore#2516