Как RFC2898DeriveBytes генерирует ключ AES?
Я видел какой-то код вроде
string password = "11111111";
byte[] salt = Encoding.ASCII.GetBytes("22222222");
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt);
RijndaelAlg.Key = key.GetBytes(RijndaelAlg.KeySize / 8);
Я вижу, что ключ генерируется Rfc2898DeriveBytes с парольной фразой и солью. Затем AES получает ключ с помощью GetBytes.
Но вопрос в том, что делает RFC2898DeriveBytes и что делает key.GetBytes(cb)? Кто-нибудь может уточнить это? Я не мог получить это из документации.
2 ответа
RFC2898 относится к спецификации шифрования на основе пароля, опубликованной в сентябре 2000 года. Rfc2898DeriveBytes
берет пароль и соль для генерации ключей. Используемый метод известен как PBKDF2 (функция получения ключа на основе пароля № 2) и определен в разделе 5.2 RFC2898. Из раздела 5.2:
PBKDF2 применяет псевдослучайную функцию (см. Пример в Приложении B.1) для получения ключей. Длина полученного ключа практически не ограничена. (Однако максимально эффективное пространство поиска для производного ключа может быть ограничено структурой базовой псевдослучайной функции. Дополнительное обсуждение см. В Приложении B.1.) PBKDF2 рекомендуется для новых приложений.
Для получения дополнительной информации см. RFC2898.
Что за что Rfc2898DeriveBytes.GetBytes
делает, он возвращает разные ключи при каждом вызове; он фактически просто применяет PBKDF2 несколько раз с тем же паролем и солью, но также с количеством итераций.
Это изложено в документе RFC, где PBKDF2 определяется как
PBKDF2 (P, S, c, dkLen)
где P
это пароль, S
это соль, c
это число итераций и dkLen
длина желаемого ключа.
RFC вообще очень интересны и исторически очень важны. RFC 1149 довольно важен, как и RFC 2324.
Из рассмотрения реализации в 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;
}