数据爱抚

前言 

前言 

接【中篇】
,在有一些场景下,我们需要对 ASP.NET Core
的加密方法进行扩展,来适应我们的需求,这个时候就需要使用到了一些 Core
提供的高级的功能。 

上一篇记录了如何在 Kestrel 中使用 HTTPS(SSL),
也是我们目前项目中实际使用到的。 

本文还列举了在集群场景下,有时候我们需要实现自己的一些方法来对Data
Protection进行分布式配置。 

数据安全往往是开发人员很容易忽略的一个部分,包括我自己。近两年业内也出现了很多因为安全问题导致了很多严重事情发生,所以安全对我们开发人员很重要,我们要对我们的代码的安全负责。 

加密扩展 

在工作中,我们常常会见到 encode,base64,sha256, rsa, hash,encryption,
md5
等,一些人对他们还傻傻分不清楚,也不知道什么时候使用他们,还有一些人认为MD5就是加密算法。 

IAuthenticatedEncryptorIAuthenticatedEncryptorDescriptor 

在 ASP.NET Core 中,为数据保护相关提供了一批新的
API,包括加密解密机制,下面就让我们来看看吧。 

IAuthenticatedEncryptor是 Data Protection
在构建其密码加密系统中的一个基础的接口。
 一般情况下一个key
对应一个IAuthenticatedEncryptor,IAuthenticatedEncryptor封装了加密操作中需要使用到的秘钥材料和必要的加密算法信息等。 

目录
 •加密,编码,哈希之间的区别
 •数据保护(Data Protection)介绍
 •ASP.NET Core 中的数据保护
 •总结 

下面是IAuthenticatedEncryptor接口提供的两个 api方法:
Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte>
additionalAuthenticatedData) : byte[]
Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte>
additionalAuthenticatedData) : byte[]

编码,加密,哈希之间的区别 

其中接口中的参数additionalAuthenticatedData表示在构建加密的时候提供的一些附属信息。 

1、编码

IAuthenticatedEncryptorDescriptor接口提供了一个创建包含类型信息IAuthenticatedEncryptor实例方法。

 编码是信息从一种形式或格式转换为另一种形式的过程,他们是可逆的。
 如 url、base64、jsunicode、utf-8等等。 

CreateEncryptorInstance() : IAuthenticatedEncryptor
ExportToXml() : XmlSerializedDescriptorInfo

2、加密
 加密是可逆的,类似于编码也是把数据从一种形式转换为另一种形式,它通过一个特定的加密的密匙,相对应的有解密的过程。加解密的算法有2种:对称加密算法和非对称加密算法。
 对称:DES、AES、SM1、RC4 等等。
 非对称:RSA、ECC、SM2 等等。 

密钥管理扩展 

3、哈希
 又叫”散列”,就是把任意长度的数据转换成固定长度的“指纹”,这个过程是不可逆的。而且只要输入发生改变,输出的
hash值也会有很大不同。
 它还有一个特性是相同的输入总是有相同的结果,
这种特性恰好合适用来用来保存密码。
 如:MD5、SHA256, SHA512, RipeMD, WHIRLPOOL等等。 

在密钥系统管理中,提供了一个基础的接口IKey,它包含以下属性: 

数据保护(Data Protection)介绍 

Activation
creation
expiration dates
Revocation status
Key identifier (a GUID)

在看数据保护官方文档的时候,微软的文档是这样写的,大致意思就是他们基于几点需求,要开发一套数据保护的库以便用来给受信任的客户端和不受信任的客户端来使用。这几点要求就是: 

IKey还提供了一个创建IAuthenticatedEncryptor实例的方法CreateEncryptorInstance。 

1、真实性、完整性
 举了一个身份验证cookie的例子,就是服务端生成了一个包含xyz权限的token,然后会在将来的某个时间过期,这个时候就需要重新请求生成一个,怎么样来保证请求的token不是被篡改过的。 

IKeyManager接口提供了一系列用来操作Key的方法,包括存储,检索操作等。他提供的高级操作有:

2、机密性
 服务器要保证请求是受信任的,所以就需要一些包含特定操作环境的信息,比如一个路径,一个权限或者一个句柄或者其他的一些东西特定于服务器的东西,这些信息不应该透漏给不受信任的客户端,也就是说类似于私钥。 

 •创建一个Key 并且持久存储
 •从存储库中获取所有的 Key
 •撤销保存到存储中的一个或多个键

3、隔离性
 然后就是要求做成一个组件,并且这个组件具有独立性,可以不依赖于系统中的其他组件。如一个bearer
token的组件,它要使用这个组件的话,也不需要引用anti-CSRF这种机制了。 

XmlKeyManager 通常情况下,开发人员不需要去实现IKeyManager来自定义一个
KeyManager。我们可以使用系统默认提供的XmlKeyManager类。 

再进一步的缩小需求范围,加密的数据不需要在系统之外的其他系统中使用,另外处理速度要尽可能的快,因为每一次web请求都会使用加密组件一次或者多次。 

XMLKeyManager是一个具体实现IKeyManager的类,它提供了一些非常有用的方法。

基于以上要求,微软提出来可以使用密码学,因为这是一个典型的密码学应用的场景。确实这是一个密码学的应用场景,并且是一个非对称加密算法的场景。但是大家都知道,非对称加密是由一个公钥和私钥用来保证安全性的,即使公钥遭泄露,整个通讯仍然是安全的,这就是它比对称加密的好处。但是非对称加密也是有缺点的,就是加密和解密花费的时间长,速度慢。 

 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 主要是规定新 Key 使用的算法。
•IXmlRepository 主要控制 Key 在哪里持久化存储。

到这里,我就想,如果让我来基于以上几点来设计开发这样一个系统,我应该怎么样设计?怎么样达到要求?
 带着这个问题,我们来进一步看看微软是怎么样做的吧? 

IXmlRepository 

下面是一些总结的设计原则 : 

IXmlRepository接口主要提供了持久化以及检索XML的方法,它只要提供了两个API:
 •GetAllElements() : IReadOnlyCollection
 •StoreElement(XElement element, string friendlyName) 

1、配置应该尽量的简单,默认情况下应该可以零配置,开发人员可以直接运行。 

我们可以通过实现IXmlRepository接口的StoreElement方法来定义data
protection xml的存储位置。 

2、提供一个简单的API,应该容易使用,并且不会轻易用错。 

GetAllElements来检索所有存在的加密的xml文件。 

3、开发人员不需要专门学习怎么样管理这些钥(公钥,私钥),系统应该自动的选择算法和管理钥的生命周期。理想情况下开发人员都不应该访问这些钥的原始文件。 

接口部分写到这里吧,因为这一篇我想把重点放到下面,更多接口的介绍大家还是去官方文档看吧~ 

4、钥应该是受保护的,不会被远程调用到。系统应该有一个自动保护机制并且可以自动应用。 

集群场景 

如果让我设计这样一个库,我可能不会想到这么多,也许只会想到前3点。 

上面的API估计看着有点枯燥,那我们就来看看我们需要在集群场景下借助于Data
Protection来做点什么吧。 

再看一下针对的受众群体: 

就像我在【上篇】总结中末尾提到的,在做分布式集群的时候,Data
Protection的一些机制我们需要知道,因为如果不了解这些可能会给你的部署带来一些麻烦,下面我们就来看看吧。 

1、应用程序开发人员和框架开发人员(不需要学习任何知识)。 

在做集群的时,我们必须知道并且明白关于 ASP.NET Core Data Protection
的三个东西:

2、应用开发人员和系统管理员(不使用默认配置,只是设定一些路径等)。 

1、程序识别者 

3、针对具有更高安全意识的开发人员提供可扩展api,或特定需求扩展(需要重写系统的组件,有一些独特的需求)。 

“Application discriminator”,它是用来标识应用程序的唯一性。
 为什么需要这个东西呢?因为在集群环境中,如果不被具体的硬件机器环境所限制,就要排除运行机器的一些差异,就需要抽象出来一些特定的标识,来标识应用程序本身并且使用该标识来区分不同的应用程序。这个时候,我们可以指定ApplicationDiscriminator。 

以上,可以看到微软在开发一个组件的时候对问题的分析,也许我们可以从中学到一些东西。 

在services.AddDataProtection(DataProtectionOptions
option)的时候,ApplicationDiscriminator可以作为参数传递,来看一下代码:

ASP.NET Core 中的数据保护 

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

 services.AddDataProtection(DataProtectionOptions option);
}

//===========扩展方法如下:

public static class DataProtectionServiceCollectionExtensions
{
 public static IDataProtectionBuilder AddDataProtection(this IServiceCollection services);

 //具有可传递参数的重载,在集群环境中需要使用此项配置
 public static IDataProtectionBuilder AddDataProtection(this IServiceCollection services, Action<DataProtectionOptions> setupAction);
}

// DataProtectionOptions 属性:
public class DataProtectionOptions
{
 public string ApplicationDiscriminator { get; set; }
} 

Web应用程序中经常需要存储一些敏感数据(如用户密码),Windows
系统为桌面程序提供了DPAPI用来使用,但是并不适用于 Web 系统。ASP.NET
Core提供了一套简单易用的API 用来保护数据。 

可以看到这个扩展返回的是一个IDataProtectionBuilder,在IDataProtectionBuilder还有一个扩展方法叫
SetApplicationName
,这个扩展方法在内部还是修改的ApplicationDiscriminator的值。也就说以下写法是等价的:

ASP.NET Core 中,数据保护主要是用来给服务端设计的,用来替换ASP.NET
1.x-4.x中的,machineKey主要是用来保证使用Form身份验证时Cookie数据的加密解密,以确保不会被修改。或者ViewState数据的加密解密不被篡改,以及对session状态标识进行验证。 

services.AddDataProtection(x => x.ApplicationDiscriminator =
“my_app_sample_identity”);

先看一下最简单的使用方法:

services.AddDataProtection().SetApplicationName(“my_app_sample_identity”); 

 using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
  public static void Main(string[] args)
  {
    // 添加数据保护到服务中
    var serviceCollection = new ServiceCollection();
    serviceCollection.AddDataProtection();
    var services = serviceCollection.BuildServiceProvider();

    // 从DI中创建一个MyClass的实例 
    var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
    instance.RunSample();
  }

  public class MyClass
  {
    IDataProtector _protector;

    // 参数 'provider' 来自 DI
    public MyClass(IDataProtectionProvider provider)
    {
      _protector = provider.CreateProtector("Contoso.MyClass.v1");
    }

    public void RunSample()
    {
      Console.Write("Enter input: ");
      string input = Console.ReadLine();

      // 加密
      string protectedPayload = _protector.Protect(input);
      Console.WriteLine($"Protect returned: {protectedPayload}");

      // 解密
      string unprotectedPayload = _protector.Unprotect(protectedPayload);
      Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
    }
  }
}

/*
 * 输出:
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ
 * Unprotect returned: Hello world!
 */ 

也就是说集群环境下同一应用程序他们需要设定为相同的值(ApplicationName or
ApplicationDiscriminator)。 

在CreateProtector(“Contoso.MyClass.v1”)中,参数“Contoso.MyClass.v1”可以理解为一个公钥,因为
ASP.NET Core Data Protection
是非对称加密(见前面介绍),所以系统中应该还有一个密钥,那么此处的密钥
ASP.NET Core 在系统内部帮你维护了。 

2、主加密键 

读到这里,有同学可能会问了,那系统中是如何帮我维护我的密钥的呢?
我们不妨先来做一个测试。 

“Master encryption
key”,主要是用来加密解密的,包括一客户端服务器在请求的过程中的一些会话数据,状态等。有几个可选项可以配置,比如使用证书或者是windows
DPAPI或者注册表等。如果是非windows平台,注册表和Windows
DPAPI就不能用了。

首先,我在我的开发环境中,先把上面的程序中的解密部分代码注释掉,然后运行上面的程序,输入一个“Hello
World!”
,得到了一个加密的字符串CfDJ8ICcgQwZZhlAlTZT…OdfH66i1PnGmpCR5e441xQ(略写)。 

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

 //windows dpaip 作为主加密键
 .ProtectKeysWithDpapi()

 //如果是 windows 8+ 或者windows server2012+ 可以使用此选项(基于Windows DPAPI-NG)
 .ProtectKeysWithDpapiNG("SID={current account SID}", DpapiNGProtectionDescriptorFlags.None)

 //如果是 windows 8+ 或者windows server2012+ 可以使用此选项(基于证书)
 .ProtectKeysWithDpapiNG("CERTIFICATE=HashId:3BCE558E2AD3E0E34A7743EAB5AEA2A9BD2575A0", DpapiNGProtectionDescriptorFlags.None)

 //使用证书作为主加密键,目前只有widnows支持,linux还不支持。
 .ProtectKeysWithCertificate();
} 

然后我把同样的程序拷贝到另外一台开发环境的机器上,然后把上面的加密部分代码注释掉,使用第一步生成的CfDJ8ICcgQwZZhlAlTZT…OdfH66i1PnGmpCR5e441xQ来解密,注意这两步中我们都使用
“Contoso.MyClass.v1” 来做为公钥。 

如果在集群环境中,他们需要具有配置相同的主加密键。 

运行程序,查看结果:

3、加密后存储位置 

图片 1

在【上篇】的时候说过,默认情况下Data Protection会生成 xml
文件用来存储session或者是状态的密钥文件。这些文件用来加密或者解密session等状态数据。 

程序抛出了一个“System.Security.Cryptography.CryptographicException”异常的结果。 

就是上篇中说的那个私钥存储位置:

为什么呢?
这是因为每一台机器都有一个自有的私钥,由于在解密的过程中,这个私钥是不同的,所以解密失败,抛出了一个异常。 

1、如果程序寄宿在 Microsoft
Azure下,存储在“%HOME%\ASP.NET\DataProtection-Keys” 文件夹。
 2、如果程序寄宿在IIS下,它被保存在HKLM注册表的ACLed特殊注册表键,并且只有工作进程可以访问,它使用windows的DPAPI加密。
 3、如果当前用户可用,即win10或者win7中,它存储在“%LOCALAPPDATA%\ASP.NET\DataProtection-Keys”文件夹,同样使用的windows的DPAPI加密。
 4、如果这些都不符合,那么也就是私钥是没有被持久化的,也就是说当进程关闭的时候,生成的私钥就丢失了。 

私钥 

集群环境下:
 最简单的方式是通过文件共享、DPAPI或者注册表,也就是说把加密过后的xml文件都存储在相同的地方。为什么说最简单,因为系统已经给封装好了,不需要写多余的代码了,但是要保证文件共享相关的端口是开放的。如下:

私钥存放在哪里呢? 

 public void ConfigureServices(IServiceCollection services) 
{
 services.AddDataProtection()
 //windows、Linux、macOS 下可以使用此种方式 保存到文件系统
 .PersistKeysToFileSystem(new System.IO.DirectoryInfo("C:\\share_keys\\"))
 //windows 下可以使用此种方式 保存到注册表
 .PersistKeysToRegistry(Microsoft.Win32.RegistryKey.FromHandle(null)) 
} 

1、如果程序寄宿在 Microsoft
Azure下,存储在“%HOME%\ASP.NET\DataProtection-Keys” 文件夹。 

你也可以自己扩展方法来自己定义一些存储,比如使用数据库或者Redis等。 

2、如果程序寄宿在IIS下,它被保存在HKLM注册表的ACLed特殊注册表键,并且只有工作进程可以访问,它使用windows的DPAPI加密。 

不过通常情况下,如果在linux上部署的话,都是需要扩展的。下面来看一下我们想要用redis存储,该怎么做呢? 

3、如果当前用户可用,即win10或者win7中,它存储在“%LOCALAPPDATA%\ASP.NET\DataProtection-Keys”文件夹,同样使用的windows的DPAPI加密。 

如何扩展加密键集合的存储位置? 

4、如果这些都不符合,那么也就是私钥是没有被持久化的,也就是说当进程关闭的时候,生成的私钥就丢失了。 

首先,定义个针对IXmlRepository接口的 redis
实现类RedisXmlRepository.cs:

下面是博主机器上的私钥文件: 

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

一个xml配置文件,位于C:\Users\用户名\AppData\Local\ASP.NET\DataProtection-Keys文件夹,名为:key-c37e3ed9-fbb5-47bc-9e8c-128afaf1c6d9.xml,内容如下:

然后任意一个扩展类中先定义一个扩展方法:

 <?xml version="1.0" encoding="utf-8"?>
<key id="c37e3ed9-fbb5-47bc-9e8c-128afaf1c6d9" version="1">
 <creationDate>2016-08-15T05:21:16.7925949Z</creationDate>
 <activationDate>2016-08-15T05:21:16.7165905Z</activationDate>
 <expirationDate>2016-11-13T05:21:16.7165905Z</expirationDate>
 <descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
  <descriptor>
   <encryption algorithm="AES_256_CBC" />
   <validation algorithm="HMACSHA256" />
   <encryptedSecret decryptorType="Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiXmlDecryptor, Microsoft.AspNetCore.DataProtection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" xmlns="http://schemas.asp.net/2015/03/dataProtection">
    <encryptedKey xmlns="">
     <!-- This key is encrypted with Windows DPAPI. -->
     <value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAArS6GBZu5C024S8VcNDckGgAAAAACAAAAAAAQZgAAAAEAACAAAABBUO4j0CscEZsdcHDAStXnDvtx+zFucmsG90sdhyjfgQAAAAAOgAAAAAIAACAAAABGr9fgvZkLAlgIZkGym5uLiufpaEcuVsp35+J96ItTYlABAADEZxVArK0QtxufuaRt/kVR2ZBZEoLhlYJ44BhvQDd6b9tN0L9Y7W2eeBPBefcZaGZk5xILwZYI5box9omwC/mp8t9wopVaratjZuNs21Al+JzxS+PeV9X0iPtRyfx2K7DJYOUT6IqoFR2ykL5MI9jvkIbUxcQOs0BKOwAHl4yAlYF2tR8pz1FkXKqZafovc11aOZeZhkfd2hiA53tan94bQOP43Z4HF+QWSazrq5IIqdFSOyZQemWL9Z7eYyoNpEktf3eGZQu/KBOg/BH5yizWa+6b7RLcEX6JdQ2/jpmnHNl+HPMIah3UZV0mRfAE2j58cUjosnV+LDQZoLn4OP70YWtO/tTBc4tsEY3n/WboL4PgPPmQ+2jfd/zmEQIon+4d7TY+mGh4c6wXAmAZF517UAHQMC1icx4HSJC8DTuWPlINihPyufejuPmLqW6CW8NAAAAA7ziObXv+Ax4Mm0AtZiGw0/IepDv/gJSxhEwLIDhfvQIQJv//G500EYtIbZJW6sWit//ypfjrUZYglHgKV+GpbA==</value>
    </encryptedKey>
   </encryptedSecret>
  </descriptor>
 </descriptor>
</key> 
 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 not be empty.", nameof(redisConnectionString));
 }

 //因为在services.AddDataProtection()的时候,已经注入了IXmlRepository,所以应该先移除掉
 //此处应该封装成为一个方法来调用,为了读者好理解,我就直接写了
 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();
} 

文件包含一个创建日期,一个过期日期。间隔为90天,当90天之后密钥就会失效,系统将自动生成一个新的密钥并设置新的密钥作为活动的密钥。只要已过期的密钥还存在于系统上,你仍然可以解密任何受保护的数据。 

最终Services中关于DataProtection是这样的:

文章不宜太长,下篇再接着写。 

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

 // ================以下是唯一标识==============

 //设置应用程序唯一标识
 .SetApplicationName("my_app_sample_identity");


 // =============以下是主加密键===============

 //windows dpaip 作为主加密键
 .ProtectKeysWithDpapi()

 //如果是 windows 8+ 或者windows server2012+ 可以使用此选项(基于Windows DPAPI-NG)
 .ProtectKeysWithDpapiNG("SID={current account SID}", DpapiNGProtectionDescriptorFlags.None)

 //如果是 windows 8+ 或者windows server2012+ 可以使用此选项(基于证书)
 .ProtectKeysWithDpapiNG("CERTIFICATE=HashId:3BCE558E2AD3E0E34A7743EAB5AEA2A9BD2575A0", DpapiNGProtectionDescriptorFlags.None)

 //使用证书作为主加密键,目前只有widnows支持,linux还不支持。
 .ProtectKeysWithCertificate();


 // ==============以下是存储位置=================

 //windows、Linux、macOS 下可以使用此种方式 保存到文件系统
 .PersistKeysToFileSystem(new System.IO.DirectoryInfo("C:\\share_keys\\"))

 //windows 下可以使用此种方式 保存到注册表
 .PersistKeysToRegistry(Microsoft.Win32.RegistryKey.FromHandle(null)) 

  // 存储到redis
 .PersistKeysToRedis(Configuration.Section["RedisConnection"])
} 

总结 

在上面的配置中,我把所有可以使用的配置都列出来了哦,实际项目中应该视实际情况选择。 

这篇文章算是对ASP.NET Core Data
Protection做了一个大致的介绍,并且包含了一个简单的使用方法。
在实际使用过程中,其实很多组件内部都会使用到它,比如Session中间件,Identity中间件,Authercation中间件等等,对于普通开发人员在编码的时候可能不会用到,但是在做系统分布式部署的时候如果你不了解这个机制可能就会遇到麻烦了(详见蟋蟀博客的这篇文章),所以还是可以期待一下下文,更加深入的了解它,掌握它。

总结 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

关于ASP.NET Core Data Protection
系列终于写完了,其实这这部分花了蛮多时间的,对于Data
Protection来说我也是一个循循渐进的学习过程,希望能帮助到一些人。

您可能感兴趣的文章:

  • win10下ASP.NET
    Core部署环境搭建步骤
  • asp.net
    core实现文件上传功能
  • ASP.NET
    Core配置教程之读取配置信息
  • ASP.NET Core集成微信登录
  • Linux(Ubuntu)下搭建ASP.NET
    Core环境
  • 云服务器下搭建ASP.NET
    Core环境
  • ASP.NET Core
    1.0实现邮件发送功能
  • ASP.NET Core MVC
    配置全局路由前缀
  • ASP.NET Core 数据保护(Data
    Protection)中篇
  • 解决asp.net
    core在输出中文时乱码的问题

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

  • ASP.NET Core 数据保护(Data
    Protection)中篇
  • ASP.NET Core 数据保护(Data
    Protection)上篇
  • ASP.NET Core Kestrel 中使用 HTTPS
    (SSL)
  • ASP.NET Core集成微信登录
  • 微信抢红包ASP.NET代码轻松实现
  • 基于ASP.NET实现日期转为大写的汉字
  • ASP.NET
    MVC5网站开发之用户资料的修改和删除3(七)
  • ASP.NET
    MVC5网站开发之用户添加和浏览2(七)
  • ASP.NET
    MVC5网站开发之用户角色的后台管理1(七)
  • ASP.NET
    程序员都非常有用的85个工具

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注