Fazer hash de senhas no ASP.NET Core

Este artigo mostra como chamar o método KeyDerivation.Pbkdf2 que permite fazer hash de uma senha usando o algoritmo PBKDF2.

Aviso

A API KeyDerivation.Pbkdf2 é um primitivo criptográfico de baixo nível que se destina a ser usado para integrar aplicativos a um protocolo ou sistema criptográfico existentes. O KeyDerivation.Pbkdf2 não deve ser usado em aplicativos novos compatíveis com logon baseado em senha e que precisam armazenar senhas com hash em um armazenamento de dados. Os aplicativos novos devem usar o PasswordHasher. Para obter mais informações sobre o PasswordHasher, confira Explorando o PasswordHasher de Identity no ASP.NET Core.

A base de código da proteção de dados inclui um pacote NuGet Microsoft.AspNetCore.Cryptography.KeyDerivation que contém funções de derivação de chave criptográfica. Esse pacote é um componente autônomo e não tem nenhuma dependência no restante do sistema de proteção de dados. O pacote pode ser usado de forma independente. A origem existe paralelamente à base de código da proteção de dados como uma conveniência.

Aviso

O código a seguir mostra como usar o KeyDerivation.Pbkdf2 para gerar uma chave secreta compartilhada. Não deve ser usado para fazer hash de uma senha para armazená-la em um armazenamento de dados.

using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using System.Security.Cryptography;

Console.Write("Enter a password: ");
string? password = Console.ReadLine();

// Generate a 128-bit salt using a sequence of
// cryptographically strong random bytes.
byte[] salt = RandomNumberGenerator.GetBytes(128 / 8); // divide by 8 to convert bits to bytes
Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");

// derive a 256-bit subkey (use HMACSHA256 with 100,000 iterations)
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
    password: password!,
    salt: salt,
    prf: KeyDerivationPrf.HMACSHA256,
    iterationCount: 100000,
    numBytesRequested: 256 / 8));

Console.WriteLine($"Hashed: {hashed}");

/*
 * SAMPLE OUTPUT
 *
 * Enter a password: Xtw9NMgx
 * Salt: CGYzqeN4plZekNC88Umm1Q==
 * Hashed: Gt9Yc4AiIvmsC1QQbe2RZsCIqvoYlst2xbz0Fs8aHnw=
 */
using System;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;

public class Program
{
    public static void Main(string[] args)
    {
        Console.Write("Enter a password: ");
        string password = Console.ReadLine();

        // generate a 128-bit salt using a cryptographically strong random sequence of nonzero values
        byte[] salt = new byte[128 / 8];
        using (var rngCsp = new RNGCryptoServiceProvider())
        {
            rngCsp.GetNonZeroBytes(salt);
        }
        Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");

        // derive a 256-bit subkey (use HMACSHA256 with 100,000 iterations)
        string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
            password: password,
            salt: salt,
            prf: KeyDerivationPrf.HMACSHA256,
            iterationCount: 100000,
            numBytesRequested: 256 / 8));
        Console.WriteLine($"Hashed: {hashed}");
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter a password: Xtw9NMgx
 * Salt: CGYzqeN4plZekNC88Umm1Q==
 * Hashed: Gt9Yc4AiIvmsC1QQbe2RZsCIqvoYlst2xbz0Fs8aHnw=
 */
 

Confira o código-fonte para uma Identity tipo PasswordHasher do ASP.NET Core para obter um caso de uso do mundo real.

Observação

Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).