Partilhar via


Gestão de chaves no ASP.NET Core

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

  • Criado - a chave existe no porta-chaves, mas ainda não foi ativada. A chave não deve ser usada para novas operações do Protect até que tenha decorrido tempo suficiente para que a chave tenha tido a chance de se propagar para todas as máquinas que estão consumindo esse porta-chaves.

  • Ativo - a chave existe no porta-chaves e deve ser usada para todas as novas operações do Protect.

  • Expirado - a chave teve sua vida útil natural e não deve mais ser usada para novas operações do Protect.

  • Revogada - a chave está comprometida e não deve ser usada para novas operações do Protect.

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

Advertência

O desenvolvedor pode ficar tentado a excluir uma chave do porta-chaves (por exemplo, excluindo o arquivo correspondente do sistema de arquivos). Nesse ponto, todos os dados protegidos pela chave ficam permanentemente indecifráveis e não há substituição de emergência como acontece com as chaves revogadas. Eliminar uma chave é um comportamento profundamente destrutivo.

Seleção de teclas padrão

Quando o sistema de proteção de dados lê o conjunto de chaves do repositório de suporte, ele tentará localizar uma chave "padrão" do conjunto de chaves. A chave padrão é usada para novas operações do Protect.

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 ajuste para permitir o desvio do relógio de servidor para servidor.) Se a chave tiver expirado ou sido revogada, e se a aplicação não tiver desativado 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 dinâmica descritas abaixo.

A razão pela qual o sistema de proteção de dados gera uma nova chave imediatamente, em vez de cair de volta para uma chave diferente, é que a nova geração de chaves deve ser tratada como uma expiração implícita de todas as chaves que foram ativadas antes da nova chave. A ideia geral é que as novas chaves podem ter sido configuradas com algoritmos diferentes ou mecanismos de encriptação em repouso distintos das chaves antigas, e o sistema deve preferir a configuração atual em invés de reverter.

Há uma exceção. Se o desenvolvedor de aplicativos tiver desabilitada a geração automática de chaves , então o sistema de proteção de dados deverá escolher algo como a chave padrão. Neste 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 se propagar para outras máquinas 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 porta-chaves estiver vazio ou todas as chaves tiverem sido revogadas, o sistema produzirá um erro na inicialização.

Expiração e rolagem de chaves

Quando uma chave é criada, ela recebe automaticamente uma data de ativação de { agora + 2 dias } e uma data de expiração de { agora + 90 dias }. O atraso de 2 dias antes da ativação dá o tempo chave para se propagar pelo sistema. Ou seja, ele permite que outros aplicativos apontando para o armazenamento de suporte observem a chave em seu próximo período de atualização automática, maximizando assim as chances de que, quando o porta-chaves se tornar ativo, ele tenha se propagado para todos os aplicativos que possam precisar usá-lo.

Se a chave padrão expirar dentro de 2 dias e se o porta-chaves 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 para o porta-chaves. Esta nova chave tem uma data de ativação de { data de expiração da chave padrão } e uma data de expiração de { agora + 90 dias }. Isso permite que o sistema role automaticamente as teclas regularmente, sem interrupção do serviço.

Pode haver circunstâncias em que uma chave será criada com ativação imediata. Um exemplo seria quando o aplicativo não é executado por um tempo e todas as chaves no porta-chaves expiram. Quando isso acontece, a chave recebe uma data de ativação de { now } sem o atraso normal de ativação de 2 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 inferior a 7 dias.

Atualização automática do porta-chaves

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

Advertência

Os desenvolvedores raramente (ou nunca) precisam usar as APIs de gerenciamento de chaves diretamente. O sistema de proteção de dados efetuará a gestão automática 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 porta-chaves. O sistema DI que forneceu a instância de IDataProtectionProvider também pode fornecer uma instância de IKeyManager para a sua utilização. Alternativamente, você pode puxar o IKeyManager diretamente do IServiceProvider como no exemplo abaixo.

Qualquer operação que modifique o porta-chaves (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 porta-chaves e recrie o cache.

O exemplo abaixo demonstra o uso da interface IKeyManager para inspecionar e manipular o porta-chaves, incluindo a revogação de chaves existentes e a geração manual de uma nova chave.

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ê gostaria de ver os comentários de código traduzidos para outros idiomas além do inglês, informe-nos em este problema de discussão do GitHub.

Armazenamento de chaves

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