C# AES加解密结果异常:密钥与IV一致仍无法正确解密
问题描述
我一直绞尽脑汁,想搞清楚为什么解密后的字节数组和流跟未加密的不一致。一开始以为是不小心把IV也加密了,但修改代码确保IV明文写入和读取后,结果还是偏差极大,读取的数据全错。我还怀疑IV还在数据开头干扰ReadBinaryData函数,但事实不是这样。比如解密后得到的文件大小有数百万字节、映射大小数千万,而实际应该是约252k字节、映射大小为5。
保存方法
private bool SaveEncryptedResources(string filePath, string key) { //filePath和key的验证已提前完成 using (var aes = Aes.Create()) { aes.Key = TransformKey(key); aes.Padding = PaddingMode.ISO10126; aes.Mode = CipherMode.CBC; //根据Etienne的建议移除了密钥大小设置 aes.GenerateIV(); byte[] binaryBuffer; //必须先写入字节数组,因为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; //根据Etienne的建议移除了密钥大小设置 var iv = new byte[aes.IV.Length]; var numBytesToRead = aes.IV.Length; var currentByte = 0; //读取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返回读取的字节数,可用于验证是否将整个文件读入缓冲区 var bytesRead = cs.Read(binaryBuffer, 0, (int)fs.Length); } } } //将二进制数据加载到类的Stream对象中 ResourceStream = new MemoryStream(binaryBuffer); }
读取方法
private void ReadBinaryData(int startPosition = 0) { //IV实际上不在二进制数据中,所以不需要起始位置 var br = new BinaryReader(ResourceStream); var fileSize = br.ReadInt32(); var mapSize = br.ReadInt32(); //*** 不再抛出异常,但得到乱码数据,比如"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(); //用文件数据填充fileMap 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: 可配置化 const int iterations = 655360; var keyGenerator = new Rfc2898DeriveBytes(password, Salt, iterations, HashAlgorithmName.SHA512); //KeyBytes为32*8=256位,适配AES return keyGenerator.GetBytes(keyBytes); }
WriteBinaryData方法
private void WriteBinaryData(BinaryWriter bw, int startPosition = 0) { //BinaryWriter本身知道类型大小,会写入预期的字节数 var resourceKeys = new Dictionary<string, int>(); var resourceFileSize = 0; bw.Write(resourceFileSize); var mapSize = FileMap.Count; bw.Write(mapSize); //写入文件映射 foreach (var resource in FileMap) { //C#的BinaryWriter会给字符串前缀长度 bw.Write(resource.Key); bw.Write(resource.Value.ResourceSize); //记录资源偏移的起始位置,后续用来填充实际文件数据的起始位置 resourceKeys.Add(resource.Key, (int)bw.BaseStream.Position); bw.Write(resource.Value.ResourceOffset); bw.Flush(); } //将文件数据写入资源文件 var position = bw.BaseStream.Position; var keys = FileMap.Keys.ToList(); foreach (var resource in keys) { var resourceFile = FileMap[resource]; //现在IV的写入和读取方式不需要再关心起始位置 resourceFile.ResourceOffset = (int)position; FileMap[resource] = resourceFile; //TODO: 替换为流式文件读取,避免文件过大无法存入内存 var fileBytes = File.ReadAllBytes(resource); bw.Write(fileBytes); position = bw.BaseStream.Position; bw.Flush(); } //是否要从资源文件总大小中排除IV? resourceFileSize = (int)bw.BaseStream.Length; //将资源文件大小从0更新为当前长度 bw.Seek(startPosition, SeekOrigin.Begin); bw.Write(resourceFileSize); bw.Flush(); //在resourceKeys字典记录的位置更新资源文件偏移,使用FileMap中存储的更新后偏移值 foreach (var resource in FileMap) { var offsetPosition = resourceKeys[resource.Key]; bw.Seek(offsetPosition, SeekOrigin.Begin); bw.Write(resource.Value.ResourceOffset); bw.Flush(); } }
内容的提问来源于stack exchange,提问作者Propagating
相关产品推荐
相关产品推荐

