取消保护已吊销在 ASP.NET Core中键的有效负载Unprotect payloads whose keys have been revoked in ASP.NET Core

    ASP.NET Core 数据保护 Api 主要不用于机密负载的无限期持久性。其他技术(如和Azure Rights Management )更适用于无限存储的情况,并且具有相应的强大密钥管理功能。也就是说,无需进行任何开发人员禁止使用 ASP.NET Core 数据保护 Api 进行长期保护的机密数据。密钥永远不会从密钥环中删除,因此,只要密钥可用且有效, 就可以始终恢复现有有效负载。

    但是,当开发人员尝试取消保护已被吊销密钥保护的数据时,会出现问题,因为在这种情况下 IDataProtector.Unprotect 会引发异常。这对于短期或暂时性负载(例如身份验证令牌)可能很合适,因为系统可以轻松地重新创建这种类型的负载,并且在最糟糕的情况下,站点访问者可能需要再次登录。但对于持久化有效负载,具有 Unprotect 引发可能导致数据丢失不可接受。

    为支持允许负载不受保护的方案(即使在面对吊销密钥的情况下),数据保护系统包含一个 IPersistedDataProtector 类型。若要获取 IPersistedDataProtector的实例,只需以正常方式获取 IDataProtector 的实例,然后尝试将 IDataProtector 强制转换为 IPersistedDataProtector

    并非所有 IDataProtector 实例都可以转换为 IPersistedDataProtector开发人员应使用C# as 运算符或类似的方法来避免由无效强制转换导致的运行时异常,并应准备适当地处理故障情况。

    IPersistedDataProtector 公开以下 API 图面:

    此 API 获取受保护的负载(作为字节数组)并返回未受保护的负载。没有基于字符串的重载。这两个 out 参数如下所示。

    • wasRevoked:如果用于保护此负载的密钥已被吊销,则设置为 true。

    警告

    ignoreRevocationErrors: true 传递到 DangerousUnprotect 方法时要格外小心。如果在调用此方法后,wasRevoked 值为 true,则将吊销用于保护此负载的密钥,并且应将有效负载的真实性视为可疑。在这种情况下,如果有一些单独的保证是可信的,例如它来自安全的数据库,而不是由不受信任的 web 客户端发送,则只能继续对未受保护的有效负载进行操作。

    1. using System;
    2. using System.IO;
    3. using System.Text;
    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. // get a protector and perform a protect operation
    18. var protector = services.GetDataProtector("Sample.DangerousUnprotect");
    19. Console.Write("Input: ");
    20. byte[] input = Encoding.UTF8.GetBytes(Console.ReadLine());
    21. var protectedData = protector.Protect(input);
    22. Console.WriteLine($"Protected payload: {Convert.ToBase64String(protectedData)}");
    23. var roundTripped = protector.Unprotect(protectedData);
    24. Console.WriteLine($"Round-tripped payload: {Encoding.UTF8.GetString(roundTripped)}");
    25. // get a reference to the key manager and revoke all keys in the key ring
    26. var keyManager = services.GetService<IKeyManager>();
    27. Console.WriteLine("Revoking all keys in the key ring...");
    28. keyManager.RevokeAllKeys(DateTimeOffset.Now, "Sample revocation.");
    29. // try calling Protect - this should throw
    30. Console.WriteLine("Calling Unprotect...");
    31. try
    32. {
    33. var unprotectedPayload = protector.Unprotect(protectedData);
    34. Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
    35. }
    36. catch (Exception ex)
    37. {
    38. Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
    39. }
    40. // try calling DangerousUnprotect
    41. Console.WriteLine("Calling DangerousUnprotect...");
    42. try
    43. {
    44. if (persistedProtector == null)
    45. {
    46. throw new Exception("Can't call DangerousUnprotect.");
    47. }
    48. bool requiresMigration, wasRevoked;
    49. var unprotectedPayload = persistedProtector.DangerousUnprotect(
    50. protectedData: protectedData,
    51. ignoreRevocationErrors: true,
    52. requiresMigration: out requiresMigration,
    53. wasRevoked: out wasRevoked);
    54. Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
    55. Console.WriteLine($"Requires migration = {requiresMigration}, was revoked = {wasRevoked}");
    56. }
    57. catch (Exception ex)
    58. {
    59. Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
    60. }
    61. }
    62. }
    63. /*
    64. * SAMPLE OUTPUT
    65. *
    66. * Input: Hello!
    67. * Protected payload: CfDJ8LHIzUCX1ZVBn2BZ...
    68. * Round-tripped payload: Hello!
    69. * Revoking all keys in the key ring...
    70. * Calling Unprotect...
    71. * CryptographicException: The key {...} has been revoked.
    72. * Calling DangerousUnprotect...
    73. * Unprotected payload: Hello!
    74. */