Schlüsselverwaltung in ASP.NET Core

Das Datenschutzsystem verwaltet automatisch die Lebensdauer der Hauptschlüssel, die zum Schutz und zur Aufhebung des Schutzes von Nutzdaten verwendet werden. Jeder Schlüssel kann in einer von vier Phasen existieren:

  • Erstellt: Der Schlüssel ist im Schlüsselbund vorhanden, wurde aber noch nicht aktiviert. Der Schlüssel sollte erst dann für neue Protect-Vorgänge verwendet werden, wenn genügend Zeit verstrichen ist, damit sich der Schlüssel auf allen Rechnern, die diesen Schlüsselbund verwenden, verteilen konnte.

  • Aktiv: Der Schlüssel befindet sich im Schlüsselbund und sollte für alle neuen Protect-Vorgänge verwendet werden.

  • Abgelaufen: Der Schlüssel hat seine natürliche Lebensdauer erreicht und sollte nicht mehr für neue Protect-Vorgänge verwendet werden.

  • Widerrufen: Der Schlüssel ist kompromittiert und darf nicht für neue Protect-Vorgänge verwendet werden.

Erstellte, aktive und abgelaufene Schlüssel können alle verwendet werden, um den Schutz eingehender Nutzdaten aufzuheben. Standardmäßig dürfen widerrufene Schlüssel nicht zum Aufheben des Schutzes von Nutzdaten verwendet werden, aber der Anwendungsentwickler kann dieses Verhalten bei Bedarf außer Kraft setzen.

Warnung

Der Entwickler könnte versucht sein, einen Schlüssel aus dem Schlüsselbund zu löschen (z. B. indem er die entsprechende Datei aus dem Dateisystem löscht). Zu diesem Zeitpunkt sind alle durch den Schlüssel geschützten Daten dauerhaft nicht mehr zu entschlüsseln. Es gibt keine Möglichkeit, den Schlüssel im Notfall außer Kraft zu setzen, wie es bei widerrufenen Schlüsseln der Fall ist. Das Löschen eines Schlüssels ist ein zutiefst destruktives Verhalten, daher bietet das Datenschutzsystem keine erstklassige API für die Durchführung dieses Vorgangs.

Standardschlüsselauswahl

Wenn das Datenschutzsystem den Schlüsselbund aus dem Sicherungsrepository liest, versucht es, einen „Standardschlüssel“ im Schlüsselbund zu finden. Der Standardschlüssel wird für neue Protect-Vorgänge verwendet.

Die allgemeine Heuristik besagt, dass das Datenschutzsystem den Schlüssel mit dem jüngsten Aktivierungsdatum als Standardschlüssel auswählt. (Es gibt einen kleinen Fudge-Faktor, um die Zeitverschiebung zwischen den Servern zu berücksichtigen.) Wenn der Schlüssel abgelaufen ist oder widerrufen wurde und wenn die Anwendung die automatische Schlüsselgenerierung nicht deaktiviert hat, wird ein neuer Schlüssel mit sofortiger Aktivierung gemäß der unten aufgeführten Richtlinie für das Ablaufen und die Erneuerung von Schlüsseln generiert.

Der Grund dafür, dass das Datenschutzsystem sofort einen neuen Schlüssel generiert, anstatt auf einen anderen Schlüssel zurückzugreifen, ist, dass die Generierung eines neuen Schlüssels als implizites Ablaufen aller Schlüssel behandelt werden sollte, die vor dem neuen Schlüssel aktiviert wurden. Der Grundgedanke ist, dass neue Schlüssel möglicherweise mit anderen Algorithmen oder Verschlüsselungsmechanismen konfiguriert wurden als die alten Schlüssel und das System die aktuelle Konfiguration dem Rückfall vorziehen sollte.

Es gibt eine Ausnahme. Wenn der Anwendungsentwickler die automatische Schlüsselgenerierung deaktiviert hat, muss das Datenschutzsystem etwas als Standardschlüssel auswählen. In diesem Fallback-Szenario wählt das System einen nicht widerrufenen Schlüssel mit dem jüngsten Aktivierungsdatum aus, wobei Schlüssel bevorzugt werden, die bereits Zeit hatten, sich auf andere Rechner im Cluster zu übertragen. Das Fallback-System wählt daher möglicherweise einen abgelaufenen Standardschlüssel. Das Fallback-System wird niemals einen widerrufenen Schlüssel als Standardschlüssel wählen. Wenn der Schlüsselbund leer ist oder alle Schlüssel widerrufen wurden, wird das System bei der Initialisierung einen Fehler ausgeben.

Ablauf und Erneuerung von Schlüsseln

Wenn ein Schlüssel erstellt wird, erhält er automatisch ein Aktivierungsdatum von { now + 2 days } und ein Ablaufdatum von { now + 90 days }. Die 2-tägige Verzögerung vor der Aktivierung gibt dem Schlüssel Zeit, sich im System zu verteilen. Das bedeutet, dass andere Anwendungen, die auf den Sicherungsspeicher verweisen, den Schlüssel bei ihrer nächsten automatischen Auffrischung erkennen können. Auf diese Weise wird die Wahrscheinlichkeit maximiert, dass der Schlüsselbund, wenn er aktiv wird, an alle Anwendungen weitergegeben wurde, die ihn möglicherweise verwenden müssen.

Wenn der Standardschlüssel innerhalb von 2 Tagen abläuft und der Schlüsselbund nicht bereits über einen Schlüssel verfügt, der nach Ablauf des Standardschlüssels aktiv wird, wird das Datenschutzsystem automatisch einen neuen Schlüssel im Schlüsselbund speichern. Dieser neue Schlüssel hat ein Aktivierungsdatum von { default key's expiration date } und ein Ablaufdatum von { now + 90 days }. Dies ermöglicht es dem System, die Schlüssel regelmäßig und ohne Unterbrechung des Dienstes automatisch auszutauschen.

Unter bestimmten Umständen kann ein Schlüssel mit sofortiger Aktivierung erstellt werden. Ein Beispiel wäre, wenn die Anwendung eine Zeit lang nicht ausgeführt wurde und alle Schlüssel im Schlüsselbund abgelaufen sind. In diesem Fall erhält der Schlüssel ein Aktivierungsdatum von { now } ohne die normale 2-tägige Aktivierungsverzögerung.

Die Standard-Lebensdauer des Schlüssels beträgt 90 Tage, ist aber wie im folgenden Beispiel konfigurierbar.

services.AddDataProtection()
       // use 14-day lifetime instead of 90-day lifetime
       .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

Ein Administrator kann die Standardeinstellung auch systemweit ändern, obwohl ein expliziter Aufruf von SetDefaultKeyLifetime jede systemweite Richtlinie außer Kraft setzt. Die Lebensdauer des Standardschlüssels darf nicht kürzer als 7 Tage sein.

Automatische Aktualisierung des Schlüsselbunds

Wenn das Datenschutzsystem initialisiert wird, liest es den Schlüsselbund aus dem zugrunde liegenden Repository und speichert ihn im Cache. Dieser Cache ermöglicht es, Vorgänge zum Schützen und Aufheben des Schutzes durchzuführen, ohne auf den Sicherungsspeicher zuzugreifen. Das System überprüft den Sicherungsspeicher etwa alle 24 Stunden oder wenn der aktuelle Standardschlüssel abläuft, je nachdem, was zuerst eintritt, auf Änderungen.

Warnung

Entwickler sollten nur sehr selten (wenn überhaupt) die Schlüsselverwaltungs-APIs direkt verwenden müssen. Das Datenschutzsystem führt eine automatische Schlüsselverwaltung wie oben beschrieben durch.

Das Datenschutzsystem stellt eine Schnittstelle IKeyManager zur Verfügung, über die Sie den Schlüsselbund einsehen und Änderungen vornehmen können. Das DI-System, das die Instanz von IDataProtectionProvider bereitgestellt hat, kann auch eine Instanz von IKeyManager für Sie bereitstellen. Alternativ können Sie IKeyManager auch direkt aus IServiceProvider pullen, wie in dem Beispiel unten.

Jeder Vorgang, der den Schlüsselbund verändert (explizite Erstellung eines neuen Schlüssels oder Durchführung einer Sperrung), macht den speicherinternen Cache ungültig. Der nächste Aufruf von Protect oder Unprotect veranlasst das Datenschutzsystem, den Schlüsselbund erneut zu lesen und den Cache neu zu erstellen.

Das folgende Beispiel zeigt, wie Sie über die IKeyManager-Schnittstelle den Schlüsselbund prüfen und bearbeiten können, einschließlich des Widerrufs vorhandener Schlüssel und der manuellen Erzeugung eines neuen Schlüssels.

using System;
using System.IO;
using System.Threading;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection()
            // point at a specific folder and use DPAPI to encrypt keys
            .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys"))
            .ProtectKeysWithDpapi();
        var services = serviceCollection.BuildServiceProvider();

        // perform a protect operation to force the system to put at least
        // one key in the key ring
        services.GetDataProtector("Sample.KeyManager.v1").Protect("payload");
        Console.WriteLine("Performed a protect operation.");
        Thread.Sleep(2000);

        // get a reference to the key manager
        var keyManager = services.GetService<IKeyManager>();

        // list all keys in the key ring
        var allKeys = keyManager.GetAllKeys();
        Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
        foreach (var key in allKeys)
        {
            Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
        }

        // revoke all keys in the key ring
        keyManager.RevokeAllKeys(DateTimeOffset.Now, reason: "Revocation reason here.");
        Console.WriteLine("Revoked all existing keys.");

        // add a new key to the key ring with immediate activation and a 1-month expiration
        keyManager.CreateNewKey(
            activationDate: DateTimeOffset.Now,
            expirationDate: DateTimeOffset.Now.AddMonths(1));
        Console.WriteLine("Added a new key.");

        // list all keys in the key ring
        allKeys = keyManager.GetAllKeys();
        Console.WriteLine($"The key ring contains {allKeys.Count} key(s).");
        foreach (var key in allKeys)
        {
            Console.WriteLine($"Key {key.KeyId:B}: Created = {key.CreationDate:u}, IsRevoked = {key.IsRevoked}");
        }
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Performed a protect operation.
 * The key ring contains 1 key(s).
 * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = False
 * Revoked all existing keys.
 * Added a new key.
 * The key ring contains 2 key(s).
 * Key {1b948618-be1f-440b-b204-64ff5a152552}: Created = 2015-03-18 22:20:49Z, IsRevoked = True
 * Key {2266fc40-e2fb-48c6-8ce2-5fde6b1493f7}: Created = 2015-03-18 22:20:51Z, IsRevoked = False
 */

Wenn Sie möchten, dass Codekommentare in anderen Sprachen als Englisch angezeigt werden, informieren Sie uns in diesem GitHub-Issue.

Schlüsselspeicher

Das Datenschutzsystem verfügt über eine Heuristik, mit der es automatisch versucht, einen geeigneten Speicherort für den Schlüssel und einen Verschlüsselungsmechanismus im Ruhezustand zu ermitteln. Der Mechanismus für die Schlüsselpersistenz ist ebenfalls vom App-Entwickler konfigurierbar. Die folgenden Dokumente behandeln die integrierten Implementierungen dieser Mechanismen: