2010-11-15 9 views
73

hej, Chciałbym zapisać hash hasła w telefonie, ale nie jestem pewien, jak to zrobić. Mogę tylko znaleźć metody szyfrowania. Jaki jest najlepszy sposób na zaszyfrowanie hasła? dziękiJak hash hasło

Odpowiedz

39

UPDATE: ta odpowiedź jest poważnie OUTDATED. Zamiast tego użyj zaleceń z https://stackoverflow.com/a/10402129/251311.

Można użyć

var md5 = new MD5CryptoServiceProvider(); 
var md5data = md5.ComputeHash(data); 

lub

var sha1 = new SHA1CryptoServiceProvider(); 
var sha1data = sha1.ComputeHash(data); 

Aby uzyskać data jako tablicy bajtów można użyć

var data = Encoding.ASCII.GetBytes(password); 

i wrócić ciąg od md5data lub sha1data

var hashedPassword = ASCIIEncoding.GetString(md5data); 
+9

NAPRAWDĘ zalecałbym używanie SHA1. MD5 jest nie-nie, chyba że zachowujesz zgodność wsteczną z istniejącym systemem. Ponadto upewnij się, że umieściłeś go w instrukcji 'using' lub wywołałeś na nim' Clear() ', gdy skończysz używać implementacji. – vcsjones

+3

@vcsjones: Nie chcę tu świętej wojny, ale 'md5' jest wystarczająco dobre dla prawie wszystkich zadań. Jego luki w zabezpieczeniach odnoszą się również do bardzo specyficznych sytuacji i niemal wymaga od atakującego, aby wiedział dużo o kryptografii. – zerkms

+4

@zerkms punkt wzięty, ale jeśli nie ma powodu dla kompatybilności wstecznej, nie ma powodu używać MD5. "Lepiej dmuchać na zimne". – vcsjones

13

używam hash i sól dla mojego szyfrowania hasła (jest to ten sam hash który wykorzystuje Asp.Net Membership):

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); 
} 
+7

-1 za używanie zwykłego SHA-1, który jest szybki. Użyj funkcji pochodnej wolnego klucza, takiej jak PBKDF2, bcrypt lub scrypt. – CodesInChaos

170

Większość innych odpowiedzi tutaj są nieco out-of-date dzięki dzisiejszym najlepszym praktykom. W związku z tym jest tutaj zastosowanie używania PBKDF2/Rfc2898DeriveBytes do przechowywania i weryfikacji haseł. Poniższy kod znajduje się w samodzielnej klasie w tym wpisie: Another example of how to store a salted password hash. Podstawy są naprawdę łatwe, więc tutaj to jest w podziale:

KROK 1 Tworzenie wartości soli z kryptograficznego PRNG:

byte[] salt; 
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]); 

KROK 2 utworzyć Rfc2898DeriveBytes i uzyskać wartość hash:

var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000); 
byte[] hash = pbkdf2.GetBytes(20); 

KROK 3 Połączyć sól i hasło bajtów do późniejszego wykorzystania:

byte[] hashBytes = new byte[36]; 
Array.Copy(salt, 0, hashBytes, 0, 16); 
Array.Copy(hash, 0, hashBytes, 16, 20); 

ETAP 4 Obracając połączeniu soli + mieszania w ciągu przechowywania

string savedPasswordHash = Convert.ToBase64String(hashBytes); 
DBContext.AddUser(new User { ..., Password = savedPasswordHash }); 

ETAP 5 Sprawdź, czy hasło wprowadzone przez użytkownika na przechowywane hasło

/* 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(); 

Uwaga : W zależności od wymagań wydajnościowych danej aplikacji można zmniejszyć wartość "10000". Minimalna wartość powinna wynosić około 1000.

+0

Dziękujemy za aktualizację i szczegółowe komentarze do kodu, duża pomoc! –

+0

To jest bardziej jak pytanie, nie jest łatwiejsze, sprawdzanie hasła, haszowanie podanego hasła i porównywanie zapisanego ciągu z tym podanym przez użytkownika i mieszane ponownie? – Daniel

+2

@ Daniel zasadniczo chodzi o użycie czegoś bardziej bezpiecznego niż sam skrót. Jeśli po prostu hash hash, nawet z solą, hasła użytkowników zostaną naruszone (i prawdopodobnie sprzedane/opublikowane), zanim będziesz miał szansę powiedzieć im, aby to zmienić. Użyj powyższego kodu, aby utrudnić atakującemu, nie jest to łatwe dla programisty. –

28

podstawie csharptest.net's wielką odpowiedź, napisałem klasę na to:

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; 
    } 
} 

Zastosowanie:

//Hash 
var hash = SecurePasswordHasher.Hash("mypassword"); 

//Verify 
var result = SecurePasswordHasher.Verify("mypassword", hash); 

Próbkę hash może być to:

$MYHASH$V1$10000$Qhxzi6GNu/Lpy3iUqkeqR/J1hh8y/h5KPDjrv89KzfCVrubn 

jak ty widzę, dodałem również iteracje w haszcie dla łatwego użycia i możliwość uaktualnienia tego, jeśli musimy uaktualnić.

+1

Tylko w celu sprawdzenia, czy zmodernizujesz silnik mieszający, który zwiększysz sekcję V1 twojego skrótu i ​​od tego? –

+1

Tak, to jest plan. Wówczas zdecydowałbyś się na podstawie "V1" i "V2", której potrzebujesz. –

+0

Dzięki za odpowiedź i klasę. Wdrażam to, gdy mówimy. –