Extensibilidade de gerenciamento de chaves no ASP.NET Core
Leia a seção de gerenciamento de chaves antes de ler esta seção, pois ela explica alguns dos conceitos fundamentais por trás dessas APIs.
Aviso: Os tipos que implementam qualquer uma das interfaces a seguir devem ser thread-safe para vários chamadores.
Chave
A interface IKey
é a representação básica de uma chave no cryptosystem. O termo chave é usado aqui no sentido abstrato, não no sentido literal de "material de chave criptográfica". Uma chave tem as seguintes propriedades:
Datas de ativação, criação e expiração
Estado de revogação
Identificador de chave (um GUID)
Além disso, IKey
expõe um método CreateEncryptor
que pode ser usado para criar um IAuthenticatedEncryptor instância vinculada a essa chave.
Além disso, IKey
expõe um método CreateEncryptorInstance
que pode ser usado para criar um IAuthenticatedEncryptor instância vinculada a essa chave.
Observação
Não há nenhuma API para recuperar o material criptográfico bruto de uma instância IKey
.
IKeyManager
A interface IKeyManager
representa um objeto responsável pelo armazenamento, recuperação e manipulação geral de chaves. Expõe três operações de alto nível:
Crie uma nova chave e mantenha-a no armazenamento.
Recupere todas as chaves do armazenamento.
Revogue uma ou mais chaves e mantenha as informações de revogação no armazenamento.
Advertência
Escrever um IKeyManager
é uma tarefa muito avançada, e a maioria dos desenvolvedores não deve tentar. Em vez disso, a maioria dos desenvolvedores deve aproveitar os recursos oferecidos pela classe
XmlKeyManager
O tipo XmlKeyManager
é a implementação concreta na caixa de IKeyManager
. Ele fornece várias facilidades úteis, incluindo depósito de chaves e criptografia de chaves em repouso. As chaves neste sistema são representadas como elementos XML (especificamente, XElement).
XmlKeyManager
depende de várias outras componentes no decurso do cumprimento das suas tarefas:
AlgorithmConfiguration
, que dita os algoritmos usados pelas novas chaves.IXmlRepository
, que controla onde as chaves são mantidas no armazenamento.IXmlEncryptor
[opcional], que permite criptografar chaves em repouso.IKeyEscrowSink
[opcional], que fornece serviços de custódia de chaves.
IXmlRepository
, que controla onde as chaves são mantidas no armazenamento.IXmlEncryptor
[opcional], que permite criptografar chaves em repouso.IKeyEscrowSink
[opcional], que fornece serviços de custódia de chaves.
Abaixo estão diagramas de alto nível que indicam como esses componentes são conectados dentro XmlKeyManager
.
Criação de Chaves / CreateNewKey
Na implementação do CreateNewKey
, o componente AlgorithmConfiguration
é usado para criar um IAuthenticatedEncryptorDescriptor
exclusivo, que é serializado como XML. Se um coletor de depósito de chave estiver presente, o XML bruto (não criptografado) será fornecido ao coletor para armazenamento de longo prazo. O XML não criptografado é então executado através de uma IXmlEncryptor
(se necessário) para gerar o documento XML criptografado. Este documento encriptado é mantido para armazenamento a longo prazo através do IXmlRepository
. (Se nenhuma IXmlEncryptor
estiver configurada, o documento não criptografado será persistido no IXmlRepository
.)
Criação de Chaves / CriarNovaChave
Na implementação do CreateNewKey
, o componente IAuthenticatedEncryptorConfiguration
é usado para criar um IAuthenticatedEncryptorDescriptor
exclusivo, que é serializado como XML. Se um coletor de depósito de chave estiver presente, o XML bruto (não criptografado) será fornecido ao coletor para armazenamento de longo prazo. O XML não criptografado é então executado através de uma IXmlEncryptor
(se necessário) para gerar o documento XML criptografado. Este documento encriptado é mantido para armazenamento a longo prazo através do IXmlRepository
. (Se nenhuma IXmlEncryptor
estiver configurada, o documento não criptografado será persistido no IXmlRepository
.)
Recuperação de Chaves / ObterTodasAsChaves
Na implementação do GetAllKeys
, os documentos XML que representam chaves e revogações são lidos a partir do IXmlRepository
subjacente. Se esses documentos forem encriptados, o sistema irá desencripta-los automaticamente.
XmlKeyManager
cria as instâncias de IAuthenticatedEncryptorDescriptorDeserializer
apropriadas para desserializar os documentos de volta em instâncias de IAuthenticatedEncryptorDescriptor
, que são então encapsuladas em instâncias de IKey
individuais. Esta coleção de instâncias IKey
é devolvida ao chamador.
Mais informações sobre os elementos XML específicos podem ser encontradas no documento de formato de armazenamento de chave .
IXmlRepositório
A interface IXmlRepository
representa um tipo que pode persistir XML e recuperar XML de um armazenamento de backup. Ele expõe duas APIs:
GetAllElements
:IReadOnlyCollection<XElement>
StoreElement(XElement element, string friendlyName)
As implementações de IXmlRepository
não precisam analisar o XML que passa por elas. Eles devem tratar os documentos XML como opacos e permitir que camadas superiores se preocupem em gerar e analisar os documentos.
Existem quatro tipos de betão embutido que implementam IXmlRepository
:
Consulte o documento dos provedores de armazenamento de chaves para obter mais informações.
Registar um IXmlRepository
personalizado é apropriado ao usar um armazenamento de apoio diferente (por exemplo, Armazenamento de Tabela do Azure).
Para alterar o repositório padrão em todo o aplicativo, registre uma instância de IXmlRepository
personalizada:
services.Configure<KeyManagementOptions>(options => options.XmlRepository = new MyCustomXmlRepository());
services.AddSingleton<IXmlRepository>(new MyCustomXmlRepository());
IXmlEncryptor
A interface IXmlEncryptor
representa um tipo que pode criptografar um elemento XML de texto sem formatação. Ele expõe uma única API:
- Encrypt(XElement plaintextElement) : EncryptedXmlInfo
Se um IAuthenticatedEncryptorDescriptor
serializado contiver quaisquer elementos marcados como "requer criptografia", então XmlKeyManager
processará esses elementos através do método Encrypt
do IXmlEncryptor
configurado, e persistirá o elemento codificado em vez do elemento de texto simples no IXmlRepository
. A saída do método Encrypt
é um objeto EncryptedXmlInfo
. Este objeto é um invólucro que contém tanto o XElement
cifrado resultante quanto o tipo que representa um IXmlDecryptor
, que pode ser usado para decifrar o elemento correspondente.
Existem quatro tipos de betão embutido que implementam IXmlEncryptor
:
Consulte a de documentos de criptografia de chave de
Para alterar o mecanismo padrão de criptografia de chave em repouso em todo o aplicativo, registre uma instância de IXmlEncryptor
personalizada:
services.Configure<KeyManagementOptions>(options => options.XmlEncryptor = new MyCustomXmlEncryptor());
services.AddSingleton<IXmlEncryptor>(new MyCustomXmlEncryptor());
IXmlDecryptor
A interface IXmlDecryptor
representa um tipo que sabe como desencriptar um XElement
que foi codificado através de um IXmlEncryptor
. Ele expõe uma única API:
- Decifrar(XElement encryptedElement) : XElement
O método Decrypt
desfaz a criptografia executada por IXmlEncryptor.Encrypt
. Geralmente, cada implementação concreta IXmlEncryptor
terá um IXmlDecryptor
concreto correspondente.
Os tipos que implementam IXmlDecryptor
devem ter um dos seguintes dois construtores públicos:
- .ctor(IServiceProvider)
- .ctor()
Observação
O IServiceProvider
passado para o construtor pode ser nulo.
IKeyEscrowSink
A interface IKeyEscrowSink
representa um tipo que pode realizar a custódia de informações confidenciais. Lembre-se de que os descritores serializados podem conter informações confidenciais (como material criptográfico), e foi isso que levou à introdução do tipo IXmlEncryptor
A Interface de custódia fornece uma escotilha de escape de emergência, permitindo o acesso ao XML bruto e serializado antes de ser transformado por qualquer IXmlEncryptor configurado . A interface expõe uma única API:
- Store(Guid keyId, elemento XElement)
Cabe à implementação IKeyEscrowSink
lidar com o elemento fornecido de forma segura e consistente com a política de negócios. Uma implementação possível poderia ser para o coletor de depósito criptografar o elemento XML usando um certificado X.509 corporativo conhecido onde a chave privada do certificado foi depositada; o tipo CertificateXmlEncryptor
pode ajudar com isso. A implementação IKeyEscrowSink
também é responsável por armazenar adequadamente o elemento fornecido.
Por padrão, nenhum mecanismo de depósito está habilitado, embora os administradores de servidor possam configurar isso globalmente. Ele também pode ser configurado programaticamente através do método IDataProtectionBuilder.AddKeyEscrowSink
como mostrado no exemplo abaixo. As sobrecargas do método AddKeyEscrowSink
refletem as sobrecargas de IServiceCollection.AddSingleton
e IServiceCollection.AddInstance
, pois instâncias IKeyEscrowSink
estão destinadas a ser singletons. Se várias instâncias IKeyEscrowSink
forem registradas, cada uma será chamada durante a geração de chaves, para que as chaves possam ser depositadas em vários mecanismos simultaneamente.
Não há API para ler material de uma instância IKeyEscrowSink
. Isso é consistente com a teoria de design do mecanismo de depósito: destina-se a tornar o material chave acessível a uma autoridade confiável e, como o aplicativo em si não é uma autoridade confiável, ele não deve ter acesso ao seu próprio material depositado.
O código de exemplo a seguir demonstra a criação e o registro de um IKeyEscrowSink
onde as chaves são depositadas de forma que apenas os membros de "CONTOSODomain Admins" possam recuperá-las.
Observação
Para executar este exemplo, você deve estar em uma máquina Windows 8 / Windows Server 2012 associada a um domínio e o controlador de domínio deve ser Windows Server 2012 ou posterior.
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>
*/