c# - 将 .Net 4.5.1 中的 KeyDerivation.Pbkdf2 转换为 .Net 4.0

标签 c# .net hash

我有两个使用.net 4.0和.net 4.6.1的项目,KeyDerivation仅在.net 4.5.1之后可用。如何使用其他哈希库(例如 Rfc2898DeriveBytes/Crypto)在 .Net 4.0 中获得相同的哈希结果

byte[] salt = new byte[128 / 8];
var hashedPassword = Convert.ToBase64String(KeyDerivation.Pbkdf2(
    password: "GN(o@D30",
    salt: salt,
    prf: KeyDerivationPrf.HMACSHA512,
    iterationCount: 100000,
    numBytesRequested: 256 / 8)
);

最佳答案

Rfc2898DeriveBytes 的问题是它仅支持 HMACSHA1(出于未知原因)。在您的情况下,您想使用 HMACSHA512。您可以使用描述的实现 here ,请注意,这不是一些自定义实现 - 作者只是获取了 Rfc2898DeriveBytes 标准内置类的源代码,并将其调整为使用您通过构造函数参数提供的 hmac 实现(并丢弃了不相关的部分)。为了清楚起见,我将在这里提供完整的源代码:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pbkdf", Justification = "Spelling is correct.")]
public class Pbkdf2
{

    /// <summary>
    /// Creates new instance.
    /// </summary>
    /// <param name="algorithm">HMAC algorithm to use.</param>
    /// <param name="password">The password used to derive the key.</param>
    /// <param name="salt">The key salt used to derive the key.</param>
    /// <param name="iterations">The number of iterations for the operation.</param>
    /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
    public Pbkdf2(HMAC algorithm, Byte[] password, Byte[] salt, Int32 iterations)
    {
        if (algorithm == null) { throw new ArgumentNullException("algorithm", "Algorithm cannot be null."); }
        if (salt == null) { throw new ArgumentNullException("salt", "Salt cannot be null."); }
        if (password == null) { throw new ArgumentNullException("password", "Password cannot be null."); }
        this.Algorithm = algorithm;
        this.Algorithm.Key = password;
        this.Salt = salt;
        this.IterationCount = iterations;
        this.BlockSize = this.Algorithm.HashSize / 8;
        this.BufferBytes = new byte[this.BlockSize];
    }

    /// <summary>
    /// Creates new instance.
    /// </summary>
    /// <param name="algorithm">HMAC algorithm to use.</param>
    /// <param name="password">The password used to derive the key.</param>
    /// <param name="salt">The key salt used to derive the key.</param>
    /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
    public Pbkdf2(HMAC algorithm, Byte[] password, Byte[] salt)
        : this(algorithm, password, salt, 1000)
    {
    }

    /// <summary>
    /// Creates new instance.
    /// </summary>
    /// <param name="algorithm">HMAC algorithm to use.</param>
    /// <param name="password">The password used to derive the key.</param>
    /// <param name="salt">The key salt used to derive the key.</param>
    /// <param name="iterations">The number of iterations for the operation.</param>
    /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
    public Pbkdf2(HMAC algorithm, String password, String salt, Int32 iterations) :
        this(algorithm, UTF8Encoding.UTF8.GetBytes(password), UTF8Encoding.UTF8.GetBytes(salt), iterations)
    {
    }

    /// <summary>
    /// Creates new instance.
    /// </summary>
    /// <param name="algorithm">HMAC algorithm to use.</param>
    /// <param name="password">The password used to derive the key.</param>
    /// <param name="salt">The key salt used to derive the key.</param>
    /// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
    public Pbkdf2(HMAC algorithm, String password, String salt) :
        this(algorithm, password, salt, 1000)
    {
    }


    private readonly int BlockSize;
    private uint BlockIndex = 1;

    private byte[] BufferBytes;
    private int BufferStartIndex = 0;
    private int BufferEndIndex = 0;


    /// <summary>
    /// Gets algorithm used for generating key.
    /// </summary>
    public HMAC Algorithm { get; private set; }

    /// <summary>
    /// Gets salt bytes.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Byte array is proper return value in this case.")]
    public Byte[] Salt { get; private set; }

    /// <summary>
    /// Gets iteration count.
    /// </summary>
    public Int32 IterationCount { get; private set; }


    /// <summary>
    /// Returns a pseudo-random key from a password, salt and iteration count.
    /// </summary>
    /// <param name="count">Number of bytes to return.</param>
    /// <returns>Byte array.</returns>
    public Byte[] GetBytes(int count)
    {
        byte[] result = new byte[count];
        int resultOffset = 0;
        int bufferCount = this.BufferEndIndex - this.BufferStartIndex;

        if (bufferCount > 0)
        { //if there is some data in buffer
            if (count < bufferCount)
            { //if there is enough data in buffer
                Buffer.BlockCopy(this.BufferBytes, this.BufferStartIndex, result, 0, count);
                this.BufferStartIndex += count;
                return result;
            }
            Buffer.BlockCopy(this.BufferBytes, this.BufferStartIndex, result, 0, bufferCount);
            this.BufferStartIndex = this.BufferEndIndex = 0;
            resultOffset += bufferCount;
        }

        while (resultOffset < count)
        {
            int needCount = count - resultOffset;
            this.BufferBytes = this.Func();
            if (needCount > this.BlockSize)
            { //we one (or more) additional passes
                Buffer.BlockCopy(this.BufferBytes, 0, result, resultOffset, this.BlockSize);
                resultOffset += this.BlockSize;
            }
            else
            {
                Buffer.BlockCopy(this.BufferBytes, 0, result, resultOffset, needCount);
                this.BufferStartIndex = needCount;
                this.BufferEndIndex = this.BlockSize;
                return result;
            }
        }
        return result;
    }


    private byte[] Func()
    {
        var hash1Input = new byte[this.Salt.Length + 4];
        Buffer.BlockCopy(this.Salt, 0, hash1Input, 0, this.Salt.Length);
        Buffer.BlockCopy(GetBytesFromInt(this.BlockIndex), 0, hash1Input, this.Salt.Length, 4);
        var hash1 = this.Algorithm.ComputeHash(hash1Input);

        byte[] finalHash = hash1;
        for (int i = 2; i <= this.IterationCount; i++)
        {
            hash1 = this.Algorithm.ComputeHash(hash1, 0, hash1.Length);
            for (int j = 0; j < this.BlockSize; j++)
            {
                finalHash[j] = (byte)(finalHash[j] ^ hash1[j]);
            }
        }
        if (this.BlockIndex == uint.MaxValue) { throw new InvalidOperationException("Derived key too long."); }
        this.BlockIndex += 1;

        return finalHash;
    }

    private static byte[] GetBytesFromInt(uint i)
    {
        var bytes = BitConverter.GetBytes(i);
        if (BitConverter.IsLittleEndian)
        {
            return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
        }
        else
        {
            return bytes;
        }
    }

}

现在,有了该类,您问题中的代码的完整模拟将是:

byte[] salt = new byte[128 / 8];
string hashedPassword;
using (var hmac = new HMACSHA512()) {
     hashedPassword = Convert.ToBase64String(new Pbkdf2(
         hmac, "GN(o@D30", Encoding.UTF8.GetString(salt), 100000).GetBytes(256 / 8));                
}

关于c# - 将 .Net 4.5.1 中的 KeyDerivation.Pbkdf2 转换为 .Net 4.0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44408355/

相关文章:

python - 为什么即使 PYTHONHASHSEED=0,Python 集合的顺序也不确定?

java - 将表 Guava 用于 hashbasedTable

url - 如何用服务器端语言获取哈希值?

c# - 从 C# 资源中读取文本文件

c# - Entity Framework 的悲观并发

.net - 有什么方法可以在.NET运行时中预分配堆,例如Java中的-Xmx/-Xms吗?

.net - 您更喜欢 .net 中的哪种配置方法?为什么?

c# - 如何从resx中的ImageListStreamer中获取图像文件?

c# - C#中的简单网络爬虫

c# - 如何使用 .NET C# 在登录屏幕中隐藏用户?