ASP.NET Core 中数据保护的非 DI 感知情境

作者:Rick Anderson

ASP.NET Core 数据保护系统通常添加到服务容器,并由依赖组件通过依赖项注入 (DI) 使用。 但是,在有些情况下,这并不可行,也非必要,尤其是在将系统导入现有应用时。

为了支持这些情况,Microsoft.AspNetCore.DataProtection.Extensions 包提供了一个具体类型 DataProtectionProvider,该类型提供一种不依赖 DI 来使用数据保护的简单方法。 类型DataProtectionProvider实现IDataProtectionProvider。 构造 DataProtectionProvider 时,只需提供一个 DirectoryInfo 实例来指示提供程序加密密钥的存储位置,如以下代码示例所示:

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder));

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8FWbAn6...ch3hAPm1NJA
 * Unprotect returned: Hello world!
 *
 * Press any key...
*/

默认情况下,DataProtectionProvider 具体类型在将原始密钥材料保存到文件系统之前不会对其进行加密。 这是为了支持以下情况:开发人员指向网络共享,数据保护系统无法自动推断出适当的密钥静态加密机制。

此外,默认情况下,DataProtectionProvider 具体类型不会隔离应用。 只要目标参数匹配,所有使用相同密钥目录的应用就可以共享有效负载。

DataProtectionProvider 构造函数接受可用于调整系统行为的可选配置回调。 以下示例演示如何通过显式调用 SetApplicationName 来恢复隔离。 它还演示如何将系统配置为使用 Windows DPAPI 自动加密保存的密钥。 如果目录指向 UNC 共享,你可能希望跨所有相关计算机分发共享证书,并通过调用 ProtectKeysWithCertificate 将系统配置为使用基于证书的加密。

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder),
            configuration =>
            {
                configuration.SetApplicationName("my app name");
                configuration.ProtectKeysWithDpapi();
            });

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

提示

DataProtectionProvider 具体类型的实例的创建成本很高。 如果应用维护此类型的多个实例,并且它们都使用相同的密钥存储目录,则应用性能可能会降低。 如果使用 DataProtectionProvider 类型,建议创建一次此类型并尽可能重复使用。 类型DataProtectionProvider以及根据它创建的所有 IDataProtector 实例对于多个调用方来说是线程安全的。