HashPasswordForStoringInConfigFile - Different Hashes for same password
I have recently implemented Hashing for my passwords in a project I am working on, and I cant seem to figure out what is going wrong.
It seems that the HashPasswordForStoringInConfigFile() function is returning different values for the same password.
I have the following code implemented which actually closely resembles the recommended algorithm to use on the MSDN documentation.
I know that SHA1 hashing is not considered very safe, but this is for a research application, and I am not too worried about it at this point.
public const int DefaultSaltSize = 5;
private static string CreateSalt()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buffer = new byte[DefaultSaltSize];
rng.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
public static string CreateHash(string password)
{
string salt = Create开发者_C百科Salt();
string saltAndPassword = String.Concat(password, salt);
string hashedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPassword,"SHA1");
hashedPassword = string.Concat(hashedPassword,salt);
return hashedPassword;
}
public static bool VerifyPassword(string username, string password,AccountDataContext context)
{
var user = context.UserAccounts.FirstOrDefault(p => p.UserName == username);
if (user != null)
{
string salt = user.Password.Substring(user.Password.Length - DefaultSaltSize);
string hashedPassword = CreateHash(password);
return hashedPassword.Equals(user.Password);
}
return false;
}
Simply put, If I have the following code.
string password1 = "password";
string password2 = "password";
var hashedPassword1 = CreateHash(password1);
var hashedPassword2 = CreateHash(password2);
var match = hashedPassword1.Equals(hashedPassword2);
//match should be True, but it is turning out False.
It seems that the FormsAuthenticationForStoringInConfigFile() is not returning the same hash for password1 and password2 in the CreateHash() method.
I understand with the salt applied they are not the same, but if you see in the code, I am removing the salt before comparing the two hashedPasswords for equality.
What could possibly be causing password1 and password2 from being hashed differently?
Your code has added salt (a random value) to the password before hashing. This is a good thing.
It means that if user A and user B use the same password, the password hashes will nevertheless be different.
Your VerifyPassword method is not using the original salt to hash the password for comparing - instead it calls CreateHash
, which calls CreateSalt
and creates new salt.
You might try something like:
public static string CreateHash(string password)
{
return CreateHash(password, CreateSalt());
}
private static string CreateHash(string password, string salt)
{
string saltAndPassword = String.Concat(password, salt);
string hashedPassword =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPassword,"SHA1");
hashedPassword = string.Concat(hashedPassword,salt);
return hashedPassword;
}
public static bool VerifyPassword(string username,
string password,AccountDataContext context)
{
var user = context.UserAccounts.FirstOrDefault(p => p.UserName == username);
if (user != null)
{
string salt = user.Password.Substring(user.Password.Length - DefaultSaltSize);
string hashedPassword = CreateHash(password, salt);
return hashedPassword.Equals(user.Password);
}
return false;
}
Even though VerifyPassword looks like it's stripping off the salt portion of the unhashed string, but the code you say should return true doesn't actually call VerifyPassword.
Your code simply generates two salted hashes and then uses String.Equals to compare them.
What happens when you use VerifyPassword instead of String.Equals?
This code doesn't work at all either. Why is it marked as being the correct answer?
The default length of Salt is set to 5
Create Salt when it takes a 5 byte array to a string it becomes 8 characters not 5
Verify Password then takes only 5 characters off for the salt not 8 so the verify will always fail as it's using 5 characters for the salt and not the 8 that was used to create the hashed password.
Below is updated code to make the above code work.
private const int DEFAULT_SALT_SIZE = 5;
private static string CreateSalt()
{
RNGCryptoServiceProvider rngCryptoServiceProvider = new RNGCryptoServiceProvider();
byte[] buffer = new byte[DEFAULT_SALT_SIZE];
rngCryptoServiceProvider.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
public static string CreateHash(string password)
{
return CreateHash(password, CreateSalt());
}
private static string CreateHash(string password, string salt)
{
string saltAndPassword = String.Concat(password, salt);
string hashedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPassword, "SHA1");
hashedPassword = string.Concat(hashedPassword, salt);
return hashedPassword;
}
public static bool VerifyPassword(string userpassword, string password)
{
byte[] bytePassword = Convert.FromBase64String(userpassword);
byte[] byteSalt = new byte[DEFAULT_SALT_SIZE];
Array.Copy(bytePassword, bytePassword.Length - DEFAULT_SALT_SIZE, byteSalt, 0, DEFAULT_SALT_SIZE);
string salt = Convert.ToBase64String(byteSalt);
string hashedPassword = CreateHash(password, salt);
return hashedPassword.Equals(userpassword);
}
This is how I called it.
string hashedPassword = Security.CreateHash("password");
if (Security.VerifyPassword(hashedPassword, "password"))
{
Response.Write("Valid");
}
else
{
Response.Write("Not Valid");
}
As long as the passwords match it returns true otherwise it will return false.
This gives you what I think was intended which was not only to include the salt inside of the hash but also add it to the outside of the hash so it all could be stored as 1 column value in a database and then used to recreate the hash on the user entered password using the stored salt value and get a match.
精彩评论