History: Password hashing used to work, now I can't get into my software
Since writing the question above (basically, just can't log in and didn't know why), I've determined that the byte arrays for the password I entered when I logged in and the password I entered when I created the user record, are completely different:
Here's the bytes from the database
Here's the bytes from the password when I failed to logged in
Now that I know that the value being passed to the database (or the value stored in the database) are wrong, I'm at a loss as to how I should fix it...
I'm not even sure why this used to work, but suddenly broke.
Thanks in advance for any suggestions!
EDIT
Here's the user login and registration code
public User Login() // returns an instance of its own class
{
var auser = ie.users.FirstOrDefault(u => String.Compare(u.emailaddress, emailaddress, false) == 0);
if (auser == null)
{
throw new ValidationException("User not found");
}
// get bytes for password in the database
byte[] passwordbytes = CryptUtility.GetBytes(auser.password);
HashGenerator h = new HashGenerator(password, auser.usersalt);
string hash = h.GetHash();
var us = ie.users.FirstOrDefault(u => String.Compare(u.emailaddress, emailaddress, false) == 0 && String.Compare(u.password, password, false) == 0);
if (us == null)
{
throw new Exception("Invalid email address and/or password.");
}
User user = new User();
user.userid = us.userid;
user.storeid = us.storeid;
user.role = us.role;
return user;
}
public void Add() // user registration
{
user newuser = new user();
newuser.storeid = storeid;
newuser.emailaddress = emailaddress;
// Generate password hash
string usersalt = SaltGenerator.GetSaltString();
HashGenerator hash = new HashGenerator(password, usersalt);
newuser.password = hash.GetHash();
newuser.role = role;
newuser.usersalt = usersalt;
ie.users.Add(newuser);
ie.SaveChanges();
}
Here's the security code to generate hash and salt values and their byte/string values as well
public class HashGenerator
{
public string pass { get; protected set; }
public string salt { get; protected set; }
public HashGenerator(string Password, string Salt)
{
pass = Password;
salt = Salt;
}
public string GetHash()
{
SHA512 sha = new SHA512CryptoServiceProvider();
byte[] data = CryptUtility.GetBytes(String.Format("{0}{1}", pass, salt));
byte[] hash = sha.ComputeHash(data);
return CryptUtility.GetString(hash);
}
}
public static class SaltGenerator
{
private static RNGCryptoServiceProvider provider = null;
private const int saltSize = 128;
static SaltGenerator()
{
provider = new RNGCryptoServiceProvider();
}
public static string GetSaltString()
{
byte[] saltBytes = new byte[saltSize];
provider.GetNonZeroBytes(saltBytes);
return CryptUtility.GetString(saltBytes);
}
}
class CryptUtility
{
public static byte[] GetBytes(string Str)
{
byte[] bytes = new byte[Str.Length * sizeof(char)];
System.Buffer.BlockCopy(Str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public static string GetString(byte[] Bytes)
{
char[] chars = new char[Bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(Bytes, 0, chars, 0, Bytes.Length);
return new string(chars);
}
}
GetBytesandGetStringmethods! It's a dirty hack, not a reliable way to convert char array to byte array and back. Always use an encoding - e.g.Encoding.UTF8.GetBytes()andEncoding.UTF8.GetString()respectively. There's many ways the same unicode string can be represented in memory (little endian vs. big endian being one possible issue, unicode normalization another, there's chars longer thansizeof(char)etc.) - your code relies onbyte[]equality, so don't guess that it will be always the same - use proper encoding.GetBytes+GetStringyou're doing, it's very likely you've ended up with invalid unicode strings at some point.stringis not a byte array. Joel Spolsky has a great article on the topic - joelonsoftware.com/articles/Unicode.html