Extensibilité de chiffrement Core dans ASP.NET Core

Avertissement

Les types qui implémentent l’une des interfaces suivantes doivent être thread-safe pour plusieurs appelants.

IAuthenticatedEncryptor

L’interface IAuthenticatedEncryptor est le composant de base du sous-système de chiffrement. Il existe généralement un IAuthenticatedEncryptor par clé. L’instance IAuthenticatedEncryptor enveloppe toutes les informations algorithmiques et matérielles de clé de chiffrement nécessaires pour effectuer des opérations de chiffrement.

Comme son nom l’indique, le type est chargé de fournir des services de chiffrement et de déchiffrement authentifiés. Il expose les deux API suivantes.

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

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

La méthode Chiffrer retourne un blob qui inclut le texte en clair chiffré et une balise d’authentification. La balise d’authentification doit englober les données authentifiées supplémentaires (AAD, additional authenticated data), bien que les AAD ne soient pas récupérables à partir de la charge utile finale. La méthode Déchiffrer valide la balise d’authentification et retourne la charge utile déchiffrée. Tous les échecs (sauf ArgumentNullException et similaires) doivent être homogénéisés en CryptographicException.

Remarque

L’instance IAuthenticatedEncryptor elle-même n’a pas besoin de contenir le matériau de clé. Par exemple, l’implémentation peut déléguer à un module de sécurité matériel (HSM, Hardware Security Module) pour toutes les opérations.

Comment créer un IAuthenticatedEncryptor

L’interface IAuthenticatedEncryptorFactory représente un type qui sait comment créer une instance IAuthenticatedEncryptor. Son API est la suivante.

  • CreateEncryptorInstance(clé IKey) : IAuthenticatedEncryptor

Pour une instance IKey donnée, tout dispositif de chiffrement authentifié créé par sa méthode CreateEncryptorInstance doit être considéré comme équivalent, comme dans l’exemple de code ci-dessous.

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

L’interface IAuthenticatedEncryptorDescriptor représente un type qui sait s’exporter vers XML. Son API est la suivante.

  • ExportToXml() : XmlSerializedDescriptorInfo

Sérialisation XML

La différence principale entre IAuthenticatedEncryptor et IAuthenticatedEncryptorDescriptor est que le descripteur sait comment créer le dispositif de chiffrement et lui fournir des arguments valides. Prenez l’exemple d’un IAuthenticatedEncryptor dont l’implémentation s’appuie sur SymmetricAlgorithm et KeyedHashAlgorithm. Le travail du dispositif de chiffrement consiste à consommer ces types, mais il ne sait pas nécessairement d’où viennent ces derniers. Il ne peut donc pas vraiment écrire une description appropriée de comment se recréer si l’application redémarre. Le descripteur agit comme un niveau supérieur en sus. Étant donné que le descripteur sait comment créer l’instance du dispositif de chiffrement (par exemple, il sait comment créer les algorithmes requis), il peut sérialiser cette connaissance sous forme XML afin que l’instance du dispositif de chiffrement puisse être recréée après une réinitialisation d’application.

Le descripteur peut être sérialisé via sa routine ExportToXml. Cette routine retourne un XmlSerializedDescriptorInfo qui contient deux propriétés : la représentation XElement du descripteur et le type qui représente un IAuthenticatedEncryptorDescriptorDeserializer pouvant être utilisé pour ressusciter ce descripteur en fonction du XElement correspondant.

Le descripteur sérialisé peut contenir des informations sensibles telles que le matériau de clé de chiffrement. Le système de protection des données dispose d’une prise en charge intégrée du chiffrement des informations avant de les conserver dans le stockage. Pour en tirer parti, le descripteur doit marquer l’élément qui contient des informations sensibles avec le nom d’attribut « requiresEncryption » (xmlns "<http://schemas.asp.net/2015/03/dataProtection>") et la valeur « true ».

Conseil

Une API d’assistance existe pour définir cet attribut. Appelez la méthode d’extension XElement.MarkAsRequiresEncryption() située dans l’espace de noms Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.

Il arrive également que le descripteur sérialisé ne contienne pas d’informations sensibles. Prenez à nouveau l’exemple d’une clé de chiffrement stockée dans un HSM. Le descripteur ne peut pas écrire le matériau de clé lors de sa propre sérialisation, car le HSM n’expose pas ces informations sous forme de texte clair. Il se peut que le descripteur écrive plutôt la version enveloppée-de-clé de la clé (si le HSM autorise l’exportation de cette façon) ou l’identificateur unique de la clé propre au HSM.

IAuthenticatedEncryptorDescriptorDeserializer

L’interface IAuthenticatedEncryptorDescriptorDeserializer représente un type qui sait comment désérialiser une instance IAuthenticatedEncryptorDescriptor à partir d’un XElement. Elle expose une seule méthode :

  • ImportFromXml(élément XElement) : IAuthenticatedEncryptorDescriptor

La méthode ImportFromXml prend le XElement retourné par IAuthenticatedEncryptorDescriptor.ExportToXml et crée un équivalent du IAuthenticatedEncryptorDescriptor d’origine.

Les types qui implémentent IAuthenticatedEncryptorDescriptorDeserializer doivent avoir l’un des deux constructeurs publics suivants :

  • .ctor(IServiceProvider)

  • .ctor()

Remarque

Il se peut que l’IServiceProvider passé au constructeur soit nul.

Fabrique de niveau supérieur

La classe AlgorithmConfiguration représente un type qui sait comment créer des instances IAuthenticatedEncryptorDescriptor. Elle expose une seule API.

  • CreateNewDescriptor() : IAuthenticatedEncryptorDescriptor

Considérez AlgorithmConfiguration comme la fabrique de niveau supérieur. La configuration sert de modèle. Elle enveloppe les informations algorithmiques (par exemple, cette configuration produit des descripteurs avec une clé principale AES-128-GCM), mais elle n’est pas encore associée à une clé spécifique.

Quand CreateNewDescriptor est appelé, un nouveau matériau de clé est créé uniquement pour cet appel et un nouveau IAuthenticatedEncryptorDescriptor est produit pour envelopper ce matériau de clé et les informations algorithmiques nécessaires pour consommer le matériau. Le matériau de clé peut être créé dans un logiciel (et conservé en mémoire), il peut être créé et conservé dans un HSM, et ainsi de suite. Le plus important c’est que deux appels à CreateNewDescriptor ne doivent jamais créer d’instances IAuthenticatedEncryptorDescriptor équivalentes.

Le type AlgorithmConfiguration sert de point d’entrée pour les routines de création de clés comme le roulement automatique de clés. Pour modifier l’implémentation de toutes les clés futures, définissez la propriété AuthenticatedEncryptorConfiguration dans KeyManagementOptions.