文件aes加密不能只调aes.create()就完事,必须手动控制key、iv、mode和padding,且iv必须随密文一起保存——否则解密必失败,不是乱码,是直接抛cryptographicexception。

直接说结论:文件 AES 加密不能只调 Aes.Create() 就完事,必须手动控制 Key、IV、Mode 和 Padding,且 IV 必须随密文一起保存——否则解密必失败,不是乱码,是直接抛 CryptographicException。
为什么 Aes.Create() 生成的实例不能直接用于文件加解密
每次调用 Aes.Create() 返回的新实例,内部会自动生成随机 Key 和随机 IV,你根本拿不到、也记不住。加密完关掉程序,再打开解密时又是一个全新实例,Key 和 IV 全变了——等于拿错钥匙开锁,连门缝都进不去。
- 不显式赋值
aes.Key,.NET 就用它自己生成的(你无法获取) - 不显式赋值
aes.IV,它也会随机生成,但下次Aes.Create()又换一套 - 硬编码
new byte[16]或写死"1234567890123456"当 IV 是常见错误:前者全零可预测,后者 UTF-8 编码后长度可能不是 16 字节,且无随机性 - 正确做法:加密前调
aes.GenerateIV(),立刻把aes.IV取出来,和密文拼一起存到文件开头
文件加密时如何安全生成并保存 IV
IV 是公开的,但必须每次唯一、长度固定为 16 字节、不可预测。它不保密,但绝不能复用——同一密钥下重复 IV 会让攻击者通过密文模式反推明文结构(比如所有“支付成功”消息开头几块密文总一样)。
- 生成:用
aes.GenerateIV(),别用Encoding.UTF8.GetBytes("my_iv")——长度难控,也不够随机 - 保存:把 16 字节
IV写在密文最前面,例如:fileStream.Write(iv, 0, iv.Length),再写密文 - 读取:解密时先读前 16 字节作为
IV,剩余部分才是密文;别用StreamReader,要用BinaryReader或直接fileStream.Read()处理原始字节 - 注意:如果文件有 BOM(如 UTF-8 带签名),误读会导致 IV 错位,解密直接报
"The input data is not a complete block"
密钥怎么设才不会触发 "Specified key is not a valid size"
AES 只接受 128/192/256 位密钥,对应 16/24/32 字节。传错长度,CryptographicException 立马报给你看,不给机会试错。
- 禁用
Encoding.UTF8.GetBytes("mykey123"):它只有 9 字节,补零或截断都不可靠 - 推荐用
Rfc2898DeriveBytes派生:传入口令(password)、盐(salt,至少 16 字节随机)、10 万轮次,再调rd.GetBytes(32)拿 AES-256 密钥 - 盐(
salt)必须和 IV 一样,随密文一起保存或通过其他安全通道传输 - 若直接给定原始密钥字节数组,务必确认
key.Length == 16 || 24 || 32,少一个字节都不行
解密失败时最该盯住的三个参数
看到 "Bad Data"、"Padding is invalid and cannot be removed"、"Length of the data to decrypt is invalid",这不是算法出问题,而是两端配置没对齐。99% 的情况,错就错在这三样上。
-
aes.Mode:加密端设CipherMode.CBC,解密端也必须是CipherMode.CBC;GCM 模式要额外处理Tag,新手别碰 -
aes.Padding:默认是Pkcs7,但显式设置更稳妥;别用None,否则空字符串加密都会报"Length of the data to encrypt is invalid" -
aes.BlockSize:AES 固定为 128,别动;如果用了RijndaelManaged,它允许改块大小,反而容易出错,一律用Aes.Create()
真正容易被忽略的是:加密和解密用的 Key 字节数组是否完全一致——不是“同一个字符串再转一次”,而是同一个 byte[] 实例或完全相同的字节序列。哪怕只差一个字节,也不会解出乱码,而是直接炸掉。










