Objective
With "Medium", in some scenarios, we need to extend the ASP.net core encryption method to accommodate our needs, and this time we need to use some of the advanced functionality provided by the core.
This article also enumerates some of the ways we need to implement our own distributed configuration of data protection in a cluster scenario.
Encryption extension
iauthenticatedencryptor and iauthenticatedencryptordescriptor
Iauthenticatedencryptor is a basic interface for Data Protection to build its cryptographic cryptography system.
In general, a key corresponding to a iauthenticatedencryptor,iauthenticatedencryptor encapsulates the cryptographic operations need to use the secret key material and the necessary encryption algorithm information.
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 represents some of the ancillary information that is provided when the encryption is built.
The Iauthenticatedencryptordescriptor interface provides a Iauthenticatedencryptor instance method to create a containing type information.
Createencryptorinstance (): Iauthenticatedencryptor
Exporttoxml (): Xmlserializeddescriptorinfo
Key Management Extensions
In key System management, a basic interface Ikey is provided, which contains the following properties:
Activation
Creation
Expiration dates
Revocation status
Key identifier (a GUID)
Ikey also provides a way to create a Iauthenticatedencryptor instance createencryptorinstance.
The Ikeymanager interface provides a series of methods for manipulating the key, including storage, retrieval operations, and so on. The advanced operations he offers are:
• Create a key and persist storage
• Get all Key from the repository
• Undo one or more keys saved to the store
Xmlkeymanager
Typically, developers do not need to implement Ikeymanager to customize a keymanager. We can use the Xmlkeymanager class provided by the system default.
Xmlkeymanager is a class that implements Ikeymanager specifically, and it provides some very 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 is mainly the algorithm used to specify the new key.
ixmlrepository mainly controls where key is persisted in storage.
ixmlrepository
The Ixmlrepository interface provides a way to persist and retrieve XML as long as it provides two APIs:
getallelements (): ireadonlycollection
storeelement (xelement element, string friendlyname)
We can define the storage location of the data protection XML by implementing the StoreElement method of the Ixmlrepository interface.
Getallelements to retrieve all existing encrypted XML files.
Part of the interface to write here, because this article I would like to focus on the following, more interface to the introduction of the official documents to see it ~
Cluster Scenario
The API above looks a bit boring, so let's see what we need to do with the data protection in the cluster scenario.
As I mentioned at the end of the "previous" summary, some of the mechanisms of Data protection we need to know when doing distributed clustering, because if you don't know these might be a bit of a hassle for your deployment, let's take a look.
When we do cluster, we have to know and understand three things about the asp.net Core Data Protection:
1. Program Recognizer
"Application Discriminator", which is used to identify the uniqueness of an application.
Why do you need this stuff? Because in a clustered environment, if not limited by a specific hardware machine environment, some of the differences in running machines need to be abstracted out to identify the application itself and use that identity to differentiate between different applications. At this time, we can specify Applicationdiscriminator.
In services. Adddataprotection (dataprotectionoptions option), Applicationdiscriminator can be passed as a parameter to 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 Dataprotectionservicecollectionextensions
{public
static Idataprotectionbuilder Adddataprotection (this iservicecollection services);
Overloads with transitive parameters that need to be configured with the public static Idataprotectionbuilder adddataprotection in a clustered environment
(this iservicecollection Services, action<dataprotectionoptions> setupaction);
Dataprotectionoptions Property: Public
class dataprotectionoptions
{public
string applicationdiscriminator {get; set;}
}
You can see that this extension returns a Idataprotectionbuilder, and in Idataprotectionbuilder there is an extension method called Setapplicationname, This extension method is internally or modified by the value of the Applicationdiscriminator. It is also said that the following wording is equivalent:
Services. Adddataprotection (x => x.applicationdiscriminator = "my_app_sample_identity");
Services. Adddataprotection (). Setapplicationname ("my_app_sample_identity");
This means that the same application in the cluster environment needs to be set to the same value (applicationname or applicationdiscriminator).
2, the main encryption key
"Master encryption Key" is mainly used to encrypt and decrypt, including some session data, status, etc. of a client server during the request. There are several options you can configure, such as using a certificate or Windows DPAPI or the registry. If you are a non-Windows platform, the registry and Windows DPAPI are not available.
public void Configureservices (iservicecollection services)
{
services. Adddataprotection ()
//windows DPAIP as the primary encryption key
. PROTECTKEYSWITHDPAPI ()
///If Windows 8+ or Windows server2012+ can use this option (based on Windows Dpapi-ng)
. Protectkeyswithdpaping ("Sid={current account SID}", Dpapingprotectiondescriptorflags.none)
//If Windows 8+ or Windows server2012+ can use this option (certificate-based)
. Protectkeyswithdpaping ("Certificate=hashid:3bce558e2ad3e0e34a7743eab5aea2a9bd2575a0", Dpapingprotectiondescriptorflags.none)
//using the certificate as the primary encryption key, currently only widnows support, Linux is not supported.
. Protectkeyswithcertificate ();
}
If they are in a clustered environment, they need to have the same primary encryption key configured.
3. Storage location after encryption
In "Last article" said that by default, data protection will generate an XML file to store the session or state of the key file. These files are used to encrypt or decrypt state data such as session.
That's the private key storage location in the previous chapter:
1, if the program is hosted under Microsoft Azure, stored in the "%home%\asp.net\dataprotection-keys" folder.
2. If the program is hosted under IIS, it is saved in the Acled Special registry key of the HKLM Registry, and only the worker process can access it using Windows DPAPI encryption.
3. If the current user is available, that is, win10 or win7, it is stored in the "%localappdata%\asp.net\dataprotection-keys" folder, with the same use of Windows DPAPI encryption.
4, if these are not met, then the private key is not persisted, that is, when the process is closed, the generated private key is lost.
In a clustered environment:
The easiest way to do this is through file sharing, DPAPI, or the registry, which means that the encrypted XML files are stored in the same place. Why is the simplest, because the system has been encapsulated, do not need to write redundant code, but to ensure that the file sharing related ports are open. As follows:
public void Configureservices (iservicecollection services)
{
services. Adddataprotection ()
//windows, Linux, MacOS can be saved to the file system in this way
. Persistkeystofilesystem (New System.IO.DirectoryInfo ("C:\\share_keys\\"))
//windows can be saved to the registry in this manner
. Persistkeystoregistry (Microsoft.Win32.RegistryKey.FromHandle (null))
}
You can also extend your own methods to define some of your own storage, such as using databases or Redis.
But normally, if deployed on Linux, it needs to be extended. Let's take a look at what we want to store with Redis.
How do I extend the storage location of the cryptographic key collection?
First, define a Redis implementation class RedisXmlRepository.cs for the Ixmlrepository interface:
public class Redisxmlrepository:ixmlrepository, IDisposable {public static readonly string redishashkey = "Dataprot
Ectionxmlrepository ";
Private Iconnectionmultiplexer _connection;
private bool _disposed = false; Public Redisxmlrepository (String connectionString, ilogger<redisxmlrepository> logger): This ( Connectionmultiplexer.connect (connectionString), logger) {} public redisxmlrepository (Iconnectionmultiplexer Connec tion, 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 Argumentnu
Llexception (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._conne ction!= null) {this._connection.
Close (); This._connection.
Dispose ();
} this._connection = null;
This._disposed = true;
}
}
}
Then define an extension method in any extended class first:
public static Idataprotectionbuilder Persistkeystoredis (this idataprotectionbuilder builder, string
redisconnectionstring) {if (builder = = null) {throw new ArgumentNullException (nameof (builder));
} if (redisconnectionstring = = null) {throw new ArgumentNullException (Nameof (redisconnectionstring)); } if (redisconnectionstring.length = = 0) {throw new ArgumentException ("Redis connection string May is empty.", N
Ameof (redisconnectionstring)); //because in services. Adddataprotection (), has been injected into the ixmlrepository, so should be removed//here should be encapsulated as a method to invoke, for the reader to understand, 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 (redisConn Ectionstring, services. Getrequiredservice<ilogger<redisxmlrepository>> ()) builder.
Services.add (descriptor); Return bUilder.
Use ();
}
In end services, about DataProtection is this:
public void Configureservices (Iservicecollection services) {services. Adddataprotection ()//================ below is the unique identification ==============//Set application unique identification.
Setapplicationname ("my_app_sample_identity"); ============= The following is the primary encryption key ===============//windows DPAIP as the primary encryption key. PROTECTKEYSWITHDPAPI ()///If Windows 8+ or Windows server2012+ can use this option (based on Windows Dpapi-ng). Protectkeyswithdpaping ("Sid={current account SID}", Dpapingprotectiondescriptorflags.none)//If Windows 8+ or Windows server2012+ can use this option (certificate-based). Protectkeyswithdpaping ("Certificate=hashid:3bce558e2ad3e0e34a7743eab5aea2a9bd2575a0", Dpapingprotectiondescriptorflags.none)//using the certificate as the primary encryption key, currently only widnows support, Linux is not supported.
Protectkeyswithcertificate (); ============== The following are storage locations =================//windows, Linux, MacOS can be saved to the file system in this way. Persistkeystofilesystem (New System.IO.DirectoryInfo ("C:\\share_keys\\"))//windows can be saved to the registry in this manner. Persistkeystoregistry (Microsoft.Win32.RegistryKey.FromHandle (NULL))//stored to Redis.Persistkeystoredis (configuration.section["Redisconnection"])}
In the above configuration, I have all the configuration can be used to list the Oh, the actual project should be selected according to the actual situation.
Summary
About ASP.net Core Data Protection series finally finished, in fact, this part of the spend a lot of time, for Data Protection I am also a follow the step-by-step learning process, hoping to help some people.
The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.