Unity安卓Focus 3构建中JSON写入及AES加密异常排查
Unity安卓Focus3项目:文件共享冲突与AES填充错误排查方案
一、文件共享冲突(IOException)解决
- 核心问题:FileStream未正确释放资源,导致文件被锁定,场景重载或构建后无法写入。编辑器同会话正常是因为Unity资源管理暂时持有句柄,但重载后锁未释放。
- 你调整FileStream模式无效,大概率是没确保流被及时关闭。必须用
using语句包裹FileStream,它会自动调用Dispose()释放资源,避免文件句柄泄漏。 - 错误写法示例(可能你的原有代码):
var stream = new FileStream(path, FileMode.OpenOrCreate); // 读写操作 stream.Close(); // 手动关闭易遗漏,异常时不会执行
- 正确写法:
// 写入文件 using (var stream = new FileStream(Application.persistentDataPath + "/save.json", FileMode.Create, FileAccess.Write, FileShare.None)) using (var writer = new StreamWriter(stream)) { var json = JsonUtility.ToJson(saveData); writer.Write(json); } // 读取文件 var path = Application.persistentDataPath + "/save.json"; if (File.Exists(path)) { using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var reader = new StreamReader(stream)) { var json = reader.ReadToEnd(); saveData = JsonUtility.FromJson<SaveData>(json); } }
- 额外注意:安卓平台
persistentDataPath默认具备读写权限,不要手动修改路径权限配置。
二、AES PKCS7填充错误(CryptographicException)解决
- 核心原因:加密和解密的参数不匹配,常见场景:
- 密钥/IV长度不符合AES要求(AES密钥必须是128/192/256位,IV必须是128位)
- 加密和解密时的填充模式、块大小不一致
- 加密后的数据被篡改或截断(比如流未关闭就结束写入)
- IV未和加密数据一起保存,解密时用了错误的IV
- 修复方案:
- 统一加密解密的AES配置:
// 统一AES实例创建方法 private static Aes CreateAesInstance(byte[] key, byte[] iv) { var aes = Aes.Create(); aes.Key = key; aes.IV = iv; aes.Mode = CipherMode.CBC; // 固定模式,加密解密必须一致 aes.Padding = PaddingMode.PKCS7; // 明确填充模式,避免默认值差异 return aes; } // 加密方法 public static byte[] EncryptData(byte[] plainData, byte[] key, byte[] iv) { using (var aes = CreateAesInstance(key, iv)) using (var encryptor = aes.CreateEncryptor()) using (var ms = new MemoryStream()) using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) { cs.Write(plainData, 0, plainData.Length); cs.FlushFinalBlock(); // 必须调用,确保填充数据写入完成 return ms.ToArray(); } } // 解密方法 public static byte[] DecryptData(byte[] encryptedData, byte[] key, byte[] iv) { using (var aes = CreateAesInstance(key, iv)) using (var decryptor = aes.CreateDecryptor()) using (var ms = new MemoryStream(encryptedData)) using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) using (var outputMs = new MemoryStream()) { cs.CopyTo(outputMs); return outputMs.ToArray(); } }
- 规范密钥生成:不要直接将字符串转字节,用
Rfc2898DeriveBytes从密码生成固定长度密钥:
// 从密码生成128位密钥和IV private static (byte[] key, byte[] iv) GenerateKeyIvFromPassword(string password, byte[] salt) { var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA256); return (pbkdf2.GetBytes(16), pbkdf2.GetBytes(16)); // 16字节=128位 }
- 保存加密数据时,需将IV与加密内容一同存储(IV无需加密),比如先写入IV长度,再写入IV,最后写入加密数据,解密时按顺序读取。
额外排查步骤
- 输出路径日志确认正确性:
Debug.Log(Application.persistentDataPath),验证路径存在且可读写。 - 先移除加密逻辑,确保文件读写正常后再重新加入加密模块,分步定位问题。
- 加密数据写入文件时,使用
File.WriteAllBytes或包裹在using的FileStream中,避免流未关闭导致数据截断。
内容的提问来源于stack exchange,提问作者Filip
相关产品推荐
相关产品推荐

