Mật khẩu băm và muối trong C #


178

Tôi vừa xem qua một trong những bài viết của DavidHayden về Mật khẩu người dùng băm .

Thực sự tôi không thể có được những gì anh ta đang cố gắng để đạt được.

Đây là mã của anh ấy:

private static string CreateSalt(int size)
{
    //Generate a cryptographic random number.
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] buff = new byte[size];
    rng.GetBytes(buff);

    // Return a Base64 string representation of the random number.
    return Convert.ToBase64String(buff);
}

private static string CreatePasswordHash(string pwd, string salt)
{
    string saltAndPwd = String.Concat(pwd, salt);
    string hashedPwd =
        FormsAuthentication.HashPasswordForStoringInConfigFile(
        saltAndPwd, "sha1");
    return hashedPwd;
}

Có phương pháp C # nào khác để băm mật khẩu và thêm muối vào nó không?


đây là một thư viện mà không được băm với muối encrypto.codeplex.com
Omu

6
Những gì bạn nên vượt qua cho kích thước trong phương pháp đầu tiên để tạo ra muối?
Shane LeBlanc

6
Liên kết bị hỏng.
osmanraifgunes

@ShaneLeBlanc Bạn nên có ít nhất nhiều bit như đầu ra của hàm. SHA1không phải là loại tiền điện tử, vì vậy ít nhất bạn nên sử dụng SHA256, nó tạo ra 256 bit hoặc 32 byte. NHƯNG, 256 bit KHÔNG dễ dàng chuyển đổi thành cơ sở 64, vì mỗi char cơ sở 64 mã hóa 6 bit và 256 không chia hết cho 6. Vì vậy, bạn cần một mẫu số chung là 6 (đối với cơ sở64) và 8 (đối với bit trong một byte) trên 256 bit, là 264 lần cắn hoặc 33 byte. TLDR: Sử dụng 33.
VSO

Câu trả lời:


248

Trên thực tế, điều này thật kỳ lạ, với các chuyển đổi chuỗi - mà nhà cung cấp thành viên thực hiện để đưa chúng vào các tệp cấu hình. Băm và muối là các đốm nhị phân, bạn không cần phải chuyển đổi chúng thành chuỗi trừ khi bạn muốn đặt chúng vào tệp văn bản.

Trong cuốn sách của tôi, Bắt đầu bảo mật ASP.NET , (ồ cuối cùng, một cái cớ để điều chỉnh cuốn sách) Tôi làm như sau

static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
{
  HashAlgorithm algorithm = new SHA256Managed();

  byte[] plainTextWithSaltBytes = 
    new byte[plainText.Length + salt.Length];

  for (int i = 0; i < plainText.Length; i++)
  {
    plainTextWithSaltBytes[i] = plainText[i];
  }
  for (int i = 0; i < salt.Length; i++)
  {
    plainTextWithSaltBytes[plainText.Length + i] = salt[i];
  }

  return algorithm.ComputeHash(plainTextWithSaltBytes);            
}

Thế hệ muối là ví dụ trong câu hỏi. Bạn có thể chuyển đổi văn bản thành mảng byte bằng cách sử dụng Encoding.UTF8.GetBytes(string). Nếu bạn phải chuyển đổi một hàm băm thành biểu diễn chuỗi của nó, bạn có thể sử dụng Convert.ToBase64StringConvert.FromBase64Stringchuyển đổi nó trở lại.

Bạn nên lưu ý rằng bạn không thể sử dụng toán tử đẳng thức trên các mảng byte, nó kiểm tra các tham chiếu và do đó bạn chỉ cần lặp qua cả hai mảng kiểm tra từng byte như vậy

public static bool CompareByteArrays(byte[] array1, byte[] array2)
{
  if (array1.Length != array2.Length)
  {
    return false;
  }

  for (int i = 0; i < array1.Length; i++)
  {
    if (array1[i] != array2[i])
    {
      return false;
    }
  }

  return true;
}

Luôn luôn sử dụng một loại muối mới cho mỗi mật khẩu. Muối không phải được giữ bí mật và có thể được lưu trữ cùng với hàm băm.


3
Cảm ơn lời khuyên này - thực sự đã giúp tôi bắt đầu. Tôi cũng đã xem qua liên kết này < dijksterhuis.org/creating-salted-hash-values-in-c > mà tôi tìm thấy là lời khuyên thực tế tốt và gương phần lớn những gì đã nói trong bài viết này
Alex P

18
Bộ tái cấu trúc câu lệnh LINQ tiện lợi cho So sánhByteArrays return array1.Length == array2.Length && !array1.Where((t, i) => t != array2[i]).Any();
thợ săn

6
@Brettski Về mặt kỹ thuật, có, nhưng có một loại muối duy nhất cho mỗi người dùng sẽ hiển thị Rainbow Table (thường được chấp nhận là cách hiệu quả nhất để bẻ khóa mật khẩu băm) thực tế vô dụng. Đây là một oveview nhanh cung cấp một cái nhìn tổng quan sâu sắc nhưng không quá sức về cách lưu trữ mật khẩu một cách an toàn và tại sao / làm thế nào tất cả hoạt động.
Kiểm lâm

3
@hunter: bạn nên thêm .ToList () để biến nó thành thời gian không đổi. ví dụ: return mảng1.Lạng == mảng2. Độ dài &&! mảng1.Where ((t, i) => t! = mảng2 [i]). ToList (). Any (); Khác, LINQ sẽ trở lại ngay khi tìm thấy một byte không bằng nhau.
Alex Rouillard

17
-1 để sử dụng hàm băm nhanh. Sử dụng một cấu trúc chậm như PBKDF2, bcrypt hoặc scrypt.
CodeInChaos

48

Những gì blowdart nói, nhưng với một ít mã hơn. Sử dụng Linq hoặc CopyTođể nối các mảng.

public static byte[] Hash(string value, byte[] salt)
{
    return Hash(Encoding.UTF8.GetBytes(value), salt);
}

public static byte[] Hash(byte[] value, byte[] salt)
{
    byte[] saltedValue = value.Concat(salt).ToArray();
    // Alternatively use CopyTo.
    //var saltedValue = new byte[value.Length + salt.Length];
    //value.CopyTo(saltedValue, 0);
    //salt.CopyTo(saltedValue, value.Length);

    return new SHA256Managed().ComputeHash(saltedValue);
}

Linq cũng có một cách dễ dàng để so sánh các mảng byte của bạn.

public bool ConfirmPassword(string password)
{
    byte[] passwordHash = Hash(password, _passwordSalt);

    return _passwordHash.SequenceEqual(passwordHash);
}

Trước khi thực hiện bất kỳ điều này, tuy nhiên, hãy kiểm tra bài này . Để băm mật khẩu, bạn có thể muốn một thuật toán băm chậm, không phải là một thuật toán nhanh.

Cuối cùng, Rfc2898DeriveByteslớp học chậm (và có thể được làm chậm hơn) và có thể trả lời phần thứ hai của câu hỏi ban đầu ở chỗ nó có thể lấy mật khẩu và muối và trả về hàm băm. Xem câu hỏi này để biết thêm thông tin. Lưu ý, Stack Exchange đang sử dụngRfc2898DeriveBytes để băm mật khẩu (mã nguồn ở đây ).


6
@MushinNoShin SHA256 là một hàm băm nhanh. Băm mật khẩu cần một hàm băm chậm, như PBKDF2, bcrypt hoặc scrypt. Xem Làm thế nào để băm mật khẩu an toàn? trên security.se để biết chi tiết.
CodeInChaos

32

Tôi đã đọc các hàm băm như SHA256 không thực sự có ý định sử dụng với việc lưu trữ mật khẩu: https://patrickmn.com/security/storing-passwords-seciously/#notpasswordhashes

Thay vào đó, các hàm phái sinh khóa thích ứng như PBKDF2, bcrypt hoặc scrypt là. Đây là một PBKDF2 dựa trên một cái mà Microsoft đã viết cho PasswordHasher trong thư viện Microsoft.AspNet.Identity của họ:

/* =======================
 * HASHED PASSWORD FORMATS
 * =======================
 * 
 * Version 3:
 * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
 * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
 * (All UInt32s are stored big-endian.)
 */

public string HashPassword(string password)
{
    var prf = KeyDerivationPrf.HMACSHA256;
    var rng = RandomNumberGenerator.Create();
    const int iterCount = 10000;
    const int saltSize = 128 / 8;
    const int numBytesRequested = 256 / 8;

    // Produce a version 3 (see comment above) text hash.
    var salt = new byte[saltSize];
    rng.GetBytes(salt);
    var subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);

    var outputBytes = new byte[13 + salt.Length + subkey.Length];
    outputBytes[0] = 0x01; // format marker
    WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
    WriteNetworkByteOrder(outputBytes, 5, iterCount);
    WriteNetworkByteOrder(outputBytes, 9, saltSize);
    Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
    Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
    return Convert.ToBase64String(outputBytes);
}

public bool VerifyHashedPassword(string hashedPassword, string providedPassword)
{
    var decodedHashedPassword = Convert.FromBase64String(hashedPassword);

    // Wrong version
    if (decodedHashedPassword[0] != 0x01)
        return false;

    // Read header information
    var prf = (KeyDerivationPrf)ReadNetworkByteOrder(decodedHashedPassword, 1);
    var iterCount = (int)ReadNetworkByteOrder(decodedHashedPassword, 5);
    var saltLength = (int)ReadNetworkByteOrder(decodedHashedPassword, 9);

    // Read the salt: must be >= 128 bits
    if (saltLength < 128 / 8)
    {
        return false;
    }
    var salt = new byte[saltLength];
    Buffer.BlockCopy(decodedHashedPassword, 13, salt, 0, salt.Length);

    // Read the subkey (the rest of the payload): must be >= 128 bits
    var subkeyLength = decodedHashedPassword.Length - 13 - salt.Length;
    if (subkeyLength < 128 / 8)
    {
        return false;
    }
    var expectedSubkey = new byte[subkeyLength];
    Buffer.BlockCopy(decodedHashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);

    // Hash the incoming password and verify it
    var actualSubkey = KeyDerivation.Pbkdf2(providedPassword, salt, prf, iterCount, subkeyLength);
    return actualSubkey.SequenceEqual(expectedSubkey);
}

private static void WriteNetworkByteOrder(byte[] buffer, int offset, uint value)
{
    buffer[offset + 0] = (byte)(value >> 24);
    buffer[offset + 1] = (byte)(value >> 16);
    buffer[offset + 2] = (byte)(value >> 8);
    buffer[offset + 3] = (byte)(value >> 0);
}

private static uint ReadNetworkByteOrder(byte[] buffer, int offset)
{
    return ((uint)(buffer[offset + 0]) << 24)
        | ((uint)(buffer[offset + 1]) << 16)
        | ((uint)(buffer[offset + 2]) << 8)
        | ((uint)(buffer[offset + 3]));
}

Lưu ý điều này yêu cầu gói nuget Microsoft.AspNetCore.Cryptography.KeyDerivation được cài đặt yêu cầu .NET Standard 2.0 (.NET 4.6.1 trở lên). Đối với các phiên bản trước của .NET, hãy xem lớp Crypto từ thư viện System.Web.Helpers của Microsoft.

Cập nhật tháng 11 năm 2015
Câu trả lời được cập nhật để sử dụng triển khai từ thư viện Microsoft khác sử dụng băm PBKDF2-HMAC-SHA256 thay vì PBKDF2-HMAC-SHA1 (lưu ý PBKDF2-HMAC-SHA1 vẫn đủ an toàn nếu iterCount đủ cao). Bạn có thể kiểm tra nguồn mã được đơn giản hóa được sao chép từ khi nó thực sự xử lý xác thực và nâng cấp băm được thực hiện từ câu trả lời trước, rất hữu ích nếu bạn cần tăng iterCount trong tương lai.


1
Lưu ý rằng có thể đáng để tăng PBKDF2IterCount lên một số cao hơn, xem security.stackexchange.com/q/3959 để biết thêm.
Michael

2
1) Giảm PBKDF2SubkeyLengthxuống 20 byte. Đó là kích thước tự nhiên f SHA1 và tăng nó vượt ra ngoài làm chậm kẻ phòng thủ mà không làm chậm kẻ tấn công. 2) Tôi khuyên bạn nên tăng số lần lặp. Tôi khuyên bạn nên 10k đến 100k tùy thuộc vào ngân sách hiệu suất của bạn. 3) Một so sánh thời gian liên tục sẽ không làm hại, nhưng không có nhiều tác động thực tế.
CodeInChaos 20/03/2015

KeyDerivationPrf, KeyDerivation và BlockCopy không được xác định, các lớp của chúng là gì?
mrbengi

@mrbengi Bạn đã cài đặt gói nuget Microsoft.AspNet.Cryptography.KeyDerivation được đề cập chưa? Nếu điều đó không phù hợp thì đây là phiên bản không yêu cầu gói nuget. Buffer.BlockCopy nên tồn tại là một phần của Hệ thống.
Michael

1
Gói nuget hiện là Microsoft.AspNetCore.Cryptography.KeyDerivation.
James Blake

25

Muối được sử dụng để tăng thêm độ phức tạp cho hàm băm, để làm cho vết nứt vũ lực khó hơn.

Từ một bài viết trên Sitepoint :

Một hacker vẫn có thể thực hiện những gì được gọi là một cuộc tấn công từ điển. Ví dụ, các bên độc hại có thể thực hiện một cuộc tấn công từ điển bằng cách lấy 100.000 mật khẩu mà họ biết mọi người sử dụng thường xuyên (ví dụ: tên thành phố, đội thể thao, v.v.), băm chúng, sau đó so sánh từng mục trong từ điển với từng hàng trong cơ sở dữ liệu bàn. Nếu các tin tặc tìm thấy một trận đấu, bingo! Họ có mật khẩu của bạn. Để giải quyết vấn đề này, tuy nhiên, chúng ta chỉ cần muối băm.

Để tạo ra một hàm băm, chúng ta chỉ cần đưa ra một chuỗi văn bản trông ngẫu nhiên, nối nó với mật khẩu do người dùng cung cấp, sau đó băm cả chuỗi được tạo ngẫu nhiên và mật khẩu thành một giá trị. Sau đó, chúng tôi lưu cả hàm băm và muối dưới dạng các trường riêng biệt trong bảng Người dùng.

Trong kịch bản này, không chỉ tin tặc cần đoán mật khẩu, họ còn phải đoán cả muối. Thêm muối vào văn bản rõ ràng sẽ cải thiện bảo mật: bây giờ, nếu tin tặc thử tấn công từ điển, anh ta phải băm 100.000 mục nhập của mình bằng muối của mỗi hàng người dùng. Mặc dù vẫn có thể, nhưng cơ hội hack thành công giảm đi triệt để.

Không có phương pháp tự động làm điều này trong .NET, vì vậy bạn sẽ đi với giải pháp ở trên.


Muối được sử dụng để bảo vệ chống lại những thứ như bảng cầu vồng. Để chống lại các cuộc tấn công từ điển, một yếu tố công việc (còn được gọi là kéo dài khóa) được yêu cầu như bất kỳ KDF tốt nào: en.wikipedia.org/wiki/Key_stretching
Erwan Legrand

11

Tôi đã tạo một lớp có phương thức sau:

  1. Tạo muối
  2. Đầu vào băm
  3. Xác thực đầu vào

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

    `



3

Tôi đã tạo một thư viện SimpleHashing.Net để làm cho quá trình băm dễ dàng với các lớp cơ bản do Microsoft cung cấp. SHA thông thường không thực sự đủ để có mật khẩu được lưu trữ an toàn nữa.

Thư viện sử dụng ý tưởng định dạng băm từ Bcrypt, nhưng vì không có triển khai MS chính thức nên tôi thích sử dụng những gì có sẵn trong khung (ví dụ PBKDF2), nhưng nó hơi khó.

Đây là một ví dụ nhanh về cách sử dụng thư viện:

ISimpleHash simpleHash = new SimpleHash();

// Creating a user hash, hashedPassword can be stored in a database
// hashedPassword contains the number of iterations and salt inside it similar to bcrypt format
string hashedPassword = simpleHash.Compute("Password123");

// Validating user's password by first loading it from database by username
string storedHash = _repository.GetUserPasswordHash(username);
isPasswordValid = simpleHash.Verify("Password123", storedHash);

2

Đây là cách tôi thực hiện .. Tôi tạo hàm băm và lưu trữ bằng ProtectedDataapi:

    public static string GenerateKeyHash(string Password)
    {
        if (string.IsNullOrEmpty(Password)) return null;
        if (Password.Length < 1) return null;

        byte[] salt = new byte[20];
        byte[] key = new byte[20];
        byte[] ret = new byte[40];

        try
        {
            using (RNGCryptoServiceProvider randomBytes = new RNGCryptoServiceProvider())
            {
                randomBytes.GetBytes(salt);

                using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
                {
                    key = hashBytes.GetBytes(20);
                    Buffer.BlockCopy(salt, 0, ret, 0, 20);
                    Buffer.BlockCopy(key, 0, ret, 20, 20);
                }
            }
            // returns salt/key pair
            return Convert.ToBase64String(ret);
        }
        finally
        {
            if (salt != null)
                Array.Clear(salt, 0, salt.Length);
            if (key != null)
                Array.Clear(key, 0, key.Length);
            if (ret != null)
                Array.Clear(ret, 0, ret.Length);
        } 
    }

    public static bool ComparePasswords(string PasswordHash, string Password)
    {
        if (string.IsNullOrEmpty(PasswordHash) || string.IsNullOrEmpty(Password)) return false;
        if (PasswordHash.Length < 40 || Password.Length < 1) return false;

        byte[] salt = new byte[20];
        byte[] key = new byte[20];
        byte[] hash = Convert.FromBase64String(PasswordHash);

        try
        {
            Buffer.BlockCopy(hash, 0, salt, 0, 20);
            Buffer.BlockCopy(hash, 20, key, 0, 20);

            using (var hashBytes = new Rfc2898DeriveBytes(Password, salt, 10000))
            {
                byte[] newKey = hashBytes.GetBytes(20);

                if (newKey != null)
                    if (newKey.SequenceEqual(key))
                        return true;
            }
            return false;
        }
        finally
        {
            if (salt != null)
                Array.Clear(salt, 0, salt.Length);
            if (key != null)
                Array.Clear(key, 0, key.Length);
            if (hash != null)
                Array.Clear(hash, 0, hash.Length);
        }
    }

    public static byte[] DecryptData(string Data, byte[] Salt)
    {
        if (string.IsNullOrEmpty(Data)) return null;

        byte[] btData = Convert.FromBase64String(Data);

        try
        {
            return ProtectedData.Unprotect(btData, Salt, DataProtectionScope.CurrentUser);
        }
        finally
        {
            if (btData != null)
                Array.Clear(btData, 0, btData.Length);
        }
    }

    public static string EncryptData(byte[] Data, byte[] Salt)
    {
        if (Data == null) return null;
        if (Data.Length < 1) return null;

        byte[] buffer = new byte[Data.Length];

        try
        {
            Buffer.BlockCopy(Data, 0, buffer, 0, Data.Length);
            return System.Convert.ToBase64String(ProtectedData.Protect(buffer, Salt, DataProtectionScope.CurrentUser));
        }
        finally
        {
            if (buffer != null)
                Array.Clear(buffer, 0, buffer.Length);
        }
    }

Làm thế nào để tôi gọi nó trong khi tiết kiệm và khi so sánh sau này?
SearchForKnowledge

2

Tôi đọc tất cả câu trả lời và tôi nghĩ rằng những người đủ, đặc biệt @ Michael bài viết với băm chậm và @CodesInChaos ý kiến tốt, nhưng tôi quyết định chia sẻ đoạn mã của tôi cho băm / xác nhận có thể hữu ích và nó không đòi hỏi [ Microsoft.AspNet.Cryptography .KeyDerivation ].

    private static bool SlowEquals(byte[] a, byte[] b)
            {
                uint diff = (uint)a.Length ^ (uint)b.Length;
                for (int i = 0; i < a.Length && i < b.Length; i++)
                    diff |= (uint)(a[i] ^ b[i]);
                return diff == 0;
            }

    private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
            {
                Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);
                pbkdf2.IterationCount = iterations;
                return pbkdf2.GetBytes(outputBytes);
            }

    private static string CreateHash(string value, int salt_bytes, int hash_bytes, int pbkdf2_iterations)
            {
                // Generate a random salt
                RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
                byte[] salt = new byte[salt_bytes];
                csprng.GetBytes(salt);

                // Hash the value and encode the parameters
                byte[] hash = PBKDF2(value, salt, pbkdf2_iterations, hash_bytes);

                //You need to return the salt value too for the validation process
                return Convert.ToBase64String(hash) + ":" + 
                       Convert.ToBase64String(hash);
            }

    private static bool ValidateHash(string pureVal, string saltVal, string hashVal, int pbkdf2_iterations)
            {
                try
                {
                    byte[] salt = Convert.FromBase64String(saltVal);
                    byte[] hash = Convert.FromBase64String(hashVal);

                    byte[] testHash = PBKDF2(pureVal, salt, pbkdf2_iterations, hash.Length);
                    return SlowEquals(hash, testHash);
                }
                catch (Exception ex)
                {
                    return false;
                }
            }

Vui lòng chú ý chức năng SlowEquals rất quan trọng, Cuối cùng, tôi hy vọng sự giúp đỡ này và Xin đừng ngần ngại tư vấn cho tôi về các phương pháp tốt hơn.


Thay vì tạo ra một vòng lặp bận rộn, tại sao không đặt vào một độ trễ không bận rộn nhân tạo. ví dụ: sử dụng Task.Delay. Điều này sẽ trì hoãn một nỗ lực vũ phu nhưng không chặn luồng hoạt động.
gburton

@gburton Cảm ơn lời khuyên của bạn. Tôi sẽ kiểm tra.
QMaster

Có một lỗi đánh máy trong CreatHash: bạn đang chuyển đổi Convert.ToBase64String (băm) sang chính nó thay vì muối. Ngoài ra, đây là một câu trả lời hay, điều này giải quyết khá nhiều vấn đề được nêu ra trong các bình luận về các câu trả lời khác.
ZeRemz

2

Sử dụng System.Web.Helpers.Cryptogói NuGet từ Microsoft. Nó tự động thêm muối vào hàm băm.

Bạn băm mật khẩu như thế này: var hash = Crypto.HashPassword("foo");

Bạn xác minh một mật khẩu như thế này: var verified = Crypto.VerifyHashedPassword(hash, "foo");


1

Nếu bạn không sử dụng lõi asp.net hoặc .net, đó cũng là một cách dễ dàng trong các dự án> = .Net Standard 2.0.

Trước tiên, bạn có thể đặt kích thước mong muốn của số băm, muối và số lần lặp có liên quan đến thời lượng của quá trình tạo băm:

private const int SaltSize = 32;
private const int HashSize = 32;
private const int IterationCount = 10000;

Để tạo mật khẩu băm và muối, bạn có thể sử dụng một cái gì đó như thế này:

public static string GeneratePasswordHash(string password, out string salt)
{
    using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, SaltSize))
    {
        rfc2898DeriveBytes.IterationCount = IterationCount;
        byte[] hashData = rfc2898DeriveBytes.GetBytes(HashSize);
        byte[] saltData = rfc2898DeriveBytes.Salt;
        salt = Convert.ToBase64String(saltData);
        return Convert.ToBase64String(hashData);
    }
}

Để xác minh xem mật khẩu mà người dùng đã nhập có hợp lệ hay không, bạn có thể kiểm tra với các giá trị trong cơ sở dữ liệu của mình:

public static bool VerifyPassword(string password, string passwordHash, string salt)
{
    using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, SaltSize))
    {
        rfc2898DeriveBytes.IterationCount = IterationCount;
        rfc2898DeriveBytes.Salt = Convert.FromBase64String(salt);
        byte[] hashData = rfc2898DeriveBytes.GetBytes(HashSize);
        return Convert.ToBase64String(hashData) == passwordHash;
    }
}

Các thử nghiệm đơn vị sau đây cho thấy việc sử dụng:

string password = "MySecret";

string passwordHash = PasswordHasher.GeneratePasswordHash(password, out string salt);

Assert.True(PasswordHasher.VerifyPassword(password, passwordHash, salt));
Assert.False(PasswordHasher.VerifyPassword(password.ToUpper(), passwordHash, salt));

Microsoft Rfc2898DeriveBytes Nguồn


-1

Trả lời phần này của câu hỏi ban đầu "Có phương pháp C # nào khác để băm mật khẩu không" Bạn có thể đạt được điều này bằng cách sử dụng ASP.NET Identity v3.0 https://www.nuget.org/packages/Microsoft.AspNet.Identity. EntityFramework / 3.0.0-rc1-chung kết

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using System.Security.Principal;

namespace HashTest{


    class Program
    {
        static void Main(string[] args)
        {

            WindowsIdentity wi = WindowsIdentity.GetCurrent();

            var ph = new PasswordHasher<WindowsIdentity>();

            Console.WriteLine(ph.HashPassword(wi,"test"));

            Console.WriteLine(ph.VerifyHashedPassword(wi,"AQAAAAEAACcQAAAAEA5S5X7dmbx/NzTk6ixCX+bi8zbKqBUjBhID3Dg1teh+TRZMkAy3CZC5yIfbLqwk2A==","test"));

        }
    }


}

-1
 protected void m_GenerateSHA256_Button1_Click(objectSender, EventArgs e)
{
string salt =createSalt(10);
string hashedPassword=GenerateSHA256Hash(m_UserInput_TextBox.Text,Salt);
m_SaltHash_TextBox.Text=Salt;
 m_SaltSHA256Hash_TextBox.Text=hashedPassword;

}
 public string createSalt(int size)
{
 var rng= new System.Security.Cyptography.RNGCyptoServiceProvider();
 var buff= new byte[size];
rng.GetBytes(buff);
 return Convert.ToBase64String(buff);
}


 public string GenerateSHA256Hash(string input,string salt)
{
 byte[]bytes=System.Text.Encoding.UTF8.GetBytes(input+salt);
 new System.Security.Cyptography.SHA256Managed();
 byte[]hash=sha256hashString.ComputedHash(bytes);
 return bytesArrayToHexString(hash);
  }

phương pháp khác là mật khẩu chuỗi = HashPasswordForStoringInConfigFile (TextBox1.Text, SHA1)
ankush shukla

-6
create proc [dbo].[hash_pass] @family nvarchar(50), @username nvarchar(50), @pass nvarchar(Max),``` @semat nvarchar(50), @tell nvarchar(50)

as insert into tbl_karbar values (@family,@username,(select HASHBYTES('SHA1' ,@pass)),@semat,@tell)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.