Extensibilité de la gestion des clés dans ASP.NET Core

Lisez la section relative à la gestion des clés avant de lire cette section, car elle explique certains des concepts fondamentaux derrière ces API.

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

Clé :

L’interface IKey est la représentation de base d’une clé dans le système de chiffrement. Le terme clé est utilisé ici au sens abstrait, et non au sens littéral de « matériel de clé de chiffrement ». Une clé se caractérise par les propriétés suivantes :

  • Dates d’activation, de création et d’expiration

  • État de révocation.

  • Identificateur de clé (GUID)

En outre, IKey expose une méthode CreateEncryptor qui peut être utilisée pour créer une instance IAuthenticatedEncryptor lié à cette clé.

En outre, IKey expose une méthode CreateEncryptorInstance qui peut être utilisée pour créer une instance IAuthenticatedEncryptor lié à cette clé.

Notes

Il n’existe aucune API pour récupérer le matériel de chiffrement brut d’une instance IKey.

IKeyManager

L’interface IKeyManager représente un objet responsable du stockage général des clés, de la récupération et de la manipulation. Il expose trois opérations de haut niveau :

  • Créez une clé et conservez-la dans le stockage.

  • Récupérez toutes les clés du stockage.

  • Révoquez une ou plusieurs clés et conservez les informations de révocation dans le stockage.

Avertissement

L’écriture d’un IKeyManager est une tâche très avancée, et la majorité des développeurs ne doivent pas la tenter. Au lieu de cela, la plupart des développeurs doivent tirer parti des fonctionnalités offertes par la classe XmlKeyManager .

XmlKeyManager

Le type XmlKeyManager est l’implémentation concrète dans la boîte de IKeyManager. Il fournit plusieurs fonctionnalités utiles, y compris l’entiercement de clé et le chiffrement des clés au repos. Les clés de ce système sont représentées en tant qu’éléments XML (plus précisément, XElement).

XmlKeyManager dépend de plusieurs autres composants au cours de l’exécution de ses tâches :

  • AlgorithmConfiguration, qui dicte les algorithmes utilisés par les nouvelles clés.

  • IXmlRepository, qui contrôle l’emplacement où les clés sont conservées dans le stockage.

  • IXmlEncryptor [facultatif], qui autorise le chiffrement des clés au repos.

  • IKeyEscrowSink [facultatif], qui fournit des services d’entiercement de clé.

  • IXmlRepository, qui contrôle l’emplacement où les clés sont conservées dans le stockage.

  • IXmlEncryptor [facultatif], qui autorise le chiffrement des clés au repos.

  • IKeyEscrowSink [facultatif], qui fournit des services d’entiercement de clé.

Vous trouverez ci-dessous des diagrammes généraux qui indiquent comment ces composants sont câblés ensemble dans XmlKeyManager.

Key Creation

Création de clé / CreateNewKey

Dans l’implémentation de CreateNewKey, le AlgorithmConfiguration composant est utilisé pour créer un unique IAuthenticatedEncryptorDescriptor, qui est ensuite sérialisé au format XML. Si un récepteur d’entiercement de clé est présent, le xml brut (non chiffré) est fourni au récepteur pour le stockage à long terme. Le code XML non chiffré est ensuite exécuté via un IXmlEncryptor (si nécessaire) pour générer le document XML chiffré. Ce document chiffré est conservé dans le stockage à long terme via le IXmlRepository. (Si aucun IXmlEncryptor n’est configuré, le document non chiffré est conservé dans IXmlRepository.)

Key Retrieval

Key Creation

Création de clé / CreateNewKey

Dans l’implémentation de CreateNewKey, le IAuthenticatedEncryptorConfiguration composant est utilisé pour créer un unique IAuthenticatedEncryptorDescriptor, qui est ensuite sérialisé au format XML. Si un récepteur d’entiercement de clé est présent, le xml brut (non chiffré) est fourni au récepteur pour le stockage à long terme. Le code XML non chiffré est ensuite exécuté via un IXmlEncryptor (si nécessaire) pour générer le document XML chiffré. Ce document chiffré est conservé dans le stockage à long terme via le IXmlRepository. (Si aucun IXmlEncryptor n’est configuré, le document non chiffré est conservé dans IXmlRepository.)

Key Retrieval

Récupération de clé / GetAllKeys

Dans l’implémentation de GetAllKeys, les documents XML représentant les clés et les révocations sont lus à partir du IXmlRepository sous-jacent. Si ces documents sont chiffrés, le système les déchiffre automatiquement. XmlKeyManager crée les instances appropriées IAuthenticatedEncryptorDescriptorDeserializer pour désérialiser les documents en instances IAuthenticatedEncryptorDescriptor, qui sont ensuite encapsulées dans des instances individuelles IKey. Cette collection d’instances IKey est retournée à l’appelant.

Vous trouverez plus d’informations sur les éléments XML particuliers dans le document sur le format de stockage de clé.

IXmlRepository

L’interface IXmlRepository représente un type qui peut conserver du XML dans et récupérer du CODE à partir d’un magasin de stockage. Il expose deux API :

  • GetAllElements :IReadOnlyCollection<XElement>

  • StoreElement(XElement element, string friendlyName)

Les implémentations de IXmlRepository n’ont pas besoin d’analyser le code XML qui les traverse. Ils doivent traiter les documents XML comme opaques et laisser les couches supérieures se soucier de la génération et de l’analyse des documents.

Il existe quatre types de béton intégrés qui implémentent IXmlRepository :

Pour plus d’informations, consultez le document fournisseurs de stockage de clés .

L’inscription d’un IXmlRepository personnalisé est appropriée lors de l’utilisation d’un autre magasin de stockage (par exemple, Stockage Table Azure).

Pour modifier le dépôt par défaut à l’échelle de l’application, inscrivez une instance personnalisé IXmlRepository :

services.Configure<KeyManagementOptions>(options => options.XmlRepository = new MyCustomXmlRepository());
services.AddSingleton<IXmlRepository>(new MyCustomXmlRepository());

IXmlEncryptor

L’interface IXmlEncryptor représente un type qui peut chiffrer un élément XML en texte clair. Elle expose une seule API :

  • Encrypt(XElement en texte brut) : EncryptedXmlInfo

Si un IAuthenticatedEncryptorDescriptor sérialisé contient des éléments marqués comme « nécessite un chiffrement », XmlKeyManager exécute ces éléments via la méthode IXmlEncryptor configuréeEncrypt et conserve l’élément chiffré plutôt que l’élément en texte clair dans IXmlRepository. La sortie de la méthode Encrypt est un objet EncryptedXmlInfo. Cet objet est un wrapper qui contient à la fois le résultat chiffré XElement et le type qui représente un IXmlDecryptor qui peut être utilisé pour déchiffrer l’élément correspondant.

Il existe quatre types de béton intégrés qui implémentent IXmlEncryptor :

Pour plus d’informations, consultez le document Chiffrement de clé au repos .

Pour modifier le mécanisme de chiffrement de clé au repos par défaut à l’échelle de l’application, inscrivez une instance personnalisé IXmlEncryptor :

services.Configure<KeyManagementOptions>(options => options.XmlEncryptor = new MyCustomXmlEncryptor());
services.AddSingleton<IXmlEncryptor>(new MyCustomXmlEncryptor());

IXmlDecryptor

L’interface IXmlDecryptor représente un type qui sait comment déchiffrer un XElement qui a été chiffré via un IXmlEncryptor. Elle expose une seule API :

  • Decrypt(XElement encryptedElement) : XElement

La méthode Decrypt annule le chiffrement effectué par IXmlEncryptor.Encrypt. En règle générale, chaque implémentation concrète IXmlEncryptor aura une implémentation concrète IXmlDecryptor correspondante.

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

  • .ctor(IServiceProvider)
  • .ctor()

Notes

Le IServiceProvider passé au constructeur peut être null.

IKeyEscrowSink

L’interface IKeyEscrowSink représente un type qui peut effectuer l’entiercement d’informations sensibles. Rappelez-vous que les descripteurs sérialisés peuvent contenir des informations sensibles (telles que du matériel de chiffrement), et c’est ce qui a conduit à l’introduction du type IXmlEncryptor en premier lieu. Toutefois, des accidents se produisent et les boucles de clés peuvent être supprimées ou endommagées.

L’interface d’entiercement fournit une trappe d’échappement d’urgence, ce qui permet d’accéder au code XML sérialisé brut avant qu’il ne soit transformé par n’importe quel IXmlEncryptor configuré. L’interface expose une seule API :

  • Store(Guid keyId, élément XElement)

Il appartient à l’implémentation IKeyEscrowSink de gérer l’élément fourni de manière sécurisée et cohérente avec la stratégie métier. Une implémentation possible pourrait être que le récepteur d’entiercement chiffre l’élément XML à l’aide d’un certificat X.509 d’entreprise connu où la clé privée du certificat a été séquestré ; le CertificateXmlEncryptor type peut vous aider avec cela. L’implémentation IKeyEscrowSink est également responsable de la persistance appropriée de l’élément fourni.

Par défaut, aucun mécanisme d’entiercement n’est activé, bien que les administrateurs de serveur puissent le configurer globalement. Il peut également être configuré par programmation via la méthode IDataProtectionBuilder.AddKeyEscrowSink comme indiqué dans l’exemple ci-dessous. La méthode AddKeyEscrowSink surcharge miroir les surcharges IServiceCollection.AddSingleton et IServiceCollection.AddInstance , car les instances IKeyEscrowSink sont destinées à être des singletons. Si plusieurs instances IKeyEscrowSink sont inscrites, chacune d’elles est appelée pendant la génération de clé, de sorte que les clés peuvent être séquestrés simultanément à plusieurs mécanismes.

Il n’existe aucune API pour lire les documents d’une instance IKeyEscrowSink. Cela est cohérent avec la théorie de conception du mécanisme d’entiercement : il est destiné à rendre le matériel clé accessible à une autorité de confiance, et puisque l’application n’est pas elle-même une autorité de confiance, elle ne doit pas avoir accès à ses propres documents bloqués.

L’exemple de code suivant illustre la création et l’inscription d’un IKeyEscrowSink où les clés sont séquestrés de telle sorte que seuls les membres de « CONTOSODomain Admins » puissent les récupérer.

Notes

Pour exécuter cet exemple, vous devez être sur un ordinateur Windows 8/Windows Server 2012 joint à un domaine, et le contrôleur de domaine doit être Windows Server 2012 ou version ultérieure.

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