前言

以前就写过很多篇了

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. Java Redis多限流

    Java Redis多限流 在Java中实现Redis多限流通常涉及使用Redis的某些特性,如INCR.EXPIRE.Lua脚本或者更高级的Redis数据结构如Redis Bitmaps.Redis ...

  2. ModuleNotFoundError: No module named 'import_export'

    当你遇到 "ModuleNotFoundError: No module named 'import_export'" 错误时,这表示你的 Python 脚本或应用程序试图导入名为 ...

  3. 吐血整理如何在Google Earth Engine上写循环 五个代码实例详细拆解

    在这里同步一篇本人的原创文章.原文发布于2023年发布在知乎专栏,转移过来时略有修改.全文共计3万余字,希望帮助到GEE小白快速进阶. 引言 这篇文章主要解答GEE中.map()和.iterate() ...

  4. CF709B 题解

    洛谷链接&CF 链接 本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读. 题目简述 给定 \(N\) 个点,在一条数轴上,位置为 \(x_1,-,x_n\),你的位置为 \(p\) ...

  5. Linux 文本文件编辑相关命令简介【Linux 常用命令系列二】

    〇.前言 本文介绍了如何通过 vim 命令,对文本文件进行打开.编辑.保存等相关操作,并通过简单的示例演示了常用用法. 一.关于文本文件的操作 1.1 打开,查看(cat).编辑(vim) 打开文本文 ...

  6. 从DDPM到DDIM(三) DDPM的训练与推理

    从DDPM到DDIM(三) DDPM的训练与推理 前情回顾 首先还是回顾一下之前讨论的成果. 扩散模型的结构和各个概率模型的意义.下图展示了DDPM的双向马尔可夫模型. 其中\(\mathbf{x}_ ...

  7. 【Project】原生JavaWeb工程 02 登陆业务的流程(第一阶段样例)

    1.对用户信息的描述 首先用户有一些基本信息: 最简单的: 用户名称 + 用户密码 然后是用户状态,例如封号,注销,停用,等等 用户名称 + 用户密码 + 账号状态 接着为了防止脚本攻击,又产生了图形 ...

  8. 【Shiro】08 SpringBoot整合

    需要的依赖的坐标: <!-- Shiro依赖 --> <dependency> <groupId>com.github.theborakompanioni</ ...

  9. 2024-08-03:用go语言,给定一个从 0 开始的字符串数组 `words`, 我们定义一个名为 `isPrefixAndSuffix` 的布尔函数,该函数接受两个字符串参数 `str1` 和

    2024-08-03:用go语言,给定一个从 0 开始的字符串数组 words, 我们定义一个名为 isPrefixAndSuffix 的布尔函数,该函数接受两个字符串参数 str1 和 str2. ...

  10. 【节选 转载】人形机器人Optimus擎天柱技术解析

    参考原文: https://www.sohu.com/a/589454391_383324?scm=9010.8000.0.0.1265 可以利用动作捕捉"学习"人类动作,依靠视觉 ...