使用Rfc2898DeriveBytes在C#中实现PBKDF2

Nic*_*ick 22 c# pbkdf2 rfc2898

伙计们,我正在尝试在C#中实现一个创建WPA共享密钥的PBKDF2函数.我在这里找到了一些:http://msdn.microsoft.com/en-us/magazine/cc163913.aspx似乎产生了一个有效的结果,但是它的一个字节太短......而且错误的PSK值.

为了测试输出,我将其与此进行比较:http://www.xs4all.nl/~rjoris/wpapsk.htmlhttp://anandam.name/pbkdf2/

我确实找到了一种方法来使用C#内置库来调用Rfc2898DeriveBytes.使用这个,我得到一个有效的输出:

Rfc2898DeriveBytes k3 = new Rfc2898DeriveBytes(pwd1, salt1, 4096);
byte[] answers = k3.GetBytes(32);
Run Code Online (Sandbox Code Playgroud)

现在,我使用Rfc2898DeriveBytes的一个限制是"盐"必须是8个八位字节长.如果它更短,则Rfc2898DeriveBytes会抛出异常.我想我所要做的就是将盐(如果它更短)填充到8个字节,我会很好.但不是!我已经尝试了几乎所有填充与较短盐的组合,但我不能复制我从上面这两个网站得到的结果.

那么底线是,这是否意味着Rfc2898DeriveBytes只是不能使用短于8个字节的源盐?如果是这样,有没有人知道我可以使用哪些C#代码为WPA预共享密钥实现PBKDF2?

Dod*_*bit 15

这是一个不需要8字节盐的实现.

您可以按如下方式计算WPA密钥:

Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(passphrase, Encoding.UTF8.GetBytes(name), 4096);
key = rfc2898.GetBytes(32);

public class Rfc2898DeriveBytes : DeriveBytes
    {
        const int BlockSize = 20;
        uint block;
        byte[] buffer;
        int endIndex;
        readonly HMACSHA1 hmacsha1;
        uint iterations;
        byte[] salt;
        int startIndex;

        public Rfc2898DeriveBytes(string password, int saltSize)
            : this(password, saltSize, 1000)
        {
        }

        public Rfc2898DeriveBytes(string password, byte[] salt)
            : this(password, salt, 1000)
        {
        }

        public Rfc2898DeriveBytes(string password, int saltSize, int iterations)
        {
            if (saltSize < 0)
            {
                throw new ArgumentOutOfRangeException("saltSize");
            }
            byte[] data = new byte[saltSize];
            new RNGCryptoServiceProvider().GetBytes(data);
            Salt = data;
            IterationCount = iterations;
            hmacsha1 = new HMACSHA1(new UTF8Encoding(false).GetBytes(password));
            Initialize();
        }

        public Rfc2898DeriveBytes(string password, byte[] salt, int iterations) : this(new UTF8Encoding(false).GetBytes(password), salt, iterations)
        {
        }

        public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations)
        {
            Salt = salt;
            IterationCount = iterations;
            hmacsha1 = new HMACSHA1(password);
            Initialize();
        }

        static byte[] Int(uint i)
        {
            byte[] bytes = BitConverter.GetBytes(i);
            byte[] buffer2 = new byte[] {bytes[3], bytes[2], bytes[1], bytes[0]};
            if (!BitConverter.IsLittleEndian)
            {
                return bytes;
            }
            return buffer2;
        }


        byte[] DeriveKey()
        {
            byte[] inputBuffer = Int(block);
            hmacsha1.TransformBlock(salt, 0, salt.Length, salt, 0);
            hmacsha1.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
            byte[] hash = hmacsha1.Hash;
            hmacsha1.Initialize();
            byte[] buffer3 = hash;
            for (int i = 2; i <= iterations; i++)
            {
                hash = hmacsha1.ComputeHash(hash);
                for (int j = 0; j < BlockSize; j++)
                {
                    buffer3[j] = (byte) (buffer3[j] ^ hash[j]);
                }
            }
            block++;
            return buffer3;
        }

        public override byte[] GetBytes(int bytesToGet)
        {
            if (bytesToGet <= 0)
            {
                throw new ArgumentOutOfRangeException("bytesToGet");
            }
            byte[] dst = new byte[bytesToGet];
            int dstOffset = 0;
            int count = endIndex - startIndex;
            if (count > 0)
            {
                if (bytesToGet < count)
                {
                    Buffer.BlockCopy(buffer, startIndex, dst, 0, bytesToGet);
                    startIndex += bytesToGet;
                    return dst;
                }
                Buffer.BlockCopy(buffer, startIndex, dst, 0, count);
                startIndex = endIndex = 0;
                dstOffset += count;
            }
            while (dstOffset < bytesToGet)
            {
                byte[] src = DeriveKey();
                int num3 = bytesToGet - dstOffset;
                if (num3 > BlockSize)
                {
                    Buffer.BlockCopy(src, 0, dst, dstOffset, BlockSize);
                    dstOffset += BlockSize;
                }
                else
                {
                    Buffer.BlockCopy(src, 0, dst, dstOffset, num3);
                    dstOffset += num3;
                    Buffer.BlockCopy(src, num3, buffer, startIndex, BlockSize - num3);
                    endIndex += BlockSize - num3;
                    return dst;
                }
            }
            return dst;
        }

        void Initialize()
        {
            if (buffer != null)
            {
                Array.Clear(buffer, 0, buffer.Length);
            }
            buffer = new byte[BlockSize];
            block = 1;
            startIndex = endIndex = 0;
        }

        public override void Reset()
        {
            Initialize();
        }

        public int IterationCount
        {
            get
            {
                return (int) iterations;
            }
            set
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException("value");
                }
                iterations = (uint) value;
                Initialize();
            }
        }

        public byte[] Salt
        {
            get
            {
                return (byte[]) salt.Clone();
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                salt = (byte[]) value.Clone();
                Initialize();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)


Che*_*eso 7

在比较.NET的Rfc2898DeriveBytes和Anandam的PBKDF2 Javascript实现的密钥派生时,我得到了匹配的结果.

我把一个将SlowAES和Anandam的PBKDF2打包成Windows脚本组件的例子.使用此实现显示与.NET RijndaelManaged类和Rfc2898DeriveBytes类的良好互操作.

也可以看看:

所有这些都比你要求的更进一步.它们都显示AES加密的互操作性.但是为了实现加密互操作,在基于密码的密钥派生上进行互操作(或匹配输出)是必要的先决条件.


小智 6

查看Microsoft链接,我做了一些更改,以使PMK与您提出的链接中发现的PMK相同.

将SHA算法从SHA256Managed更改为SHA1Managed,用于内部和外部哈希.

将HASH_SIZE_IN_BYTES更改为等于20而不是34.

这会生成正确的WPA密钥.

我知道这有点晚了,但我刚刚开始寻找这种信息,并认为我可以帮助其他人.如果有人读过这篇文章,关于PRF功能的任何想法以及如何在C#中做到这一点?

  • @Sam--该文章来自2005年,从那时起安全性已经发生了很大变化.SHA1易受攻击不是因为它可以被反转,或者因为冲突的数量太高而是因为它是_fast_,所以现代云处理使暴力攻击变得太容易了.SHA256并不慢,所以非常脆弱.像PBKDF2/RFC2898这样的键拉伸算法像SHA一样使用哈希值并重复数千次,因此它是_slow_,这使得任何暴力攻击都变得更加困难.在这种情况下,SHA1和SHA256之间的差异并不大. (2认同)