ASP. NET Core Data Protection (Data Protection Cluster scenario), coreprotection

Source: Internet
Author: User

ASP. NET Core Data Protection (Data Protection Cluster scenario), coreprotection

Preface

Next, in some scenarios, we need to perform operations on ASP. to meet our needs, we need to use some advanced features provided by Core.

This article also lists some of the methods we need to implement to perform distributed configuration of Data Protection in cluster scenarios.

Encryption Extension

IAuthenticatedEncryptorAndIAuthenticatedEncryptorDescriptor

IAuthenticatedEncryptor is a basic interface of Data Protection in building its password encryption system.
Generally, a key corresponds to an IAuthenticatedEncryptor. IAuthenticatedEncryptor encapsulates the key material and necessary encryption algorithm information required for encryption operations.

The following are two api methods provided by the IAuthenticatedEncryptor interface:
Decrypt (ArraySegment <byte> ciphertext, ArraySegment <byte> additionalAuthenticatedData): byte []
Encrypt (ArraySegment <byte> plaintext, ArraySegment <byte> additionalAuthenticatedData): byte []

The parameter additionalAuthenticatedData in the interface indicates some ancillary information provided during encryption.

The IAuthenticatedEncryptorDescriptor interface provides a method to create an IAuthenticatedEncryptor instance containing the type information.

CreateEncryptorInstance (): IAuthenticatedEncryptor
ExportToXml (): XmlSerializedDescriptorInfo

Key management extension 

In key system management, a basic interface IKey is provided, which includes the following attributes:

Activation
Creation
Expiration dates
Revocation status
Key identifier (a GUID)

IKey also provides a CreateEncryptorInstance method for creating an IAuthenticatedEncryptor instance.

The IKeyManager interface provides a series of Key operations methods, including storage and retrieval operations. His advanced operations include:

• Create a Key and store it permanently
• Retrieve all keys from the Repository
• Undo one or more keys saved to the storage

XmlKeyManager
Generally, developers do not need to implement IKeyManager to customize a KeyManager. We can use the XmlKeyManager class provided by the system by default.

XMLKeyManager is a class that implements IKeyManager. It provides some useful methods.

 public sealed class XmlKeyManager : IKeyManager, IInternalXmlKeyManager{ public XmlKeyManager(IXmlRepository repository, IAuthenticatedEncryptorConfiguration configuration, IServiceProvider services); public IKey CreateNewKey(DateTimeOffset activationDate, DateTimeOffset expirationDate); public IReadOnlyCollection<IKey> GetAllKeys(); public CancellationToken GetCacheExpirationToken(); public void RevokeAllKeys(DateTimeOffset revocationDate, string reason = null); public void RevokeKey(Guid keyId, string reason = null);} 

• IAuthenticatedEncryptorConfiguration mainly specifies the algorithm used by the new Key.
• IXmlRepository mainly controls where keys are stored persistently.

IXmlRepository 

The IXmlRepository interface mainly provides persistence and XML retrieval methods. It only provides two APIs:
• GetAllElements (): IReadOnlyCollection
• StoreElement (XElement element, string friendlyName)

We can define the storage location of data protection xml by implementing the StoreElement method of the IXmlRepository interface.

GetAllElements to retrieve all existing encrypted xml files.

Write the interface here, because I want to focus on this article. For more information about interfaces, see the official documentation ~

Cluster scenario 

The above API looks a bit boring, so let's take a look at what we need to do with Data Protection in cluster scenarios.

As I mentioned at the end of the [previous] summary, we need to know some Data Protection mechanisms during distributed clusters, if you do not know this, it may cause some trouble for your deployment. Let's take a look at it.

When creating a cluster, we must know and understand three things about ASP. NET Core Data Protection:

1. program recognition 

"Application discriminator" is used to identify the uniqueness of an Application.
Why do we need this? In the cluster environment, if you are not restricted by the specific hardware machine environment, you must exclude some differences between running machines and abstract some specific identifiers, to identify the application itself and use this identifier to distinguish different applications. In this case, we can specify ApplicationDiscriminator.

When services. AddDataProtection (DataProtectionOptions option), ApplicationDiscriminator can be passed as a parameter. Let's take a look at the Code:

Public void ConfigureServices (IServiceCollection services) {services. addDataProtection (); services. addDataProtection (DataProtectionOptions option);} // ============= the extension method is as follows: public static class extends {public static IDataProtectionBuilder AddDataProtection (this IServiceCollection Service ); // you can use this item to configure public static IDataProtectionBuilder AddDataProtection (this IServiceCollection services, Action <DataProtectionOptions> setupAction);} // DataProtectionOptions attributes: public class DataProtectionOptions {public string ApplicationDiscriminator {get; set ;}}

We can see that this extension returns an IDataProtectionBuilder. In IDataProtectionBuilder, there is also an extension method called SetApplicationName. This extension method also modifies the value of ApplicationDiscriminator internally. The following statement is equivalent:

Services. AddDataProtection (x => x. ApplicationDiscriminator = "my_app_sample_identity ");

Services. AddDataProtection (). SetApplicationName ("my_app_sample_identity ");

In other words, in the cluster environment, the same application needs to be set to the same value (ApplicationName or ApplicationDiscriminator ).

2. Primary encryption key 

"Master encryption key" is mainly used for encryption and decryption, including session data and status of a client server during the request process. There are several options to configure, such as using a certificate, windows DPAPI, or registry. If it is not a windows platform, the Registry and Windows DPAPI cannot be used.

Public void ConfigureServices (IServiceCollection services) {services. addDataProtection () // windows dpaip acts as the primary encryption key. protectKeysWithDpapi () // This option can be used if it is windows 8 + or windows server2012 + (based on Windows DPAPI-NG ). protectKeysWithDpapiNG ("SID = {current account SID}", DpapiNGProtectionDescriptorFlags. none) // if it is windows 8 + or windows server2012 +, you can use this option (based on the certificate ). protectKeysWithDpapiNG ("CERTIFICATE = HashId: 3BCE558E 2AD3E0E34A7743EAB5AEA2A9BD2575A0 ", DpapiNGProtectionDescriptorFlags. None) // use the certificate as the primary encryption key. Currently, only widnows is supported, but not linux. . ProtectKeysWithCertificate ();}

In the cluster environment, they need to have the same master encryption key configured.

3. Encrypted storage location 

As mentioned in [previous], Data Protection will generate an xml file by default to store session or state key files. These files are used to encrypt or decrypt session and other State data.

The private key storage location mentioned in the previous article:

1. If the program is hosted in Microsoft Azure, it is stored in the "% HOME % \ ASP. NET \ DataProtection-Keys" folder.
2. If the program is hosted in IIS, it is stored in the ACLed special registry key of the HKLM registry and accessible only by working processes, it is encrypted using windows DPAPI.
3. If the current user is available, that is, win10 or win7, it is stored in the "% LOCALAPPDATA % \ ASP. NET \ DataProtection-Keys" folder, and windows DPAPI encryption is also used.
4. If none of these are met, the private key is not persistent. That is to say, when the process is closed, the generated private key is lost.

Cluster Environment:
The simplest way is through file sharing, DPAPI, or registry. That is to say, the encrypted xml files are stored in the same place. The most simple reason is that the system has been encapsulated and no extra code needs to be written. However, ensure that the port related to file sharing is open. As follows:

Public void ConfigureServices (IServiceCollection services) {services. addDataProtection () // This method can be used in windows, Linux, and macOS to save data to the file system. persistKeysToFileSystem (new System. IO. directoryInfo ("C: \ cmd_keys \") // you can save it to the Registry in windows. persistKeysToRegistry (Microsoft. win32.RegistryKey. fromHandle (null ))}

You can also customize storage by using your own extension methods, such as using databases or Redis.

But in general, if it is deployed on linux, it needs to be extended. Next let's take a look at how we want to use redis for storage?

How can I extend the storage location of an encryption key set? 

First, define a redis implementation class RedisXmlRepository. cs for the IXmlRepository interface:

 public class RedisXmlRepository : IXmlRepository, IDisposable{ public static readonly string RedisHashKey = "DataProtectionXmlRepository";  private IConnectionMultiplexer _connection;  private bool _disposed = false;  public RedisXmlRepository(string connectionString, ILogger<RedisXmlRepository> logger)  : this(ConnectionMultiplexer.Connect(connectionString), logger) { }  public RedisXmlRepository(IConnectionMultiplexer connection, ILogger<RedisXmlRepository> logger) {  if (connection == null)  {   throw new ArgumentNullException(nameof(connection));  }   if (logger == null)  {   throw new ArgumentNullException(nameof(logger));  }   this._connection = connection;  this.Logger = logger;   var configuration = Regex.Replace(this._connection.Configuration, @"password\s*=\s*[^,]*", "password=****", RegexOptions.IgnoreCase);  this.Logger.LogDebug("Storing data protection keys in Redis: {RedisConfiguration}", configuration); }  public ILogger<RedisXmlRepository> Logger { get; private set; }  public void Dispose() {  this.Dispose(true); } public IReadOnlyCollection<XElement> GetAllElements() {  var database = this._connection.GetDatabase();  var hash = database.HashGetAll(RedisHashKey);  var elements = new List<XElement>();   if (hash == null || hash.Length == 0)  {   return elements.AsReadOnly();  }   foreach (var item in hash.ToStringDictionary())  {   elements.Add(XElement.Parse(item.Value));  }   this.Logger.LogDebug("Read {XmlElementCount} XML elements from Redis.", elements.Count);  return elements.AsReadOnly(); }  public void StoreElement(XElement element, string friendlyName) {  if (element == null)  {   throw new ArgumentNullException(nameof(element));  }   if (string.IsNullOrEmpty(friendlyName))  {   friendlyName = Guid.NewGuid().ToString();  }   this.Logger.LogDebug("Storing XML element with friendly name {XmlElementFriendlyName}.", friendlyName);   this._connection.GetDatabase().HashSet(RedisHashKey, friendlyName, element.ToString()); } protected virtual void Dispose(bool disposing) {  if (!this._disposed)  {   if (disposing)   {    if (this._connection != null)    {     this._connection.Close();     this._connection.Dispose();    }   }    this._connection = null;   this._disposed = true;  } }} 

Then, define an extension method in any extension class:

Public static IDataProtectionBuilder PersistKeysToRedis (this IDataProtectionBuilder builder, string redisConnectionString) {if (builder = null) {throw new partition (nameof (builder);} if (redisConnectionString = null) {throw new ArgumentNullException (nameof (redisConnectionString);} if (redisConnectionString. length = 0) {throw new ArgumentException ("Redis connection string may n Ot be empty. ", nameof (redisConnectionString);} // because. when AddDataProtection () is used, IXmlRepository has been injected, so we should first remove it. // here we should encapsulate it as a method for calling, so that readers can better understand it, I directly wrote for (int I = builder. services. count-1; I> = 0; I --) {if (builder. services [I]?. ServiceType = descriptor. serviceType) {builder. services. removeAt (I) ;}} var descriptor = ServiceDescriptor. singleton <IXmlRepository> (services => new RedisXmlRepository (redisConnectionString, services. getRequiredService <ILogger <RedisXmlRepository> () builder. services. add (descriptor); return builder. use ();}

In the end, the DataProtection in Services is as follows:

Public void ConfigureServices (IServiceCollection services) {services. addDataProtection () // ======================== The following is a unique identifier ============================/// set the application unique program id. setApplicationName ("my_app_sample_identity "); // ================== The following is the primary encryption key ==============================/// windows dpaip the primary encryption key. protectKeysWithDpapi () // This option can be used if it is windows 8 + or windows server2012 + (based on Windows DPAPI-NG ). protectKeysWithDpapiNG ("SID = {current account SID}", Dpap INGProtectionDescriptorFlags. none) // if it is windows 8 + or windows server2012 +, you can use this option (based on the certificate ). protectKeysWithDpapiNG ("CERTIFICATE = HashId: 3BCE558E2AD3E0E34A7743EAB5AEA2A9BD2575A0", DpapiNGProtectionDescriptorFlags. none) // use the certificate as the primary encryption key. Currently, only widnows support the certificate, but not linux.. ProtectKeysWithCertificate (); // =========================== linux and macOS can be saved to the file system in this way. persistKeysToFileSystem (new System. IO. directoryInfo ("C: \ cmd_keys \") // you can save it to the Registry in windows. persistKeysToRegistry (Microsoft. win32.RegistryKey. fromHandle (null) // store it in redis. persistKeysToRedis (Configuration. section ["RedisConnection"])}

In the above configuration, I have listed all the available configurations. The actual project should be selected based on the actual situation.

Summary 

About ASP. NET Core Data Protection Series has finally been written. In fact, this Part has taken a lot of time. for Data Protection, I am also a gradual learning process, hoping to help some people.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.