Partilhar via


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.

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

Criação de Chaves / CreateNewKey

Na implementação do CreateNewKey, o componente AlgorithmConfiguration é usado para criar um IAuthenticatedEncryptorDescriptorexclusivo, 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

Criação de Chaves

Criação de Chaves / CriarNovaChave

Na implementação do CreateNewKey, o componente IAuthenticatedEncryptorConfiguration é usado para criar um IAuthenticatedEncryptorDescriptorexclusivo, 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

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 IXmlRepositorysubjacente. 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 IXmlEncryptorconfigurado, 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 em repouso para obter mais informações.

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 em primeiro lugar. No entanto, acidentes acontecem, e os chaveiros podem ser excluídos ou corrompidos.

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