Extensibilidade de criptografia principal no ASP.NET Core

Aviso

Os tipos que implementam qualquer uma das interfaces a seguir devem ser thread-safe para vários chamadores.

IAuthenticatedEncryptor

A interface IAuthenticatedEncryptor é o bloco de construção básico do subsistema criptográfico. Geralmente, há um IAuthenticatedEncryptor por chave e a instância IAuthenticatedEncryptor encapsula todo o material de chave criptográfica e as informações algorítmicas necessárias para executar operações criptográficas.

Como o nome sugere, o tipo é responsável por fornecer serviços de criptografia e descriptografia autenticados. Ele expõe as duas APIs a seguir.

  • Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> additionalAuthenticatedData) : byte[]

  • Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData) : byte[]

O método Encrypt retorna um blob que inclui o texto sem formatação cifrada e uma marca de autenticação. A marca de autenticação deve abranger os dados autenticados adicionais (AAD), embora o próprio AAD não precise ser recuperável da carga final. O método Decrypt valida a marca de autenticação e retorna a carga decifrada. Todas as falhas (exceto ArgumentNullException e similares) devem ser homogeneizadas para CryptographicException.

Observação

A própria instância IAuthenticatedEncryptor não precisa conter o material de chave. Por exemplo, a implementação pode delegar a um HSM para todas as operações.

Como criar um IAuthenticatedEncryptor

A interface IAuthenticatedEncryptorFactory representa um tipo que sabe como criar uma instância IAuthenticatedEncryptor . Sua API é a seguinte.

  • CreateEncryptorInstance(IKey key) : IAuthenticatedEncryptor

Para qualquer instância IKey, todos os criptografadores autenticados criados pelo método CreateEncryptorInstance devem ser considerados equivalentes, como no exemplo de código abaixo.

// we have an IAuthenticatedEncryptorFactory instance and an IKey instance
IAuthenticatedEncryptorFactory factory = ...;
IKey key = ...;

// get an encryptor instance and perform an authenticated encryption operation
ArraySegment<byte> plaintext = new ArraySegment<byte>(Encoding.UTF8.GetBytes("plaintext"));
ArraySegment<byte> aad = new ArraySegment<byte>(Encoding.UTF8.GetBytes("AAD"));
var encryptor1 = factory.CreateEncryptorInstance(key);
byte[] ciphertext = encryptor1.Encrypt(plaintext, aad);

// get another encryptor instance and perform an authenticated decryption operation
var encryptor2 = factory.CreateEncryptorInstance(key);
byte[] roundTripped = encryptor2.Decrypt(new ArraySegment<byte>(ciphertext), aad);


// the 'roundTripped' and 'plaintext' buffers should be equivalent

IAuthenticatedEncryptorDescriptor (somente ASP.NET Core 2.x)

A interface IAuthenticatedEncryptorDescriptor representa um tipo que sabe como se exportar para XML. Sua API é a seguinte.

  • ExportToXml() : XmlSerializedDescriptorInfo

Serialização XML

A principal diferença entre IAuthenticatedEncryptor e IAuthenticatedEncryptorDescriptor é que o descritor sabe como criar o criptografador e fornecê-lo com argumentos válidos. Considere um IAuthenticatedEncryptor cuja implementação depende de SymmetricAlgorithm e KeyedHashAlgorithm. O trabalho do criptografador é consumir esses tipos, mas ele não sabe necessariamente de onde esses tipos vieram, portanto, ele não pode realmente escrever uma descrição adequada de como se recriar se o aplicativo for reiniciado. O descritor atua como um nível mais alto sobre isso. Como o descritor sabe como criar a instância do criptografador (por exemplo, ele sabe como criar os algoritmos necessários), ele pode serializar esse conhecimento no formulário XML para que a instância do criptografador possa ser recriada após uma redefinição de aplicativo.

O descritor pode ser serializado por meio de sua rotina ExportToXml. Essa rotina retorna um XmlSerializedDescriptorInfo que contém duas propriedades: a representação XElement do descritor e o Type que representa um IAuthenticatedEncryptorDescriptorDeserializer que pode ser usado para ressuscitar esse descritor dado o XElement correspondente.

O descritor serializado pode conter informações confidenciais, como material de chave criptográfica. O sistema de proteção de dados tem suporte interno para criptografar informações antes de persistir no armazenamento. Para aproveitar isso, o descritor deve marcar o elemento que contém informações confidenciais com o nome do atributo "requiresEncryption" (xmlns "<http://schemas.asp.net/2015/03/dataProtection>"), valor "true".

Dica

Há uma API auxiliar para definir esse atributo. Chame o método de extensão XElement.MarkAsRequiresEncryption() localizado no namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.

Também pode haver casos em que o descritor serializado não contém informações confidenciais. Considere novamente o caso de uma chave criptográfica armazenada em um HSM. O descritor não pode gravar o material de chave ao se serializar, pois o HSM não exporá o material em forma de texto sem formatação. Em vez disso, o descritor pode gravar a versão encapsulada por chave da chave (se o HSM permitir a exportação dessa forma) ou o próprio identificador exclusivo do HSM para a chave.

IAuthenticatedEncryptorDescriptorDeserializer

A interface IAuthenticatedEncryptorDescriptorDeserializer representa um tipo que sabe como desserializar uma instância IAuthenticatedEncryptorDescriptor de um XElement. Ele expõe um único método:

  • ImportFromXml(elemento XElement) : IAuthenticatedEncryptorDescriptor

O método ImportFromXml usa o XElement que foi retornado por IAuthenticatedEncryptorDescriptor.ExportToXml e cria um equivalente do IAuthenticatedEncryptorDescriptor original.

Os tipos que implementam IAuthenticatedEncryptorDescriptorDeserializer devem ter um dos dois construtores públicos a seguir:

  • .ctor(IServiceProvider)

  • .ctor()

Observação

O IServiceProvider passado para o construtor pode ser nulo.

A fábrica de nível superior

A classe AlgorithmConfiguration representa um tipo que sabe como criar instâncias IAuthenticatedEncryptorDescriptor . Ele expõe uma única API.

  • CreateNewDescriptor() : IAuthenticatedEncryptorDescriptor

Pense em AlgorithmConfiguration como a fábrica de nível superior. A configuração serve como um modelo. Ele encapsula informações algorítmicas (por exemplo, essa configuração produz descritores com uma chave mestra AES-128-GCM), mas ainda não está associada a uma chave específica.

Quando CreateNewDescriptor é chamado, o material de chave novo é criado exclusivamente para essa chamada e um novo IAuthenticatedEncryptorDescriptor é produzido, que encapsula esse material de chave e as informações algorítmicas necessárias para consumir o material. O material de chave pode ser criado em software (e mantido na memória), ele pode ser criado e mantido dentro de um HSM e assim por diante. O ponto crucial é que as duas chamadas para CreateNewDescriptor nunca devem criar instâncias IAuthenticatedEncryptorDescriptor equivalentes.

O tipo AlgorithmConfiguration serve como o ponto de entrada para rotinas de criação de chave, como rolagem automática de chave. Para alterar a implementação de todas as chaves futuras, defina a propriedade AuthenticatedEncryptorConfiguration em KeyManagementOptions.