我对C#很陌生,所以请耐心等待我。我知道这个问题在很多时候被问了很多,但是我找不到问题的答案。
我正在保存一些数据,在将其写入文件之前,我将其转换为二进制文件并将其存储在数组中,我将其加密然后写入文件。我以块(32字节)加密数据。以同样的方式,我以32字节的块读取数据,然后解密该数据,然后这应该重复,直到文件结束。但是当谈到解密时,会抛出以下错误:
填充无效且无法删除。
我使用相同的密钥和iv(硬编码直到我开始工作)
这是我的加密代码,没有问题:
//result
byte[] data = new byte[32];
//setup encryption (AES)
SymmetricAlgorithm aes = Aes.Create();
byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9,50};
byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);
FileStream fStream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 1024, false);
//prepare data to write (byte array 'data') ...
//encrypt
MemoryStream m = new MemoryStream();
using (Stream c = new CryptoStream(m, encryptor, CryptoStreamMode.Write))
c.Write(data, 0, data.Length);
data = m.ToArray();
fStream.Write(data, 0, data.Length);
这是我的解密代码:
FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
//setup encryption (AES)
SymmetricAlgorithm aes = Aes.Create();
byte[] key = { 145, 12, 32, 245, 98, 132, 98, 214, 6, 77, 131, 44, 221, 3, 9, 50 };
byte[] iv = { 15, 122, 132, 5, 93, 198, 44, 31, 9, 39, 241, 49, 250, 188, 80, 7 };
ICryptoTransform decryptor = aes.CreateDecryptor(key, iv);
//result
byte[] data = new byte[32];
//loop for reading the whole file ...
int len = fStream.Read(data, 0, 32);
//decrypt
MemoryStream m = new MemoryStream();
using (Stream c = new CryptoStream(m, decryptor, CryptoStreamMode.Write))
c.Write(data, 0, data.Length); //The exception is thrown in this line
data = m.ToArray();
//using the decrypted data and then looping back to reading and decrypting...
我尝试了所有我能想到的(这不是很多,因为我对密码学很新),我到处搜索,我找不到解决问题的方法。我还帮助自己完成了 C#in a Nutshell 。
如果有人知道为什么会这样,我会非常感激,因为我没有想法。
感谢您的时间和答案。
编辑: 似乎加密数据的大小是48字节(比原始数据多12个字节)。为什么会这样?我认为如果它们不是块大小的倍数(16字节,我的数据是32字节),它只会添加字节。数据总是更大,并且不断增加(我需要知道为了正确读取和解密)。
注意:我不能直接使用其他流,因为我需要控制输出格式,我相信在内存中加密也更安全,更快。
答案 0 :(得分:6)
根据您的修改:
编辑:加密数据的大小似乎是48字节(比原始数据多12个字节)。为什么会这样?我认为如果它们不是块大小的倍数(16字节,我的数据是32字节),它只会添加字节。数据总是更大,并且不断增加(我需要知道为了正确读取和解密)。
如果加密数据是48个字节,那比原始数组大16个字节。这是有道理的,因为算法使用填充数据,因为默认值为PKCS7(即使大小与块大小匹配,因为它填充到块大小的 next 倍数)。如果您希望将其保留为32个字节,只需将Padding更改为None
即可aes.Padding = PaddingMode.None;
答案 1 :(得分:3)
您似乎将明文的长度视为密文的长度。这不是一个安全的假设。
为什么要在 FileStream
和MemoryStream
之间进行复制,您可以将FileStream
直接传递给加密器/解密器。
在PKCS7中,至少有一个填充字节(用于存储填充字节数)。因此输出大小为Ceil16(input.Length + 1)
或(input.Length & ~15) + 1
。
答案 2 :(得分:2)
缺点是AES以16字节的块加密消息。如果您的消息不是16字节的偶数倍,则最后一个块的算法需要稍微不同;具体来说,最后一个块必须用算法已知的值“填充”作为填充值(通常为零,有时候像空格字符值一样)。
通过将数据放入固定长度的字节数组中,您自己就是这样做的。您自己填充了数据,但解密器现在正在尝试对最后一个块进行解填,并获取它不能识别为其加密器对应物将添加的填充的字节值。
密钥不是填充消息。您可以使用BitConverter类将字节数组转换为IConvertible类型(值类型和字符串),然后使用它来代替滚动您自己的字节数组。然后,当你解密时,你可以从解密流中读取密文长度,但不要指望解密结果中有那么多实际字节。