我一直在绞尽脑汁想弄清楚为什么我解密的字节数组和流与未加密的字节数组不同。
起初我以为是因为我不小心加密了 IV,但是在更改代码以确保它们以纯文本编写并从该纯文本读取之后,我仍然得到截然不同的结果并且导致数据不正确正在阅读。
接下来我认为 IV 可能仍然在我的数据的开头,这弄乱了我的 ReadBinaryData 函数,但事实并非如此。
就像我得到一个数百万字节的文件大小和一个数百万字节的地图大小,它应该是 ~252k 字节和 5 的地图大小。
我有以下3种方法:
首先一个保存方法:
private bool SaveEncryptedResources(string filePath, string key)
{
//validation for filePath and key occur earlier
using (var aes = Aes.Create())
{
aes.Key = TransformKey(key);
aes.Padding = PaddingMode.ISO10126;
aes.Mode = CipherMode.CBC;
//Removed Key size per Etienne
aes.GenerateIV();
byte[] binaryBuffer;
//Have to write to byte[] because there is some jumping around in WriteBinaryData();
using (var ms = new MemoryStream())
{
using (var bw = new BinaryWriter(ms))
{
var startPosition = aes.IV.Length;
WriteBinaryData(bw, startPosition);
ms.Seek(0, SeekOrigin.Begin);
binaryBuffer = ms.ToArray();
}
}
var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (var fs = new FileStream(filePath, FileMode.OpenOrCreate))
{
fs.Write(aes.IV, 0, aes.IV.Length);
using (var cs = new CryptoStream(fs, encryptor, CryptoStreamMode.Write))
{
using (var bw = new BinaryWriter(cs))
{
bw.Write(binaryBuffer);
bw.Flush();
cs.FlushFinalBlock();
}
}
}
}
return true;
}
第二种加载方式:
private void LoadEncryptedResources(string filePath, string key)
{
byte[] binaryBuffer;
using (var fs = new FileStream(filePath, FileMode.Open))
{
using (var aes = Aes.Create())
{
aes.Key = TransformKey(key);
aes.Padding = PaddingMode.ISO10126;
aes.Mode = CipherMode.CBC;
//Removed Key size per Etienne
var iv = new byte[aes.IV.Length];
var numBytesToRead = aes.IV.Length;
var currentByte = 0;
//read the IV
while (numBytesToRead > 0)
{
var n = fs.Read(iv, currentByte, numBytesToRead);
if (n == 0) break;
currentByte += n;
numBytesToRead -= n;
}
aes.IV = iv;
using (var cs = new CryptoStream(fs, aes.CreateDecryptor(aes.Key, aes.IV), CryptoStreamMode.Read))
{
binaryBuffer = new byte[(int)fs.Length];
//Read returns the number of bytes read and can be used to validate
//that we actually read the entire file into our buffer
var bytesRead = cs.Read(binaryBuffer, 0, (int)fs.Length);
}
}
}
//load binary data into class Stream object
ResourceStream = new MemoryStream(binaryBuffer);
}
最后一个读取方法:
private void ReadBinaryData(int startPosition = 0)
{
//IV doesn't actually appear to be in binary data, so start position is not needed
var br = new BinaryReader(ResourceStream);
var fileSize = br.ReadInt32();
var mapSize = br.ReadInt32();
//*** Exception no longer thrown, but getting garbled data such as "E:\Proj�� \C#\PixelSharper\PixelSharper.Core\CoreConfiguration.cs" ***.//
for (var i = 0; i < mapSize; i++)
{
var resourceName = br.ReadString();
var resourceSize = br.ReadInt32();
var resourceOffset = br.ReadInt32();
//populate fileMap with file data
FileMap[resourceName] = new ResourceFile(resourceSize, resourceOffset);
}
}
哦,这也是关键的转变:
private static readonly byte[] Salt =
new byte[] { 2, 3, 16, 125, 21, 232, 4, 189 };
private static byte[] TransformKey(string password, int keyBytes = 32)
{
//TODO: make this configurable
const int iterations = 655360;
var keyGenerator = new Rfc2898DeriveBytes(password, Salt,
iterations, HashAlgorithmName.SHA512);
//KeyBytes are 32*8 = 256 bits for AES
return keyGenerator.GetBytes(keyBytes);
}
这是您可能从文档中错过的花絮:
更改 KeySize 值会重置密钥并生成新的随机密钥。每当调用 KeySize 属性设置器时(包括为它分配相同的值时),都会发生这种情况。
这对于 .NET 中的所有对称算法实现都是通用的。
当你设置
KeySize
:
aes.KeySize = 256;
你在设置密钥后做,所以随机生成一个新密钥,打破一切。无论如何,您不需要明确设置密钥大小:只需设置一个有效大小的密钥即可。