前言

以前就写过很多篇了

Asp.net core 学习笔记 ( Data protection )

Asp.net core 学习笔记 Secret 和 Data Protect Azure key-vault & Storage Account 第 2 篇

Azure 入门系列 (第五篇 Azure Storage)

这篇作为最新最完整的版本呗.

注意

由于本篇会涉及到 Key Vault, 所以建议先阅读上一篇 ASP.NET Core – User Secrets & Azure Key Vault

参考

Docs – Get started with the Data Protection APIs in ASP.NET Core

Docs – Configure ASP.NET Core Data Protection

What & Why Data Protection?

项目中许多地方需要用到加密技术, 比如 identity cookie 为了优化性能, 我们会把 permission 放入 user cookie 中.

但我们又要确保 user 不可以伪造 cookie 自行添加 permission, 这时就需要给 cookie value 做一个对称加密, 来防止 user 自行修改了.

Data Protection 是 ASP.NET Core 封装的对称加密功能, 底层用的是 AES 算法,

为什么需要封装呢?

因为对称加密需要一个密码 (AKA Key), 而这个 Key 最好要加密, 而且最好能 rotating (定期换新的), 同时不要和程序放在一块,

为了要解决这些安全隐患, 实现起来就变得麻烦多了, 所以 ASP.NET Core 自然是需要替我们封装这些繁琐的实现咯.

Data Protection Get Started

创建项目和 add package

dotnet new console -o DataProtectionConsole
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.AspNetCore.DataProtection

program.cs

setup service provider

var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection();
var serviceProvider = serviceCollection.BuildServiceProvider();

get service and create protector

var dataProtectionProvider = serviceProvider.GetRequiredService<IDataProtectionProvider>();
var dataProtector = dataProtectionProvider.CreateProtector(purpose: "Protect something");

purpose 类似一种分组管理方式. 不同的 purpose 互相是不能加密解密的哦

加密

var value = "something";
var protectedValue = dataProtector.Protect(value);
Console.WriteLine(protectedValue); // CfDJ8NB2rn7O5ZZMspmM-QDkaxhMqnhj--a6CHu-cb1griw4wTEKx_OtQIOF96avvIBJP2zG1ItO-4HTE1-bokCsxvLxRJbmk-E_Pwevlcc0LiWOw96dLfDOSQVnOmecc4DWHQ

加密的时候, 不需要提供任何 Key 或 密码, 因为 Data Protection 已经封装了 Key 的管理. (下面会详细讲解)

注: 每一次 protect 出来的乱码都是不一样的哦, 即使内容完全一样, 出来的乱发也不同, 但是最终都可以 unprotect 回去.

解密

var unProtectedValue = dataProtector.Unprotect(protectedValue);
Console.WriteLine(unProtectedValue); // something

Where Is the Key?

对称加密的 Key 在哪里呢? Windows 下的路径是

C:\Users\your-username\AppData\Local\ASP.NET\DataProtection-Keys\key-guid-id.xml

key-guid-id.xml 内容

<?xml version="1.0" encoding="utf-8"?>
<key id="7eae76d0-e5ce-4c96-b299-8cf900e46b18" version="1">
<creationDate>2023-03-08T09:12:40.0799225Z</creationDate>
<activationDate>2023-03-10T06:31:16.4098058Z</activationDate>
<expirationDate>2023-06-06T09:12:39.8251348Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=7.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=7.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+sBAAAARY09aZD0aECmsN7kb/ruLwAAAAACAAAAAAAQZgAAAAEAACAAAAAiAQnencq6XArJWM5MY+Ntm6LvsdDyMFV0FZe5Luf5ygAAAAAOgAAAAAIAACAAAAAoJ/CtKpmbBh7ppGI1X/FeQQxfTkDSWIJPsvGWkBeMnlABAABeFPs03di3J20sBT45vWk2duy7INBH293oOIlDQoURB6xnHDZtS4lIHZWxWNRi/ZLO0vFUXo/4Qwr7VPY3GHRpHyyd25CCzVul68G0o1kqJ1S4M2KMep07yq2Zv07e64JVi6BaXGIsr2arypw2fhaRCAMyLtZsuytihuXkEWtlF4Us6A3/o9C5x6NYApR+NUO9UvHRB5BptkRxlaPggrvRtadrtGl3ewD7RfQyqGP6H23TT/aCbyZudOb+V9aYg0K+G0ujaU8+ntMFpxFNCp6qmXnvbpjwDZCbLc5w3wiHO5BdBK/O8JvPSvzqxwj4GHPWdTSCupKs6lZR7CKgY09nPCqDW+xa2hBSHzPFv1y5yKFaLsKoydYtvWW38+mCsY5bkV7JNfdzN7IRe277C4Gsjmewj6pqVeSqQEgL9pGUxNSyLY/C5nE0W9KPbjpOowZAAAAAZBBvj0lXrenuKM588c9mMMqgU2uCSgeOKscy092nQYKSqj8APXlrW62r4OTbiriukFOp4abCw2ik3L7pWwjpKQ==</value>
</encryptedKey>
</encryptedSecret>
</descriptor>
</descriptor>
</key>

1. 它有 expiration date, 过期后 Data Protection 会创建一个新的 xml file. (默认时效是 90 days, 可以调, 最低是 7 days)

serviceCollection.AddDataProtection().SetDefaultKeyLifetime(TimeSpan.FromDays(7));

2. <!-- This key is encrypted with Windows DPAPI. -->

在 Windows 下, Key 默认会使用 Windows DPAPI 进行加密.

自定义 xml 存放路径和加密 Key 的方式

自定义 xml 存放路径

serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"));

通过 PersistKeysToFileSystem 可以指定 xml 存放的位置.

注: 一旦设置了 PersistKeysToFileSystem, Key 就不自动加密了.

我们可以设置 ProtectKeysWithDpapi 让它用回 default 的 Windows Dpapi 来加密 Key

serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"))
.ProtectKeysWithDpapi();

自定义加密 Key 方式

除了用 Windows Dpapi, 也可以用 certificate .pfx 来加密.

var rootPath = $@"{AppContext.BaseDirectory}..\..\..\";
var certificatePath = rootPath + "certificate.pfx"; if (!System.IO.File.Exists(certificatePath))
{
// create certificate
using var algorithm = RSA.Create(keySizeInBits: 2048);
var subject = new X500DistinguishedName($"CN=DataProtectionKey");
var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
var newCertificate = request.CreateSelfSigned(
notBefore: DateTimeOffset.UtcNow,
notAfter: DateTimeOffset.UtcNow.AddYears(2)
);
var pfxBytes = newCertificate.Export(X509ContentType.Pfx, "my password");
File.WriteAllBytes(certificatePath, pfxBytes);
} var rowData = File.ReadAllBytes(certificatePath);
var certificate = new X509Certificate2(rowData, "my password"); var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"))
.ProtectKeysWithCertificate(certificate);

Key Rotation

密码要定期换才安全.

serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(AppContext.BaseDirectory + @"..\..\..\DataProtection-Keys"))
.ProtectKeysWithCertificate(certificate)
.UnprotectKeysWithAnyCertificate(certificate1, certificate2);

通过 UnprotectKeysWithAnyCertificate 可以插入过期的 Key, 这些用于 unprotect 以前加密的 protected value. 而 ProtectKeysWithCertificate 则负责加密解密最新的 protected value.

保存 xml 到 Azure Storage

既然我们可以自定义存放路径, 那在 Production 时就应该进来将程序和密码分开保存.

Azure Storage 是一个不错的选择.

创建 Storage Account

az storage account create --name "stgdataprotection" --resource-group "Stooges" --location "Southeast Asia" --sku "Standard_GRS" --kind "StorageV2"

get storage account connection string

az storage account show-connection-string --name "stgdataprotection" --resource-group "Stooges"

connection string 很长, 权限很大, 属于 secret level 不要外泄哦.

创建 container

az storage container create --name "security" --connection-string "your connection string"

create blob and upload key.xml

az storage blob upload --file "C:\keatkeat\my-projects\asp.net core\7.0\DataProtection\DataProtectionConsole\key.xml" --container-name "security" --name "data-protection\key.xml" --connection-string "your connection string"

这个 key.xml 是一个框来的. 和本地不同, 往后每一次新的 Key, Data Protection 不会自动创建新的 xml, 而是不断的往这个 key.xml 里面添加新的 Key

key.xml 长这样.

<?xml version="1.0" encoding="utf-8"?>
<repository></repository>

后来长这样

<?xml version="1.0" encoding="utf-8"?>
<repository>
<key id="xxx">...</key>
<key id="yyy">...</key>
<key id="zzz">...</key>
</repository>

Connect Data Protection to Azure Storage

add package

Azure.Extensions.AspNetCore.DataProtection.Blobs

program.cs

serviceCollection.AddDataProtection()
.PersistKeysToAzureBlobStorage(
connectionString: "your connection string",
containerName: "security",
blobName: "data-protection/key.xml"
)
.ProtectKeysWithDpapi();

提醒: connection string 是敏感信息. 记得使用 Users Secret 来管理.

使用 Azure Key Vault Key 作为 Data Protection 加密的 Key

用来加密 Data Protection Key 的 certificate 也是敏感信息, 虽然放在 store 里面已经有一定的安全了, 但是我们也可以选择使用 Azure Key Vault 服务来管理 Key.

创建 Key Vault

az keyvault create --name "StgDataProtection-KV" --resource-group "Stooges" --location "Southeast Asia"

如果有做 Secret 了也可以公用, 不需要做 2 个哦.

创建 Key

az keyvault key create --name "DataProtectionKey" --vault-name "StgDataProtection-KV" --kty RSA

让 VM 有权限访问 Key

az keyvault set-policy --name "StgDataProtection-KV" --object-id "your vm object id" --key-permissions all

为了方便我直接 allow 所有 permission, 具体 Data Protection 需要用到多少我也不清楚, az keyvault set-policy --help 可以查看所有 permission

add package

dotnet add package Azure.Extensions.AspNetCore.DataProtection.Keys
dotnet add package Azure.Identity

program.cs

serviceCollection.AddDataProtection()
.PersistKeysToAzureBlobStorage(
connectionString: "connection string",
containerName: "security",
blobName: "data-protection/key.xml"
)
.ProtectKeysWithAzureKeyVault(
new Uri("https://stgdataprotection-kv.vault.azure.net/keys/DataProtectionKey/411111aaf3174ec4b5ac4111afd9add8"),
new DefaultAzureCredential()
);

通过 CLI 可以获取到 Key 的 URI

az keyvault key show --name "DataProtectionKey" --vault-name "StgDataProtection-KV" --query 'key.kid' -o tsv

另外, 非 Azure VM 也是可以用 Client App 的方式访问 Key, 上一篇教过了, 这篇就不再给例子了.

Key Rotation

Azure Key 支持 ratation. ProtectKeysWithAzureKeyVault 内部会替我们处理好 UnprotectKeysWithAnyCertificate, 我们直接开启 rotation 就可以了.

rotation.json

{
"lifetimeActions": [
{
"action": "Rotate",
"timeAfterCreate": "P7D",
"timeBeforeExpiry": null
}
]
}

Azure CLI

az keyvault key rotation-policy update --vault-name "StgDataProtection-KV" --name "DataProtectionKey" --value "C:\Users\keatk\Desktop\rotation.json"

注: --value 可以放 .json 路径也可以直接放 json value. 如果放路劲的话, 确保一定不要有空格, 不然它会以为你放的是 value, 那就解析错误了.

查看

az keyvault key rotation-policy show --vault-name "StgDataProtection-KV" --name "DataProtectionKey"  

set expiryTime

by default Key 是永远都可以用的, 如果你希望让它有过期的概念可以这样 set

{
"lifetimeActions": [
{
"action": "Rotate",
"timeAfterCreate": "P7D",
"timeBeforeExpiry": null
},
{
"action": "Notify",
"timeAfterCreate": null,
"timeBeforeExpiry": "P30D"
}
],
"attributes": {
"expiryTime": "P2Y"
}
}

注意哦, 一旦过期了就表示无法解密之前加密的 value 了哦. 通常是不需要让它过期的. 因为我们做了 rotation 已经很安全了.

Key 和 Key 傻傻分不清

Data Protection 的 Key (简称 DP Key) 是用来对数据做对称加密的. 这个 DP Key 要定期换, 有 rotation 概念, Data Protection 会负责.

DP Key 本身也需要被加密, 通常使用非对称加密. 所有又需要一个 certificate. 而这个 certificate 也需要有 rotation 概念, 只要是密码都需要定期换才安全.

我们可以用 Azure Key Vault 的 Key (简称 AZ Key) 作为这个 certification.

所以 AZ Key 用非对称加密来加密 DP Key, 而 DP Key 用对称加密来加密 value.

它们两个 Key 都有 rotation 概念.

Azure Key Vault 小迷糊, 解答

参考: Azure Key Vault keys, secrets and certificates overview

Azure Key Vault 有 3 个主要 services. 分别是

保存 Secret, 保存 Key, 保存 Certification

Secret 通常用于保存各做小密码

Key 通常用于保存非对称加密/签名 的 Key

Certificate 在 Key 的基础上做了一些自动 renew 的功能, 本质上和 Key 的用途是一样的.

Azure VM connect to Key Vault 是不需要任何密码在本机上的 (它 build-in 了 connect 的方式, 所以这个方案可以算是最安全的)

非 Azure VM connect to Key Vault 则需要先 connect to Client App, 而 connect Client App 最少需要一个 certificate 在本机 store 里. 所以比起 Azure VM 多了一点点点的隐患.

程序其余要地方, 要用到小密码, Key, Certificate 的时候, 统统使用 Key Vault 就对了.

具体例子

Secret 看这篇 ASP.NET Core – User Secrets & Azure Key Vault

Key 看本篇 ASP.NET Core – Data Protection & Azure Storage + Azure Key Vault

Certificate 看这篇 Azure – Key Vault Certificate

ASP.NET Core – Data Protection & Azure Storage + Azure Key Vault的更多相关文章

  1. 基于ASP.NET Core Data Protection生成验证token

    ASP.NET Core Data Protection 不仅提供了非对称加密能力,而且提供了灵活的秘钥存储方式以及一致的加解密接口(Protect与Unprotect).Session中用到了它,C ...

  2. 集群环境下,你不得不注意的ASP.NET Core Data Protection 机制

    引言 最近线上环境遇到一个问题,就是ASP.NET Core Web应用在单个容器使用正常,扩展多个容器无法访问的问题.查看容器日志,发现以下异常: System.Security.Cryptogra ...

  3. ASP.NET Core 2.0中的Azure Blob存储

    问题 如何在ASP.NET Core中使用Azure Blob存储 解 创建一个类库并添加NuGet包 - WindowsAzure.Storage 添加一个类来封装设置, publicclass A ...

  4. 【Azure DevOps系列】使ASP.NET Core应用程序托管到Azure Web App Service

    使用Azure DevOps Project设置ASP.NET项目 我们需要先在Azure面板中创建一个Azure WebApp服务,此处步骤我将省略,然后点击部署中心如下图所示: 此处我选择的是Az ...

  5. 消除 ASP.NET Core 告警 "No XML encryptor configured. Key may be persisted to storage in unencrypted form"

    在 ASP.NET Core 中如果在 DataProtection 中使用了 PersistKeysToFileSystem 或 PersistKeysToFileSystem services.A ...

  6. ASP.NET Core 数据保护(Data Protection 集群场景)【下】

    前言 接[中篇],在有一些场景下,我们需要对 ASP.NET Core 的加密方法进行扩展,来适应我们的需求,这个时候就需要使用到了一些 Core 提供的高级的功能. 本文还列举了在集群场景下,有时候 ...

  7. ASP.NET Core 数据保护(Data Protection)【上】

    前言 上一篇博客记录了如何在 Kestrel 中使用 HTTPS(SSL), 也是我们目前项目中实际使用到的. 数据安全往往是开发人员很容易忽略的一个部分,包括我自己.近两年业内也出现了很多因为安全问 ...

  8. ASP.NET Core 数据保护(Data Protection)【中】

    前言 上篇主要是对 ASP.NET Core 的 Data Protection 做了一个简单的介绍,本篇主要是介绍一下API及使用方法. API 接口 ASP.NET Core Data Prote ...

  9. 翻译 - ASP.NET Core 托管和部署 - 在 Linux 上使用 Nginx 托管 ASP.NET Core 网站

    翻译自 https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-5.0 本文介 ...

  10. Asp.net core 学习笔记 ( identity server 4 JWT Part )

    更新 : id4 使用这个 DbContext 哦 dotnet ef migrations add identity-server-init --context PersistedGrantDbCo ...

随机推荐

  1. 面试官:Dubbo一次RPC请求经历哪些环节?

    大家好,我是三友~~ 今天继续探秘系列,扒一扒一次RPC请求在Dubbo中经历的核心流程. 本文是基于Dubbo3.x版本进行讲解 一个简单的Demo 这里还是老样子,为了保证文章的完整性和连贯性,方 ...

  2. django信号中的条件判断不符合时如何提示错误并返回

    在Django中,如果你在信号(Signal)处理函数中需要进行条件判断,如果条件不符合,你可以触发一个异常,并在视图或其他地方捕获这个异常,然后返回相应的错误提示. 以下是一个简单的例子,演示如何在 ...

  3. C#枚举高级应用

    文章开头先看一道题: 在设计某小型项目的数据库(假设用的是 MySQL)时,如果给用户表(User)添加一个字段(Roles)用来存储用户的角色,你会给这个字段设置什么类型?提示:要考虑到角色在后端开 ...

  4. LLM并行训练6-激活优化

    前置知识 Activation 激活指的是一些在fp时计算得到的临时tensor, 会用于bp时的计算. 如果能在fp计算后把临时tensor缓存下来就可以加速bp, 缺点在于某些激活会占用大量显存. ...

  5. 玄机-第二章日志分析-mysql应急响应

    目录 前言 简介 应急开始 准备工作 日志分析 步骤 1 步骤 2 步骤 3 步骤 4 总结 补充mysql中的/var/log/mysql/erro.log 记录上传文件信息的原因 前言 这里应急需 ...

  6. Java 基于Hutool实现DES加解密

    POM.XML配置 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...

  7. 从DDPM到DDIM(四) 预测噪声与后处理

    从DDPM到DDIM(四) 预测噪声与后处理 前情回顾 下图展示了DDPM的双向马尔可夫模型. 训练目标.最大化证据下界等价于最小化以下损失函数: \[\boldsymbol{\theta}^*=\u ...

  8. 对比python学julia(第三章:游戏编程)--(第二节)公主迎圣诞(4)

    4.  碰撞检测 .得分及生命 在第 4 个阶段,利用GameZero的碰撞检测功能,使公主角色能够接到雪花 .礼物或剪刀. 在"sdgz"项目目录中 ,把 version3.jl ...

  9. 国产操作系统 “银河麒麟操作系统V10” 试用失败历程

    面对外国的科技封锁,具有自主产权的国产软件已经变得迫在眉睫了,几天前在新闻上看到国产的操作"银河麒麟操作系统V10"已经发布,于是抱着尝鲜的心态想着去试着用用.虽然都是基于linu ...

  10. PyTorch的TensorBoard用法示例

    原文: https://www.emperinter.info/2020/07/30/tensorboard-in-pytorch/ 缘由 自己上次安装好PyTorch以及训练了一下官方的数据,今天看 ...