Administración de claves en ASP.NET Core

El sistema de protección de datos administra automáticamente la duración de las claves maestras usadas para proteger y desproteger cargas. Cada clave puede existir en una de las cuatro fases:

  • Creado: la clave existe en el anillo de teclas, pero aún no se ha activado. La clave no debe usarse para las nuevas operaciones de Protección hasta que haya transcurrido suficiente tiempo, de forma que la clave haya tenido la oportunidad de propagarse a todas las máquinas que consuman este anillo de claves.

  • Activo: la clave existe en el anillo de claves y se debería usar para todas las nuevas operaciones de Protección.

  • Expirado: la clave ejecutó su duración natural y ya no se debería usar para las nuevas operaciones de Protección.

  • Revocado: la clave está en peligro y no debería usarse para las nuevas operaciones de Protección.

Las claves creadas, activas y expiradas se pueden usar para desproteger las cargas entrantes. Es posible que las claves revocadas de forma predeterminada no se usen para desproteger cargas, pero el desarrollador de aplicaciones podría invalidar este comportamiento si fuera necesario.

Advertencia

Es posible que el desarrollador tenga la tentación de eliminar una clave del anillo de claves (por ejemplo, eliminando el archivo correspondiente del sistema de archivos). En ese momento, todos los datos protegidos por la clave no se podrán descifrar de forma permanente y no habrá ninguna invalidación de emergencia como la de las claves revocadas. La eliminación de una clave supone un comportamiento realmente destructivo y, por consiguiente, el sistema de protección de datos no expondrá ninguna API de primera clase para realizar esta operación.

Selección de clave predeterminada

Cuando el sistema de protección de datos lea el anillo de claves del repositorio de respaldo, intentará localizar una clave "predeterminada" del anillo de claves. La clave predeterminada se usa para las nuevas operaciones de Protección.

El modelo heurístico general consiste en que el sistema de protección de datos elegirá la clave con la fecha de activación más reciente como clave predeterminada. (Hay un pequeño factor de error que permite la asimetría de reloj de servidor a servidor). Si la clave hubiera expirado o revocado, y si la aplicación no hubiera deshabilitado la generación automática de claves, se generará una nueva clave con activación inmediata por la expiración de la clave y la reversión de la directiva siguiente.

La razón por la que el sistema de protección de datos generará una nueva clave inmediatamente en lugar de volver a otra clave es que la nueva generación de claves se debería tratar como una expiración implícita de todas las claves que se activaron antes de la nueva clave. La idea general es que las nuevas claves se hayan configurado con diferentes algoritmos o mecanismos de cifrado en reposo que las claves antiguas, y el sistema debería preferir la configuración actual sobre la reversión.

Hay una excepción. Si el desarrollador de aplicaciones hubiera deshabilitado la generación automática de claves, el sistema de protección de datos deberá elegir algo como clave predeterminada. En este escenario de reserva, el sistema elegirá la clave no revocada con la fecha de activación más reciente, con preferencia dada a las claves que hayan tenido tiempo de propagarse a otras máquinas del clúster. El sistema de reserva podría acabar eligiendo una clave predeterminada expirada como resultado. El sistema de reserva nunca elegirá una clave revocada como clave predeterminada y, si el anillo de teclas estuviera vacío o se hubiera revocado cada clave, el sistema producirá un error al inicializarse.

Expiración y reversión de claves

Cuando se crea una clave, se le asigna automáticamente una fecha de activación de { ahora + 2 días } y una fecha de expiración de { ahora + 90 días }. El retraso de 2 días antes de la activación proporciona el tiempo clave para la propagación a través del sistema. Es decir, permite que otras aplicaciones que apunten a la memoria auxiliar observen la clave en su siguiente período de actualización automática, maximizando las posibilidades de que cuando el anillo de claves se active se haya propagado a todas las aplicaciones que podrían necesitar su uso.

Si la clave predeterminada expirara en un plazo de 2 días y si el anillo de claves aún no tuviera una clave que estuviera activa tras la expiración de la clave predeterminada, el sistema de protección de datos conservará automáticamente una nueva clave en el anillo de claves. Esta nueva clave tiene una fecha de activación de { fecha de expiración de la clave predeterminada } y una fecha de expiración de { ahora + 90 días }. Esto permitirá que el sistema implemente automáticamente las claves de forma periódica sin interrupción del servicio.

Podría haber circunstancias en las que se creará una clave con activación inmediata. Un ejemplo sería cuando la aplicación no se haya ejecutado durante un tiempo y todas las claves del anillo de claves hayan expirado. Cuando esto suceda, la clave recibirá una fecha de activación de { ahora } sin el retraso normal de activación de 2 días.

La duración predeterminada de la clave es de 90 días, aunque es posible configurarla como en el ejemplo siguiente.

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

Un administrador también puede cambiar el valor predeterminado de todo el sistema, aunque una llamada explícita a SetDefaultKeyLifetime invalidará cualquier directiva de todo el sistema. La duración de la clave predeterminada no puede ser inferior a 7 días.

Actualización automática del anillo de claves

Cuando se inicialice el sistema de protección de datos, leerá el anillo de claves del repositorio subyacente y lo almacenará en la memoria caché. Esta caché permite que las operaciones Proteger y Desproteger continúen sin alcanzar la memoria auxiliar. El sistema comprobará automáticamente la memoria auxiliar para ver los cambios cada 24 horas, aproximadamente, o cuando expire la clave predeterminada actual, lo que ocurra primero.

Advertencia

Los desarrolladores necesitarán muy rara vez (si es que alguna) usar directamente las API de administración de claves. El sistema de protección de datos realizará la administración automática de claves tal y como se ha descrito anteriormente.

El sistema de protección de datos expone una interfaz IKeyManager que se puede usar para inspeccionar y realizar cambios en el anillo de claves. El sistema de DI que proporcionó la instancia de IDataProtectionProvider también puede proporcionar una instancia de IKeyManager para su consumo. Como alternativa, podría extraer directamente IKeyManager de IServiceProvider como en el ejemplo siguiente.

Cualquier operación que modifique el anillo de claves (creando una nueva clave explícitamente o realizando una revocación) invalidará la caché en memoria. La siguiente llamada a Protect o Unprotect hará que el sistema de protección de datos vuelva a leer el anillo de claves y vuelva a crear la caché.

En el ejemplo siguiente, se muestra cómo usar la interfaz IKeyManager para inspeccionar y manipular el anillo de teclas, incluyendo la revocación de claves existentes y la generación manual de nuevas clave.

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

Si quiere que los comentarios de código se traduzcan en más idiomas además del inglés, háganoslo saber en este problema de debate de GitHub.

Almacenamiento de claves

El sistema de protección de datos tiene un modelo heurístico en el que intenta deducir automáticamente una ubicación de almacenamiento de claves adecuada y un mecanismo de cifrado en reposo. El desarrollador de aplicaciones también puede configurar el mecanismo de persistencia de claves. En los siguientes documentos se describen las implementaciones incluidas de estos mecanismos: