Asp.Net Identity 2.0 커스텀 로그인 (AspNetUsers의 PasswordHash 비교 루틴)

 

 Asp.Net MVC5에서는 Identity 클래스를 제공, 사용해서 사용자 인증을 처리합니다.

이렇게 하면 DB에 AspNetUsers 테이블에 사용자 정보가 저장되고, 회원등록 및 로긴은 Entity framework를 통해서 자동으로 처리됩니다.

이 때 OWIN이나 Aspnet Identity, EF6을 사용하지 않는 별도의 프로젝트에서 해당 테이블에 접근해 로그인만 체크해 주고 싶을 수 있습니다.


AspNet에서는 Microsoft.AspNet.Identity.PasswordHasher 클래스에서 해당 비밀번호 검증을 담당합니다.

이 부분은 https://aspnetidentity.codeplex.com/SourceControl/latest#src/Microsoft.AspNet.Identity.Core/Crypto.cs 를 참고하시면 됩니다.


간단하게 DB에 직접 붙어서 Aspnet Identity로 생성된 계정에 로그인하는 코드입니다.

핵심은 아무래도 VerifyHashedPassword 메소드가 되겠네요. SqlConn은 제가 임의로 만들어 사용하는 SqlClient 헬퍼클래스입니다.


using System;
using System.Security.Cryptography;
using System.Data;
 
namespace IdentityTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Retry:
            Console.Write(" - ID : ");
            string id = Console.ReadLine();
            Console.Write(" - PW : ");
            string pw = Console.ReadLine();
 
            bool logined = false;
 
            using (SqlConn sql = new SqlConn())
            {
                sql.ConnStr = "Server=0.0.0.0;Database=xx;User ID=xx;Password=xx;Connection Timeout=10;";
 
                DataTable result = sql.SqlSelect($"select top 1 * from AspNetUsers where UserName = '{id}'");
                if (result.Rows.Count > 0)
                {
                     logined = VerifyHashedPassword(result.Rows[0]["PasswordHash"].ToString(), pw);
                }
            }
 
            Console.WriteLine("Login 결과 : " + (logined ? "성공":"실패"));
            Console.WriteLine("------------------- Retry -------------------");
            goto Retry;
        }
 
        public static string HashPassword(string password)
        {
            byte[] salt;
            byte[] buffer2;
            if (password == null)
            {
                throw new ArgumentNullException("password");
            }
            using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8))
            {
                salt = bytes.Salt;
                buffer2 = bytes.GetBytes(0x20);
            }
            byte[] dst = new byte[0x31];
            Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
            Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
            return Convert.ToBase64String(dst);
        }
 
        public static bool VerifyHashedPassword(string hashedPassword, string password)
        {
            byte[] buffer4;
            if (hashedPassword == null)
            {
                return false;
            }
            if (password == null)
            {
                throw new ArgumentNullException("password");
            }
            byte[] src = Convert.FromBase64String(hashedPassword);
            if ((src.Length != 0x31) || (src[0] != 0))
            {
                return false;
            }
            byte[] dst = new byte[0x10];
            Buffer.BlockCopy(src, 1, dst, 0, 0x10);
            byte[] buffer3 = new byte[0x20];
            Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
            using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, dst, 0x3e8))
            {
                buffer4 = bytes.GetBytes(0x20);
            }
            return ByteArraysEqual(buffer3, buffer4);
        }
 
        private static bool ByteArraysEqual(byte[] a, byte[] b)
        {
            if (== null && b == null)
            {
                return true;
            }
            if (== null || b == null || a.Length != b.Length)
            {
                return false;
            }
            var areSame = true;
            for (var i = 0 ; i < a.Length ; i++)
            {
                areSame &= (a[i] == b[i]);
            }
            return areSame;
        }
 
 
    }
}


실행 결과는 아래와 같습니다.


+ Recent posts