Расширяемость управления ключами в ASP.NET Core
Ознакомьтесь с разделом управления ключами перед чтением этого раздела, так как он объясняет некоторые основные понятия, лежащие в основе этих API.
Предупреждение. Типы, реализующие любой из следующих интерфейсов, должны быть потокобезопасны для нескольких вызывающих.
Ключ
Интерфейс IKey
— это базовое представление ключа в криптосистеме. Ключ термина используется здесь в абстрактном смысле, а не в буквальном смысле "материал криптографического ключа". Ключ имеет следующие свойства:
Даты активации, создания и окончания срока действия
Состояние отзыва.
Идентификатор ключа (GUID)
Кроме того, IKey
предоставляет CreateEncryptor
метод, который можно использовать для создания экземпляра IAuthenticatedEncryptor, привязанного к этому ключу.
Кроме того, IKey
предоставляет CreateEncryptorInstance
метод, который можно использовать для создания экземпляра IAuthenticatedEncryptor, привязанного к этому ключу.
Примечание.
Нет API для получения необработанного криптографического материала из экземпляра IKey
.
IKeyManager
Интерфейс IKeyManager
представляет объект, отвечающий за общее хранилище ключей, извлечение и манипуляцию. Он предоставляет три высокоуровневые операции:
Создайте новый ключ и сохраните его в хранилище.
Получение всех ключей из хранилища.
Отмените один или несколько ключей и сохраните сведения о отзывах в хранилище.
Предупреждение
IKeyManager
Написание является очень сложной задачей, и большинство разработчиков не должны пытаться его. Вместо этого большинство разработчиков должны воспользоваться объектами, предлагаемыми классом XmlKeyManager .
XmlKeyManager
Тип XmlKeyManager
— это встроенная конкретная реализация IKeyManager
. Он предоставляет несколько полезных средств, в том числе депонирования ключей и шифрования ключей rest. Ключи в этой системе представлены как XML-элементы (в частности, XElement).
XmlKeyManager
зависит от нескольких других компонентов в ходе выполнения своих задач:
AlgorithmConfiguration
, который диктует алгоритмы, используемые новыми ключами.IXmlRepository
, который определяет, где ключи сохраняются в хранилище.IXmlEncryptor
[необязательно], который позволяет шифровать ключи по адресу rest.IKeyEscrowSink
[необязательно], предоставляющий службы депонирования ключей.
IXmlRepository
, который определяет, где ключи сохраняются в хранилище.IXmlEncryptor
[необязательно], который позволяет шифровать ключи по адресу rest.IKeyEscrowSink
[необязательно], предоставляющий службы депонирования ключей.
Ниже приведены высокоуровневые схемы, указывающие, как эти компоненты связаны между XmlKeyManager
собой.
Создание ключа / CreateNewKey
В реализации компонент используется для создания уникального CreateNewKey
AlgorithmConfiguration
IAuthenticatedEncryptorDescriptor
компонента, который затем сериализуется как XML. Если приемник депонирования ключа присутствует, необработанный (незашифрованный) XML предоставляется приемнику для долгосрочного хранения. Затем незашифрованный XML выполняется через IXmlEncryptor
(при необходимости) для создания зашифрованного XML-документа. Этот зашифрованный документ сохраняется в долгосрочном хранилище через хранилище IXmlRepository
. (Если не IXmlEncryptor
настроено, незашифрованный документ сохраняется в IXmlRepository
.)
Создание ключа / CreateNewKey
В реализации компонент используется для создания уникального CreateNewKey
IAuthenticatedEncryptorConfiguration
IAuthenticatedEncryptorDescriptor
компонента, который затем сериализуется как XML. Если приемник депонирования ключа присутствует, необработанный (незашифрованный) XML предоставляется приемнику для долгосрочного хранения. Затем незашифрованный XML выполняется через IXmlEncryptor
(при необходимости) для создания зашифрованного XML-документа. Этот зашифрованный документ сохраняется в долгосрочном хранилище через хранилище IXmlRepository
. (Если не IXmlEncryptor
настроено, незашифрованный документ сохраняется в IXmlRepository
.)
Извлечение ключей / GetAllKeys
В реализации GetAllKeys
XML-документы, представляющие ключи и отзыва, считываются из базового IXmlRepository
. Если эти документы шифруются, система автоматически расшифровывает их. XmlKeyManager
создает соответствующие IAuthenticatedEncryptorDescriptorDeserializer
экземпляры для десериализации документов обратно в IAuthenticatedEncryptorDescriptor
экземпляры, которые затем упаковываются в отдельные IKey
экземпляры. Эта коллекция экземпляров IKey
возвращается вызывающей объекту.
Дополнительные сведения о конкретных XML-элементах можно найти в документе формата хранилища ключей.
IXmlRepository
Интерфейс IXmlRepository
представляет тип, который может сохранять XML и извлекать XML из резервного хранилища. Он предоставляет два API:
GetAllElements
:IReadOnlyCollection<XElement>
StoreElement(XElement element, string friendlyName)
IXmlRepository
Реализации не нужно анализировать XML-код, проходящий через них. Они должны рассматривать XML-документы как непрозрачные и позволяют более высоким слоям беспокоиться о создании и анализе документов.
Существует четыре встроенных конкретных типа, реализующих IXmlRepository
:
Дополнительные сведения см. в документе поставщиков хранилища ключей.
Регистрация пользовательского варианта IXmlRepository
подходит при использовании другого резервного хранилища (например, хранилища таблиц Azure).
Чтобы изменить приложение репозитория по умолчанию, зарегистрируйте пользовательский IXmlRepository
экземпляр:
services.Configure<KeyManagementOptions>(options => options.XmlRepository = new MyCustomXmlRepository());
services.AddSingleton<IXmlRepository>(new MyCustomXmlRepository());
IXmlEncryptor
Интерфейс IXmlEncryptor
представляет тип, который может шифровать xml-элемент обычного текста. Он предоставляет один API:
- Encrypt(XElement plaintextElement) : EncryptedXmlInfo
Если сериализованный IAuthenticatedEncryptorDescriptor
содержит все элементы, помеченные как "требует шифрования", XmlKeyManager
то будет выполнять эти элементы с помощью метода настроенногоIXmlEncryptor
Encrypt
, и он будет сохранять зашифрованный элемент, а не элемент обычного текста в объектеIXmlRepository
. Выходные данные Encrypt
метода — это EncryptedXmlInfo
объект. Этот объект представляет собой оболочку, содержащую как результирующий зашифрованный XElement
, так и тип, который представляет IXmlDecryptor
собой объект, который можно использовать для расшифровки соответствующего элемента.
Существует четыре встроенных конкретных типа, реализующих IXmlEncryptor
:
Дополнительные сведения см. в rest документе по шифрованию ключей.
Чтобы изменить стандартный механизм шифрования ключей поrest умолчанию, зарегистрируйте пользовательский IXmlEncryptor
экземпляр:
services.Configure<KeyManagementOptions>(options => options.XmlEncryptor = new MyCustomXmlEncryptor());
services.AddSingleton<IXmlEncryptor>(new MyCustomXmlEncryptor());
IXmlDecryptor
Интерфейс IXmlDecryptor
представляет тип, который знает, как расшифровать расшифровку XElement
, которая была зашифрована через объект IXmlEncryptor
. Он предоставляет один API:
- Decrypt(XElement encryptedElement) : XElement
Метод Decrypt
отменяет шифрование, выполненное IXmlEncryptor.Encrypt
. Как правило, каждая конкретная IXmlEncryptor
реализация будет иметь соответствующую конкретную IXmlDecryptor
реализацию.
Типы, которые реализуют IXmlDecryptor
, должны иметь один из следующих двух открытых конструкторов:
- .ctor(IServiceProvider)
- .ctor()
Примечание.
Переданный IServiceProvider
конструктору может иметь значение NULL.
IKeyEscrowSink
Интерфейс IKeyEscrowSink
представляет тип, который может выполнять депонирования конфиденциальной информации. Помните, что сериализованные дескрипторы могут содержать конфиденциальную информацию (например, криптографический материал), и это привело к внедрению типа IXmlEncryptor в первую очередь. Однако аварии происходят, а круги ключей могут быть удалены или повреждены.
Интерфейс escrow предоставляет аварийный экранный люк, позволяя получить доступ к необработанным сериализованным XML, прежде чем он преобразуется любым настроенным IXmlEncryptor. Интерфейс предоставляет один API:
- Store(Guid keyId, элемент XElement)
Это до IKeyEscrowSink
реализации для обработки предоставленного элемента в безопасном режиме, согласованном с бизнес-политикой. Одна из возможных реализаций может быть для приемника escrow для шифрования XML-элемента с помощью известного корпоративного сертификата X.509, в котором был депонирован закрытый ключ сертификата; Тип CertificateXmlEncryptor
может помочь с этим. Реализация IKeyEscrowSink
также отвечает за сохранение предоставленного элемента соответствующим образом.
По умолчанию механизм депонирования не включен, хотя администраторы серверов могут настроить это глобально. Его также можно настроить программным способом IDataProtectionBuilder.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;
using Microsoft.Extensions.Logging;
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/library/cc778824(v=ws.10).aspx.
_escrowEncryptor = new DpapiNGXmlEncryptor(
"SID=S-1-5-21-1004336348-1177238915-682003330-512",
DpapiNGProtectionDescriptorFlags.None,
new LoggerFactory());
}
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>
*/
ASP.NET Core