Teilen über


Erweiterbarkeit der Schlüsselverwaltung in ASP.NET Core

Lesen Sie vor diesem Abschnitt den Abschnitt Schlüsselverwaltung, da darin einige der grundlegenden Konzepte dieser APIs erläutert werden.

Warnung: Typen, die eine der folgenden Schnittstellen implementieren, sollten für mehrere Aufrufer threadsicher sein.

Schlüssel

Die IKey-Schnittstelle ist die grundlegende Darstellung eines Schlüssels im Kryptosystem. Der Begriff Schlüssel wird hier im abstrakten und nicht im wörtlichen Sinne von „kryptografisches Schlüsselmaterial“ verwendet. Ein Schlüssel verfügt über folgende Eigenschaften:

  • Aktivierungs-, Erstellungs- und Ablaufdatum

  • Sperrstatus

  • Schlüsselbezeichner (eine GUID)

IKey macht außerdem eine CreateEncryptor-Methode verfügbar, die zum Erstellen einer IAuthenticatedEncryptor-Instanz verwendet werden kann, die an diesen Schlüssel gebunden ist.

IKey macht außerdem eine CreateEncryptorInstance-Methode verfügbar, die zum Erstellen einer IAuthenticatedEncryptor-Instanz verwendet werden kann, die an diesen Schlüssel gebunden ist.

Hinweis

Es gibt keine API zum Abrufen des kryptografischen Rohmaterials aus einer IKey-Instanz.

IKeyManager

Die IKeyManager-Schnittstelle stellt ein Objekt dar, das für allgemeines Speichern, Abrufen und Bearbeiten von Schlüsseln verantwortlich ist. Es macht drei wichtige Vorgänge verfügbar:

  • Erstellen eines neuen Schlüssels und dauerhaftes Speichern im Speicher.

  • Abrufen aller Schlüssel aus dem Speicher.

  • Widerrufen eines oder mehrerer Schlüssel, und dauerhaftes Speichern dieser Information im Speicher.

Warnung

Das Schreiben eines IKeyManager ist eine sehr fortgeschrittene Aufgabe, und die meisten Entwickler sollten sich nicht daran versuchen. Stattdessen sollten sie die von den in der XmlKeyManager-Klasse angebotenen Funktionen Gebrauch machen.

XmlKeyManager

Der XmlKeyManager-Typ ist die im Lieferumfang enthaltene konkrete Implementierung von IKeyManager. Er bietet mehrere nützliche Funktionen, einschließlich einer Hinterlegung von Schlüsseln sowie die Verschlüsselung ruhender Schlüssel. Schlüssel in diesem System werden als XML-Elemente (insbesondere XElement) dargestellt.

XmlKeyManager hängt im Zuge der Aufgabenerfüllung von mehreren anderen Komponenten ab:

  • AlgorithmConfiguration, die die von neuen Schlüsseln verwendeten Algorithmen diktiert.

  • IXmlRepository, das steuert, wo Schlüssel dauerhaft im Speicher gespeichert werden.

  • IXmlEncryptor [optional], der das Verschlüsseln ruhender Schlüssel ermöglicht.

  • IKeyEscrowSink [optional], das Hinterlegungsdienste für Schlüssel bereitstellt.

  • IXmlRepository, das steuert, wo Schlüssel dauerhaft im Speicher gespeichert werden.

  • IXmlEncryptor [optional], der das Verschlüsseln ruhender Schlüssel ermöglicht.

  • IKeyEscrowSink [optional], das Hinterlegungsdienste für Schlüssel bereitstellt.

Im Folgenden finden Sie allgemeine Diagramme, die angeben, wie diese Komponenten in XmlKeyManager zusammenwirken.

Key Creation

Schlüsselerstellung/CreateNewKey

Bei Implementierung von CreateNewKey wird die AlgorithmConfiguration-Komponente verwendet, um einen eindeutigen IAuthenticatedEncryptorDescriptor zu erstellen, der dann als XML serialisiert wird. Wenn eine Hinterlegungssenke vorhanden ist, wird die unformatierte (unverschlüsselte) XML-Datei der Senke zur langfristigen Speicherung bereitgestellt. Die unverschlüsselte XML-Datei durchläuft dann (falls erforderlich) einen IXmlEncryptor, um das verschlüsselte XML-Dokument zu generieren. Dieses verschlüsselte Dokument wird über das IXmlRepository langfristig im Speicher abgespeichert. (Wenn kein IXmlEncryptor konfiguriert ist, wird das unverschlüsselte Dokument im IXmlRepository gespeichert.)

Key Retrieval

Key Creation

Schlüsselerstellung/CreateNewKey

Bei Implementierung von CreateNewKey wird die IAuthenticatedEncryptorConfiguration-Komponente verwendet, um einen eindeutigen IAuthenticatedEncryptorDescriptor zu erstellen, der dann als XML serialisiert wird. Wenn eine Hinterlegungssenke vorhanden ist, wird die unformatierte (unverschlüsselte) XML-Datei der Senke zur langfristigen Speicherung bereitgestellt. Die unverschlüsselte XML-Datei durchläuft dann (falls erforderlich) einen IXmlEncryptor, um das verschlüsselte XML-Dokument zu generieren. Dieses verschlüsselte Dokument wird über das IXmlRepository langfristig im Speicher abgespeichert. (Wenn kein IXmlEncryptor konfiguriert ist, wird das unverschlüsselte Dokument im IXmlRepository gespeichert.)

Key Retrieval

Schlüsselabruf/GetAllKeys

Bei der Implementierung von GetAllKeys werden die XML-Dokumente, die Schlüssel und Sperrungen darstellen, aus dem zugrunde liegenden IXmlRepository ausgelesen. Wenn diese Dokumente verschlüsselt sind, entschlüsselt das System sie automatisch. XmlKeyManager erstellt die entsprechenden IAuthenticatedEncryptorDescriptorDeserializer-Instanzen, um die Dokumente wieder in IAuthenticatedEncryptorDescriptor-Instanzen zu deserialisieren, die dann in von einzelnen IKey-Instanzen umschlossen werden. Diese Auflistung von IKey-Instanzen wird an den Aufrufer zurückgegeben.

Weitere Informationen zu den jeweiligen XML-Elementen finden Sie im Dokument zum Schlüsselspeicherformat.

IXmlRepository

Die IXmlRepository-Schnittstelle stellt einen Typ dar, der XML in einem Sicherungsspeicher langfristig speichern und abrufen kann. Er macht zwei APIs verfügbar:

  • GetAllElements :IReadOnlyCollection<XElement>

  • StoreElement(XElement element, string friendlyName)

Implementierungen von IXmlRepository müssen die XML-Dateien, die sie durchlaufen, nicht parsen. Sie sollten die XML-Dokumente als undurchsichtig behandeln und es höheren Ebenen überlassen, die Dokumente zu generieren und zu parsen.

Es gibt vier integrierte konkrete Typen, die IXmlRepository implementieren:

Weitere Informationen finden Sie im Dokument über Anbieter von Schlüsselspeichern .

Die Registrierung eines benutzerdefinierten IXmlRepository Ist bei Verwendung eines anderen Sicherungsspeichers (z. B. Azure Table Storage) geeignet.

Um das Standardrepository für die gesamte Anwendung zu ändern, registrieren Sie eine benutzerdefinierte IXmlRepository-Instanz:

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

IXmlEncryptor

Die IXmlEncryptor-Schnittstelle stellt einen Typ dar, der ein XML-Element im Klartext verschlüsseln kann. Er macht eine einzelne API verfügbar:

  • Encrypt(XElement plaintextElement) : EncryptedXmlInfo

Wenn ein serialisierter IAuthenticatedEncryptorDescriptor ein Element enthält, das mit „erfordert Verschlüsselung“ gekennzeichnet ist, führt XmlKeyManager diese Elemente über die konfigurierte IXmlEncryptor-Methode von Encrypt aus, und das entschlüsselte Element wird anstelle des Klartextelements im IXmlRepository beibehalten. Die Ausgabe der Encrypt-Methode ist ein EncryptedXmlInfo-Objekt. Dieses Objekt ist ein Wrapper, der sowohl das resultierende entschlüsselte XElement als auch den Typ enthält, der einen IXmlDecryptor darstellt, mit dem das entsprechende Element entschlüsselt werden kann.

Es gibt vier integrierte konkrete Typen, die IXmlEncryptor implementieren:

Weitere Informationen finden Sie im Dokument zur Verschlüsselung ruhender Schlüssel.

Registrieren Sie eine benutzerdefinierte IXmlEncryptor-Instanz, um den Standardmechanismus für die Verschlüsselung ruhender Schlüssel anwendungsweit zu ändern:

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

IXmlDecryptor

Die IXmlDecryptor-Schnittstelle stellt einen Typ dar, der die Informationen darüber besitzt, wie ein XElement entschlüsselt wird, das mithilfe von IXmlEncryptor verschlüsselt wurde. Er macht eine einzelne API verfügbar:

  • Decrypt(XElement encryptedElement) : XElement

Die Decrypt-Methode hebt die von IXmlEncryptor.Encrypt durchgeführte Verschlüsselung auf. In der Regel wird jede konkrete IXmlEncryptor-Implementierung eine entsprechende konkrete IXmlDecryptor-Implementierung haben.

Typen, die IXmlDecryptor implementieren, sollten über einen der beiden folgenden öffentlichen Konstruktoren verfügen:

  • .ctor(IServiceProvider)
  • .ctor()

Hinweis

Der an den Konstruktor übergebene IServiceProvider kann NULL sein.

IKeyEscrowSink

Die IKeyEscrowSink-Schnittstelle stellt einen Typ dar, der die Hinterlegung vertraulicher Informationen durchführen kann. Denken Sie daran, dass serialisierte Deskriptoren möglicherweise vertrauliche Informationen (z. B. kryptografisches Material) enthalten. Diese Tatsache hat zur Einführung des IXmlEncryptor-Typs geführt. Es kann jedoch zu Unfällen kommen, bei denen Schlüsselringe gelöscht oder beschädigt werden.

Die Escrow-Schnittstelle bietet gewissermaßen einen Notausgang, der den Zugriff auf die unformatierte serialisierte XML-Datei ermöglicht, bevor sie von einem konfigurierten IXmlEncryptor transformiert wird. Die Schnittstelle macht eine einzelne API verfügbar:

  • Store(Guid keyId, XElement-Element)

Es ist die Aufgabe der IKeyEscrowSink-Implementierung, mit dem bereitgestellten Element auf sichere und den geschäftlichen Richtlinien entsprechenden Weise umzugehen. Eine mögliche Implementierung könnte darin bestehen, dass die Hinterlegungssenke das XML-Element mithilfe eines bekannten X.509-Unternehmenszertifikats verschlüsselt, bei dem der private Schlüssel des Zertifikats hinterlegt wurde. Dabei kann der CertificateXmlEncryptor-Typ kann dabei helfen. Die IKeyEscrowSink-Implementierung ist auch dafür verantwortlich, das das bereitgestellte Element entsprechend beibehalten wird.

Standardmäßig ist kein Hinterlegungsmechanismus aktiviert. Serveradministratoren können dies aber global konfigurieren. Er kann auch programmgesteuert über die IDataProtectionBuilder.AddKeyEscrowSink-Methode konfiguriert werden, wie im folgenden Beispiel gezeigt wird. Die AddKeyEscrowSink-Methodenüberladungen spiegeln die IServiceCollection.AddSingleton- und IServiceCollection.AddInstance-Überladungen, da IKeyEscrowSink-Instanzen Singletons sein sollen. Wenn mehrere IKeyEscrowSink-Instanzen registriert werden, wird jede während der Schlüsselgenerierung aufgerufen, sodass Schlüssel gleichzeitig an mehrere Mechanismen übergeben werden können.

Es gibt keine API zum Lesen von Material aus einer IKeyEscrowSink-Instanz. Dies steht im Einklang mit der Designtheorie des Hinterlegungsmechanismus: Er soll das Schlüsselmaterial einer vertrauenswürdige Autorität zugänglich machen, und da die Anwendung selbst keine solche ist, sollte sie keinen Zugriff auf das von ihr verwahrte Material haben.

Der folgende Beispielcode veranschaulicht das Erstellen und Registrieren einer IKeyEscrowSink, bei dem Schlüssel so hinterlegt werden, dass nur Mitglieder von „CONTOSODomain-Administratoren“ sie wiederherstellen können.

Hinweis

Zum Ausführen dieses Beispiels müssen Sie sich auf einem in die Domäne eingebundenen Windows 8-/Windows Server 2012-Computer befinden, und der Domänencontroller muss Windows Server 2012 oder höher sein.

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