为什么要创建256个字节的派生密码,然后再从结果中获取字节?

时间:2018-07-24 18:15:25

标签: c# aes

我找到了一个使用AES加密来加密文本的示例。代码是这样的:

public static string Encrypt(string PlainText, string Password,
    string Salt = "Kosher", string HashAlgorithm = "SHA1",   
    int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",    
    int KeySize = 256)    
{    
    if (string.IsNullOrEmpty(PlainText))    
        return "";

    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);    
    byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);    
    byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);    
    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);    
    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);    
    RijndaelManaged SymmetricKey = new RijndaelManaged();    
    SymmetricKey.Mode = CipherMode.CBC;    
    byte[] CipherTextBytes = null;    
    using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes))    
    {    
        using (MemoryStream MemStream = new MemoryStream())    
        {    
            using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))    
            {    
                CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);    
                CryptoStream.FlushFinalBlock();    
                CipherTextBytes = MemStream.ToArray();    
                MemStream.Close();    
                CryptoStream.Close();    
            }    
        }    
    }

    SymmetricKey.Clear();    
    return Convert.ToBase64String(CipherTextBytes);
}

我的问题是:AES算法的密钥是如何生成的?这2行:

    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);

    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);

首先,它创建一个256字节的派生密钥,然后,创建一个获取此派生密钥的伪随机字节的密钥。它必须除以8,因为AES算法需要128、182或256位,而不是字节。在这种情况下,密钥的导出方式是256个字节,而AES的密钥将是256位。

但是为什么要这么做呢?是否会更好地创建具有所需长度而不是256字节而是256位(256字节/ 8)的派生密钥?因此,不需要使用派生密钥的1/8字节来创建新密钥。

此外,在该方法的描述中,getBytes()方法还说它返回了伪随机密钥字节。难道AES密钥在每种情况下都不同吗?如果它是伪随机密钥字节,如何从解密中再次生成AES密钥?

谢谢。

1 个答案:

答案 0 :(得分:1)

  

首先,它创建一个256个字节的派生密钥

在哪里?我看不到创建任何256字节密钥。

  

,然后创建一个密钥,获取该派生密钥的伪随机字节。必须将其除以8,因为AES算法需要128、182或256位,而不是字节

是的,KeySize(按常规C#命名约定应为keySize)的函数输入是以位为单位的,但是GetBytes是以字节为单位的输入。 x / 8是该转换的三个正确答案之一((x + 7) / 8是另一个,而x & 7 == 0 ? x / 8 : throw new ArgumentException(nameof(x))是第三个答案)

  

但是为什么要这么做呢?是否会更好地创建具有所需长度而不是256字节而是256位(256字节/ 8)的派生密钥?因此,不需要使用派生密钥的1/8字节来创建新密钥。

这样做会很好。但是,既然已经这样做了,那就没有“更好”了。

  

此外,在该方法的描述中,getBytes()方法还说它返回了伪随机密钥字节。难道AES密钥在每种情况下都不同吗?如果它是伪随机密钥字节,如何从解密中再次生成AES密钥?

我必须指出一点:没有getBytes方法。 C#是区分大小写的语言,方法名称为GetBytes

pseudorandom:注意到或涉及由确定的计算过程生成的满足统计检验的随机数。

PasswordDeriveBytesPBKDF1的实现(除了它超出了PBKDF1的限制),这是一种确定性算法。给定相同的输入(密码,种子,迭代计数,伪随机函数(哈希算法)),则会产生相同的输出。稍微更改任何输入,输出就会明显不同。

Rfc2898DeriveBytesPBKDF2的实现)也是确定性的但混乱的算法。

因此,您通过提供所有相同的输入,在其中一个(但不是跨它们)中再次产生相同的答案。

使用基于密码的加密(PKCS#5)时,流程是

  • 选择PRF
  • 选择一个迭代计数
  • 生成随机盐
  • 写下这些选择
  • 应用这三件事,再加上密码以生成密钥
  • 加密数据
  • 写下加密数据

解密时

  • 阅读PRF
  • 读取迭代计数
  • 读盐
  • 应用这三件事,再加上密码以生成密钥
  • 读取加密的数据
  • 解密
  • 参加

尽管此代码正确执行了该部分,但IV和Salt不应为ASCII(或UTF8)字符串,而应为“仅为字节”(byte[])。如果需要将它们作为字符串进行传输,则它们应该是base64或其他“任意” binary-to-text encoding