Управление ключами в ASP.NET Core

Система защиты данных автоматически управляет временем существования главных ключей, используемых для защиты и отмены защиты полезных данных. Каждый ключ может существовать на одном из четырех этапов:

  • Создано — ключ существует в кольце ключей, но еще не активирован. Ключ не следует использовать для новых операций защиты до тех пор, пока не истекло достаточно времени, что ключ имел возможность распространяться на все компьютеры, использующие это кольцо ключей.

  • Активный — ключ существует в кольце ключей и должен использоваться для всех новых операций Защиты.

  • Истек срок действия— ключ выполняет свое естественное время существования и больше не должен использоваться для новых операций защиты.

  • Отменено — ключ скомпрометирован и не должен использоваться для новых операций Защиты.

Созданные, активные и просроченные ключи могут использоваться для отмены защиты входящих полезных данных. Отозванные ключи по умолчанию не могут использоваться для отмены защиты полезных данных, но разработчик приложения может переопределить это поведение при необходимости.

Предупреждение

Разработчик может заманить удалить ключ из круга ключей (например, удалив соответствующий файл из файловой системы). На этом этапе все данные, защищенные ключом, безвозвратно не шифруются, и нет переопределения чрезвычайных ситуаций, как есть с отозванными ключами. Удаление ключа является действительно разрушительным поведением, и, следовательно, система защиты данных не предоставляет API первого класса для выполнения этой операции.

Выбор ключа по умолчанию

Когда система защиты данных считывает кольцо ключей из резервного репозитория, она попытается найти ключ по умолчанию из кольца ключей. Ключ по умолчанию используется для новых операций Protect.

Общая эвристика заключается в том, что система защиты данных выбирает ключ с последней датой активации в качестве ключа по умолчанию. (Существует небольшой фактор fudge, чтобы разрешить отклонение часов между серверами.) Если срок действия ключа истек или отозван, и если приложение не отключило автоматическое создание ключей, новый ключ будет создан с немедленной активацией на срок действия ключа и скользящей политики ниже.

Причина, по которой система защиты данных немедленно создает новый ключ, а не возвращается к другому ключу, заключается в том, что новое поколение ключей должно рассматриваться как неявное истечение срока действия всех ключей, которые были активированы до нового ключа. Общая идея заключается в том, что новые ключи могут быть настроены с различными алгоритмами или механизмами шифрования неактивных ключей, а система должна предпочесть текущую конфигурацию при возврате.

Существует исключение. Если разработчик приложения отключил автоматическое создание ключей, система защиты данных должна выбрать что-то в качестве ключа по умолчанию. В этом резервном сценарии система выберет неизозванный ключ с последней датой активации, при этом предпочтение отдается ключам, которые имели время для распространения на другие компьютеры в кластере. Резервная система может в конечном итоге выбрать ключ по умолчанию с истекшим сроком действия. Резервная система никогда не будет выбирать отозванный ключ в качестве ключа по умолчанию, и если кольцо ключей пусто или все ключи были отозваны, система будет вызывать ошибку при инициализации.

Истечение срока действия ключа и сверка

При создании ключа автоматически присваивается дата активации { + 2 дня } и дата окончания срока действия { сейчас + 90 дней }. 2-дневная задержка перед активацией дает время ключа для распространения по системе. То есть это позволяет другим приложениям, указывающим на резервное хранилище, наблюдать за ключом при следующем периоде автоматического обновления, таким образом максимизируя вероятность того, что когда кольцо ключей становится активным, оно распространяется на все приложения, которые, возможно, потребуется использовать его.

Если срок действия ключа по умолчанию истекает в течение 2 дней, и если кольцо ключей еще не имеет ключа, который будет активен после истечения срока действия ключа по умолчанию, система защиты данных будет автоматически сохранять новый ключ в круге ключей. Этот новый ключ имеет дату активации { дата окончания срока действия ключа по умолчанию } и дата окончания срока действия { сейчас + 90 дней }. Это позволяет системе автоматически развертывать ключи на регулярной основе без прерывания работы службы.

Могут возникнуть обстоятельства, когда ключ будет создан с немедленной активацией. Одним из примеров будет, когда приложение не выполняется в течение некоторого времени, и срок действия всех ключей в кольце ключей истек. Когда это происходит, ключ получает дату активации {now } без обычной задержки активации 2-дневной активации.

Время существования ключа по умолчанию составляет 90 дней, хотя это можно настроить, как показано в следующем примере.

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

Администратор также может изменить системное расширение по умолчанию, хотя явный вызов SetDefaultKeyLifetime переопределит любую политику на уровне системы. Время существования ключа по умолчанию не может быть короче 7 дней.

Автоматическое обновление круга ключей

При инициализации системы защиты данных он считывает кольцо ключей из базового репозитория и кэширует его в памяти. Этот кэш позволяет выполнять операции защиты и отмены защиты без попадания в резервное хранилище. Система автоматически проверка резервное хранилище для изменений примерно каждые 24 часа или когда истекает срок действия текущего ключа по умолчанию.

Предупреждение

Разработчики должны очень редко (если когда-либо) использовать API управления ключами напрямую. Система защиты данных будет выполнять автоматическое управление ключами, как описано выше.

Система защиты данных предоставляет интерфейс IKeyManager , который можно использовать для проверки и внесения изменений в кольцо ключей. Система DI, предоставляемая экземпляром IDataProtectionProvider , также может предоставить экземпляр IKeyManager для вашего потребления. Кроме того, можно извлечь IKeyManager прямо из приведенного IServiceProvider ниже примера.

Любая операция, которая изменяет кольцо ключей (создание нового ключа явным образом или выполнение отзыва) приведет к недопустимости кэша в памяти. Следующий вызов Protect или Unprotect приведет к тому, что система защиты данных перечитает кольцо ключей и повторно создает кэш.

В приведенном ниже примере показано использование IKeyManager интерфейса для проверки и управления кругом ключей, включая отзыв существующих ключей и создание нового ключа вручную.

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
 */

Если вы хотите увидеть комментарии к коду, переведенные на языки, отличные от английского, сообщите нам на странице обсуждения этой проблемы на сайте GitHub.

Хранилище ключей

Система защиты данных имеет эвристика, при которой она пытается вывести соответствующее расположение хранилища ключей и механизм шифрования неактивных данных автоматически. Механизм сохраняемости ключей также настраивается разработчиком приложений. В следующих документах рассматриваются встроенные реализации этих механизмов: