2016-01-22 178 views
5

我需要创建一个PBKDF2-SHA256密码哈希,但遇到了一些麻烦。如何在C#/ Bouncy Castle中创建PBKDF2-SHA256密码哈希

我下载了Bouncy Castle回购协议,但在单元测试中找到了我要找的东西,有点卡住了。

找到一些示例代码here,但这只是做SHA1。代码的关键位是:

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

我需要将此从SHA1更改为SHA256。

从Java文档和this post,它看起来像下面是可能的,但在C#库中的构造函数没有超载。

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

查找有关堆栈溢出another article,我认为以下是可能的,但SHA哈希算法并不在查找列表,所以下面将无法正常工作。

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

我需要做些什么才能使这个工作?

注意:如果您阅读了这篇文章,但不知道如何在Bouncy Castle,但是确实知道另一种方式,我仍然感谢您的帮助。

+0

的源代码是供用户修改。为什么不这样做? –

+1

@JamesKPolk - 一个有效的评论。原因是:A.因为我对B和B的理解都不太理解。我认为在任何情况下,以任何方式来滚动你自己都是一种严肃的禁忌。但是,也许这只是篡改设置它可能是好的。我认为必须有一种方法,但也许C#BouncyCastle落后于Java版本,它只是不包括在内?如果真的没有办法在C#中做到这一点,那么我可能会尝试拉请求并根据Java版本更新代码。我真的宁愿有可用的东西,我可以使用。 – HockeyJ

回答

7

编辑(上一页回答历史的简洁,删除)

现在有一个Bouncy Castle Crypto API NuGet包可以使用。或者,您可以直接从GitHub获取source,该工具可以工作。我从NuGet那里得到了标准的Bouncy Castle,在撰写本文时,它尚未更新到1.8.1。

为了搜索者的利益,这里有一个用于散列的C#helper类。已经测试过多个线程,看起来很好。

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

} 

使用例为Pkcs5S2ParametersGenerator()

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

已确认上面引用的Bouncy Castle Crypto API nuget包现在是1.8.1版本 – kyle