上篇主要是对 ASP.NET Core 的 Data Protection 做了一个简单的介绍,本篇主要是介绍一下API及使用方法。
ASP.NET Core Data Protectio 主要对普通开发人员提供了两个接口,IDataProtectionProvider
和 IDataProtector
。
我们先看一下这两个接口的关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Microsoft.AspNetCore.DataProtection</span> { <span class="hljs-comment">//</span> <span class="hljs-comment">// 摘要:</span> <span class="hljs-comment">// An interface that can provide data protection services.</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IDataProtector</span> : <span class="hljs-title">IDataProtectionProvider</span> { <span class="hljs-keyword">byte</span>[] Protect(<span class="hljs-keyword">byte</span>[] plaintext); <span class="hljs-keyword">byte</span>[] Unprotect(<span class="hljs-keyword">byte</span>[] protectedData); } } |
可以看到,IDataProtector
继承自IDataProtectionProvider
,并且提供了两个方法 Protect
和 Unprotect
,从命名来看,一个是加密,一个是解密。而他们的签名都是传入一个byte数组,这也就意味着他们可以加密和解密一切对象。返回的也是byte数组,也就是说在实际的使用过程中,我们应该自己添加或者使用系统的一些扩展方法来具体化我们的需求。
我们再看一下IDataProtectionProvider
接口:
1 2 3 4 5 6 7 8 |
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Microsoft.AspNetCore.DataProtection</span> { <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IDataProtectionProvider</span> { <span class="hljs-function">IDataProtector <span class="hljs-title">CreateProtector</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> purpose</span>)</span>; } } |
IDataProtectionProvider
提供了一个方法,通过传入一个 purpose
字符串(见后面详细介绍)来生成一个IDataProtector
接口对象。
从这个接口的命名来看,它以Provider结尾,也就是说这部分我们可以实现自己的一套加解密的东西。
我们在阅读微软项目的源代码的时候,经常看一些以xxxxProvider结尾的对象,那么它的职责是什么,同时扮演什么样的角色呢?
其实这是微软专门为ASP.NET设计的一个设计模式,叫Provider Model
设计模式,也可以说它是由微软发明的,它不属于23种设计模式中的一种,从功能上来看的话,应该是工厂和策略的结合体。自ASP.NET 2.0开始,微软就开始引入这种设计模式,最开始主要是用于实现应用程序的配置的多个实现。比如开发者最熟悉的web.config
中, 针对于数据库连接字符串的配置, 还有二进制,再比如XML啊等等很多,现在其他地方这种模式也用的越来越多起来。
再来说一下CreateProtector
方法签名中的 purpose
这个字符串,在上一篇博文中为了读者好理解,我把传入的purpose
说成可以理解为一个公钥,其实这个说法是不严谨的,可以理解为一个标识,指示当前Protector的用途。
在使用IDataProtector的时候,会发现它还有一些扩展方法位于Microsoft.AspNetCore.DataProtection
命名空间下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DataProtectionCommonExtensions</span> { <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">CreateProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtectionProvider provider, IEnumerable<<span class="hljs-keyword">string</span>> purposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">CreateProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtectionProvider provider, <span class="hljs-keyword">string</span> purpose, <span class="hljs-keyword">params</span> <span class="hljs-keyword">string</span>[] subPurposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">GetDataProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceProvider services, IEnumerable<<span class="hljs-keyword">string</span>> purposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">GetDataProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceProvider services, <span class="hljs-keyword">string</span> purpose, <span class="hljs-keyword">params</span> <span class="hljs-keyword">string</span>[] subPurposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">Protect</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtector protector, <span class="hljs-keyword">string</span> plaintext</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">Unprotect</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtector protector, <span class="hljs-keyword">string</span> protectedData</span>)</span>; } |
可以看到,CreateProtector
还提供了可以传多个purpose
的方法(IEnumerable,params string[]),为什么会有这种需求呢?
其实DataProtector是有层次结构的,再看一下IDataProtector
接口,它自身也实现了IDataProtectionProvider
接口,就是说IDataProtector
自身也可以再创建IDataProtector
。
举个例子:我们在做一个消息通讯的系统,在消息通讯的过程中,需要对用户的会话进行加密,我们使用CreateProtector("Security.BearerToken")加密。但是加密的时候并不能保证消息是不受信任的客户端发过来的,所以想到了CreateProtector("username")来进行加密,这个时候假如有一个用户的用户名叫“Security.BearerToken”,那么就和另外一个使用Security.BearerToken作为标示的 Protector 冲突了,所以我们可以使用
CreateProtector([ “Security.BearerToken”, “User: username” ])这种方式。它相当于
provider.CreateProtector(“Security.BearerToken).CreateProtector(“User: username”)
。 意思就是先创建一个Protector叫“Security.BearerToken”,然后再在purpose1下创建一个名为“User: username”的Protector。
在Microsoft.AspNetCore.Cryptography.KeyDerivation
命名空间下提供了一个KeyDerivation.Pbkdf2
方法用来对用户密码进行哈希。
有些时候,我们需要一些具有过期或者到期时间的加密字符串,比如一个用户在找回密码的时候,我们向用户的邮箱发送一封带有重置命令的一封邮件,这个重置命令就需要有一个过期时间了,超过这个过期时间后就失效,在以前我们可能需要向数据库存储一个时间来标记发送时间,然后再解密对比和数据库的时间差来验证。
现在我们不需要这么做了,ASP.NET Core 默认提供了一个接口叫 ITimeLimitedDataProtector
,我们先看一下这个接口的定义:
1 2 3 4 5 6 7 |
CreateProtector(<span class="hljs-built_in">string</span> purpose) : ITimeLimitedDataProtector This API <span class="hljs-keyword">is</span> similar <span class="hljs-keyword">to</span> the existing IDataProtectionProvider.CreateProtector <span class="hljs-keyword">in</span> that it can be used <span class="hljs-keyword">to</span> create purpose chains <span class="hljs-keyword">from</span> a root time-limited protector. Protect(<span class="hljs-built_in">byte</span>[] plaintext, DateTimeOffset expiration) : <span class="hljs-built_in">byte</span>[] Protect(<span class="hljs-built_in">byte</span>[] plaintext, TimeSpan lifetime) : <span class="hljs-built_in">byte</span>[] Protect(<span class="hljs-built_in">byte</span>[] plaintext) : <span class="hljs-built_in">byte</span>[] Protect(<span class="hljs-built_in">string</span> plaintext, DateTimeOffset expiration) : <span class="hljs-built_in">string</span> Protect(<span class="hljs-built_in">string</span> plaintext, TimeSpan lifetime) : <span class="hljs-built_in">string</span> Protect(<span class="hljs-built_in">string</span> plaintext) : <span class="hljs-built_in">string</span> |
ITimeLimitedDataProtector
提供了数个重载方法用来设定带有生命周期的加密方法,用户可以通过Date TimeOffset
,TimeSpan
等参数来设置时间。
有对应的加密,就有相对应的解密方法,在这里就不详细介绍了。有兴趣的同学可以去看一下官方文档。
在我们的 ASP.NET Core 运行的时候,系统会基于当前机器的运行环境默认配置一些关于 Data Protection 的东西,但是有些时候可能需要对这些配置做一些改变,比如在分布式部署的时候,在上一篇博文的末尾也提到过,下面就来看一下具体怎么配置的吧。
上篇文章已经提到过,我们通过以下方式来把 Data Protection 注册到服务中:
1 2 3 4 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection(); } |
其中AddDataProtection
返回的是一个 IDataProtectionBuilder
接口,这个接口提供了一个扩展方法PersistKeysToFileSystem()
来存储私钥。可以通过它传入一个路径来指定私钥存储的位置:
1 2 3 4 5 6 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>) </span>{ services.AddDataProtection() .PersistKeysToFileSystem(<span class="hljs-keyword">new</span> DirectoryInfo(<span class="hljs-string">@"\\server\share\directory\"</span>)); } |
可以传入一个共享文件夹,来存储私钥,这样在不同机器的私钥就可以保存到一个位置了。可以通过此种方式在分布式部署的时候,隔离开了机器的差异化。
如果你觉得不安全,还可以配置一个X.509证书来,进行加密:
1 2 3 4 5 6 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>) </span>{ services.AddDataProtection() .PersistKeysToFileSystem(<span class="hljs-keyword">new</span> DirectoryInfo(<span class="hljs-string">@"\\server\share\directory\"</span>)) .ProtectKeysWithCertificate(<span class="hljs-string">"thumbprint"</span>); } |
上篇文章讲过,Data Protection 的默认保存时间是90天,你可以通过以下方式来修改默认的保存时间:
1 2 3 4 5 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection() .SetDefaultKeyLifetime(TimeSpan.FromDays(<span class="hljs-number">14</span>)); } |
默认情况下,即使使用相同的物理密钥库,Data Protection 也会把不同的应用程序隔离开,因为这样可以防止从一个应用程序获取另外一个应用程序的密钥。所以如果是相同的应用程序,可以设置相同的应用程序名称:
1 2 3 4 5 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection() .SetApplicationName(<span class="hljs-string">"my application"</span>); } |
有时候需要禁用应用程序生成密钥,或者是说我只有一个程序用来生成或者管理密钥,其他程序只是负责读的话,那么可以这样:
1 2 3 4 5 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection() .DisableAutomaticKeyGeneration(); } |
修改加密算法
可以使用UseCryptographicAlgorithms
方法来修改ASP.NET Core Data Protection的默认加密算法,如下:
1 2 3 4 5 6 |
services.AddDataProtection() .UseCryptographicAlgorithms(<span class="hljs-keyword">new</span> AuthenticatedEncryptionSettings() { EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC, ValidationAlgorithm = ValidationAlgorithm.HMACSHA256 }); |
本篇主要是介绍了一些常用的API, 下篇介绍一些高级的用法。
本文地址:http://www.cnblogs.com/savorboard/p/dotnet-core-data-protection.html
作者博客:Savorboard
欢迎转载,请在明显位置给出出处及链接