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

数据保护系统自动管理主密钥用于保护和取消保护负载的生存期。The data protection system automatically manages the lifetime of master keys used to protect and unprotect payloads. 每个键可以存在于四个阶段之一:Each key can exist in one of four stages:

  • 此键创建的密钥环中存在但尚未激活。Created - the key exists in the key ring but has not yet been activated. 密钥不应用于新的保护操作,直到有足够的时间已过该密钥没有机会将传播到所有计算机正在使用此密钥环。The key shouldn't be used for new Protect operations until sufficient time has elapsed that the key has had a chance to propagate to all machines that are consuming this key ring.

  • 活动-密钥密钥环中存在,应使用对所有新的保护操作。Active - the key exists in the key ring and should be used for all new Protect operations.

  • 过期的密钥已经运行了其自然的生存期内,不再应该用于新的保护操作。Expired - the key has run its natural lifetime and should no longer be used for new Protect operations.

  • 撤消-密钥遭到破坏,并且不能对新的保护操作。Revoked - the key is compromised and must not be used for new Protect operations.

创建、 活动和已过期的密钥可能都可用来取消保护传入的负载。Created, active, and expired keys may all be used to unprotect incoming payloads. 默认情况下已吊销的密钥不可能用于取消保护有效负载,但应用程序开发人员可以重写此行为如有必要。Revoked keys by default may not be used to unprotect payloads, but the application developer can override this behavior if necessary.

警告

开发人员可能想要删除密钥从密钥环 (例如,通过从文件系统中删除相应的文件)。The developer might be tempted to delete a key from the key ring (e.g., by deleting the corresponding file from the file system). 此时,密钥保护的所有数据都都将永久无法被破解,并且都没有不紧急的重写一样也使用已吊销的密钥。At that point, all data protected by the key is permanently undecipherable, and there's no emergency override like there's with revoked keys. 正在删除密钥是真正破坏性的行为,并因此数据保护系统公开没有第一类 API 执行此操作。Deleting a key is truly destructive behavior, and consequently the data protection system exposes no first-class API for performing this operation.

默认密钥选择Default key selection

当数据保护系统从后备存储库读取密钥环时,它将尝试查找从密钥环中的"默认"密钥。When the data protection system reads the key ring from the backing repository, it will attempt to locate a "default" key from the key ring. 默认密钥用于保护的新操作。The default key is used for new Protect operations.

常规的启发式方法是,数据保护系统会选择最新的激活日期与默认密钥的密钥。The general heuristic is that the data protection system chooses the key with the most recent activation date as the default key. (没有小种附加因素,以允许服务器到服务器时钟偏差。)如果密钥已过期或吊销,并且如果应用程序禁用自动密钥生成,则将立即激活每个与生成新密钥密钥过期和滚动下面的策略。(There's a small fudge factor to allow for server-to-server clock skew.) If the key is expired or revoked, and if the application has not disabled automatic key generation, then a new key will be generated with immediate activation per the key expiration and rolling policy below.

数据保护系统的原因会立即生成新密钥,而不是回退到不同的密钥是生成新密钥应视为隐式在新密钥之前激活的所有密钥的过期时间。The reason the data protection system generates a new key immediately rather than falling back to a different key is that new key generation should be treated as an implicit expiration of all keys that were activated prior to the new key. 一般理念是新的密钥可能已使用不同的算法或静态加密机制比旧密钥配置和系统应首选而回退的当前配置。The general idea is that new keys may have been configured with different algorithms or encryption-at-rest mechanisms than old keys, and the system should prefer the current configuration over falling back.

没有异常。There's an exception. 如果应用程序开发人员具有禁用自动密钥生成,则数据保护系统必须选择用作默认键。If the application developer has disabled automatic key generation, then the data protection system must choose something as the default key. 在此回退方案中,系统将使用首选项提供给有时间传播到群集中的其他计算机的密钥选择的最新的激活日期,非撤消键。In this fallback scenario, the system will choose the non-revoked key with the most recent activation date, with preference given to keys that have had time to propagate to other machines in the cluster. 回退系统最终可能因此选择的已过期的默认密钥。The fallback system may end up choosing an expired default key as a result. 回退系统永远不会选择用作默认键已撤消的键,如果密钥环为空或已被吊销的每个键,然后系统将发出初始化时出错。The fallback system will never choose a revoked key as the default key, and if the key ring is empty or every key has been revoked then the system will produce an error upon initialization.

密钥的过期和滚动Key expiration and rolling

创建密钥时,它自动具有提供的激活日期为 {now + 2 天} 和 {now + 90 天} 的到期日期。When a key is created, it's automatically given an activation date of { now + 2 days } and an expiration date of { now + 90 days }. 激活前 2 天的延迟提供关键的时间才能传遍整个系统。The 2-day delay before activation gives the key time to propagate through the system. 也就是说,它允许在后备存储指向其他应用程序以观察该密钥在其下一步的自动刷新周期,从而最大化,密钥环会成为的活动已传播到所有应用程序,可能需要使用它的可能性。That is, it allows other applications pointing at the backing store to observe the key at their next auto-refresh period, thus maximizing the chances that when the key ring does become active it has propagated to all applications that might need to use it.

如果默认密钥将在 2 天内过期,并且密钥环尚不具有一个键,它将处于活动状态的默认密钥到期后,数据保护系统将自动保留密钥环的新键。If the default key will expire within 2 days and if the key ring doesn't already have a key that will be active upon expiration of the default key, then the data protection system will automatically persist a new key to the key ring. 此新的密钥都有的激活日期为 {默认密钥到期日期} 和 {now + 90 天} 的到期日期。This new key has an activation date of { default key's expiration date } and an expiration date of { now + 90 days }. 这允许系统以自动服务不会中断定期轮转密钥。This allows the system to automatically roll keys on a regular basis with no interruption of service.

可能会有情况,在其中立即激活与创建密钥。There might be circumstances where a key will be created with immediate activation. 一个示例是当应用程序未运行的时间和过期密钥环中的所有键。One example would be when the application hasn't run for a time and all keys in the key ring are expired. 在此情况下,该密钥有 {现在} 的不正常 2 天的激活延迟激活日期。When this happens, the key is given an activation date of { now } without the normal 2-day activation delay.

默认密钥生存期为 90 天,但这是可配置,如以下示例所示。The default key lifetime is 90 days, though this is configurable as in the following example.

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

管理员还可以更改默认系统范围内,但显式调用SetDefaultKeyLifetime将覆盖任何系统范围的策略。An administrator can also change the default system-wide, though an explicit call to SetDefaultKeyLifetime will override any system-wide policy. 默认密钥生存期不能为短于 7 天。The default key lifetime cannot be shorter than 7 days.

自动密钥环刷新Automatic key ring refresh

数据保护系统初始化时,它从基础存储库中读取密钥环,并将其缓存在内存中。When the data protection system initializes, it reads the key ring from the underlying repository and caches it in memory. 此缓存,保护和取消保护操作继续进行而不会达到后备存储。This cache allows Protect and Unprotect operations to proceed without hitting the backing store. 大约每隔 24 小时或当前的默认密钥过期,具体取决于第一个,系统将自动检查更改的后备存储。The system will automatically check the backing store for changes approximately every 24 hours or when the current default key expires, whichever comes first.

警告

开发人员应很少 (如果有) 需要直接使用密钥管理 Api。Developers should very rarely (if ever) need to use the key management APIs directly. 数据保护系统将执行自动密钥管理,如上文所述。The data protection system will perform automatic key management as described above.

数据保护系统公开某接口IKeyManager,可以用来检查和更改密钥环。The data protection system exposes an interface IKeyManager that can be used to inspect and make changes to the key ring. 提供的实例的 DI 系统IDataProtectionProvider还可以提供的一个实例IKeyManager使用费。The DI system that provided the instance of IDataProtectionProvider can also provide an instance of IKeyManager for your consumption. 或者,你可以拉取IKeyManager直接从IServiceProvider如下面的示例中所示。Alternatively, you can pull the IKeyManager straight from the IServiceProvider as in the example below.

这会修改密钥环 (显式创建的新项或执行吊销) 的任何操作将使内存中缓存失效。Any operation which modifies the key ring (creating a new key explicitly or performing a revocation) will invalidate the in-memory cache. 下次调用ProtectUnprotect将导致重新读取密钥环,并重新创建缓存的数据保护系统。The next call to Protect or Unprotect will cause the data protection system to reread the key ring and recreate the cache.

下面的示例演示了如何使用IKeyManager界面检查和操作密钥环,包括撤销的现有密钥和手动生成新的密钥。The sample below demonstrates using the IKeyManager interface to inspect and manipulate the key ring, including revoking existing keys and generating a new key manually.

using System;
using System.IO;
using System.Threading;
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();

        // perform a protect operation to force the system to put at least
        // one key in the key ring
        services.GetDataProtector("Sample.KeyManager.v1").Protect("payload");
        Console.WriteLine("Performed a protect operation.");
        Thread.Sleep(2000);

        // get a reference to the key manager
        var keyManager = services.GetService<IKeyManager>();

        // list all keys in the key ring
        var allKeys = keyManager.GetAllKeys();
        Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
        foreach (var key in allKeys)
        {
            Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
        }

        // revoke all keys in the key ring
        keyManager.RevokeAllKeys(DateTimeOffset.Now, reason: "Revocation reason here.");
        Console.WriteLine("Revoked all existing keys.");

        // add a new key to the key ring with immediate activation and a 1-month expiration
        keyManager.CreateNewKey(
            activationDate: DateTimeOffset.Now,
            expirationDate: DateTimeOffset.Now.AddMonths(1));
        Console.WriteLine("Added a new key.");

        // list all keys in the key ring
        allKeys = keyManager.GetAllKeys();
        Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
        foreach (var key in allKeys)
        {
            Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
        }
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Performed a protect operation.
 * The key ring contains 1 key(s).
 * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = False
 * Revoked all existing keys.
 * Added a new key.
 * The key ring contains 2 key(s).
 * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = True
 * Key {2266fc40-e2fb-48c6-8ce2-5fde6b1493f7}: Created = 2015-03-18 22:20:51Z, IsRevoked = False
 */

密钥存储Key storage

数据保护系统具有启发式方法,它会尝试自动推断相应的密钥存储位置和静态加密机制。The data protection system has a heuristic whereby it attempts to deduce an appropriate key storage location and encryption-at-rest mechanism automatically. 密钥持久性机制也是由应用开发人员可配置的。The key persistence mechanism is also configurable by the app developer. 以下文档讨论这些机制的内置实现:The following documents discuss the in-box implementations of these mechanisms: