ASP.NET Core中的密钥管理Key management in ASP.NET Core

本文内容

数据保护系统自动管理用于保护和取消保护负载的主密钥的生存期。每个密钥可以有以下四个阶段之一:

  • 已创建-密钥在密钥环中存在,但尚未激活。在经过足够的时间之后,密钥有机会传播到使用此密钥环的所有计算机之前,不应将该密钥用于新的保护操作。

  • 活动-密钥在密钥环中存在,并且应该用于所有新的保护操作。

  • 已过期-密钥已运行其自然生存期,不应再用于新的保护操作。

  • 已吊销-密钥已泄露,不能用于新的保护操作。

已创建、活动和过期的密钥均可用于取消保护传入有效负载。默认情况下,吊销的密钥不能用于取消保护有效负载,但应用程序开发人员可以在必要时重写此行为

警告

开发人员可能会尝试从密钥环中删除密钥(例如,通过从文件系统中删除相应的文件)。此时,由该密钥保护的所有数据都将永久 undecipherable,而且不会有任何紧急替代,就像吊销密钥一样。删除密钥是真正的破坏性行为,因此数据保护系统不会公开用于执行此操作的第一类 API。

默认键选择Default key selection

当数据保护系统从后备存储库读取密钥环时,它将尝试从密钥环中查找 "默认" 密钥。默认密钥用于新的保护操作。

一般试探法是数据保护系统选择最新激活日期为默认密钥的密钥。(对于服务器到服务器时钟的偏差,有一个奶油的小因素。)如果密钥已过期或已被吊销,并且如果应用程序未禁用自动密钥生成,则会生成一个新的密钥,并按下面的密钥过期和滚动策略立即激活。

数据保护系统立即生成新密钥,而不是回退到不同的密钥,这是因为新密钥生成应被视为在新密钥之前激活的所有密钥的隐式过期。一般情况下,可能已使用不同于旧密钥的算法或静态加密机制配置了新密钥,并且系统应更倾向于回退当前配置。

出现异常。如果应用程序开发人员禁用了自动密钥生成,则数据保护系统必须选择某些项作为默认密钥。在此回退方案中,系统会选择具有最新激活日期的非吊销密钥,并为有时间传播到群集中的其他计算机的密钥提供首选项。回退系统可能最终会选择过期的默认密钥。回退系统永远不会选择吊销的密钥作为默认密钥,如果密钥环为空或每个密钥已被吊销,则系统会在初始化时产生错误。

密钥过期和滚动Key expiration and rolling

创建密钥后,会自动向其提供一个 {now + 2 days} 的激活日期和过期日期 {now + 90 天}。激活前的2天延迟提供通过系统传播的关键时间。也就是说,它允许指向后备存储的其他应用程序在其下一次自动刷新期间观察密钥,从而最大限度地提高密钥环变为活动状态的所有应用程序的可能性。

如果默认密钥将在2天内过期,且密钥环还没有在默认密钥过期后处于活动状态的密钥,则数据保护系统会自动将新密钥保存到密钥环。此新密钥的激活日期为 {默认密钥的过期日期},到期日期为 {now + 90 天}。这使系统能够定期自动滚动更新密钥,而不会中断服务。

在某些情况下,将使用立即激活创建密钥。例如,当应用程序未运行一段时间并且密钥环中的所有密钥都已过期时。发生这种情况时,会为该密钥提供一个 {now} 的激活日期,而不会出现常规的2天激活延迟。

默认密钥生存期为90天,但可以配置这种生存期,如以下示例中所示。

  1. services.AddDataProtection()
  2. // use 14-day lifetime instead of 90-day lifetime
  3. .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

管理员还可以更改默认系统范围,尽管对 SetDefaultKeyLifetime 的显式调用将覆盖任何系统范围内的策略。默认密钥生存期不能短于7天。

自动密钥环刷新Automatic key ring refresh

当数据保护系统初始化时,它将从底层存储库读取密钥环,并将其缓存在内存中。此缓存允许在不命中后备存储的情况下继续保护和取消保护操作。系统将每隔24小时自动检查一次后备存储的更改或当前的默认密钥过期时,以先达到的时间为准。

警告

开发人员极少需要直接使用密钥管理 Api。数据保护系统将执行上述自动密钥管理。

数据保护系统公开了可用于检查和更改密钥环的接口 IKeyManager提供 IDataProtectionProvider 实例的 DI 系统还可以为你的消耗提供 IKeyManager 的实例。或者,可以直接从 IServiceProvider 拉取 IKeyManager,如以下示例中所示。

修改密钥环(显式创建新密钥或执行吊销)的任何操作都会使内存中缓存失效。ProtectUnprotect 的下一次调用将导致数据保护系统重新读取密钥环并重新创建缓存。

下面的示例演示如何使用 IKeyManager 接口来检查和操作密钥环,包括吊销现有密钥并手动生成新密钥。

  1. using System;
  2. using System.IO;
  3. using System.Threading;
  4. using Microsoft.AspNetCore.DataProtection;
  5. using Microsoft.AspNetCore.DataProtection.KeyManagement;
  6. using Microsoft.Extensions.DependencyInjection;
  7. public class Program
  8. {
  9. public static void Main(string[] args)
  10. {
  11. var serviceCollection = new ServiceCollection();
  12. serviceCollection.AddDataProtection()
  13. // point at a specific folder and use DPAPI to encrypt keys
  14. .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
  15. .ProtectKeysWithDpapi();
  16. var services = serviceCollection.BuildServiceProvider();
  17. // perform a protect operation to force the system to put at least
  18. // one key in the key ring
  19. services.GetDataProtector("Sample.KeyManager.v1").Protect("payload");
  20. Console.WriteLine("Performed a protect operation.");
  21. Thread.Sleep(2000);
  22. // get a reference to the key manager
  23. var keyManager = services.GetService<IKeyManager>();
  24. // list all keys in the key ring
  25. var allKeys = keyManager.GetAllKeys();
  26. Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
  27. foreach (var key in allKeys)
  28. {
  29. Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
  30. }
  31. // revoke all keys in the key ring
  32. keyManager.RevokeAllKeys(DateTimeOffset.Now, reason: "Revocation reason here.");
  33. Console.WriteLine("Revoked all existing keys.");
  34. // add a new key to the key ring with immediate activation and a 1-month expiration
  35. keyManager.CreateNewKey(
  36. activationDate: DateTimeOffset.Now,
  37. expirationDate: DateTimeOffset.Now.AddMonths(1));
  38. Console.WriteLine("Added a new key.");
  39. // list all keys in the key ring
  40. allKeys = keyManager.GetAllKeys();
  41. Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
  42. foreach (var key in allKeys)
  43. {
  44. Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
  45. }
  46. }
  47. }
  48. /*
  49. * SAMPLE OUTPUT
  50. *
  51. * Performed a protect operation.
  52. * The key ring contains 1 key(s).
  53. * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = False
  54. * Revoked all existing keys.
  55. * Added a new key.
  56. * The key ring contains 2 key(s).
  57. * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = True
  58. * Key {2266fc40-e2fb-48c6-8ce2-5fde6b1493f7}: Created = 2015-03-18 22:20:51Z, IsRevoked = False
  59. */

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

密钥存储Key storage

数据保护系统具有试探法,它尝试自动推导适当的密钥存储位置和静态加密机制。应用程序开发人员也可以配置密钥持久性机制。以下文档介绍了这些机制的内置实现: