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

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

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

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

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

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

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

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

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

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

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

Общая эвристика заключается в том, что система защиты данных выбирает ключ с последней датой активации в качестве ключа по умолчанию. (Существует небольшой фактор 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 , который можно использовать для проверки и внесения изменений в круг ключей. Система внедрения зависимостей 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.

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

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