生产环境必须显式配置adddataprotection,否则多实例下因密钥不共享、应用名冲突、轮换失效导致unprotect()抛cryptographicexception;需持久化密钥、统一setapplicationname、谨慎设置purpose字符串。

生产环境不配 AddDataProtection 链式调用,直接跑在多实例或容器里会解密失败——密钥不共享、应用名冲突、密钥轮换后旧数据全失效,不是“加密没生效”,是系统根本没按你预期初始化。
为什么 Unprotect() 总抛 CryptographicException
这不是代码写错了,而是密钥上下文断裂了。常见原因有三个:
- 应用重启后密钥丢失(默认临时存储,路径在
%LOCALAPPDATA%\ASP.NET\DataProtection-Keys或~/.aspnet/DataProtection-Keys) - 多个服务实例用了同一份密钥目录但没设
SetApplicationName("MyApp-Prod"),导致互相污染 - 旧密钥被自动轮换(默认 90 天),而你存的加密字符串没做迁移处理,
Unprotect()找不到匹配密钥
验证方法:在相同进程里连续调用 Protect() → Unprotect() 成功,但换个进程或重启后失败,基本锁定密钥持久化问题。
跨服务器部署必须显式配置密钥存储
别信“默认就支持集群”——ASP.NET Core 默认密钥只存本地磁盘,且无共享机制。选哪种方式取决于你的基础设施:
- Linux 容器/K8s:用
PersistKeysToFileSystem(new DirectoryInfo("/shared/keys")),挂载同一 NFS 或 hostPath 卷,确保所有 Pod 有读写权限 - Azure 环境:装
Microsoft.AspNetCore.DataProtection.AzureKeyVault,用ProtectKeysWithAzureKeyVault()把密钥材料托管出去 - Windows Server 场景:可选
PersistKeysToRegistry(),但注意注册表权限必须给运行账户(如IIS AppPool\DefaultAppPool)
关键点:PersistKeysToFileSystem 不等于“能用”,目录权限不对照样报 UnauthorizedAccessException;K8s 中若用 emptyDir,等同于没配。
CreateProtector("Auth.Ticket") 的 purpose 字符串不是随便起的
purpose 是隔离边界,不是备注。它参与密钥派生计算,不同字符串 = 不同密钥流,无法互通:
- 同一个
IDataProtectionProvider实例,CreateProtector("Email.Token")和CreateProtector("Email.ResetToken")产出的密文互不可逆 - 微服务间若要共享令牌(比如 Auth 服务签发、API 服务校验),必须共用完全一致的 purpose 字符串,且所有服务使用同一套密钥源
- 升级时改 purpose(如从
"v1"改成"v2")是安全演进手段,但旧数据需单独迁移,不能指望新 protector 解开老密文
别把 purpose 当日志字段塞业务 ID 或用户 ID——它会被拼进密钥派生路径,高频变更会导致密钥爆炸式增长,拖慢性能。
非 DI 场景下用 DataProtectionProvider.Create() 要补 SetApplicationName
在控制台程序、Worker Service 或遗留系统集成时,绕过 DI 直接 new DataProtectionProvider 很方便,但默认行为危险:
- 不设
SetApplicationName:所有用同一密钥目录的应用都能互相解密,毫无隔离 - 不加密密钥文件:
DataProtectionProvider.Create(dir)默认不调用ProtectKeysWithDpapi()或ProtectKeysWithCertificate(),密钥 XML 明文落盘 - 开发机和 CI 环境共用密钥目录?只要没设唯一
SetApplicationName,测试数据可能意外解密上线密文
正确写法:DataProtectionProvider.Create(dir, builder => builder.SetApplicationName("MyWorker-Dev")) —— 回调里必须显式设置,不能依赖构造函数默认值。
最易被忽略的一点:密钥目录的文件系统权限和 SetApplicationName 必须同步管理。改了应用名却不清理旧密钥目录,系统会同时加载新旧两套密钥环,既浪费内存,又让密钥轮换逻辑变得不可预测。











