Extensibilidad de criptografía básica en ASP.NET Core

Advertencia

Los tipos que implementan cualquiera de las interfaces siguientes deben ser seguros para subprocesos para varios autores de llamadas.

IAuthenticatedEncryptor

La interfaz IAuthenticatedEncryptor es el bloque de creación básico del subsistema criptográfico. Por lo general, hay un IAuthenticatedEncryptor por clave y la instancia de IAuthenticatedEncryptor encapsula todo el material de clave criptográfica y la información algorítmica necesaria para realizar operaciones criptográficas.

Como su nombre sugiere, el tipo es responsable de proporcionar servicios de cifrado y descifrado autenticados. Expone las dos API siguientes.

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

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

El método Encrypt devuelve un blob que incluye el texto no cifrado y una etiqueta de autenticación. La etiqueta de autenticación debe englobar los datos autenticados adicionales (AAD), aunque no es necesario que los propios AAD puedan recuperarse de la carga final. El método Decrypt valida la etiqueta de autenticación y devuelve la carga descifrada. Todos los errores (excepto ArgumentNullException y similares) deben homogeneizarse a CryptographicException.

Nota

La propia instancia de IAuthenticatedEncryptor no necesita contener realmente el material de clave. Por ejemplo, la implementación podría delegar en un HSM para todas las operaciones.

Cómo crear un IAuthenticatedEncryptor

La interfaz IAuthenticatedEncryptorFactory representa un tipo que sabe cómo crear una instancia de IAuthenticatedEncryptor . Su API es la siguiente.

  • CreateEncryptorInstance(IKey key) : IAuthenticatedEncryptor

Para cualquier instancia de IKey determinada, los cifradores autenticados creados por su método CreateEncryptorInstance deben considerarse equivalentes, como en el ejemplo de código siguiente.

// 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 (solo ASP.NET Core 2.x)

La interfaz IAuthenticatedEncryptorDescriptor representa un tipo que sabe cómo exportarse a XML. Su API es la siguiente.

  • ExportToXml() : XmlSerializedDescriptorInfo

Serialización XML

La diferencia principal entre IAuthenticatedEncryptor e IAuthenticatedEncryptorDescriptor es que el descriptor sabe cómo crear el cifrador y proporcionarlo con argumentos válidos. Considere un IAuthenticatedEncryptor cuya implementación se basa en SymmetricAlgorithm y KeyedHashAlgorithm. El trabajo del cifrador es consumir estos tipos, pero no sabe necesariamente de dónde proceden estos tipos, por lo que no puede escribir realmente una descripción adecuada de cómo volver a crearse si se reinicia la aplicación. El descriptor actúa como un nivel superior sobre esto. Dado que el descriptor sabe cómo crear la instancia del cifrador (por ejemplo, sabe cómo crear los algoritmos necesarios), puede serializar ese conocimiento en formato XML para que la instancia del cifrador se pueda volver a crear después de un restablecimiento de la aplicación.

El descriptor se puede serializar a través de su rutina ExportToXml. Esta rutina devuelve un XmlSerializedDescriptorInfo que contiene dos propiedades: la representación XElement del descriptor y el tipo que representa un IAuthenticatedEncryptorDeserializer que se puede usar para resucitar este descriptor dado el XElement correspondiente.

El descriptor serializado puede contener información confidencial, como el material de clave criptográfica. El sistema de protección de datos tiene compatibilidad integrada para cifrar información antes de que se conserve en el almacenamiento. Para aprovechar esto, el descriptor debe marcar el elemento que contiene información confidencial con el nombre de atributo "requiresEncryption" (xmlns "<http://schemas.asp.net/2015/03/dataProtection>"), valor "true".

Sugerencia

Hay una API auxiliar para establecer este atributo. Llame al método de extensión XElement.MarkAsRequiresEncryption() ubicado en el espacio de nombres Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.

También puede haber casos en los que el descriptor serializado no contenga información confidencial. Considere de nuevo el caso de una clave criptográfica almacenada en un HSM. El descriptor no puede escribir el material de clave al serializarse, ya que el HSM no expondrá el material en formato de texto no cifrado. En su lugar, el descriptor podría escribir la versión ajustada con clave de la clave (si el HSM permite la exportación de este modo) o el propio identificador único del HSM para la clave.

IAuthenticatedEncryptorDescriptorDeserializer

La interfaz IAuthenticatedEncryptorDeserializer representa un tipo que sabe cómo deserializar una instancia de IAuthenticatedEncryptorDescriptor desde un XElement. Expone un único método:

  • ImportFromXml(XElement element) : IAuthenticatedEncryptorDescriptor

El método ImportFromXml toma el XElement devuelto por IAuthenticatedEncryptorDescriptor.ExportToXml y crea un equivalente del IAuthenticatedEncryptorDescriptor original.

Los tipos que implementan IAuthenticatedEncryptorDescriptorDeserializer deben tener uno de los dos constructores públicos siguientes:

  • .ctor(IServiceProvider)

  • .ctor()

Nota:

El IServiceProvider que se pase al constructor puede ser null.

Fábrica de nivel superior

La clase AlgorithmConfiguration representa un tipo que sabe cómo crear instancias de IAuthenticatedEncryptorDescriptor . Expone una sola API.

  • CreateNewDescriptor() : IAuthenticatedEncryptorDescriptor

Piense en AlgorithmConfiguration como la fábrica de nivel superior. La configuración actúa como plantilla. Ajusta la información algorítmica (por ejemplo, esta configuración genera descriptores con una clave maestra AES-128-GCM), pero aún no está asociada a una clave específica.

Cuando se llama a CreateNewDescriptor, el material de clave nueva se crea únicamente para esta llamada y se produce un nuevo IAuthenticatedEncryptorDescriptor que encapsula este material clave y la información algorítmica necesaria para consumir el material. El material clave podría crearse en software (y mantenerse en memoria), se podría crear y mantener dentro de un HSM, etc. El punto fundamental es que las dos llamadas a CreateNewDescriptor nunca deben crear instancias equivalentes de IAuthenticatedEncryptorDescriptor.

El tipo AlgorithmConfiguration actúa como punto de entrada para rutinas de creación de claves, como la rotación automática de claves. Para cambiar la implementación de todas las claves futuras, establezca la propiedad AuthenticatedEncryptorConfiguration en KeyManagementOptions.