먼저 SqlMembershipProvider가 수행중인 작업에 대해 간단하게 설명하겠습니다. 공급자는 바이트 []와 암호를 유니 코드 바이트 배열로 인코딩 된 암호를 결합하여 두 개의 바이트를 서로 연결하여 더 큰 단일 바이트 배열로 결합합니다. 꽤 직설적 인. 그런 다음 추상화 (MembershipAdapter)를 통해 실제 작업이 완료된 MachineKeySection으로 전달합니다.
이 핸드 오프의 중요한 부분은 빈 IV (초기화 벡터)를 사용하고 서명을 수행하지 않도록 MachineKeySection에 지시한다는 것입니다. 그 빈 IV는 진짜 lynchpin입니다. 왜냐하면 machineKey 요소에는 IV 속성이 없기 때문에 머리를 긁어서 공급자가이 측면을 어떻게 처리하는지 궁금해 할 때 그렇습니다. 소스 코드를 파고 들자마자 MachineKeySection 코드의 암호화 코드를 추출하고 멤버쉽 코드와 결합하여 더 완벽한 해시 코드를 얻을 수 있습니다. 전체 소스 : 다른 알고리즘이있는 경우
public class SQLPasswordHasher : PasswordHasher
{
public override string HashPassword(string password)
{
return base.HashPassword(password);
}
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
string[] passwordProperties = hashedPassword.Split('|');
if (passwordProperties.Length != 3)
{
return base.VerifyHashedPassword(hashedPassword, providedPassword);
}
else
{
string passwordHash = passwordProperties[0];
int passwordformat = int.Parse(passwordProperties[1]);
string salt = passwordProperties[2];
if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase))
{
return PasswordVerificationResult.SuccessRehashNeeded;
}
else
{
return PasswordVerificationResult.Failed;
}
}
}
//This is copied from the existing SQL providers and is provided only for back-compat.
private string EncryptPassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0) // MembershipPasswordFormat.Clear
return pass;
byte[] bIn = Encoding.Unicode.GetBytes(pass);
byte[] bSalt = Convert.FromBase64String(salt);
byte[] bRet = null;
if (passwordFormat == 1)
{ // MembershipPasswordFormat.Hashed
HashAlgorithm hm = HashAlgorithm.Create("SHA1");
if (hm is KeyedHashAlgorithm)
{
KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
if (kha.Key.Length == bSalt.Length)
{
kha.Key = bSalt;
}
else if (kha.Key.Length < bSalt.Length)
{
byte[] bKey = new byte[kha.Key.Length];
Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
kha.Key = bKey;
}
else
{
byte[] bKey = new byte[kha.Key.Length];
for (int iter = 0; iter < bKey.Length;)
{
int len = Math.Min(bSalt.Length, bKey.Length - iter);
Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
iter += len;
}
kha.Key = bKey;
}
bRet = kha.ComputeHash(bIn);
}
else
{
byte[] bAll = new byte[bSalt.Length + bIn.Length];
Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
bRet = hm.ComputeHash(bAll);
}
}
else //MembershipPasswordFormat.Encrypted, aka 2
{
byte[] bEncrypt = new byte[bSalt.Length + bIn.Length];
Buffer.BlockCopy(bSalt, 0, bEncrypt, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bEncrypt, bSalt.Length, bIn.Length);
// Distilled from MachineKeyConfigSection EncryptOrDecryptData function, assuming AES algo and paswordCompatMode=Framework20 (the default)
MemoryStream stream = new MemoryStream();
var aes = new AesCryptoServiceProvider();
aes.Key = HexStringToByteArray(MachineKey.DecryptionKey);
aes.GenerateIV();
aes.IV = new byte[aes.IV.Length];
ICryptoTransform transform = aes.CreateEncryptor();
CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write);
stream2.Write(bEncrypt, 0, bEncrypt.Length);
stream2.FlushFinalBlock();
bRet = stream.ToArray();
stream2.Close();
//
}
return Convert.ToBase64String(bRet);
}
public static byte[] HexStringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars/2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i/2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
private static MachineKeySection MachineKey
{
get
{
//Get encryption and decryption key information from the configuration.
System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
return cfg.GetSection("system.web/machineKey") as MachineKeySection;
}
}
}
, 다음 단계는 동일 매우 가까운 것입니다,하지만 당신은 MachineKeySection의 소스에 첫 번째 다이빙을 원하고 그들이 일을 초기화하는 방법을주의 깊게 둘러 보지 수 있습니다. 해피 코딩!