How to hash a password

后端 未结 9 704
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-22 14:49

I\'d like to store the hash of a password on the phone, but I\'m not sure how to do it. I can only seem to find encryption methods. How should the password be hashed properl

相关标签:
9条回答
  • 2020-11-22 15:13
    1. Create a salt,
    2. Create a hash password with salt
    3. Save both hash and salt
    4. decrypt with password and salt... so developers cant decrypt password
    public class CryptographyProcessor
    {
        public string CreateSalt(int size)
        {
            //Generate a cryptographic random number.
              RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
             byte[] buff = new byte[size];
             rng.GetBytes(buff);
             return Convert.ToBase64String(buff);
        }
    
    
          public string GenerateHash(string input, string salt)
          { 
             byte[] bytes = Encoding.UTF8.GetBytes(input + salt);
             SHA256Managed sHA256ManagedString = new SHA256Managed();
             byte[] hash = sHA256ManagedString.ComputeHash(bytes);
             return Convert.ToBase64String(hash);
          }
    
          public bool AreEqual(string plainTextInput, string hashedInput, string salt)
          {
               string newHashedPin = GenerateHash(plainTextInput, salt);
               return newHashedPin.Equals(hashedInput); 
          }
     }
    
    0 讨论(0)
  • 2020-11-22 15:13

    In ASP.NET Core, use PasswordHasher<TUser>.
     • Namespace: Microsoft.AspNetCore.Identity
     • Assembly: Microsoft.Extensions.Identity.Core.dll (NuGet | Source)


    To hash a password, use HashPassword():

    var hashedPassword = new PasswordHasher<object?>().HashPassword(null, password);
    

    To verify a password, use VerifyHashedPassword():

    var passwordVerificationResult = new PasswordHasher<object?>().VerifyHashedPassword(null, hashedPassword, password);
    switch (passwordVerificationResult)
    {
        case PasswordVerificationResult.Failed:
            Console.WriteLine("Password incorrect.");
            break;
        
        case PasswordVerificationResult.Success:
            Console.WriteLine("Password ok.");
            break;
        
        case PasswordVerificationResult.SuccessRehashNeeded:
            Console.WriteLine("Password ok but should be rehashed and updated.");
            break;
        
        default:
            throw new ArgumentOutOfRangeException();
    }
    
    

    Pros:

    • Part of the .NET platform. Much safer and trustworthier than building your own crypto algorithm.
    • Configurable iteration count and future compatibility (see PasswordHasherOptions).
    • Took Timing Attack into consideration when verifying password (source), just like what PHP and Go did.

    Cons:

    • Hashed password format incompatible with those hashed by other libraries or in other languages.
    0 讨论(0)
  • 2020-11-22 15:20

    I think using KeyDerivation.Pbkdf2 is better than Rfc2898DeriveBytes.

    Example and explanation: Hash passwords in ASP.NET Core

    using System;
    using System.Security.Cryptography;
    using Microsoft.AspNetCore.Cryptography.KeyDerivation;
     
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.Write("Enter a password: ");
            string password = Console.ReadLine();
     
            // generate a 128-bit salt using a secure PRNG
            byte[] salt = new byte[128 / 8];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(salt);
            }
            Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");
     
            // derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
            string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
                password: password,
                salt: salt,
                prf: KeyDerivationPrf.HMACSHA1,
                iterationCount: 10000,
                numBytesRequested: 256 / 8));
            Console.WriteLine($"Hashed: {hashed}");
        }
    }
     
    /*
     * SAMPLE OUTPUT
     *
     * Enter a password: Xtw9NMgx
     * Salt: NZsP6NnmfBuYeJrrAKNuVQ==
     * Hashed: /OOoOer10+tGwTRDTrQSoeCxVTFr6dtYly7d0cPxIak=
     */
    

    This is a sample code from the article. And it's a minimum security level. To increase it I would use instead of KeyDerivationPrf.HMACSHA1 parameter

    KeyDerivationPrf.HMACSHA256 or KeyDerivationPrf.HMACSHA512.

    Don't compromise on password hashing. There are many mathematically sound methods to optimize password hash hacking. Consequences could be disastrous. Once a malefactor can get his hands on password hash table of your users it would be relatively easy for him to crack passwords given algorithm is weak or implementation is incorrect. He has a lot of time (time x computer power) to crack passwords. Password hashing should be cryptographically strong to turn "a lot of time" to "unreasonable amount of time".

    One more point to add

    Hash verification takes time (and it's good). When user enters wrong user name it's takes no time to check that user name is incorrect. When user name is correct we start password verification - it's relatively long process.

    For a hacker it would be very easy to understand if user exists or doesn't.

    Make sure not to return immediate answer when user name is wrong.

    Needless to say : never give an answer what is wrong. Just general "Credentials are wrong".

    0 讨论(0)
  • 2020-11-22 15:23

    Most of the other answers here are somewhat out-of-date with today's best practices. As such here is the application of using PBKDF2/Rfc2898DeriveBytes to store and verify passwords. The following code is in a stand-alone class in this post: Another example of how to store a salted password hash. The basics are really easy, so here it is broken down:

    STEP 1 Create the salt value with a cryptographic PRNG:

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

    STEP 2 Create the Rfc2898DeriveBytes and get the hash value:

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

    STEP 3 Combine the salt and password bytes for later use:

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

    STEP 4 Turn the combined salt+hash into a string for storage

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

    STEP 5 Verify the user-entered password against a stored password

    /* 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, 100000);
    byte[] hash = pbkdf2.GetBytes(20);
    /* Compare the results */
    for (int i=0; i < 20; i++)
        if (hashBytes[i+16] != hash[i])
            throw new UnauthorizedAccessException();
    

    Note: Depending on the performance requirements of your specific application, the value 100000 can be reduced. A minimum value should be around 10000.

    0 讨论(0)
  • 2020-11-22 15:27

    @csharptest.net's and Christian Gollhardt's answers are great, thank you very much. But after running this code on production with millions of record, I discovered there is a memory leak. RNGCryptoServiceProvider and Rfc2898DeriveBytes classes are derived from IDisposable but we don't dispose of them. I will write my solution as an answer if someone needs with disposed version.

    public static 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
            using (var rng = new RNGCryptoServiceProvider())
            {
                byte[] salt;
                rng.GetBytes(salt = new byte[SaltSize]);
                using (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 $"$HASH|V1${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>
        /// Checks if hash is supported.
        /// </summary>
        /// <param name="hashString">The hash.</param>
        /// <returns>Is supported?</returns>
        public static bool IsHashSupported(string hashString)
        {
            return hashString.Contains("HASH|V1$");
        }
    
        /// <summary>
        /// Verifies 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("$HASH|V1$", "").Split('$');
            var iterations = int.Parse(splittedHashString[0]);
            var base64Hash = splittedHashString[1];
    
            // Get hash bytes
            var hashBytes = Convert.FromBase64String(base64Hash);
    
            // Get salt
            var salt = new byte[SaltSize];
            Array.Copy(hashBytes, 0, salt, 0, SaltSize);
    
            // Create hash with given salt
            using (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;
            }
    
        }
    }
    

    Usage:

    // Hash
    var hash = SecurePasswordHasher.Hash("mypassword");
    
    // Verify
    var result = SecurePasswordHasher.Verify("mypassword", hash);
    
    0 讨论(0)
  • 2020-11-22 15:34

    UPDATE: THIS ANSWER IS SERIOUSLY OUTDATED. Please use the recommendations from the https://stackoverflow.com/a/10402129/251311 instead.

    You can either use

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

    or

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

    To get data as byte array you could use

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

    and to get back string from md5data or sha1data

    var hashedPassword = ASCIIEncoding.GetString(md5data);
    
    0 讨论(0)
提交回复
热议问题