You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,最后写入加密数据,解密时按顺序读取。

额外排查步骤

  1. 输出路径日志确认正确性:Debug.Log(Application.persistentDataPath),验证路径存在且可读写。
  2. 先移除加密逻辑,确保文件读写正常后再重新加入加密模块,分步定位问题。
  3. 加密数据写入文件时,使用File.WriteAllBytes或包裹在using的FileStream中,避免流未关闭导致数据截断。

内容的提问来源于stack exchange,提问作者Filip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 19:20:47