ASP. NET Core Data Protection (Data Protection)

  • 2021-08-16 23:36:06
  • OfStack

Preface

The first part mainly introduces ASP. NET Core Data Protection, and this part mainly introduces API and its usage.

API interface

ASP. NET Core Data Protectio mainly provides two interfaces for ordinary developers, IDataProtectionProvider and IDataProtector.
Let's first look at the relationship between these two interfaces:


 namespace Microsoft.AspNetCore.DataProtection
{
 //
 //  Summary :
 //  An interface that can provide data protection services.
 public interface IDataProtector : IDataProtectionProvider
 {

  byte[] Protect(byte[] plaintext);

  byte[] Unprotect(byte[] protectedData);
 }
} 

As you can see, IDataProtector inherits from IDataProtectionProvider and provides two methods, Protect and Unprotect, one for encryption and one for decryption. Their signatures are passed into an byte array, which means they can encrypt and decrypt 1-cut objects. The byte array is also returned, which means that in the actual use process, we should add or use some extension methods of the system to embody our requirements.

Let's look at the IDataProtectionProvider interface again:


 namespace Microsoft.AspNetCore.DataProtection
{
 public interface IDataProtectionProvider
 {

  IDataProtector CreateProtector(string purpose);
 }
} 

IDataProtectionProvider provides a method to generate an IDataProtector interface object by passing in an purpose string (described in more detail later).
Judging from the naming of this interface, it ends with Provider, which means that we can realize our own set of encryption and decryption.

When we read the source code of Microsoft projects, we often look at an object ending in xxxxProvider. So what is its responsibility and what role does it play at the same time?
In fact, this is a design pattern specially designed by Microsoft for ASP. NET, which is called Provider Model design pattern. It can also be said that it was invented by Microsoft. It does not belong to one of 23 design patterns. From a functional point of view, it should be a combination of factory and strategy. This design pattern has been introduced by Microsoft since ASP. NET 2.0, starting with multiple implementations of application configuration. For example, web. config, which developers are most familiar with, has binary system for the configuration of database connection string, and XML, etc. Now this mode is used more and more in other places.

Let's talk about the string purpose in the signature of CreateProtector method. In the last blog post, for readers' good understanding, I said that the incoming purpose can be understood as a public key. In fact, this statement is not rigorous and can be understood as a logo indicating the current use of Protector.

When you use IDataProtector, you will find that it also has one extension method under the Microsoft. AspNetCore. DataProtection namespace:


 public static class DataProtectionCommonExtensions
{
 public static IDataProtector CreateProtector(this IDataProtectionProvider provider, IEnumerable<string> purposes);

 public static IDataProtector CreateProtector(this IDataProtectionProvider provider, string purpose, params string[] subPurposes);

 public static IDataProtector GetDataProtector(this IServiceProvider services, IEnumerable<string> purposes);

 public static IDataProtector GetDataProtector(this IServiceProvider services, string purpose, params string[] subPurposes);

 public static string Protect(this IDataProtector protector, string plaintext);

 public static string Unprotect(this IDataProtector protector, string protectedData);
} 

As you can see, CreateProtector also provides a way to pass multiple purpose (IEnumerable, params string []). Why is there such a requirement?

In fact, DataProtector has a hierarchical structure. If you look at the IDataProtector interface, it also implements the IDataProtectionProvider interface, which means that IDataProtector itself can also create IDataProtector.

For example, we are doing a message communication system. In the process of message communication, we need to encrypt the user's session. We use CreateProtector ("Security. BearerToken") for encryption. However, when encrypting, it can't guarantee that the message is sent by an untrusted client, so I thought of CreateProtector ("username") to encrypt it. At this time, if there is a user named "Security. BearerToken", it will conflict with another Protector that uses Security. BearerToken as a label, so we can use
CreateProtector (["Security. BearerToken", "User: username"]). It is equivalent to
provider. CreateProtector ("Security. BearerToken). CreateProtector (" User: username "). This means creating an Protector called" Security. BearerToken "and then creating an User called" User: username "under purpose1.

User password hash

An KeyDerivation. Pbkdf2 method is provided under the Microsoft. AspNetCore. Cryptography. KeyDerivation namespace to hash the user password.

Encryption with Lifecycle Constraints

Sometimes, We need some encrypted strings with expiration or expiration time. For example, when a user retrieves his password, We send an email with a reset command to the user's mailbox, and this reset command needs to have an expiration time, which will expire after this expiration time. In the past, we may need to store a time to the database to mark the sending time, and then decrypt the comparison and the time difference of the database to verify.

Now we don't need to do this. ASP. NET Core provides an interface called ITimeLimitedDataProtector by default. Let's first look at the definition of this interface:


CreateProtector(string purpose) : ITimeLimitedDataProtector This API is similar to the existing IDataProtectionProvider.CreateProtector in that it can be used to create purpose chains from a root time-limited protector.
Protect(byte[] plaintext, DateTimeOffset expiration) : byte[]
Protect(byte[] plaintext, TimeSpan lifetime) : byte[]
Protect(byte[] plaintext) : byte[]
Protect(string plaintext, DateTimeOffset expiration) : string
Protect(string plaintext, TimeSpan lifetime) : string
Protect(string plaintext) : string 

ITimeLimitedDataProtector provides several overloaded methods to set encryption methods with life cycle. Users can set the time through Date, TimeOffset, TimeSpan and other parameters.

If there is a corresponding encryption, there is a corresponding decryption method, which will not be introduced in detail here. Interested students can go to see the official documents.

Configure data protection

When our ASP. NET Core is running, the system will configure something about Data Protection by default based on the running environment of the current machine, but sometimes it may be necessary to make some changes to these configurations, such as distributed deployment, which was also mentioned at the end of the last blog post. Let's look at how to configure it.

As mentioned in the previous article, we registered Data Protection with the service in the following ways:


 public void ConfigureServices(IServiceCollection services)
{
 services.AddDataProtection();
} 

Where AddDataProtection returns an IDataProtectionBuilder interface that provides an extension method PersistKeysToFileSystem () to store the private key. You can pass 1 path through it to specify the location of the private key store:


 public void ConfigureServices(IServiceCollection services)
{
 services.AddDataProtection()
  .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));

} 

You can pass in a shared folder to store private keys, so that private keys on different machines can be saved to one location. In this way, the differentiation of machines can be isolated during distributed deployment.
If you feel insecure, you can also configure an X. 509 certificate for encryption:


 public void ConfigureServices(IServiceCollection services)
{
 services.AddDataProtection()
  .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
  .ProtectKeysWithCertificate("thumbprint");
} 

As mentioned in the previous article, the default save time of Data Protection is 90 days. You can modify the default save time in the following ways:


 public void ConfigureServices(IServiceCollection services)
{
 services.AddDataProtection()
  .SetDefaultKeyLifetime(TimeSpan.FromDays(14));
} 

By default, Data Protection isolates different applications even if the same physical keystore is used, because it prevents getting the keys from one application to another. So if it is the same application, you can set the same application name:


 public void ConfigureServices(IServiceCollection services)
{
 services.AddDataProtection()
  .SetApplicationName("my application");
} 

Sometimes it is necessary to disable the application from generating keys, or if I only have one program to generate or manage keys, and other programs are only responsible for reading them, then this can be done:


 public void ConfigureServices(IServiceCollection services)
{
 services.AddDataProtection()
  .DisableAutomaticKeyGeneration();
} 

Modify the encryption algorithm

You can use the UseCryptographicAlgorithms method to modify the default encryption algorithm for ASP. NET Core Data Protection, as follows:


 namespace Microsoft.AspNetCore.DataProtection
{
 public interface IDataProtectionProvider
 {

  IDataProtector CreateProtector(string purpose);
 }
} 

0

Summary:

This article mainly introduces some commonly used API, and the next article introduces some advanced usage.


Related articles: