Расширение управления ключами

Совет

Прочтите раздел управление ключами, прежде чем начать изучать этот раздел, поскольку там объясняются основные концепции этих API.

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

Типы, которые реализуют один из следующих интерфейсов, должны быть потоково безопасны для нескольких вызывающих элементов.

Ключ

Интерфейс IKey является базовым представлением ключа в криптосистеме. Термин “ключ” используется здесь в абстрактном смысле. У ключа есть следующие свойства:

  • Активация, создание и срок истечения
  • Статус отзыва
  • Идентификатор ключа (GUID)

Кроме того, IKey использует метод CreateEncryptorInstance для создания экземпляра IAuthenticatedEncryptor, связанного с этим ключом.

Примечание

Не существует API, чтобы получить “сырой” криптографический материал из экземпляра IKey.

IKeyManager

Интерфейс IKeyManager представляет объект, отвечающий за хранение, извлечение и управление ключами. У него есть три высокоуровневых операции:

  • Создать новый ключ и сохранить его.
  • Извлечь все ключи из хранилища.
  • Аннулировать один или несколько ключей и сохранить эту информацию.

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

Написание IKeyManager - это очень продвинутая задача, и большинству разработчиков не стоит этого делать. Им лучше использовать класс XmlKeyManager.

XmlKeyManager

Тип XmlKeyManager является реализацией IKeyManager. Он предлагает несколько полезных возможностей, включая условное сохранение ключей и шифрование ключей “at rest”. Ключи в этой системе представлены в виде XML элементов (в частности, XElement).

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

  • IAuthenticatedEncryptorConfiguration, который обозначает алгоритмы, используемые новыми ключами.
  • IXmlRepository контролирует, где ключи располагаются в хранилище.
  • IXmlEncryptor [optional] позволяет шифровать ключи “at rest”.
  • IKeyEscrowSink [optional] предлагает сервисы по размещению ключей.

Ниже показано, как эти компоненты совместно работают внутри XmlKeyManager.

Key Creation

Key Creation / CreateNewKey

В реализации CreateNewKey компонент IAuthenticatedEncryptorConfiguration используется для создания уникального IAuthenticatedEncryptorDescriptor, который сериализуется как XML. Если существует условное хранилище ключей, “сырой” (незашифрованный) XML будет отправлен в это хранилище для долгосрочного хранения. Затем незашифрованный XML проходит через IXmlEncryptor (если требуется) для генерации зашифрованного XML документа. Этот зашифрованный документ сохраняется в долгосрочном хранилище с помощью IXmlRepository. (Если не настроен IXmlEncryptor, незашифрованный документ сохраняется в IXmlRepository.)

Key Retrieval

Key Retrieval / GetAllKeys

В реализации GetAllKeys XML документы, представляющие ключи и информацию об их аннулировании, считываются из IXmlRepository. Если эти документы зашифрованы, система автоматически их расшифрует. XmlKeyManager создает соответствующий экземпляр IAuthenticatedEncryptorDescriptorDeserializer, чтобы десериализовать документ обратно в экземпляр IAuthenticatedEncryptorDescriptor, который затем заключается в отдельный экземпляр IKey. Коллекция экземпляров IKey возвращается вызывающему элементу.

Дальнейшую информацию по конкретным XML можно найти в документе по формату хранения ключей.

IXmlRepository

Интерфейс IXmlRepository представляет тип, который может сохранять и извлекать XML из резервного хранилища. У него есть два API:

  • GetAllElements() : IReadOnlyCollection<XElement>
  • StoreElement(XElement element, string friendlyName)

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

Есть два встроенных типа, которые реализуют IXmlRepository: FileSystemXmlRepository и RegistryXmlRepository. См. документ по провайдерам хранения ключей. Регистрацию пользовательского IXmlRepository можно использовать для различных резервных хранилищ, например, Azure Blob Storage. Чтобы сменить репозиторий по умолчанию, зарегистрируйте в сервисном провайдере пользовательский синглтон IXmlRepository.

IXmlEncryptor

Интерфейс IXmlEncryptor представляет тип, который может зашифровать XML. У него есть один API:

  • Encrypt(XElement plaintextElement) : EncryptedXmlInfo

Если сериализованный IAuthenticatedEncryptorDescriptor содержит любые элементы, отмеченные как “requires encryption”, тогда XmlKeyManager пропустит эти элементы через настроенный IXmlEncryptor метод Encrypt и отправит все это в IXmlRepository. Метод Encrypt возвращает объект EncryptedXmlInfo. Это объект содержит результирующий XElement и Type, представляющий IXmlDecryptor, который можно использовать для расшифровки соответствующего элемента.

Есть четыре встроенных типа, которые реализуют IXmlEncryptor: CertificateXmlEncryptor, DpapiNGXmlEncryptor, DpapiXmlEncryptor и NullXmlEncryptor. См. документ по шифрованию ключей “at rest”. Чтобы сменить механизм шифрования ключей “at rest”, зарегистрируйте в сервисном провайдере пользовательский синглтон IXmlEncryptor.

IXmlDecryptor

Интерфейс IXmlDecryptor представляет тип, который знает, как расшифровать XElement, который был зашифрован через IXmlEncryptor. У него есть один API:

  • Decrypt(XElement encryptedElement) : XElement

Метод Decrypt отменяет шифрование, выполненное IXmlEncryptor.Encrypt. Обычно у каждой конкретной реализации IXmlEncryptor есть соответствующая реализация IXmlDecryptor.

У типов, которые реализуют IXmlDecryptor, должен быть один из двух следующих открытых конструкторов:

  • .ctor(IServiceProvider)
  • .ctor()

Примечание

IServiceProvider, переданный конструктору, может быть null.

IKeyEscrowSink

Интерфейс IKeyEscrowSink представляет тип, который может условно депонировать чувствительную информацию. Сериализованные дескрипторы могут содержать чувствительную информацию (например, криптографический материал), и тогда на первое место выходит тип IXmlEncryptor. Однако иногда так происходит, что ключи можно повредить или удалить.

Данный интерфейс предоставляет нам помощь в данном случае, разрешая доступ к “сырому” сериализованному XML, прежде чем он будет трансформирован и настроен с помощью IXmlEncryptor. У интерфейса есть один API:

  • Store(Guid keyId, XElement element)

Это зависит от реализации IKeyEscrowSink, как обрабатывать предоставленный элемент. Одна реализация может предназначаться для условного хранилища, чтобы зашифровать XML элемент с помощью сертификата X.509; тип CertificateXmlEncryptor помогает при этом. Реализация IKeyEscrowSink также отвечает за хранение соответствующего элемента.

По умолчанию условное хранилище отключено, хотя администраторы могут настроить это глобально. Также есго можно настроить программно с помощью метода DataProtectionConfiguration.AddKeyEscrowSink, как показано в примере ниже. Метод AddKeyEscrowSink переопределяет зеркало для переопределенных вариантов IServiceCollection.AddSingleton и IServiceCollection.AddInstance, поскольку экземпляры IKeyEscrowSink являются синглтонами. если зарегистрировано несколько экземпляров IKeyEscrowSink, во время генерирования ключей они все будут вызываться, так что ключи можно одновременно условно депонировать для нескольких механизмов.

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

В следующем примере мы создаем и регистрируем IKeyEscrowSink, где работать с ключами могут только члены “CONTOSODomain Admins”.

Примечание

Чтобы запустить этот пример, вы должны находиться под Windows 8/Windows Server 2012, и доменным контроллером должен быть Windows Server 2012 или более поздние версии.

using System;
using System.IO;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection()
            .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
            .ProtectKeysWithDpapi()
            .AddKeyEscrowSink(sp => new MyKeyEscrowSink(sp));
        var services = serviceCollection.BuildServiceProvider();

        // get a reference to the key manager and force a new key to be generated
        Console.WriteLine("Generating new key...");
        var keyManager = services.GetService<IKeyManager>();
        keyManager.CreateNewKey(
            activationDate: DateTimeOffset.Now,
            expirationDate: DateTimeOffset.Now.AddDays(7));
    }

    // A key escrow sink where keys are escrowed such that they
    // can be read by members of the CONTOSO\Domain Admins group.
    private class MyKeyEscrowSink : IKeyEscrowSink
    {
        private readonly IXmlEncryptor _escrowEncryptor;

        public MyKeyEscrowSink(IServiceProvider services)
        {
            // Assuming I'm on a machine that's a member of the CONTOSO
            // domain, I can use the Domain Admins SID to generate an
            // encrypted payload that only they can read. Sample SID from
            // https://technet.microsoft.com/en-us/library/cc778824(v=ws.10).aspx.
            _escrowEncryptor = new DpapiNGXmlEncryptor(
                "SID=S-1-5-21-1004336348-1177238915-682003330-512",
                DpapiNGProtectionDescriptorFlags.None,
                services);
        }

        public void Store(Guid keyId, XElement element)
        {
            // Encrypt the key element to the escrow encryptor.
            var encryptedXmlInfo = _escrowEncryptor.Encrypt(element);

            // A real implementation would save the escrowed key to a
            // write-only file share or some other stable storage, but
            // in this sample we'll just write it out to the console.
            Console.WriteLine($"Escrowing key {keyId}");
            Console.WriteLine(encryptedXmlInfo.EncryptedElement);

            // Note: We cannot read the escrowed key material ourselves.
            // We need to get a member of CONTOSO\Domain Admins to read
            // it for us in the event we need to recover it.
        }
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Generating new key...
 * Escrowing key 38e74534-c1b8-4b43-aea1-79e856a822e5
 * <encryptedKey>
 *   <!-- This key is encrypted with Windows DPAPI-NG. -->
 *   <!-- Rule: SID=S-1-5-21-1004336348-1177238915-682003330-512 -->
 *   <value>MIIIfAYJKoZIhvcNAQcDoIIIbTCCCGkCAQ...T5rA4g==</value>
 * </encryptedKey>
 */
Поделись хорошей новостью с друзьями!
Следи за новостями!