Gerenciamento de chaves no ASP.NET Core

O sistema de proteção de dados gerencia automaticamente o tempo de vida de chaves mestras usadas para proteger e desproteger cargas. Cada chave pode existir em um dos quatro estágios:

  • Criada: a chave existe no chaveiro, mas ainda não foi ativada. A chave não deve ser usada para novas operações de Proteção até que tenha decorrido tempo suficiente para que a chave tenha tido a chance de se propagar para todos os computadores que estão consumindo esse chaveiro.

  • Ativa: a chave existe no chaveiro e deve ser usada para todas as novas operações de Proteção.

  • Expirada: a chave executou seu tempo de vida natural e não deve mais ser usada para novas operações de Proteção.

  • Revogada: a chave está comprometida e não deve ser usada para novas operações de Proteção.

As chaves criadas, ativas e expiradas podem ser usadas para desproteger as cargas de entrada. As chaves revogadas por padrão podem não ser usadas para desproteger cargas, mas o desenvolvedor do aplicativo pode substituir esse comportamento, se necessário.

Aviso

O desenvolvedor pode ser tentado a excluir uma chave do chaveiro (por exemplo, excluindo o arquivo correspondente do sistema de arquivos). Nesse ponto, todos os dados protegidos pela chave são permanentemente indecifráveis e não há substituição de emergência como há com chaves revogadas. Excluir uma chave é um comportamento verdadeiramente destrutivo e, consequentemente, o sistema de proteção de dados não expõe nenhuma API de primeira classe para executar essa operação.

Seleção de chave padrão

Quando o sistema de proteção de dados ler o chaveiro do repositório de backup, ele tentará localizar uma chave "padrão" a partir do chaveiro. A chave padrão é usada para novas operações de Proteção.

A heurística geral é que o sistema de proteção de dados escolhe a chave com a data de ativação mais recente como a chave padrão. (Há um pequeno fator de falsificação para permitir a distorção do relógio de servidor para servidor.) Se a chave tiver expirado ou sido revogada e se o aplicativo não tiver desabilitado a geração automática de chaves, uma nova chave será gerada com ativação imediata de acordo com a expiração da chave e a política sem interrupção abaixo.

O motivo pelo qual o sistema de proteção de dados gera uma nova chave imediatamente em vez de voltar para uma chave diferente é que a nova geração de chave deve ser tratada como uma expiração implícita de todas as chaves que foram ativadas antes da nova chave. A ideia geral é que novas chaves possam ter sido configuradas com algoritmos ou mecanismos de criptografia em repouso diferentes das chaves antigas, e o sistema deve preferir a configuração atual em vez de recuar.

Há uma exceção. Se o desenvolvedor de aplicativos tiver desabilitado a geração automática de chaves, o sistema de proteção de dados deverá escolher algo como a chave padrão. Nesse cenário de fallback, o sistema escolherá a chave não revogada com a data de ativação mais recente, com preferência dada às chaves que tiveram tempo de propagar para outros computadores no cluster. O sistema de fallback pode acabar escolhendo uma chave padrão expirada como resultado. O sistema de fallback nunca escolherá uma chave revogada como a chave padrão e, se o chaveiro estiver vazio ou cada chave tiver sido revogada, o sistema produzirá um erro após a inicialização.

Expiração e rolagem da chave

Quando uma chave é criada, ela recebe automaticamente uma data de ativação de { agora + 2 dias } e uma data de validade de { agora + 90 dias }. O atraso de dois dias antes da ativação dá tempo à chave para se propagar pelo sistema. Ou seja, isso permite que outros aplicativos que apontam para o repositório de backup observem a chave no próximo período de atualização automática, maximizando assim as chances de que, quando o chaveiro se tornar ativo, ele tenha se propagado para todos os aplicativos que possam precisar usá-la.

Se a chave padrão expirar dentro de dois dias e se o chaveiro ainda não tiver uma chave que estará ativa após a expiração da chave padrão, o sistema de proteção de dados persistirá automaticamente uma nova chave no chaveiro. Essa nova chave tem uma data de ativação de { data de validade da chave padrão } e uma data de validade de { agora + 90 dias }. Isso permite que o sistema alterne automaticamente chaves regularmente sem interrupção de serviço.

Pode haver circunstâncias em que uma chave será criada com ativação imediata. Um exemplo seria quando o aplicativo não for executado por um tempo e todas as chaves no chaveiro expirarem. Quando isso acontece, a chave recebe uma data de ativação de { now } sem o atraso normal de ativação de dois dias.

O tempo de vida da chave padrão é de 90 dias, embora isso seja configurável como no exemplo a seguir.

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

Um administrador também pode alterar o padrão em todo o sistema, embora uma chamada explícita para SetDefaultKeyLifetime substitua qualquer política em todo o sistema. O tempo de vida da chave padrão não pode ser menor que sete dias.

Atualização automática do chaveiro

Quando o sistema de proteção de dados é inicializado, ele lê o chaveiro do repositório subjacente e o armazena em cache na memória. Esse cache permite que as operações Proteger e Desproteger prossiga sem atingir o repositório de backup. O sistema verificará automaticamente o repositório de backup para alterações aproximadamente a cada 24 horas ou quando a chave padrão atual expirar, o que ocorrer primeiro.

Aviso

Os desenvolvedores devem precisar usar as APIs de gerenciamento de chaves diretamente muito raramente . O sistema de proteção de dados executará o gerenciamento automático de chaves, conforme descrito acima.

O sistema de proteção de dados expõe uma interface IKeyManager que pode ser usada para inspecionar e fazer alterações no chaveiro. O sistema de DI que forneceu a instância do IDataProtectionProvider também pode fornecer uma instância do IKeyManager para seu consumo. Como alternativa, você pode efetuar pull do IKeyManager diretamente do IServiceProvider como no exemplo abaixo.

Qualquer operação que modifique o chaveiro (criando uma nova chave explicitamente ou executando uma revogação) invalidará o cache na memória. A próxima chamada para Protect ou Unprotect fará com que o sistema de proteção de dados releia o chaveiro e recrie o cache.

O exemplo a seguir demonstra como usar a interface IKeyManager para inspecionar e manipular o anel de chaves, incluindo revogar chaves existentes e gerar uma nova chave manualmente.

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

Se você quiser ver os comentários de código traduzidos para idiomas diferentes do inglês, informe-nos neste problema de discussão do GitHub.

Armazenamento de chave

O sistema de proteção de dados tem uma heurística através da qual tenta deduzir automaticamente um local de armazenamento de chaves apropriado e um mecanismo de criptografia em repouso. O mecanismo de persistência de chave também é configurável pelo desenvolvedor do aplicativo. Os documentos a seguir discutem as implementações in-box desses mecanismos: