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.