C# Rfc2898/PBKDF2,其中SHA256作为c中的摘要#

C# Rfc2898/PBKDF2,其中SHA256作为c中的摘要#,c#,cryptography,C#,Cryptography,我想在c#中使用Rfc2898来派生一个键。我还需要使用SHA256作为Rfc2898的摘要。我找到了类Rfc2898DeriveBytes,但它使用SHA-1,我看不到让它使用不同摘要的方法 有没有一种方法可以在c#中使用Rfc2898,并将SHA256作为摘要(缺少从头开始的实现)?参见Bruno Garcia的答案 卡斯滕:请接受这个答案,而不是这个 在我开始回答这个问题时,Rfc2898DeriveBytes无法配置为使用不同的哈希函数。然而,与此同时,它得到了改进;见布鲁诺·加西亚的

我想在c#中使用Rfc2898来派生一个键。我还需要使用SHA256作为Rfc2898的摘要。我找到了类
Rfc2898DeriveBytes
,但它使用SHA-1,我看不到让它使用不同摘要的方法


有没有一种方法可以在c#中使用Rfc2898,并将SHA256作为摘要(缺少从头开始的实现)?

参见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<keyLength;i++){
                extendedkey[salt.Length]=(byte)(((i+1)>>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<iterationCount;j++){
                    u=hmac.ComputeHash(u);
                    for(int k=0;k<f.Length;k++){
                        f[k]^=u[k];
                    }
                }
                ms.Write(f,0,f.Length);
                Array.Clear(u,0,u.Length);
                Array.Clear(f,0,f.Length);
            }
            byte[] dk=new byte[dklen];
            ms.Position=0;
            ms.Read(dk,0,dklen);
            ms.Position=0;
            for(long i=0;i<ms.Length;i++){
                ms.WriteByte(0);
            }
            Array.Clear(extendedkey,0,extendedkey.Length);
            return dk;
        }
    }
//注意:迭代计数应该
//尽可能高而不引起
//无理拖延。还要注意,密码
//salt是字节数组,而不是字符串。使用后,
//应清除密码和salt(使用Array.Clear)
公共静态字节[]PBKDF2Sha256GetBytes(整数dklen,字节[]密码,字节[]salt,整数迭代计数){
使用(var hmac=new System.Security.Cryptography.HMACSHA256(密码)){
int hashLength=hmac.HashSize/8;
如果((hmac.HashSize&7)!=0)
hashLength++;
int keyLength=dklen/hashLength;
if((长)dklen>(0xffffffffffl*hashLength)| | dklen24)&0xFF);
扩展键[salt.Length+1]=(字节)((i+1)>>16)和0xFF);
扩展键[salt.Length+2]=(字节)((i+1)>>8)和0xFF);
扩展键[salt.Length+3]=(字节)((i+1))&0xFF);
字节[]u=hmac.ComputeHash(扩展键);
数组.Clear(扩展键,salt.Length,4);
字节[]f=u;

对于(int j=1;j您可以使用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;
    }
}
}
namespace System.Security.Cryptography
{
利用制度全球化;
使用System.IO;
使用系统文本;
[System.Runtime.InteropServices.ComVisible(true)]
公共类Rfc2898DeriveBytes\u HMACSHA512:DeriveBytes
{
专用字节[]m_缓冲区;
专用字节[]m_salt;
私有HMACSHA512 m_HMACSHA512;//PBKDF2中使用的伪随机生成器函数
私有单元m_迭代;
私家尤因穆大厦;;
私人国际货币基金组织startIndex;
私有内部m_endIndex;
私有静态RNGCryptoServiceProvider\u rng;
专用静态RNGCryptoServiceProvider StaticRandomNumberGenerator
{
得到
{
如果(_rng==null)
{
_rng=新的RNGCryptoServiceProvider();
}
返回(rng);;
}
}
私有const int BlockSize=20;
//
//公共建设者
// 
public Rfc2898DeriveBytes_HMACSHA512(字符串密码,int-saltSize):this(密码,saltSize,1000){}
公共Rfc2898DeriveBytes_HMACSHA512(字符串密码、int saltSize、int迭代次数)
{
如果(盐度<0)
抛出新ArgumentOutOfRangeException(“saltSize”,Environment.GetResourceString(“ArgumentOutOfRange_NeedNonNegnNum”);
字节[]salt=新字节[saltSize];
StaticRandomNumberGenerator.GetBytes(salt);
盐=盐;
迭代次数=迭代次数;
m_HMACSHA512=新的HMACSHA512(新的utf8编码(false).GetBytes(密码));
初始化();
}
public Rfc2898DeriveBytes_HMACSHA512(字符串密码,字节[]salt):this(密码,salt,1000){}
公共Rfc2898DeriveBytes_HMACSHA512(字符串密码,字节[]salt,int迭代):这(新的UTF8Encoding(false).GetBytes(密码),salt,迭代){}
公共Rfc2898DeriveBytes_HMACSHA512(字节[]密码,字节[]salt,整数迭代)
{
盐=盐;
迭代次数=迭代次数;
m_HMACSHA512=新的HMACSHA512(密码);
初始化();
}
//
//公共财产
//
公共整数迭代计数
{
获取{return(int)m_迭代;}
设置
{
如果(值=大小)
{
InternalBlockCopy(m_Buffer,m_startIndex,密码,0,大小);
m_startIndex=m_endIndex=0;
偏移量+=大小;
}
其他的
{
InternalBlockCopy(m_Buffer,m_startinex,password,0,cb);
m_startIndex+=cb;
返回密码;
}
}
//Assert(m_startIndex==0&&m_endIndex==0,“内部缓冲区中的开始或结束索引无效”);
while(偏移量块大小)
{
Buffer.InternalBlockCopy(T_块,0,密码,偏移量,块大小);
偏移量+=块大小;
}
其他的
{
Buffer.InternalBlockCopy(T_块,0,密码,偏移量,余数);
偏移量+=余数;
InternalBlockCopy(T_块、余数、m_缓冲区、m_startIndex、BlockSize-余数);
m_endIndex+=(块大小-余数);
返回密码;
}
}
返回密码;
}
公共覆盖无效重置()
{
初始化();
}
私有void初始化()
{
如果(m_buffer!=null)
Array.Clear(m_buffer,0,m_buffer.Length);
m_buffer=新字节[块大小];
m_块=1;
m_startIndex=m_endIndex=0;
}
内部静态字节[]Int(uint i)
{
字节[]b=位转换器.GetBytes(i);
byte[]littleEndianBytes={b[3],b[2],b[1],b[0]};
返回BitConverter.IsLittleEndian
var rfcGenSha1 = new Rfc2898DeriveBytes<HMACSHA1>(b => new HMACSHA1(b), key, ...)
var rfcGenSha256 = new Rfc2898DeriveBytes<HMACSHA256>(b => new HMACSHA256(b), key, ...)
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);
byte[] bytes;
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
{
    bytes = deriveBytes.GetBytes(PBKDF2SubkeyLength);
}