BinaryFormatter + CryptoStream 在 .NET Core/5+ 中不可用,因 BinaryFormatter 已被弃用;应改用 System.Text.Json 先序列化为字节,再通过 CryptoStream 加密,且需严格分离序列化与加密步骤。

用 BinaryFormatter + CryptoStream 会出错?别用它
直接套用旧式序列化加加密流的写法,大概率在 .NET Core / .NET 5+ 上失败——BinaryFormatter 已被标记为过时且默认禁用,反序列化时还会抛 SerializationException 或 NotSupportedException。这不是配置问题,是设计层面的弃用。
- 仅限 .NET Framework 4.7.2 及更早版本可用(且不安全)
- 现代项目应改用
System.Text.Json或Newtonsoft.Json序列化为字节,再送入加密流 - 加密前必须确保对象可序列化:避免字段含
FileStream、委托、未标记[Serializable]的嵌套类型(JSON 序列化则无需[Serializable])
推荐做法:先序列化为 byte[],再写入 CryptoStream
这是最可控、兼容性最好的路径。核心是把“序列化”和“加密”拆成两步,中间用内存缓冲衔接,避免流嵌套导致的 Cannot access a closed Stream 或长度截断问题。
- 用
JsonSerializer.SerializeToUtf8Bytes()(.NET Core 3.0+)生成原始字节,比ToString()+Encoding.UTF8.GetBytes()少一次编码转换 -
CryptoStream必须配合支持随机访问的底层流(如FileStream),不能传MemoryStream后再转——否则加密后无法保证完整性校验 - 记得在
CryptoStream后调用FlushFinalBlock()(或用using自动完成),否则末尾加密块可能丢失
using var file = File.Create("data.enc");
using var aes = Aes.Create();
aes.Key = key; // 32 bytes
aes.IV = iv; // 16 bytes
using var encryptor = aes.CreateEncryptor();
using var cryptoStream = new CryptoStream(file, encryptor, CryptoStreamMode.Write);
var jsonBytes = JsonSerializer.SerializeToUtf8Bytes(obj);
cryptoStream.Write(jsonBytes);
// using 会自动 FlushFinalBlock
如何安全读取并反序列化加密文件
顺序必须严格逆向:打开文件 → 构建 CryptoStream(解密模式)→ 全部读入内存 → 再用 JSON 反序列化。不能边解密边反序列化,因为 JsonSerializer.Deserialize<T>() 需要完整 UTF-8 字节流。
- 务必验证
IV和Key与加密时完全一致,哪怕一个字节不同也会导致解密后 JSON 格式错误,抛JsonException - 不要尝试用
StreamReader包裹CryptoStream后传给Deserialize——解密流不是文本流,UTF-8 BOM 或中途截断会导致解析失败 - 如果文件较大(>10MB),考虑分块解密到临时文件再处理,避免内存峰值过高
using var file = File.OpenRead("data.enc");
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var decryptor = aes.CreateDecryptor();
using var cryptoStream = new CryptoStream(file, decryptor, CryptoStreamMode.Read);
var decryptedBytes = cryptoStream.ReadAllBytes(); // .NET 5+ 扩展方法
var obj = JsonSerializer.Deserialize<MyData>(decryptedBytes);
密钥和 IV 怎么存才不算埋雷
硬编码 byte[32] 或写死字符串到源码里,等于把锁芯焊死在门上。生产环境必须分离密钥生命周期管理。
-
IV可随文件明文存储(如前 16 字节),但每次加密必须用新随机值,不可复用 - 主密钥建议走系统级保护:Windows 用
ProtectedData(DPAPI),Linux/macOS 用SecretService或KeychainAPI;容器环境优先用 Vault 或云 KMS - 若只能本地存,至少用
ProtectedMemory短期保护内存中的密钥,且绝不日志打印、不序列化、不用字符串拼接构造
加密本身不难,难的是让密钥不变成最弱一环。很多人卡在解密失败,最后发现是 IV 被截断了两个字节,或者 Key 解码时用了 Encoding.ASCII 而不是 Convert.FromBase64String。










