充气城堡 C# 中的 PBKDF2

发布于 2024-09-08 22:46:15 字数 469 浏览 0 评论 0原文

我一直在搞乱 C# Bouncy Castle API 以查找如何进行 PBKDF2 密钥派生。

我现在真的一无所知。

我尝试阅读 Pkcs5S2ParametersGenerator.cs 和 PBKDF2Params.cs 文件,但我真的不知道该怎么做。

根据我迄今为止所做的研究,PBKDF2 需要一个字符串(或 char[]),它是密码、盐和迭代计数。

到目前为止,我最有希望和最明显的是 PBKDF2Params 和 Pkcs5S2ParametersGenerator。

这些似乎都不接受字符串或 char[]。

有没有人用 C# 做过这个或者对此有任何线索?或者也许有人已经用 Java 实现了 BouncyCastle 并且可以提供帮助?

提前非常感谢:)

更新:我已经找到了如何在 Bouncy Castle 中执行此操作。请看下面的答案:)

I've being messing around the C# Bouncy Castle API to find how to do a PBKDF2 key derivation.

I am really clueless right now.

I tried reading through the Pkcs5S2ParametersGenerator.cs and PBKDF2Params.cs files but i really cant figure out how to do it.

According to the research I have done so far, PBKDF2 requires a string (or char[]) which is the password, a salt and an iteration count.

So far the most promising and most obvious i've come so far is the PBKDF2Params and Pkcs5S2ParametersGenerator.

None of these seems to be accepting a string or a char[].

Has anyone done this in C# or have any clue about this? Or perhaps someone who has implemented BouncyCastle in Java and can help?

Thanx a lot in advance :)

UPDATE: I have found how to do this in Bouncy Castle. Look below for answer :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

始终不够爱げ你 2024-09-15 22:46:15

经过几个小时的检查代码后,我发现最简单的方法是获取 Pkcs5S2ParametersGenerator.cs 中的几部分代码并创建我自己的类,当然该类使用其他 BouncyCastle API。这与 Dot Net Compact Framework (Windows Mobile) 完美配合。这相当于 Rfc2898DeriveBytes 类,该类不存在于 Dot Net Compact Framework 2.0/3.5 中。好吧,也许不是完全等效,但可以完成工作:)

这是 PKCS5/PKCS#5

使用的 PRF(伪随机函数)将是 HMAC-SHA1

首先,首先。从 http://www.bouncycastle.org/csharp/ 下载 Bouncy Castle 编译的程序集,添加BouncyCastle.Crypto.dll 作为您项目的引用。

之后使用以下代码创建新的类文件。

using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;

namespace PBKDF2_PKCS5
{
    class PBKDF2
    {

        private readonly IMac hMac = new HMac(new Sha1Digest());

        private void F(
            byte[] P,
            byte[] S,
            int c,
            byte[] iBuf,
            byte[] outBytes,
            int outOff)
        {
            byte[] state = new byte[hMac.GetMacSize()];
            ICipherParameters param = new KeyParameter(P);

            hMac.Init(param);

            if (S != null)
            {
                hMac.BlockUpdate(S, 0, S.Length);
            }

            hMac.BlockUpdate(iBuf, 0, iBuf.Length);

            hMac.DoFinal(state, 0);

            Array.Copy(state, 0, outBytes, outOff, state.Length);

            for (int count = 1; count != c; count++)
            {
                hMac.Init(param);
                hMac.BlockUpdate(state, 0, state.Length);
                hMac.DoFinal(state, 0);

                for (int j = 0; j != state.Length; j++)
                {
                    outBytes[outOff + j] ^= state[j];
                }
            }
        }

        private void IntToOctet(
            byte[] Buffer,
            int i)
        {
            Buffer[0] = (byte)((uint)i >> 24);
            Buffer[1] = (byte)((uint)i >> 16);
            Buffer[2] = (byte)((uint)i >> 8);
            Buffer[3] = (byte)i;
        }

        // Use this function to retrieve a derived key.
        // dkLen is in octets, how much bytes you want when the function to return.
        // mPassword is the password converted to bytes.
        // mSalt is the salt converted to bytes
        // mIterationCount is the how much iterations you want to perform. 
        

        public byte[] GenerateDerivedKey(
            int dkLen,
            byte[] mPassword,
            byte[] mSalt,
            int mIterationCount
            )
        {
            int hLen = hMac.GetMacSize();
            int l = (dkLen + hLen - 1) / hLen;
            byte[] iBuf = new byte[4];
            byte[] outBytes = new byte[l * hLen];

            for (int i = 1; i <= l; i++)
            {
                IntToOctet(iBuf, i);

                F(mPassword, mSalt, mIterationCount, iBuf, outBytes, (i - 1) * hLen);
            }

        //By this time outBytes will contain the derived key + more bytes.
       // According to the PKCS #5 v2.0: Password-Based Cryptography Standard (www.truecrypt.org/docs/pkcs5v2-0.pdf) 
       // we have to "extract the first dkLen octets to produce a derived key".

       //I am creating a byte array with the size of dkLen and then using
       //Buffer.BlockCopy to copy ONLY the dkLen amount of bytes to it
       // And finally returning it :D

        byte[] output = new byte[dkLen];

        Buffer.BlockCopy(outBytes, 0, output, 0, dkLen);

        return output;
        }


    }
}

那么如何使用这个功能呢?简单的! :)
这是一个非常简单的示例,其中密码和盐由用户提供。

private void cmdDeriveKey_Click(object sender, EventArgs e)
        {
            byte[] salt = ASCIIEncoding.UTF8.GetBytes(txtSalt.Text);

            PBKDF2 passwordDerive = new PBKDF2();
            

      // I want the key to be used for AES-128, thus I want the derived key to be
      // 128 bits. Thus I will be using 128/8 = 16 for dkLen (Derived Key Length) . 
      //Similarly if you wanted a 256 bit key, dkLen would be 256/8 = 32. 

            byte[] result = passwordDerive.GenerateDerivedKey(16, ASCIIEncoding.UTF8.GetBytes(txtPassword.Text), salt, 1000);

           //result would now contain the derived key. Use it for whatever cryptographic purpose now :)
           //The following code is ONLY to show the derived key in a Textbox.

            string x = "";

            for (int i = 0; i < result.Length; i++)
            {
                x += result[i].ToString("X");
            }

            txtResult.Text = x;

        }

如何检查这是否正确?
有一个 PBKDF2 的在线 javascript 实现
http://anandam.name/pbkdf2/

我得到了一致的结果:)
如果有人得到不正确的结果,请报告:)

希望这对某人有帮助:)

更新:确认使用此处提供的测试向量

https://datatracker.ietf.org/doc/html/draft-josefsson-pbkdf2-test-vectors-00

更新:
或者,对于 salt,我们可以使用 RNGCryptoServiceProvider。确保引用 System.Security.Cryptography 命名空间。

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();        
            
byte[] salt = new byte[16];

rng.GetBytes(salt);

After hours and hours of going through the code, I found that the easiest way to do this is to take a few parts of the code in Pkcs5S2ParametersGenerator.cs and create my own class which of course use other BouncyCastle API's. This works perfectly with the Dot Net Compact Framework (Windows Mobile). This is the equivalent of Rfc2898DeriveBytes class which is not present in the Dot Net Compact Framework 2.0/3.5. Well, maybe not the EXACT equivalent but does the job :)

This is PKCS5/PKCS#5

The PRF (Pseudo Random Function) which is used will be HMAC-SHA1

First things, first. Download the Bouncy Castle compiled assembly from http://www.bouncycastle.org/csharp/, add the BouncyCastle.Crypto.dll as a reference to your project.

After that create new class file with the code below.

using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;

namespace PBKDF2_PKCS5
{
    class PBKDF2
    {

        private readonly IMac hMac = new HMac(new Sha1Digest());

        private void F(
            byte[] P,
            byte[] S,
            int c,
            byte[] iBuf,
            byte[] outBytes,
            int outOff)
        {
            byte[] state = new byte[hMac.GetMacSize()];
            ICipherParameters param = new KeyParameter(P);

            hMac.Init(param);

            if (S != null)
            {
                hMac.BlockUpdate(S, 0, S.Length);
            }

            hMac.BlockUpdate(iBuf, 0, iBuf.Length);

            hMac.DoFinal(state, 0);

            Array.Copy(state, 0, outBytes, outOff, state.Length);

            for (int count = 1; count != c; count++)
            {
                hMac.Init(param);
                hMac.BlockUpdate(state, 0, state.Length);
                hMac.DoFinal(state, 0);

                for (int j = 0; j != state.Length; j++)
                {
                    outBytes[outOff + j] ^= state[j];
                }
            }
        }

        private void IntToOctet(
            byte[] Buffer,
            int i)
        {
            Buffer[0] = (byte)((uint)i >> 24);
            Buffer[1] = (byte)((uint)i >> 16);
            Buffer[2] = (byte)((uint)i >> 8);
            Buffer[3] = (byte)i;
        }

        // Use this function to retrieve a derived key.
        // dkLen is in octets, how much bytes you want when the function to return.
        // mPassword is the password converted to bytes.
        // mSalt is the salt converted to bytes
        // mIterationCount is the how much iterations you want to perform. 
        

        public byte[] GenerateDerivedKey(
            int dkLen,
            byte[] mPassword,
            byte[] mSalt,
            int mIterationCount
            )
        {
            int hLen = hMac.GetMacSize();
            int l = (dkLen + hLen - 1) / hLen;
            byte[] iBuf = new byte[4];
            byte[] outBytes = new byte[l * hLen];

            for (int i = 1; i <= l; i++)
            {
                IntToOctet(iBuf, i);

                F(mPassword, mSalt, mIterationCount, iBuf, outBytes, (i - 1) * hLen);
            }

        //By this time outBytes will contain the derived key + more bytes.
       // According to the PKCS #5 v2.0: Password-Based Cryptography Standard (www.truecrypt.org/docs/pkcs5v2-0.pdf) 
       // we have to "extract the first dkLen octets to produce a derived key".

       //I am creating a byte array with the size of dkLen and then using
       //Buffer.BlockCopy to copy ONLY the dkLen amount of bytes to it
       // And finally returning it :D

        byte[] output = new byte[dkLen];

        Buffer.BlockCopy(outBytes, 0, output, 0, dkLen);

        return output;
        }


    }
}

So how to use this function? Simple! :)
This is a very simple example where the password and the salt is provided by the user.

private void cmdDeriveKey_Click(object sender, EventArgs e)
        {
            byte[] salt = ASCIIEncoding.UTF8.GetBytes(txtSalt.Text);

            PBKDF2 passwordDerive = new PBKDF2();
            

      // I want the key to be used for AES-128, thus I want the derived key to be
      // 128 bits. Thus I will be using 128/8 = 16 for dkLen (Derived Key Length) . 
      //Similarly if you wanted a 256 bit key, dkLen would be 256/8 = 32. 

            byte[] result = passwordDerive.GenerateDerivedKey(16, ASCIIEncoding.UTF8.GetBytes(txtPassword.Text), salt, 1000);

           //result would now contain the derived key. Use it for whatever cryptographic purpose now :)
           //The following code is ONLY to show the derived key in a Textbox.

            string x = "";

            for (int i = 0; i < result.Length; i++)
            {
                x += result[i].ToString("X");
            }

            txtResult.Text = x;

        }

How to check whether this is correct?
There is an online javascript implementation of PBKDF2
http://anandam.name/pbkdf2/

I got consistent results :)
Please report if anyone is getting an incorrect result :)

Hope this helps someone :)

UPDATE: Confirmed working with test vectors provided here

https://datatracker.ietf.org/doc/html/draft-josefsson-pbkdf2-test-vectors-00

UPDATE:
Alternatively, for the salt we can use a RNGCryptoServiceProvider. Make sure to reference the System.Security.Cryptography namespace.

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();        
            
byte[] salt = new byte[16];

rng.GetBytes(salt);
╰◇生如夏花灿烂 2024-09-15 22:46:15

我自己也遇到了这个问题,并找到了更直接的方法。至少从 Bouncy Castle 1.7 开始,您可以这样做(在 VB 中使用 Org.BouncyCastle.Crypto):

Dim bcKeyDer As New Generators.Pkcs5S2ParametersGenerator()
bcKeyDer.Init(password, salt, keyIterations)
Dim bcparam As Parameters.KeyParameter = bcKeyDer.GenerateDerivedParameters("aes256", 256)
Dim key1() As Byte = bcparam.GetKey()

我已经针对 .Net 的 System.Security.Cryptography 对此进行了测试,并且它有效!

I just had this problem myself, and found a more direct approach. As of at least Bouncy Castle 1.7 you can do it like this (in VB using Org.BouncyCastle.Crypto):

Dim bcKeyDer As New Generators.Pkcs5S2ParametersGenerator()
bcKeyDer.Init(password, salt, keyIterations)
Dim bcparam As Parameters.KeyParameter = bcKeyDer.GenerateDerivedParameters("aes256", 256)
Dim key1() As Byte = bcparam.GetKey()

I have tested this against .Net's System.Security.Cryptography, and it works!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文