7

I saw some code like

string password = "11111111";
byte[] salt = Encoding.ASCII.GetBytes("22222222");
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt);
RijndaelAlg.Key = key.GetBytes(RijndaelAlg.KeySize / 8);

I can see the key is generated by Rfc2898DeriveBytes with passphrase and salt. Then AES retrieves the key by GetBytes.

But the question is, what does RFC2898DeriveBytes do and what key.GetBytes(cb) do? Could anyone elaborate this? I couldn't get it from the documentation.

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
Kelvin
  • 1,103
  • 2
  • 11
  • 16

2 Answers2

12

RFC2898 refers to a password-based cryptography specification published in September 2000. Effectively, Rfc2898DeriveBytes takes a password and salt to generate keys. The method it uses is known as PBKDF2 (Password Based Key Derivation Function #2) and is defined in section 5.2 of RFC2898. From section 5.2:

PBKDF2 applies a pseudorandom function (see Appendix B.1 for an example) to derive keys. The length of the derived key is essentially unbounded. (However, the maximum effective search space for the derived key may be limited by the structure of the underlying pseudorandom function. See Appendix B.1 for further discussion.) PBKDF2 is recommended for new applications.

For further details, see RFC2898.

As for what Rfc2898DeriveBytes.GetBytes does, it returns a different key on each invocation; it effectively just applies PBKDF2 repeatedly with the same password and salt but also an iteration count.

This is outlined in RFC doc where PBKDF2 is defined as

PBKDF2 (P, S, c, dkLen)

where P is the password, S is the salt, c is the iteration count and dkLen is the length of the desired key.

The RFCs are in general very interesting and historically quite important. RFC 1149 is quite important, as is RFC 2324.

Community
  • 1
  • 1
jason
  • 236,483
  • 35
  • 423
  • 525
  • it returns a different key on each invocation But I found out the keys returned are all the same – Kelvin Dec 21 '09 at 00:15
  • @Kelvin: Are you sure that you're using it on the same instance of `Rfc2898DeriveBytes`? It's effectively impossible to see the same bytes returned on successive invocations of `Rfc2898DeriveBytes.GetBytes` on the same instance of `Rfc2898DeriveBytes`. – jason Dec 21 '09 at 01:02
  • RijndaelManaged RijndaelAlg = new RijndaelManaged(); string password = "11111111"; Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes("22222222")); RijndaelAlg.Key = key.GetBytes(RijndaelAlg.KeySize / 8); byte[] a = key.GetBytes(32); for (int i = 0; i < a.Length; i++) { logWindow.AppendText(a[i] + " "); } – Kelvin Dec 21 '09 at 02:36
  • 1
    @Kelvin: You're only calling `Rfc2898DeriveBytes.GetBytes` once. So now I see the confusion. I said you'll see different output on successive invocations on the same instance. But you're not executing `GetBytes` on the same instance of `Rfc2898DeriveBytes`. You're executing the above code multiple times and seeing the same output. This is by design. The algorithm is deterministic. – jason Dec 21 '09 at 02:41
  • Sry, still a bit confused. code: Rfc2898DeriveBytes key = new Rfc2898DeriveBytes() RijndaelAlg.Key = key.GetBytes(RijndaelAlg.KeySize / 8); ur comments: But you're not executing GetBytes on the same instance of Rfc2898DeriveBytes BUT: key.GetBytes, isn't it excuting GetBytes on the same instance? – Kelvin Dec 21 '09 at 20:23
  • You've only called `Rfc2898DeriveBytes.GetBytes` once. Can you explain in detail what you're confused by? – jason Dec 21 '09 at 23:56
5

From looking at the implementation in Reflector:

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

public Rfc2898DeriveBytes(string password, int saltSize, int iterations)
{
    if (saltSize < 0)
    {
        throw new ArgumentOutOfRangeException("saltSize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }
    byte[] data = new byte[saltSize];
    Utils.StaticRandomNumberGenerator.GetBytes(data);
    this.Salt = data;
    this.IterationCount = iterations;
    this.m_hmacsha1 = new HMACSHA1(new UTF8Encoding(false).GetBytes(password));
    this.Initialize();
}


public override byte[] GetBytes(int cb)
{
    if (cb <= 0)
    {
        throw new ArgumentOutOfRangeException("cb", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }
    byte[] dst = new byte[cb];
    int dstOffset = 0;
    int count = this.m_endIndex - this.m_startIndex;
    if (count > 0)
    {
        if (cb < count)
        {
            Buffer.InternalBlockCopy(this.m_buffer, this.m_startIndex, dst, 0, cb);
            this.m_startIndex += cb;
            return dst;
        }
        Buffer.InternalBlockCopy(this.m_buffer, this.m_startIndex, dst, 0, count);
        this.m_startIndex = this.m_endIndex = 0;
        dstOffset += count;
    }
    while (dstOffset < cb)
    {
        byte[] src = this.Func();
        int num3 = cb - dstOffset;
        if (num3 > 20)
        {
            Buffer.InternalBlockCopy(src, 0, dst, dstOffset, 20);
            dstOffset += 20;
        }
        else
        {
            Buffer.InternalBlockCopy(src, 0, dst, dstOffset, num3);
            dstOffset += num3;
            Buffer.InternalBlockCopy(src, num3, this.m_buffer, this.m_startIndex, 20 - num3);
            this.m_endIndex += 20 - num3;
            return dst;
        }
    }
    return dst;
}
Mikael Svenson
  • 39,181
  • 7
  • 73
  • 79
  • If you still have it, it would be useful to see the private functions of this class, particularly the part that does the iterating. – Phil May 28 '15 at 22:23
  • Use something like http://ilspy.net/ and check the code yourself :) I use Reflector, but should be the same. – Mikael Svenson May 29 '15 at 14:15
  • 1
    @Phil_1984_ You can find the reference source on microsoft's reference source site, in this case [link](http://referencesource.microsoft.com/#mscorlib/system/security/cryptography/rfc2898derivebytes.cs) http://referencesource.microsoft.com/#mscorlib/system/security/cryptography/rfc2898derivebytes.cs . – Luc C Jun 10 '15 at 12:20