2016-01-22 16 views
5

Tôi cần phải tạo một băm mật khẩu PBKDF2-SHA256, nhưng đang gặp một số sự cố.Cách tạo băm mật khẩu PBKDF2-SHA256 trong C#/Bouncy Castle

Tôi đã tải xuống bản thử nghiệm Bouncy Castle, nhưng có một chút khó khăn khi tìm thấy những gì tôi đang tìm kiếm trong Bài kiểm tra đơn vị.

Tìm thấy một số mã mẫu here, nhưng điều này chỉ thực hiện SHA1. Mã khóa chính là:

/// <summary> 
/// Computes the PBKDF2-SHA1 hash of a password. 
/// </summary> 
/// <param name="password">The password to hash.</param> 
/// <param name="salt">The salt.</param> 
/// <param name="iterations">The PBKDF2 iteration count.</param> 
/// <param name="outputBytes">The length of the hash to generate, in bytes.</param> 
/// <returns>A hash of the password.</returns> 
private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes) 
{ 
    var pdb = new Pkcs5S2ParametersGenerator(); 
    pdb.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()), salt, 
       iterations); 
    var key = (KeyParameter)pdb.GenerateDerivedMacParameters(outputBytes * 8); 
    return key.GetKey(); 
} 

Tôi cần thay đổi điều này từ SHA1 thành SHA256.

Từ tài liệu Java và this post, có vẻ như sau đây là có thể, nhưng không có quá tải trên hàm tạo trong thư viện C#.

var pdb = new Pkcs5S2ParametersGenerator(new Sha256Derived()); 

Tìm another article trên stack overflow, tôi nghĩ sau đây có thể có thể, nhưng các thuật toán băm SHA không nằm trong danh sách tra cứu, vì vậy sau đây sẽ không hoạt động.

var bcparam = (KeyParameter)pdb.GenerateDerivedParameters("sha256", outputBytes * 8); 

Tôi cần làm gì để làm việc này?

Lưu ý: Nếu bạn đọc và không biết làm thế nào trong Lâu đài Bouncy, nhưng bạn biết cách khác, tôi vẫn đánh giá cao sự giúp đỡ của bạn.

+0

Nguồn cho Pkcs5S2ParametersGenerator() có sẵn để bạn có thể sửa đổi. Tại sao không làm điều đó? –

+1

@JamesKPolk - Nhận xét hợp lệ. Lý do là: A. Bởi vì tôi không hiểu rõ điều đó và B. Tôi tin rằng việc tự xoay sở của mình theo bất kỳ cách nào được coi là không nghiêm trọng khi nói đến mã hóa. Tuy nhiên có lẽ vì điều này chỉ là giả mạo với các thiết lập nó có thể là ok. Tôi giả định có phải là một cách, nhưng có lẽ C# BouncyCastle là tụt lại phía sau phiên bản java và nó chỉ không được bao gồm? Nếu có thực sự không phải là một cách để làm điều này trong C# thì tôi có thể thử một yêu cầu kéo và cập nhật mã, dựa trên phiên bản Java. Sẽ thực sự thay vì có một cái gì đó có sẵn mà tôi chỉ có thể sử dụng. – HockeyJ

Trả lời

7

EDIT (trước lịch sử câu trả lời lấy ra cho ngắn gọn)

Hiện nay, một NuGet gói Bouncy Castle Crypto API có thể được sử dụng. Ngoài ra, bạn có thể nhận trực tiếp số source từ GitHub, thao tác này sẽ hoạt động. Tôi đã có Lâu đài Bouncy chuẩn từ NuGet, mà chưa được cập nhật lên 1.8.1 tại thời điểm viết.

Vì lợi ích của người tìm kiếm, đây là lớp trợ giúp C# cho băm. Đã thử nghiệm trên nhiều chủ đề và có vẻ tốt.

/// <summary> 
/// Contains the relevant Bouncy Castle Methods required to encrypt a password. 
/// References NuGet Package BouncyCastle.Crypto.dll 
/// </summary> 
public class BouncyCastleHashing 
{ 
    private SecureRandom _cryptoRandom; 

    public BouncyCastleHashing() 
    { 
     _cryptoRandom = new SecureRandom(); 
    } 

    /// <summary> 
    /// Random Salt Creation 
    /// </summary> 
    /// <param name="size">The size of the salt in bytes</param> 
    /// <returns>A random salt of the required size.</returns> 
    public byte[] CreateSalt(int size) 
    { 
     byte[] salt = new byte[size]; 
     _cryptoRandom.NextBytes(salt); 
     return salt; 
    } 

    /// <summary> 
    /// Gets a PBKDF2_SHA256 Hash (Overload) 
    /// </summary> 
    /// <param name="password">The password as a plain text string</param> 
    /// <param name="saltAsBase64String">The salt for the password</param> 
    /// <param name="iterations">The number of times to encrypt the password</param> 
    /// <param name="hashByteSize">The byte size of the final hash</param> 
    /// <returns>A base64 string of the hash.</returns> 
    public string PBKDF2_SHA256_GetHash(string password, string saltAsBase64String, int iterations, int hashByteSize) 
    { 
     var saltBytes = Convert.FromBase64String(saltAsBase64String); 

     var hash = PBKDF2_SHA256_GetHash(password, saltBytes, iterations, hashByteSize); 

     return Convert.ToBase64String(hash); 
    } 

    /// <summary> 
    /// Gets a PBKDF2_SHA256 Hash (CORE METHOD) 
    /// </summary> 
    /// <param name="password">The password as a plain text string</param> 
    /// <param name="salt">The salt as a byte array</param> 
    /// <param name="iterations">The number of times to encrypt the password</param> 
    /// <param name="hashByteSize">The byte size of the final hash</param> 
    /// <returns>A the hash as a byte array.</returns> 
    public byte[] PBKDF2_SHA256_GetHash(string password, byte[] salt, int iterations, int hashByteSize) 
    { 
     var pdb = new Pkcs5S2ParametersGenerator(new Org.BouncyCastle.Crypto.Digests.Sha256Digest()); 
     pdb.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()), salt, 
        iterations); 
     var key = (KeyParameter)pdb.GenerateDerivedMacParameters(hashByteSize * 8); 
     return key.GetKey(); 
    } 

    /// <summary> 
    /// Validates a password given a hash of the correct one. (OVERLOAD) 
    /// </summary> 
    /// <param name="password">The original password to hash</param> 
    /// <param name="salt">The salt that was used when hashing the password</param> 
    /// <param name="iterations">The number of times it was encrypted</param> 
    /// <param name="hashByteSize">The byte size of the final hash</param> 
    /// <param name="hashAsBase64String">The hash the password previously provided as a base64 string</param> 
    /// <returns>True if the hashes match</returns> 
    public bool ValidatePassword(string password, string salt, int iterations, int hashByteSize, string hashAsBase64String) 
    { 
     byte[] saltBytes = Convert.FromBase64String(salt); 
     byte[] actualHashBytes = Convert.FromBase64String(hashAsBase64String); 
     return ValidatePassword(password, saltBytes, iterations, hashByteSize, actualHashBytes); 
    } 

    /// <summary> 
    /// Validates a password given a hash of the correct one (MAIN METHOD). 
    /// </summary> 
    /// <param name="password">The password to check.</param> 
    /// <param name="correctHash">A hash of the correct password.</param> 
    /// <returns>True if the password is correct. False otherwise.</returns> 
    public bool ValidatePassword(string password, byte[] saltBytes, int iterations, int hashByteSize, byte[] actualGainedHasAsByteArray) 
    { 
     byte[] testHash = PBKDF2_SHA256_GetHash(password, saltBytes, iterations, hashByteSize); 
     return SlowEquals(actualGainedHasAsByteArray, testHash); 
    } 

    /// <summary> 
    /// Compares two byte arrays in length-constant time. This comparison 
    /// method is used so that password hashes cannot be extracted from 
    /// on-line systems using a timing attack and then attacked off-line. 
    /// </summary> 
    /// <param name="a">The first byte array.</param> 
    /// <param name="b">The second byte array.</param> 
    /// <returns>True if both byte arrays are equal. False otherwise.</returns> 
    private 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; 
    } 

} 

Cách sử dụng mã ví dụ

public void CreatePasswordHash_Single() 
{ 
    int iterations = 100000; // The number of times to encrypt the password - change this 
    int saltByteSize = 64; // the salt size - change this 
    int hashByteSize = 128; // the final hash - change this 

    BouncyCastleHashing mainHashingLib = new BouncyCastleHashing(); 

    var password = "password"; // That's really secure! :) 

    byte[] saltBytes = mainHashingLib.CreateSalt(saltByteSize); 
    string saltString = Convert.ToBase64String(saltBytes); 

    string pwdHash = mainHashingLib.PBKDF2_SHA256_GetHash(password, saltString, iterations, hashByteSize); 

    var isValid = mainHashingLib.ValidatePassword(password, saltBytes, iterations, hashByteSize, Convert.FromBase64String(pwdHash)); 

} 
+0

đã xác nhận gói nuget Bouncy Castle Crypto API được tham chiếu ở trên là phiên bản 1.8.1 – kyle

Các vấn đề liên quan