How does RFC2898DeriveBytes generate an AES key?
Asked Answered
S

2

8

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.

Sonar answered 20/12, 2009 at 22:6 Comment(0)
A
13

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.

Anthropomorphism answered 20/12, 2009 at 22:25 Comment(6)
it returns a different key on each invocation But I found out the keys returned are all the sameSonar
@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.Anthropomorphism
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] + " "); }Sonar
@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.Anthropomorphism
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?Sonar
You've only called Rfc2898DeriveBytes.GetBytes once. Can you explain in detail what you're confused by?Anthropomorphism
C
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;
}
Conde answered 20/12, 2009 at 22:27 Comment(3)
If you still have it, it would be useful to see the private functions of this class, particularly the part that does the iterating.Chert
Use something like ilspy.net and check the code yourself :) I use Reflector, but should be the same.Conde
@Phil_1984_ You can find the reference source on microsoft's reference source site, in this case link referencesource.microsoft.com/#mscorlib/system/security/… .Sokol

© 2022 - 2024 — McMap. All rights reserved.