ASP.NET Core에서 암호 해시Hash passwords in ASP.NET Core

데이터 보호 코드 베이스에는 암호화 키 파생 함수를 제공해주는 Microsoft.AspNetCore.Cryptography.KeyDerivation 패키지가 포함되어 있습니다.The data protection code base includes a package Microsoft.AspNetCore.Cryptography.KeyDerivation which contains cryptographic key derivation functions. 이 패키지는 독립적인 구성 요소로서 데이터 보호 시스템의 나머지 다른 부분에 의존하지 않습니다.This package is a standalone component and has no dependencies on the rest of the data protection system. 이 패키지는 완벽히 독립적으로 사용이 가능합니다.It can be used completely independently. 다만 편의상 소스가 데이터 보호 코드 베이스와 함께 위치해 있을 뿐입니다.The source exists alongside the data protection code base as a convenience.

이 패키지는 PBKDF2 알고리즘을 이용해서 비밀번호 해싱을 수행하는 KeyDerivation.Pbkdf2 메서드를 지원합니다.The package currently offers a method KeyDerivation.Pbkdf2 which allows hashing a password using the PBKDF2 algorithm. 이 API는 .NET 프레임워크의 기존 Rfc2898DeriveBytes 형식과 매우 유사하지만, 세 가지 중요한 차이점이 존재합니다:This API is very similar to the .NET Framework's existing Rfc2898DeriveBytes type, but there are three important distinctions:

  1. KeyDerivation.Pbkdf2 메서드는 다양한 PRF를 사용할 수 있는 반면 (현재 HMACSHA1, HMACSHA256, 그리고 HMACSHA512를 지원), Rfc2898DeriveBytes 형식은 HMACSHA1만 지원합니다.The KeyDerivation.Pbkdf2 method supports consuming multiple PRFs (currently HMACSHA1, HMACSHA256, and HMACSHA512), whereas the Rfc2898DeriveBytes type only supports HMACSHA1.

  2. KeyDerivation.Pbkdf2 메서드는 현재 운영 체제를 감지해서 가장 최적화된 구현 루틴을 선택하기 때문에 상황에 따라 훨씬 향상된 성능을 제공합니다.The KeyDerivation.Pbkdf2 method detects the current operating system and attempts to choose the most optimized implementation of the routine, providing much better performance in certain cases. (Windows 8에서 Rfc2898DeriveBytes보다 약 10배에 가까운 성능을 보여줍니다.)(On Windows 8, it offers around 10x the throughput of Rfc2898DeriveBytes.)

  3. KeyDerivation.Pbkdf2 메서드는 호출자가 모든 매개 변수를 지정해야 합니다 (솔트, PRF, 그리고 반복 횟수까지).The KeyDerivation.Pbkdf2 method requires the caller to specify all parameters (salt, PRF, and iteration count). 반면 Rfc2898DeriveBytes 형식은 이에 대한 기본값들을 제공해줍니다.The Rfc2898DeriveBytes type provides default values for these.

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 secure PRNG
        byte[] salt = new byte[128 / 8];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(salt);
        }
        Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");
 
        // derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
        string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
            password: password,
            salt: salt,
            prf: KeyDerivationPrf.HMACSHA1,
            iterationCount: 10000,
            numBytesRequested: 256 / 8));
        Console.WriteLine($"Hashed: {hashed}");
    }
}
 
/*
 * SAMPLE OUTPUT
 *
 * Enter a password: Xtw9NMgx
 * Salt: NZsP6NnmfBuYeJrrAKNuVQ==
 * Hashed: /OOoOer10+tGwTRDTrQSoeCxVTFr6dtYly7d0cPxIak=
 */

실제 사용 사례를 살펴보려면 ASP.NET Core Identity의 PasswordHasher 형식의 소스 코드를 참고하시기 바랍니다.See the source code for ASP.NET Core Identity's PasswordHasher type for a real-world use case.