c# - salted - salt password hash
Cómo hash una contraseña (4)
En base a csharptest.net''s excelente respuesta de csharptest.net''s , escribí una clase para esto:
public sealed class SecurePasswordHasher
{
/// <summary>
/// Size of salt
/// </summary>
private const int SaltSize = 16;
/// <summary>
/// Size of hash
/// </summary>
private const int HashSize = 20;
/// <summary>
/// Creates a hash from a password
/// </summary>
/// <param name="password">the password</param>
/// <param name="iterations">number of iterations</param>
/// <returns>the hash</returns>
public static string Hash(string password, int iterations)
{
//create salt
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[SaltSize]);
//create hash
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
var hash = pbkdf2.GetBytes(HashSize);
//combine salt and hash
var hashBytes = new byte[SaltSize + HashSize];
Array.Copy(salt, 0, hashBytes, 0, SaltSize);
Array.Copy(hash, 0, hashBytes, SaltSize, HashSize);
//convert to base64
var base64Hash = Convert.ToBase64String(hashBytes);
//format hash with extra information
return string.Format("$MYHASH$V1${0}${1}", iterations, base64Hash);
}
/// <summary>
/// Creates a hash from a password with 10000 iterations
/// </summary>
/// <param name="password">the password</param>
/// <returns>the hash</returns>
public static string Hash(string password)
{
return Hash(password, 10000);
}
/// <summary>
/// Check if hash is supported
/// </summary>
/// <param name="hashString">the hash</param>
/// <returns>is supported?</returns>
public static bool IsHashSupported(string hashString)
{
return hashString.Contains("$MYHASH$V1$");
}
/// <summary>
/// verify a password against a hash
/// </summary>
/// <param name="password">the password</param>
/// <param name="hashedPassword">the hash</param>
/// <returns>could be verified?</returns>
public static bool Verify(string password, string hashedPassword)
{
//check hash
if (!IsHashSupported(hashedPassword))
{
throw new NotSupportedException("The hashtype is not supported");
}
//extract iteration and Base64 string
var splittedHashString = hashedPassword.Replace("$MYHASH$V1$", "").Split(''$'');
var iterations = int.Parse(splittedHashString[0]);
var base64Hash = splittedHashString[1];
//get hashbytes
var hashBytes = Convert.FromBase64String(base64Hash);
//get salt
var salt = new byte[SaltSize];
Array.Copy(hashBytes, 0, salt, 0, SaltSize);
//create hash with given salt
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
byte[] hash = pbkdf2.GetBytes(HashSize);
//get result
for (var i = 0; i < HashSize; i++)
{
if (hashBytes[i + SaltSize] != hash[i])
{
return false;
}
}
return true;
}
}
Uso:
//Hash
var hash = SecurePasswordHasher.Hash("mypassword");
//Verify
var result = SecurePasswordHasher.Verify("mypassword", hash);
Un hash de muestra podría ser esto:
$MYHASH$V1$10000$Qhxzi6GNu/Lpy3iUqkeqR/J1hh8y/h5KPDjrv89KzfCVrubn
Como puede ver, también he incluido las iteraciones en el hash para facilitar el uso y la posibilidad de actualizarlo, si tenemos que actualizar.
hola, me gustaría almacenar el hash de una contraseña en el teléfono, pero no estoy seguro de cómo hacerlo. Parece que solo puedo encontrar métodos de encriptación. ¿Cuál es la mejor manera de hash la contraseña? Gracias
La mayoría de las otras respuestas aquí están algo desactualizadas con las mejores prácticas actuales. Como tal, aquí está la aplicación de usar PBKDF2 / Rfc2898DeriveBytes para almacenar y verificar contraseñas. El siguiente código está en una clase independiente en esta publicación: Otro ejemplo de cómo almacenar un hash de contraseña salada . Los conceptos básicos son realmente fáciles, así que aquí está desglosado:
PASO 1 Crea el valor de sal con un PRNG criptográfico:
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
PASO 2 Crea el Rfc2898DeriveBytes y obtén el valor hash:
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
PASO 3 Combine los bytes de sal y contraseña para usarlos posteriormente:
byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
PASO 4 Gire la sal combinada + hash en una cadena para almacenamiento
string savedPasswordHash = Convert.ToBase64String(hashBytes);
DBContext.AddUser(new User { ..., Password = savedPasswordHash });
PASO 5 Verificar la contraseña ingresada por el usuario contra una contraseña almacenada
/* Fetch the stored value */
string savedPasswordHash = DBContext.GetUser(u => u.UserName == user).Password;
/* Extract the bytes */
byte[] hashBytes = Convert.FromBase64String(savedPasswordHash);
/* Get the salt */
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
/* Compute the hash on the password the user entered */
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
/* Compare the results */
for (int i=0; i < 20; i++)
if (hashBytes[i+16] != hash[i])
throw new UnauthorizedAccessException();
Nota: Según los requisitos de rendimiento de su aplicación específica, se puede reducir el valor ''10000''. Un valor mínimo debería ser alrededor de 1000.
Utilizo un hash y una sal para el cifrado de mi contraseña (es el mismo hash que utiliza la Membresía Asp.Net):
private string PasswordSalt
{
get
{
var rng = new RNGCryptoServiceProvider();
var buff = new byte[32];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
}
private string EncodePassword(string password, string salt)
{
byte[] bytes = Encoding.Unicode.GetBytes(password);
byte[] src = Encoding.Unicode.GetBytes(salt);
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
byte[] inarray = algorithm.ComputeHash(dst);
return Convert.ToBase64String(inarray);
}
ACTUALIZACIÓN : ESTA RESPUESTA ESTÁ SERIAMENTE DESPLAZADA . Utilice las recomendaciones de https://.com/a/10402129/251311 lugar.
Puedes usar
var md5 = new MD5CryptoServiceProvider();
var md5data = md5.ComputeHash(data);
o
var sha1 = new SHA1CryptoServiceProvider();
var sha1data = sha1.ComputeHash(data);
Para obtener data
como matriz de bytes, puede usar
var data = Encoding.ASCII.GetBytes(password);
y para recuperar cadenas de md5data
o sha1data
var hashedPassword = ASCIIEncoding.GetString(md5data);