Rfc2898 / PBKDF2与SHA256作为c#中的摘要

我想在c#中使用Rfc2898来获取密钥。 我还需要使用SHA256作为Rfc2898的摘要。 我找到了类Rfc2898DeriveBytes ,但它使用SHA-1,我没有看到让它使用不同的摘要的方法。

有没有办法在带有SHA256的c#中使用Rfc2898作为摘要(从头开始实现它)?

请参阅Bruno Garcia的回答。

卡斯滕:请接受这个答案,而不是这个答案。

当我开始这个答案时,Rfc2898DeriveBytes无法配置为使用不同的哈希函数。 与此同时,它已得到改善; 看布鲁诺加西亚的回答。

对于较旧的.NET框架的用户,这仍然有用:

 // NOTE: The iteration count should // be as high as possible without causing // unreasonable delay. Note also that the password // and salt are byte arrays, not strings. After use, // the password and salt should be cleared (with Array.Clear) public static byte[] PBKDF2Sha256GetBytes(int dklen, byte[] password, byte[] salt, int iterationCount){ using(var hmac=new System.Security.Cryptography.HMACSHA256(password)){ int hashLength=hmac.HashSize/8; if((hmac.HashSize&7)!=0) hashLength++; int keyLength=dklen/hashLength; if((long)dklen>(0xFFFFFFFFL*hashLength) || dklen<0) throw new ArgumentOutOfRangeException("dklen"); if(dklen%hashLength!=0) keyLength++; byte[] extendedkey=new byte[salt.Length+4]; Buffer.BlockCopy(salt,0,extendedkey,0,salt.Length); using(var ms=new System.IO.MemoryStream()){ for(int i=0;i>24)&0xFF); extendedkey[salt.Length+1]=(byte)(((i+1)>>16)&0xFF); extendedkey[salt.Length+2]=(byte)(((i+1)>>8)&0xFF); extendedkey[salt.Length+3]=(byte)(((i+1))&0xFF); byte[] u=hmac.ComputeHash(extendedkey); Array.Clear(extendedkey,salt.Length,4); byte[] f=u; for(int j=1;j 

.NET Core有一个新的Rfc2898DeriveBytes实现。

CoreFX版本不再具有硬编码的散列算法

该代码可在Github上获得 。 它于2017年3月合并为master,并随.NET Core 2.0一起提供。

我知道这是一个老问题,但对于遇到它的任何人,您现在可以使用Microsoft.AspNetCore.Cryptography.KeyDerivation nuget包中的KeyDerivation.Pbkdf2。 它是asp.net核心中使用的。

不幸的是,它会增加大量不真正需要的参考资料。 您可以复制代码并将其粘贴到您自己的项目中(尽管您现在必须维护密码代码,这是一个PITA)

BCL Rfc2898DeriveBytes硬编码使用sha-1。

KeyDerivation.Pbkdf2允许完全相同的输出,但它也允许HMAC SHA-256和HMAC SHA-512。 它也更快; 在我的机器上大约5次 – 这对安全性有好处,因为它允许更多的回合,这使得破解者的生活更加艰难(顺便说一句sha-512比sha-256或sha1更少的gpu友好)。 api更简单,启动:

 byte[] salt = ... string password = ... var rounds = 50000; // pick something bearable var num_bytes_requested = 16; // 128 bits is fine var prf = KeyDerivationPrf.HMACSHA512; // or sha256, or sha1 byte[] hashed = KeyDerivation.Pbkdf2(password, salt, prf, rounds, num_bytes_requested); 

它来自nuget包Microsoft.AspNetCore.Cryptography.KeyDerivation ,它依赖于asp.net核心; 它运行在.net 4.5.1或.net标准1.3或更高版本上。

你可以使用Bouncy Castle。 C#规范列出了算法“PBEwithHmacSHA-256”,它只能是带有SHA-256的PBKDF2。

对于它的价值,这里是Microsoft的实现副本,但SHA-1替换为SHA512:

 namespace System.Security.Cryptography { using System.Globalization; using System.IO; using System.Text; [System.Runtime.InteropServices.ComVisible(true)] public class Rfc2898DeriveBytes_HMACSHA512 : DeriveBytes { private byte[] m_buffer; private byte[] m_salt; private HMACSHA512 m_HMACSHA512; // The pseudo-random generator function used in PBKDF2 private uint m_iterations; private uint m_block; private int m_startIndex; private int m_endIndex; private static RNGCryptoServiceProvider _rng; private static RNGCryptoServiceProvider StaticRandomNumberGenerator { get { if (_rng == null) { _rng = new RNGCryptoServiceProvider(); } return _rng; } } private const int BlockSize = 20; // // public constructors // public Rfc2898DeriveBytes_HMACSHA512(string password, int saltSize) : this(password, saltSize, 1000) { } public Rfc2898DeriveBytes_HMACSHA512(string password, int saltSize, int iterations) { if (saltSize < 0) throw new ArgumentOutOfRangeException("saltSize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); byte[] salt = new byte[saltSize]; StaticRandomNumberGenerator.GetBytes(salt); Salt = salt; IterationCount = iterations; m_HMACSHA512 = new HMACSHA512(new UTF8Encoding(false).GetBytes(password)); Initialize(); } public Rfc2898DeriveBytes_HMACSHA512(string password, byte[] salt) : this(password, salt, 1000) { } public Rfc2898DeriveBytes_HMACSHA512(string password, byte[] salt, int iterations) : this(new UTF8Encoding(false).GetBytes(password), salt, iterations) { } public Rfc2898DeriveBytes_HMACSHA512(byte[] password, byte[] salt, int iterations) { Salt = salt; IterationCount = iterations; m_HMACSHA512 = new HMACSHA512(password); Initialize(); } // // public properties // public int IterationCount { get { return (int)m_iterations; } set { if (value <= 0) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); m_iterations = (uint)value; Initialize(); } } public byte[] Salt { get { return (byte[])m_salt.Clone(); } set { if (value == null) throw new ArgumentNullException("value"); if (value.Length < 8) throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Cryptography_PasswordDerivedBytes_FewBytesSalt"))); m_salt = (byte[])value.Clone(); Initialize(); } } // // public methods // public override byte[] GetBytes(int cb) { if (cb <= 0) throw new ArgumentOutOfRangeException("cb", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); byte[] password = new byte[cb]; int offset = 0; int size = m_endIndex - m_startIndex; if (size > 0) { if (cb >= size) { Buffer.InternalBlockCopy(m_buffer, m_startIndex, password, 0, size); m_startIndex = m_endIndex = 0; offset += size; } else { Buffer.InternalBlockCopy(m_buffer, m_startIndex, password, 0, cb); m_startIndex += cb; return password; } } //BCLDebug.Assert(m_startIndex == 0 && m_endIndex == 0, "Invalid start or end index in the internal buffer."); while (offset < cb) { byte[] T_block = Func(); int remainder = cb - offset; if (remainder > BlockSize) { Buffer.InternalBlockCopy(T_block, 0, password, offset, BlockSize); offset += BlockSize; } else { Buffer.InternalBlockCopy(T_block, 0, password, offset, remainder); offset += remainder; Buffer.InternalBlockCopy(T_block, remainder, m_buffer, m_startIndex, BlockSize - remainder); m_endIndex += (BlockSize - remainder); return password; } } return password; } public override void Reset() { Initialize(); } private void Initialize() { if (m_buffer != null) Array.Clear(m_buffer, 0, m_buffer.Length); m_buffer = new byte[BlockSize]; m_block = 1; m_startIndex = m_endIndex = 0; } internal static byte[] Int(uint i) { byte[] b = BitConverter.GetBytes(i); byte[] littleEndianBytes = { b[3], b[2], b[1], b[0] }; return BitConverter.IsLittleEndian ? littleEndianBytes : b; } // This function is defined as follow : // Func (S, i) = HMAC(S || i) | HMAC2(S || i) | ... | HMAC(iterations) (S || i) // where i is the block number. private byte[] Func() { byte[] INT_block = Int(m_block); m_HMACSHA512.TransformBlock(m_salt, 0, m_salt.Length, m_salt, 0); m_HMACSHA512.TransformFinalBlock(INT_block, 0, INT_block.Length); byte[] temp = m_HMACSHA512.Hash; m_HMACSHA512.Initialize(); byte[] ret = temp; for (int i = 2; i <= m_iterations; i++) { temp = m_HMACSHA512.ComputeHash(temp); for (int j = 0; j < BlockSize; j++) { ret[j] ^= temp[j]; } } // increment the block count. m_block++; return ret; } } } 

除了用HMACSHA1替换HMACSHA1 HMACSHA512 ,还需要添加一个StaticRandomNumberGenerator属性,因为Utils.StaticRandomNumberGenerator是微软程序集internal的,你需要添加static byte[] Int(uint i)方法,因为microsoft的Utils.Int也是internal 。 除此之外,代码有效。

对于那些需要它的人,.NET Framework 4.7.2包含Rfc2898DeriveBytes的重载 ,允许指定散列算法:

 byte[] bytes; using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256)) { bytes = deriveBytes.GetBytes(PBKDF2SubkeyLength); } 

目前的HashAlgorithmName选项是:

  • MD5
  • SHA1
  • SHA256
  • SHA3​​84
  • SHA512

虽然这是一个老问题,因为我在问题可配置Rfc2898DeriveBytes中添加了对这个问题的引用,其中我询问Rfc2898DeriveBytes算法的通用实现是否正确。

我现在测试并validation了如果为TAlgorithm提供TAlgorithm作为TAlgorithm的.NET实现,它会生成完全相同的哈希值

为了使用该类,必须提供HMAC算法的构造函数,该算法需要字节数组作为第一个参数。

例如:

 var rfcGenSha1 = new Rfc2898DeriveBytes(b => new HMACSHA1(b), key, ...) var rfcGenSha256 = new Rfc2898DeriveBytes(b => new HMACSHA256(b), key, ...) 

这需要算法在此时inheritanceHMAC,我相信只要算法的构造函数接受构造函数的字节数组,就可以减少限制以要求从KeyedHashAlgorithm而不是HMACinheritance。