views:

290

answers:

2

I saw some code like

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

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

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

+3  A: 

RFC2898 refers to a password-based cryptography specification published in September 2000. Effectively, Rfc2898DeriveBytes takes a password and salt to generate keys. The method 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.

Jason
it returns a different key on each invocationBut I found out the keys returned are all the same
Kelvin
@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
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
@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
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 Rfc2898DeriveBytesBUT:key.GetBytes, isn't it excuting GetBytes on the same instance?
Kelvin
You've only called `Rfc2898DeriveBytes.GetBytes` once. Can you explain in detail what you're confused by?
Jason
+1  A: 

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