2016-01-22 50 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

Pkcs5S2ParametersGeneratorのソースコード()変更するために利用可能です。なぜそれをしないのですか? –

+1

@JamesKPolk - 有効なコメントです。その理由は次のとおりです。A.それは私がそれをよく理解していないからです。B.私はあなた自身を何らかの方法で巻き込むことは、暗号化に関しては深刻なノー・ノーと考えています。しかし、おそらくこれは設定を改ざんしているだけなので、大丈夫かもしれません。私は方法が必要であると仮定しましたが、多分C#BouncyCastleはJavaバージョンの後ろに遅れていて、それはちょうど含まれていませんか?実際にC#でこれを行う方法がない場合は、Javaバージョンに基づいてプルリクエストを試してコードを更新することがあります。本当にむしろ私はちょうど使用できる何かがありましたか? – HockeyJ

答えて

7

EDIT(前の回答履歴を簡潔にするため削除)

使用することができBouncy Castle Crypto API NuGetパッケージが用意されました。あるいは、GitHubから直接sourceを入手することもできます。これは動作します。私はNuGetの標準的なBouncy Castleを持っていましたが、これは書面の時点で1.8.1に更新されていませんでした。

サーチャーのために、ここにはハッシュのためのC#ヘルパークラスがあります。複数のスレッドでテストし、うまくいるようです。

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

} 

使用例

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

関連する問題