Created
June 10, 2011 02:59
-
-
Save mikeobrien/1018166 to your computer and use it in GitHub Desktop.
Managed implementation of SQL Server pwdencrypt function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SqlHashedPassword | |
{ | |
private static readonly Random Random = new Random(); | |
private static readonly SHA1CryptoServiceProvider Hash = new SHA1CryptoServiceProvider(); | |
private static readonly byte[] Header = new byte[] {1, 0}; | |
private readonly byte[] _hash; | |
private readonly byte[] _salt; | |
private SqlHashedPassword(byte[] salt, byte[] hash) | |
{ | |
_hash = hash; | |
_salt = salt; | |
} | |
public static SqlHashedPassword FromHash(byte[] hash) | |
{ | |
return new SqlHashedPassword(hash.Skip(2).Take(4).ToArray(), hash.Skip(6).Take(20).ToArray()); | |
} | |
public static SqlHashedPassword Create(string password) | |
{ | |
var salt = new byte[4]; | |
Random.NextBytes(salt); | |
return Create(salt, password); | |
} | |
private static SqlHashedPassword Create(byte[] salt, string password) | |
{ | |
return new SqlHashedPassword(salt, Hash.ComputeHash(EncodePassword(salt, password))); | |
} | |
public bool MatchesPassword(string password) | |
{ | |
return Create(_salt, password)._hash.SequenceEqual(_hash); | |
} | |
public byte[] ToBytes() | |
{ | |
return Header.Concat(_salt).Concat(_hash).ToArray(); | |
} | |
private static byte[] EncodePassword(byte[] salt, string password) | |
{ | |
return Encoding.ASCII.GetBytes(password). | |
Concat(password.Length % 2 == 1 ? new byte[] {0} : new byte[] {}). | |
Concat(salt).ToArray(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[TestFixture] | |
public class SqlHashedPasswordTests | |
{ | |
private const string EvenLengthPassword = "yada"; | |
private static readonly byte[] EvenLengthPasswordHash = new byte[] | |
{ 1, 0, 168, 79, 223, 13, 117, 114, 181, 0, 146, 247, 174, 27, | |
122, 120, 152, 194, 46, 147, 121, 128, 114, 241, 53, 90 }; | |
private const string OddLengthPassword = "yada2"; | |
private static readonly byte[] OddLengthPasswordHash = new byte[] | |
{ 1, 0, 232, 132, 155, 209, 92, 158, 1, 252, 148, 103, 204, 44, | |
201, 110, 129, 19, 199, 169, 32, 36, 16, 178, 242, 138 }; | |
[Test] | |
public void Should_Hash_Plain_Text_Password() | |
{ | |
SqlHashedPassword.Create(EvenLengthPassword).ToBytes().ShouldNotBeEmpty(); | |
} | |
[Test] | |
public void Should_Generate_A_Different_Hash_For_The_Same_Even_Length_Password() | |
{ | |
SqlHashedPassword.Create(EvenLengthPassword).ToBytes().ShouldNotEqual(SqlHashedPassword.Create(EvenLengthPassword).ToBytes()); | |
} | |
[Test] | |
public void Should_Open_Hashed_Even_Length_Password() | |
{ | |
SqlHashedPassword.FromHash(EvenLengthPasswordHash).ToBytes().ShouldEqual(EvenLengthPasswordHash); | |
} | |
[Test] | |
public void Same_Even_Length_Password_Should_Match_Hashed_Password() | |
{ | |
SqlHashedPassword.FromHash(EvenLengthPasswordHash).MatchesPassword(EvenLengthPassword).ShouldBeTrue(); | |
} | |
[Test] | |
public void Different_Even_Length_Password_Should_Not_Match_Hashed_Password() | |
{ | |
SqlHashedPassword.FromHash(EvenLengthPasswordHash).MatchesPassword("badpassword").ShouldBeFalse(); | |
} | |
[Test] | |
public void Should_Generate_A_Different_Hash_For_The_Same_Odd_Length_Password() | |
{ | |
SqlHashedPassword.Create(OddLengthPassword).ToBytes().ShouldNotEqual(SqlHashedPassword.Create(OddLengthPassword).ToBytes()); | |
} | |
[Test] | |
public void Should_Open_Hashed_Odd_Length_Password() | |
{ | |
SqlHashedPassword.FromHash(OddLengthPasswordHash).ToBytes().ShouldEqual(OddLengthPasswordHash); | |
} | |
[Test] | |
public void Same_Odd_Length_Password_Should_Match_Hashed_Password() | |
{ | |
SqlHashedPassword.FromHash(OddLengthPasswordHash).MatchesPassword(OddLengthPassword).ShouldBeTrue(); | |
} | |
[Test] | |
public void Different_Odd_Length_Password_Should_Not_Match_Hashed_Password() | |
{ | |
SqlHashedPassword.FromHash(OddLengthPasswordHash).MatchesPassword("badpassword").ShouldBeFalse(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment