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 (a == null && b == null) { return true; } if (a == 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; } } }
실행 결과는 아래와 같습니다.