Reputation:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Sergio");
Console.WriteLine(HashString("Sergio"));
Console.WriteLine(HashString("Sergio"));
Console.WriteLine(HashString("Sergio"));
Console.ReadKey();
}
public static string HashString(string value)
{
int minSaltSize = 4;
int maxSaltSize = 8;
Random random = new Random();
int saltSize = random.Next(minSaltSize, maxSaltSize);
byte[] saltBytes = new byte[saltSize];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(saltBytes);
//Convert the string value into a byte array.
UTF8Encoding utf8Encoder = new UTF8Encoding();
byte[] plainTextBytes = utf8Encoder.GetBytes(value);
//Allocate an array to hold the text bytes and the salt bytes.
byte[] plainTextWithSaltBytes = new Byte[saltBytes.Length + plainTextBytes.Length];
for (int i = 0; i < plainTextBytes.Length; i++)
{
plainTextWithSaltBytes[i] = plainTextBytes[i];
}
for (int i = 0; i < saltBytes.Length; i++)
{
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
}
HashAlgorithm hash = new SHA256Managed();
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
string hashedValue = Convert.ToBase64String(hashBytes);
return hashedValue;
}
}
The result of my code is that hashed "Sergio" is different everytime. This is caused by the random factor included in the salt. My question is, am I supposed to save the salt somewhere? Or do I define a constant salt in my code?
Thank you!
Upvotes: 1
Views: 1448
Reputation: 1035
I've been a fan of GUIDs for the salt and SHA512 for the hash. CPU time isn't an issue since it's only during authentication.
but yes, you need to store the hash so you can append it to the incoming password.
storedpassword = hash(salt+password)
if you forget the salt, you can't compare the values.
Upvotes: 0
Reputation: 47164
Absolutly. Use always a new salt per hashed password and not a constant. Having the same salt for all passwords would be unsafe (defense against rainbow tables).
Save it somewhere, e.g. in a database in an extra field or even in the same field with the hashed password together (bcrypt for example does it similar like that) if you can't add an extra field to your database logic.
But always use a new random salt per password.
Upvotes: 0
Reputation: 64248
You can use this class to create the salt/hash and combine the two values together:
public sealed class PasswordHash
{
const int SaltSize = 16, HashSize = 20, HashIter = 10000;
readonly byte[] _salt, _hash;
public PasswordHash(string password)
{
new RNGCryptoServiceProvider().GetBytes(_salt = new byte[SaltSize]);
_hash = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
}
public PasswordHash(byte[] hashBytes)
{
Array.Copy(hashBytes, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hashBytes, SaltSize, _hash = new byte[HashSize], 0, HashSize);
}
public PasswordHash(byte[] salt, byte[] hash)
{
Array.Copy(salt, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hash, 0, _hash = new byte[HashSize], 0, HashSize);
}
public byte[] ToArray()
{
byte[] hashBytes = new byte[SaltSize + HashSize];
Array.Copy(_salt, 0, hashBytes, 0, SaltSize);
Array.Copy(_hash, 0, hashBytes, SaltSize, HashSize);
return hashBytes;
}
public byte[] Salt { get { return (byte[])_salt.Clone(); } }
public byte[] Hash { get { return (byte[])_hash.Clone(); } }
public bool Verify(string password)
{
byte[] test = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
for (int i = 0; i < HashSize; i++)
if (test[i] != _hash[i])
return false;
return true;
}
}
Upvotes: 1
Reputation: 77657
You should save the salt someplace because you need to use the same salt when computing the hash again. However, you don't want to use a constant because that reduces the effectiveness of the salt.
Upvotes: 1
Reputation: 33173
If you are using different salts then you will want to store both the hashed password and the salt in the db. You can also chose to store the salt in an app.config / web.config file if it is the same salt.
Upvotes: 1