我的情况是我需要安全地加密/解密n长度的文件,理想情况下使用Rijndael,但绝对是256位加密。
我以前玩过加密,并且非常高兴地加密/解密字符串和字节数组。 但是,因为我不知道文件的大小(并且所讨论的文件可能非常大(~2.5gb)非常可行,我不能将它们加载到字节数组中并对它们进行加密/解密我以前的单一界限。
所以,经过对Google的一些阅读后,我知道答案是以块的形式加密和解密文件,因此我生成了以下代码:
private static void Enc(string decryptedFileName, string encryptedFileName)
{
FileStream fsOutput = File.OpenWrite(encryptedFileName);
FileStream fsInput = File.OpenRead(decryptedFileName);
byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");
fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
fsOutput.Write(IVBytes, 0, 16);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC};
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordDB.GetBytes(256 / 8), IVBytes);
CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write);
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
fsInput.Read(chunkData, 0, chunkSize);
cryptoStream.Write(chunkData, 0, chunkData.Length);
}
cryptoStream.Close();
fsInput.Close();
fsInput.Dispose();
cryptoStream.Dispose();
}
private static void Dec(string encryptedFileName, string decryptedFileName)
{
FileStream fsInput = File.OpenRead(encryptedFileName);
FileStream fsOutput = File.OpenWrite(decryptedFileName);
byte[] buffer = new byte[8];
fsInput.Read(buffer, 0, 8);
long fileLength = BitConverter.ToInt64(buffer, 0);
byte[] IVBytes = new byte[16];
fsInput.Read(IVBytes, 0, 16);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordDB.GetBytes(256 / 8), IVBytes);
CryptoStream cryptoStream = new CryptoStream(fsOutput,decryptor,CryptoStreamMode.Write);
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
fsInput.Read(chunkData, 0, chunkSize);
cryptoStream.Write(chunkData, 0, chunkData.Length);
}
cryptoStream.Close();
cryptoStream.Dispose();
fsInput.Close();
fsInput.Dispose();
}
这一切对我来说都“好看”,但遗憾的是看起来似乎是在欺骗!
加密工作没有错误,但在解密期间,“cryptoStream.Close()”方法抛出以下异常:
System.Security.Cryptography.CryptographicException 未处理的消息=“填充是 无效且无法删除。“
Source =“mscorlib”StackTrace: 在System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte [] inputBuffer,Int32 inputOffset,Int32 inputCount,Byte []&amp; OutputBuffer中, Int32 outputOffset,PaddingMode paddingMode,Boolean fLast) 在System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte [] inputBuffer,Int32 inputOffset,Int32 inputCount) 在System.Security.Cryptography.CryptoStream.FlushFinalBlock() 在System.Security.Cryptography.CryptoStream.Dispose(布尔值 处置) 在System.IO.Stream.Close()
未加密的文件大小似乎与预期的文件大小不匹配(范围从大约8个字节到大约60个)
我通过改变RijndaelManaged对象创建行来“修复”异常以包含填充类型,如下所示:
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.None };
但文件大小仍然不匹配,可以预见的是,刚刚未加密的文件是胡扯!
我承认我现在已经超出了加密/解密的舒适区域,这可能是一个新手的错误 - 但我无法发现它!
非常感谢任何有关解决此问题的帮助!
答案 0 :(得分:5)
问题是我正在使用:
passwordDB.GetBytes(256 / 8)
在加密和解密方法中的RijndaelManaged对象的构造函数中,我在尝试解密之前没有重新初始化passwordDB对象。
解决方案是简单地在Enc和Dec方法的第一行中包含passwordDB对象的构造,如下所示:
private static void Enc(string decryptedFileName, string encryptedFileName)
{
PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
byte[] passwordBytes = passwordDB.GetBytes(128 / 8);
using (FileStream fsOutput = File.OpenWrite(encryptedFileName))
{
using(FileStream fsInput = File.OpenRead(decryptedFileName))
{
byte[] IVBytes = Encoding.ASCII.GetBytes("1234567890123456");
fsOutput.Write(BitConverter.GetBytes(fsInput.Length), 0, 8);
fsOutput.Write(IVBytes, 0, 16);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.ANSIX923};
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(passwordBytes, IVBytes);
using (CryptoStream cryptoStream = new CryptoStream(fsOutput, encryptor, CryptoStreamMode.Write))
{
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
if (bytesRead != 16)
{
for (int x = bytesRead - 1; x < chunkSize; x++)
{
chunkData[x] = 0;
}
}
cryptoStream.Write(chunkData, 0, chunkSize);
}
}
cryptoStream.FlushFinalBlock();
}
}
}
}
private static void Dec(string encryptedFileName, string decryptedFileName)
{
PasswordDeriveBytes passwordDB = new PasswordDeriveBytes("ThisIsMyPassword", Encoding.ASCII.GetBytes("thisIsMysalt!"), "MD5", 2);
byte[] passwordBytes = passwordDB.GetBytes(128 / 8);
using (FileStream fsInput = File.OpenRead(encryptedFileName))
{
using (FileStream fsOutput = File.OpenWrite(decryptedFileName))
{
byte[] buffer = new byte[8];
fsInput.Read(buffer, 0, 8);
long fileLength = BitConverter.ToInt64(buffer, 0);
byte[] IVBytes = new byte[16];
fsInput.Read(IVBytes, 0, 16);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC,Padding=PaddingMode.ANSIX923};
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(passwordBytes, IVBytes);
using (CryptoStream cryptoStream = new CryptoStream(fsOutput, decryptor, CryptoStreamMode.Write))
{
for (long i = 0; i < fsInput.Length; i += chunkSize)
{
byte[] chunkData = new byte[chunkSize];
int bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
cryptoStream.Write(chunkData, 0, bytesRead);
}
}
}
}
}
}
知道它必须是一个小学生的错误:P
答案 1 :(得分:3)
Stream.Read方法返回实际从流中读取的字节数。
您应该将此返回值用作下一行Write方法中的最后一个参数。
我的代码看起来像这样:
byte[] chunkData = new byte[chunkSize];
var bytesRead = 0;
while ((bytesRead = fsInput.Read(chunkData, 0, chunkSize)) > 0)
{
cryptoStream.Write(chunkData, 0, bytesRead);
}
答案 2 :(得分:0)
有一个加密/解密Streams的CryptoStream类