使用者-取消有效负载

取消保护已吊销在 ASP.NET Core 中键的有效负载

ASP.NET Core 数据保护 Api 主要不用于机密负载的无限期持久性。 等其他技术Windows CNG DPAPIAzure Rights Management更适合的无限期存储方案,并且必须相应地强密钥管理功能。 也就是说,无需进行任何开发人员禁止使用 ASP.NET Core 数据保护 Api 进行长期保护的机密数据。 永远不会删除密钥从密钥环,因此IDataProtector.Unprotect始终可以恢复现有的负载,只要键是可用且有效。

但是,则会产生问题而开发人员尝试取消保护数据作为保护已撤消的密钥,IDataProtector.Unprotect这种情况下将引发异常。 这可能是相当不错的短期或临时负载 (如身份验证令牌),如轻松可以通过在系统中,重新创建这些类型的有效负载和最坏的情形站点访问者可能需要重新登录。 但对于持久化有效负载,让Unprotectthrow 可能导致不可接受的数据丢失。

IPersistedDataProtector

若要支持允许有效负载为即使在面临吊销密钥时未受保护的方案,数据保护系统包含IPersistedDataProtector类型。 若要获取的实例IPersistedDataProtector,只需获取的实例IDataProtector在正常情况下,请尝试强制转换IDataProtectorIPersistedDataProtector

备注

不是所有IDataProtector实例可以强制转换为IPersistedDataProtector 开发人员应使用C#运算符或类似引起无效强制转换,以避免运行时异常和它们应准备好适当地处理失败的情况。

IPersistedDataProtector 公开了以下 API 图面:

DangerousUnprotect(byte[] protectedData, bool ignoreRevocationErrors,
     out bool requiresMigration, out bool wasRevoked) : byte[]

此 API 采用受保护的负载 (作为字节数组),并返回未受保护的有效负载。 没有任何基于字符串的重载。 这两个输出参数如下所示。

  • requiresMigration: 将设置为 true,如果用于保护此有效负载的密钥不再是活动的默认密钥,例如,用来保护此有效负载的关键是旧密钥滚动操作以来已执行的位置。 调用方可能想要考虑重新保护的有效负载,具体取决于其业务需求。

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

警告

传递时应格外小心ignoreRevocationErrors: trueDangerousUnprotect方法。 调用此方法后的,如果wasRevoked值为 true,然后用来保护此有效负载的密钥已被吊销,并且有效负载的真实性应将其视为可疑。 在这种情况下,仅继续操作上不受保护的有效负载有一定程度上,它是可信的例如保证单独来自安全的数据库,而不是由不受信任的 web 客户端发送。

using System;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection()
            // point at a specific folder and use DPAPI to encrypt keys
            .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
            .ProtectKeysWithDpapi();
        var services = serviceCollection.BuildServiceProvider();

        // get a protector and perform a protect operation
        var protector = services.GetDataProtector("Sample.DangerousUnprotect");
        Console.Write("Input: ");
        byte[] input = Encoding.UTF8.GetBytes(Console.ReadLine());
        var protectedData = protector.Protect(input);
        Console.WriteLine($"Protected payload: {Convert.ToBase64String(protectedData)}");

        // demonstrate that the payload round-trips properly
        var roundTripped = protector.Unprotect(protectedData);
        Console.WriteLine($"Round-tripped payload: {Encoding.UTF8.GetString(roundTripped)}");

        // get a reference to the key manager and revoke all keys in the key ring
        var keyManager = services.GetService<IKeyManager>();
        Console.WriteLine("Revoking all keys in the key ring...");
        keyManager.RevokeAllKeys(DateTimeOffset.Now, "Sample revocation.");

        // try calling Protect - this should throw
        Console.WriteLine("Calling Unprotect...");
        try
        {
            var unprotectedPayload = protector.Unprotect(protectedData);
            Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
        }

        // try calling DangerousUnprotect
        Console.WriteLine("Calling DangerousUnprotect...");
        try
        {
            IPersistedDataProtector persistedProtector = protector as IPersistedDataProtector;
            if (persistedProtector == null)
            {
                throw new Exception("Can't call DangerousUnprotect.");
            }

            bool requiresMigration, wasRevoked;
            var unprotectedPayload = persistedProtector.DangerousUnprotect(
                protectedData: protectedData,
                ignoreRevocationErrors: true,
                requiresMigration: out requiresMigration,
                wasRevoked: out wasRevoked);
            Console.WriteLine($"Unprotected payload: {Encoding.UTF8.GetString(unprotectedPayload)}");
            Console.WriteLine($"Requires migration = {requiresMigration}, was revoked = {wasRevoked}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
        }
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Input: Hello!
 * Protected payload: CfDJ8LHIzUCX1ZVBn2BZ...
 * Round-tripped payload: Hello!
 * Revoking all keys in the key ring...
 * Calling Unprotect...
 * CryptographicException: The key {...} has been revoked.
 * Calling DangerousUnprotect...
 * Unprotected payload: Hello!
 * Requires migration = True, was revoked = True
 */
目录