Gestione delle chiavi in ASP.NET Core

Il sistema di protezione dei dati gestisce automaticamente la durata delle chiavi master usate per proteggere e rimuovere la protezione dei payload. Ogni chiave può esistere in una delle quattro fasi seguenti:

  • Creato: la chiave esiste nell'anello della chiave ma non è ancora stata attivata. La chiave non deve essere usata per le nuove operazioni di protezione fino a quando non è trascorso un tempo sufficiente per cui la chiave ha avuto la possibilità di propagarsi a tutti i computer che utilizzano questo anello chiave.

  • Attivo: la chiave esiste nell'anello di tasti e deve essere usata per tutte le nuove operazioni di protezione.

  • Scaduta: la chiave ha eseguito la sua durata naturale e non deve più essere usata per le nuove operazioni di protezione.

  • Revocata: la chiave viene compromessa e non deve essere usata per le nuove operazioni di protezione.

Le chiavi create, attive e scadute possono essere usate per rimuovere la protezione dei payload in ingresso. Le chiavi revocate per impostazione predefinita non possono essere usate per rimuovere la protezione dei payload, ma lo sviluppatore dell'applicazione può eseguire l'override di questo comportamento , se necessario.

Avviso

Lo sviluppatore potrebbe essere tentato di eliminare una chiave dall'anello del tasto (ad esempio, eliminando il file corrispondente dal file system). A questo punto, tutti i dati protetti dalla chiave non sono indecifrabili in modo permanente e non esiste alcun override di emergenza, ad esempio con chiavi revocate. L'eliminazione di una chiave è un comportamento veramente distruttivo e di conseguenza il sistema di protezione dei dati non espone alcuna API di prima classe per l'esecuzione di questa operazione.

Selezione della chiave predefinita

Quando il sistema di protezione dei dati legge l'anello di tasti dal repository di backup, tenterà di individuare una chiave "predefinita" dall'anello chiave. La chiave predefinita viene usata per le nuove operazioni protect.

L'euristica generale è che il sistema di protezione dei dati sceglie la chiave con la data di attivazione più recente come chiave predefinita. C'è un piccolo fattore di fudge per consentire l'asimmetria dell'orologio da server a server. Se la chiave è scaduta o revocata e se l'applicazione non ha disabilitato la generazione automatica della chiave, verrà generata una nuova chiave con attivazione immediata in base alla scadenza della chiave e ai criteri in sequenza seguenti.

Il motivo per cui il sistema di protezione dei dati genera immediatamente una nuova chiave anziché eseguire il fallback a una chiave diversa è che la nuova generazione di chiavi deve essere considerata come una scadenza implicita di tutte le chiavi attivate prima della nuova chiave. L'idea generale è che le nuove chiavi potrebbero essere state configurate con algoritmi o meccanismi di crittografia inattivi diversi rispetto alle chiavi precedenti e il sistema dovrebbe preferire la configurazione corrente rispetto al fallback.

C'è un'eccezione. Se lo sviluppatore dell'applicazione ha disabilitato la generazione automatica delle chiavi, il sistema di protezione dei dati deve scegliere qualcosa come chiave predefinita. In questo scenario di fallback, il sistema sceglierà la chiave non revocata con la data di attivazione più recente, con la preferenza assegnata alle chiavi che hanno avuto tempo per propagarsi ad altri computer nel cluster. Il sistema di fallback può finire per scegliere una chiave predefinita scaduta di conseguenza. Il sistema di fallback non sceglierà mai una chiave revocata come chiave predefinita e se l'anello della chiave è vuoto o ogni chiave è stata revocata, il sistema genererà un errore al momento dell'inizializzazione.

Scadenza e sequenza delle chiavi

Quando viene creata una chiave, viene automaticamente assegnata una data di attivazione di { now + 2 days } e una data di scadenza di { now + 90 days }. Il ritardo di 2 giorni prima dell'attivazione fornisce il tempo di propagazione della chiave nel sistema. Ciò significa che consente ad altre applicazioni che puntano all'archivio di backup di osservare la chiave al successivo periodo di aggiornamento automatico, ottimizzando così le probabilità che, quando l'anello di tasti diventa attivo, è stato propagato a tutte le applicazioni che potrebbero dover usarlo.

Se la chiave predefinita scadrà entro 2 giorni e se l'anello della chiave non dispone già di una chiave che sarà attiva alla scadenza della chiave predefinita, il sistema di protezione dei dati renderà automaticamente persistente una nuova chiave per l'anello di tasti. Questa nuova chiave ha una data di attivazione di { data di scadenza della chiave predefinita } e una data di scadenza di { now + 90 days }. Ciò consente al sistema di eseguire il rollback automatico delle chiavi a intervalli regolari senza interruzioni del servizio.

Potrebbe verificarsi un caso in cui verrà creata una chiave con attivazione immediata. Un esempio potrebbe essere quando l'applicazione non è stata eseguita per un periodo di tempo e tutte le chiavi nell'anello di chiave sono scadute. In questo caso, alla chiave viene assegnata una data di attivazione di { now } senza il normale ritardo di attivazione di 2 giorni.

La durata della chiave predefinita è di 90 giorni, anche se questa opzione è configurabile come nell'esempio seguente.

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

Un amministratore può anche modificare l'intero sistema predefinito, anche se una chiamata esplicita a SetDefaultKeyLifetime sostituirà qualsiasi criterio a livello di sistema. La durata della chiave predefinita non può essere inferiore a 7 giorni.

Aggiornamento automatico dell'anello di tasti

Quando il sistema di protezione dei dati viene inizializzato, legge l'anello di chiave dal repository sottostante e lo memorizza nella cache in memoria. Questa cache consente alle operazioni Proteggi e Rimuovi protezione di continuare senza colpire l'archivio di backup. Il sistema controlla automaticamente l'archivio di backup per verificare la presenza di modifiche circa ogni 24 ore o quando la chiave predefinita corrente scade, a seconda di quale sia la prima.

Avviso

Gli sviluppatori devono raramente (se mai) usare direttamente le API di gestione delle chiavi. Il sistema di protezione dei dati eseguirà la gestione automatica delle chiavi, come descritto in precedenza.

Il sistema di protezione dei dati espone un'interfaccia IKeyManager che può essere usata per controllare e apportare modifiche all'anello di tasti. Il sistema di inserimento delle dipendenze fornito dall'istanza di IDataProtectionProvider può anche fornire un'istanza di IKeyManager per l'utilizzo. In alternativa, è possibile eseguire il IKeyManager pull direttamente da IServiceProvider come nell'esempio seguente.

Qualsiasi operazione che modifica l'anello di chiave (creando una nuova chiave in modo esplicito o eseguendo una revoca) invaliderà la cache in memoria. La chiamata successiva a Protect o Unprotect farà in modo che il sistema di protezione dei dati rileggere l'anello di chiave e ricreare la cache.

L'esempio seguente illustra l'uso dell'interfaccia IKeyManager per controllare e modificare l'anello di tasti, inclusa la revoca delle chiavi esistenti e la generazione manuale di una nuova chiave.

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

Per visualizzare i commenti del codice tradotti in lingue diverse dall'inglese, segnalarlo in questo problema di discussione su GitHub.

Archiviazione chiavi

Il sistema di protezione dei dati ha un'euristica in base alla quale tenta di dedurre automaticamente un percorso di archiviazione delle chiavi appropriato e un meccanismo di crittografia inattivi. Il meccanismo di persistenza delle chiavi è configurabile anche dallo sviluppatore di app. I documenti seguenti illustrano le implementazioni predefinite di questi meccanismi: