Data protection still works after deleting all keys from the database

271 views Asked by At

Please consider this code:

ServiceProvider services;
private void InitializeKeys()
{
    var serviceCollection = new ServiceCollection();
    serviceCollection.AddDbContext<MyContext>();
    serviceCollection.AddDataProtection().SetDefaultKeyLifetime(TimeSpan.FromDays(14)).PersistKeysToDbContext<MyContext>();
    services = serviceCollection.BuildServiceProvider();
    services.GetDataProtector("MyClass.v1").Protect("payload");
}

and MyContext:

public class MyContext : DbContext, IDataProtectionKeyContext
{
    public MyContext()
    {

    }

    public MyContext(DbContextOptions<MyContext> options) : base(options)
    {

    }

    public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = null!;


    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=.;Database=ThisIsMyDB;Trusted_Connection=True;TrustServerCertificate=True");
    }
}

after I run my project one key was built successfully in database:

enter image description here

and protection was did successfully:

enter image description here

Code for protection:

var Provider = DataProtectionProvider.Create("MyClass.v1");
IDataProtector _protector = Provider.CreateProtector("MyClass.v1");
txtEncryptResult.Text = _protector.Protect(txtEncryptValue.Text);

This is when the case becomes interesting that I stopped the running application and I deleted all keys from database.

enter image description here

Now I want to decrypt protected value that I got from previous step and it still works:

enter image description here

Code for unprotect:

var Provider = DataProtectionProvider.Create("MyClass.v1");
IDataProtector _protector = Provider.CreateProtector("MyClass.v1");
txtDecryptResult.Text = _protector.Unprotect(txtDecryptValue.Text);

Why does it work whereas all keys were deleted??

Thanks


Edit 1)

Based on @Dai answer, I updated my codes and it works well:

var Provider = services.GetDataProtector("MyClass.v1");
IDataProtector _protector = Provider.CreateProtector("MyClass.v1");
txtEncryptResult.Text = _protector.Protect(txtEncryptValue.Text);
1

There are 1 answers

0
Dai On BEST ANSWER

The problem is here:

var provider = DataProtectionProvider.Create("MyClass.v1");

Your code is using the static class DataProtectionProvider, which is just a utility class with factory methods for getting a quick-and-dirty IDataProtectionProvider instance without using (or participating in) a DI container - so the DataProtectionProvider.Create method will not use your ServiceCollection from InitializeKeys at all.

...and this is mentioned in the documentation for DataProtectionProvider:

Use these methods when not using dependency injection to provide the service to the application.

As per the documentation, the various DataProtectionProvider.Create overloads will return an IDataProtectionProvider that uses a (unspecified) local filesystem location to store the keys: (emphasis mine)

Contains factory methods for creating an IDataProtectionProvider where keys are stored at a particular location on the file system.


If you want to always get an IDataProtectionProvider that always uses the same configuration/options as set in your InitializeKeys method then you'll need to ensure you only ever call services.GetDataProtector("MyClass.v1") and never call the static DataProtectionProvider.Create methods.


When I run it in Linqpad on my computer, DataProtectionProvider.Create(String) returns a Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector which uses Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager which saves keys to C:\Users\$me\AppData\Local\ASP.NET\DataProtection-Keys - but this behaviour is machine-specific (the internal DefaultKeyStorageDirectories.GetKeyStorageDirectoryImpl() method will return different directories based on your computer's OS and certain environment variables) - so you shouldn't depend on any assumptions you make about it.

Remember that this library is still meant for use only with ASP.NET Core - I'm honestly surprised it works at all in a desktop program.