【问题标题】:Using AESManaged with byte arrays将 AESManaged 与字节数组一起使用
【发布时间】:2013-11-29 09:51:42
【问题描述】:

我正在尝试实现内存中的 AESManaged 加密/解密。这里的代码就是基于这个:

Encrypting/Decrypting large files (.NET)

加密部分似乎起作用了,也就是说,没有例外。但是解密部分会抛出“索引超出数组范围”的错误。

在早期的代码中,转换是这样初始化的:

 aes = new AesManaged();
 aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
 aes.KeySize = aes.LegalKeySizes[0].MaxSize;            
 Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(Key, salt, 1);
 aes.Key = key.GetBytes(aes.KeySize / 8);
 aes.IV = key.GetBytes(aes.BlockSize / 8);
 aes.Mode = CipherMode.CBC;
 transform = aes.CreateDecryptor(aes.Key, aes.IV);


void AESDecrypt(ref byte[] inB)
{
    using (MemoryStream destination = new MemoryStream(inB, 0, inB.Length))
    {
        using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
            {
                try
                {
                    using (MemoryStream source = new MemoryStream(inB, 0, inB.Length))
                    {
                        if (source.CanWrite==true)
                        {
                            source.Write(inB, 0, inB.Length);
                            source.Flush(); //<<inB is unchanged by the write
                        }
                    }
                }
                catch (CryptographicException exception)
                {
                    if (exception.Message == "Padding is invalid and cannot be removed.")
                        throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception);
                    else
                        throw;
                }
            }
        } <====At this point I get an IndexOutofBounds exception.
    }
}

似乎有问题的行可能是: 使用 (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))

【问题讨论】:

  • 你试过什么?喜欢调试你的自己的代码吗?
  • 是的,不知道你在做什么。
  • if (source.CanWrite==true)。如果 CanWrite 为假怎么办?那你什么都不写?如果您仍然不处理此案,最好让它失败。
  • 关于您的实际问题,您发送的数据是错误的。错误的密钥、IV 或字节。所以也要显示加密。顺便说一句,任何人都可以在没有解密通知的情况下修改您的加密数据。您应该使用经过身份验证的加密(AES-GCM 非常安全)。
  • 另外,IV 对于每个加密应该是唯一的。使用 CBC 很幸运,但对于其他模式,不同数据的相同 IV 会 100% 破坏安全性。

标签: c# memorystream cryptostream


【解决方案1】:

您没有向 CryptoStream 提供任何数据,它需要一些数据,因为它正在尝试删除填充。尝试注释掉整个 try/catch 代码块,你会得到同样的错误。

CryptoStream 为空,但您要求它读取填充。在“new AesManaged()”行之后,添加:aes.Padding = PaddingMode.None。现在你的代码可以工作了,尽管它不会解密任何东西。由于您没有向 CryptoStream 提供任何内容,并且没有要求它读取任何填充,因此它不再抱怨。它什么也不做。您有一个错误,您没有将密文提供给 CryptoStream。

试试这个而不是 MemoryStream 作为源:

using (BinaryWriter source = new BinaryWriter(cryptoStream))
{
    source.Write(inB, 0, inB.Length);
}

现在涉及到 CryptoStream,它将接收 inB 进行解密。

您可能在处理填充时遇到问题。在编写代码时(修复大括号拼写错误),您要求解密器去除填充,但您不会修剪输出数组(ref byte[] inB),那么您怎么知道返回了多少数据?它总是会返回与输入相同的长度,但只会覆盖解密的数量。

这里是一些示例数据:

尝试 32 个零字节的密钥和 16 个零字节的 IV:

aes.Key = new byte[32];
aes.IV = new byte[16];

并将此密文解密为 inB:

byte[] inB = { 0xf9, 0x14, 0x32, 0x2a, 0x7a, 0x35, 0xf9, 0xef, 0x27, 0x98, 0x1a, 0x86, 0xe2, 0x80, 0x5e, 0x9b };

如果你设置 Padding.None,那么你会看到我原来的纯文本“Hello”只覆盖了 inB 的前五个字节。其余 11 个字节不变。填充已删除(默认)并且未写入目标流。

现在设置 Padding.None 并尝试一下。由于我 确实 填充了数据,您将看到目标现在包含“Hello”,后跟 11 个字节的值 11 - 填充。由于这次没有删除填充,因此您会看到它已写入输出。

另外,正如 usr 所说,每次使用密钥加密时,IV 都应该是唯一的。您每次都派生相同的 IV 和密钥。如果此密钥只使用一次,那很好。如果多次使用同一个密钥,这是一个错误。 IV应该是唯一的。它可以明文发送——不需要保密。

【讨论】:

    猜你喜欢
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 2017-03-19
    • 2015-10-05
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    相关资源
    最近更新 更多