将AESManaged与字节数组一起使用

时间:2013-11-15 21:10:56

标签: c# memorystream cryptostream

我正在尝试实现内存中的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))

1 个答案:

答案 0 :(得分:1)

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

CryptoStream是空的,但你要求它读取填充。在“新AesManaged()”行之后,添加:aes.Padding = PaddingMode.None。现在你的代码会工作,虽然它不会解密任何东西。既然你没有给CryptoStream喂食,也没有要求它读取任何填充,它就不再抱怨了。它什么都不做。您有一个错误,因为您没有将密文提供给CryptoStream。

尝试使用此代替MemoryStream for source:

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应该是独一无二的。它可以明确发送 - 它不需要保密。