첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

■ KeyDerivation 클래스의 Pbkdf2 정적 메소드를 사용해 패스워드 해시값을 구하는 방법을 보여준다.

TestProject.zip
0.00MB

▶ Program.cs

using System.Security.Cryptography;

using Microsoft.AspNetCore.Cryptography.KeyDerivation;

namespace TestProject;

/// <summary>
/// 프로그램
/// </summary>
class Program
{
    //////////////////////////////////////////////////////////////////////////////////////////////////// Method
    ////////////////////////////////////////////////////////////////////////////////////////// Static
    //////////////////////////////////////////////////////////////////////////////// Private

    #region 네트워크 바이트 순서 쓰기 - WriteNetworkByteOrder(targetByteArray, offset, value)

    /// <summary>
    /// 네트워크 바이트 순서 쓰기
    /// </summary>
    /// <param name="targetByteArray">타겟 바이트 배열</param>
    /// <param name="offset">오프셋</param>
    /// <param name="value">32비트 부호없는 정수</param>
    private static void WriteNetworkByteOrder(byte[] targetByteArray, int offset, uint value)
    {
        targetByteArray[offset + 0] = (byte)(value >> 24);
        targetByteArray[offset + 1] = (byte)(value >> 16);
        targetByteArray[offset + 2] = (byte)(value >> 8 );
        targetByteArray[offset + 3] = (byte)(value >> 0 );
    }

    #endregion
    #region 네트워크 바이트 순서 읽기 - ReadNetworkByteOrder(sourceByteArray, offset)

    /// <summary>
    /// 네트워크 바이트 순서 읽기
    /// </summary>
    /// <param name="sourceByteArray">소스 바이트 배열</param>
    /// <param name="offset">오프셋</param>
    /// <returns>32비트 부호없는 정수</returns>
    private static uint ReadNetworkByteOrder(byte[] sourceByteArray, int offset)
    {
        return ((uint)(sourceByteArray[offset + 0]) << 24) |
               ((uint)(sourceByteArray[offset + 1]) << 16) |
               ((uint)(sourceByteArray[offset + 2]) << 8 ) |
               ((uint)(sourceByteArray[offset + 3]));
    }

    #endregion

    #region 패스워드 해시 계산하기 - CalculatePasswordHash(password)

    /// <summary>
    /// 패스워드 해시 계산하기
    /// </summary>
    /// <param name="password">패스워드</param>
    /// <returns>패스워드 해시</returns>
    private static string CalculatePasswordHash(string password)
    {
        KeyDerivationPrf prf = KeyDerivationPrf.HMACSHA256;

        RandomNumberGenerator generator = RandomNumberGenerator.Create();

        const int iterationCount     = 10000;
        const int saltSize           = 128 / 8;
        const int byteCountRequested = 256 / 8;

        byte[] saltByteArray = new byte[saltSize];

        generator.GetBytes(saltByteArray);

        byte[] subsidaryKeyByteArray = KeyDerivation.Pbkdf2(password, saltByteArray, prf, iterationCount, byteCountRequested);

        byte[] targetByteArray = new byte[13 + saltByteArray.Length + subsidaryKeyByteArray.Length];

        targetByteArray[0] = 0x01; // Format Marker

        WriteNetworkByteOrder(targetByteArray, 1, (uint)prf     );
        WriteNetworkByteOrder(targetByteArray, 5, iterationCount);
        WriteNetworkByteOrder(targetByteArray, 9, saltSize      );

        Buffer.BlockCopy(saltByteArray, 0, targetByteArray, 13, saltByteArray.Length);

        Buffer.BlockCopy(subsidaryKeyByteArray, 0, targetByteArray, 13 + saltSize, subsidaryKeyByteArray.Length);

        return Convert.ToBase64String(targetByteArray);
    }

    #endregion
    #region 해시 검증하기 - VerifyHash(passwordHashed, password)

    /// <summary>
    /// 해시 검증하기
    /// </summary>
    /// <param name="passwordHashed">해시 패스워드</param>
    /// <param name="password">패스워드</param>
    /// <returns>해시 검증 결과</returns>
    private static bool VerifyHash(string passwordHashed, string password)
    {
        byte[] passwordHashedByteArray = Convert.FromBase64String(passwordHashed);

        if(passwordHashedByteArray[0] != 0x01)
        {
            return false;
        }

        KeyDerivationPrf prf            = (KeyDerivationPrf)ReadNetworkByteOrder(passwordHashedByteArray, 1);
        int              iterationCount = (int             )ReadNetworkByteOrder(passwordHashedByteArray, 5);
        int              saltLength     = (int             )ReadNetworkByteOrder(passwordHashedByteArray, 9);

        if(saltLength < 128 / 8)
        {
            return false;
        }

        byte[] saltByteArray = new byte[saltLength];

        Buffer.BlockCopy(passwordHashedByteArray, 13, saltByteArray, 0, saltByteArray.Length);

        int subsidaryKeyLength = passwordHashedByteArray.Length - 13 - saltByteArray.Length;

        if(subsidaryKeyLength < 128 / 8)
        {
            return false;
        }

        byte[] expectedSubsidaryKeyByteArray = new byte[subsidaryKeyLength];

        Buffer.BlockCopy(passwordHashedByteArray, 13 + saltByteArray.Length, expectedSubsidaryKeyByteArray, 0, expectedSubsidaryKeyByteArray.Length);

        byte[] actualSubsidaryKeyByteArray = KeyDerivation.Pbkdf2(password, saltByteArray, prf, iterationCount, subsidaryKeyLength);

        return actualSubsidaryKeyByteArray.SequenceEqual(expectedSubsidaryKeyByteArray);
    }

    #endregion

    #region 프로그램 시작하기 - Main()

    /// <summary>
    /// 프로그램 시작하기
    /// </summary>
    private static void Main()
    {
        string password = "test1234";

        string passwordHashed = CalculatePasswordHash(password);

        Console.WriteLine($"패스워드      : {password}");
        Console.WriteLine($"해시 패시워드 : {passwordHashed}");
        Console.WriteLine($"검증 결과     : {VerifyHash(passwordHashed, password)}");
    }

    #endregion
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요