ASP.NET Core 中的密钥存储提供程序

数据保护系统默认采用发现机制来确定加密密钥的保存位置。 开发人员可以替代默认发现机制并手动指定该位置。

警告

如果指定显式密钥保存位置,则数据保护系统将注销默认密钥 rest 加密机制,因此不再对密钥进行 rest 加密。 建议另外为生产部署指定显式密钥加密机制

文件系统

若要配置基于文件系统的密钥存储库,请调用 PersistKeysToFileSystem 配置例程,如下所示。 提供指向应存储密钥的存储库的 DirectoryInfo

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys\"));
}

DirectoryInfo 可以指向本地计算机上的目录,也可以指向网络共享上的文件夹。 如果指向本地计算机上的目录(并且场景是只有本地计算机上的应用才需要访问权限以使用此存储库),请考虑使用 Windows DPAPI(在 Windows 上)对密钥进行 rest 加密。 否则,请考虑使用 X.509 证书对密钥进行 rest 加密。

Azure 存储

Azure.Extensions.AspNetCore.DataProtection.Blobs 包允许将数据保护密钥存储在 Azure Blob 存储中。 可以在 Web 应用的多个实例之间共享密钥。 应用可以跨多个服务器共享身份验证 Cookie 或 CSRF 保护。

若要配置 Azure Blob 存储提供程序,请调用 PersistKeysToAzureBlobStorage 重载之一。

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(new Uri("<blob URI including SAS token>"));
}

如果 Web 应用作为 Azure 服务运行,则可以使用连接字符串通过 Azure.Storage.Blobs 向 Azure 存储进行身份验证。

警告

本文介绍连接字符串的使用。 使用本地数据库时,用户无需进行身份验证,但在生产环境中,连接字符串有时包括进行身份验证的密码。 资源所有者密码凭据(ROPC)是在生产数据库中应避免的安全风险。 生产应用应使用可用的最安全的身份验证流。 有关部署到测试或生产环境的应用的身份验证的详细信息,请参阅 安全身份验证流

string connectionString = "<connection_string>";
string containerName = "my-key-container";
string blobName = "keys.xml";
BlobContainerClient container = new BlobContainerClient(connectionString, containerName);

// optional - provision the container automatically
await container.CreateIfNotExistsAsync();

BlobClient blobClient = container.GetBlobClient(blobName);

services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(blobClient);

注意

存储帐户的连接字符串可以在 Azure 门户的“访问密钥”部分下找到,也可以通过运行以下 CLI 命令找到:

az storage account show-connection-string --name <account_name> --resource-group <resource_group>

Redis

Microsoft.AspNetCore.DataProtection.StackExchangeRedis 包允许将数据保护密钥存储在 Redis 缓存中。 可以在 Web 应用的多个实例之间共享密钥。 应用可以跨多个服务器共享身份验证 Cookie 或 CSRF 保护。

Microsoft.AspNetCore.DataProtection.Redis 包允许将数据保护密钥存储在 Redis 缓存中。 可以在 Web 应用的多个实例之间共享密钥。 应用可以跨多个服务器共享身份验证 Cookie 或 CSRF 保护。

若要在 Redis 上配置,请调用 PersistKeysToStackExchangeRedis 重载之一:

public void ConfigureServices(IServiceCollection services)
{
    var redis = ConnectionMultiplexer.Connect("<URI>");
    services.AddDataProtection()
        .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys");
}

若要在 Redis 上配置,请调用 PersistKeysToRedis 重载之一:

public void ConfigureServices(IServiceCollection services)
{
    var redis = ConnectionMultiplexer.Connect("<URI>");
    services.AddDataProtection()
        .PersistKeysToRedis(redis, "DataProtection-Keys");
}

有关详情,请参阅以下主题:

注册表

仅适用于 Windows 部署。

有时,应用可能对文件系统没有写入访问权限。 以应用作为虚拟服务帐户(例如 w3wp.exe 的应用池 identity)运行的场景为例。 在这些情况下,管理员可以预配由服务帐户 identity 访问的注册表项。 调用 PersistKeysToRegistry 扩展方法,如下所示。 提供指向加密密钥存储位置的 RegistryKey

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToRegistry(Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Sample\keys", true));
}

重要

建议使用 Windows DPAPI 对密钥进行 rest 加密。

Entity Framework Core

Microsoft.AspNetCore.DataProtection.EntityFrameworkCore 包提供一种使用 Entity Framework Core 将数据保护密钥存储到数据库的机制。 必须将 Microsoft.AspNetCore.DataProtection.EntityFrameworkCore NuGet 包添加到项目文件,它不是 Microsoft.AspNetCore.App 元包的一部分。

使用此包,可以在 Web 应用的多个实例之间共享密钥。

要配置 EF Core 提供程序,请调用 PersistKeysToDbContext 方法:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    // Add a DbContext to store your Database Keys
    services.AddDbContext<MyKeysContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("MyKeysConnection")));

    // using Microsoft.AspNetCore.DataProtection;
    services.AddDataProtection()
        .PersistKeysToDbContext<MyKeysContext>();

    services.AddDefaultIdentity<IdentityUser>()
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

若要查看翻译为非英语语言的代码注释,请在 此 GitHub 讨论问题中告诉我们。

泛型参数 TContext 必须继承自 DbContext,并且必须实现 IDataProtectionKeyContext

using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

namespace WebApp1
{
    class MyKeysContext : DbContext, IDataProtectionKeyContext
    {
        // A recommended constructor overload when using EF Core 
        // with dependency injection.
        public MyKeysContext(DbContextOptions<MyKeysContext> options) 
            : base(options) { }

        // This maps to the table that stores keys.
        public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
    }
}

创建 DataProtectionKeys 表。

在包管理器控制台 (PMC) 窗口中执行以下命令:

Add-Migration AddDataProtectionKeys -Context MyKeysContext
Update-Database -Context MyKeysContext

MyKeysContext 是上述代码示例中定义的 DbContext。 如果使用具有不同名称的 DbContext,请将 DbContext 名称替换为 MyKeysContext

DataProtectionKeys 类/实体采用下表所示的结构。

属性/字段 CLR 类型 SQL 类型
Id int int、PK、IDENTITY(1,1)、非 null
FriendlyName string nvarchar(MAX)、null
Xml string nvarchar(MAX)、null

自定义密钥存储库

如果内置机制不适用,开发人员可以通过提供自定义 IXmlRepository 来指定自己的密钥保存机制。