AES加密解密/本地文件读写/Json解析_011
发表于2018-08-27
关于数据加密,前段时间刚刚用过AES,这里就简单记录一下啦
大概思路就是:
1. 直接读取Json文件的数据,转换为二进制流
2. 对数据进行加密处理,得到加密后的二进制流
3. 将二进制文件保存到本地
4. 读取本地加密文件,进行解密操作
5. Json解析
(刚开始的做法,是把二进制文件转化为字符串,然后保存,在读取的时候出现了问题,报错存贮的和读取的字符串不一致,应该是中间数据转换的时候格式有问题,就直接保存了二进制文件)
加密解密部分:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Text; using System.Security.Cryptography; namespace AESEncrypt { public class AESEncryptTool : MonoBehaviour { private static AESEncryptTool instance; public static AESEncryptTool Instance { get { if (instance == null) { instance = new GameObject("AESEncryptTool").AddComponent<AESEncryptTool>(); instance.Init(); } return instance; } } byte[] _keyBytes; byte[] _ivBytes; //初始化 void Init() { //获取密码, 密码写在预设体, 不要写在代码里, 防止外界直接通过代码窃取 UnityEngine.Object obj = Resources.Load("AESEncryptPassword"); if(obj == null) { Debug.Log("<color=red> Init Error , Not Resources.Load(AEDEncryptPassword) </color>"); } GameObject go = obj as GameObject; AEDEncryptPassword code = go.GetComponent<AEDEncryptPassword>(); _keyBytes = Encoding.UTF8.GetBytes(code.CryptKey); _ivBytes = Encoding.UTF8.GetBytes(code.CryptIV); } //加密数据 public byte[] Encrypt(byte[] contentBytes) { RijndaelManaged rm = new RijndaelManaged(); rm.Key = _keyBytes; rm.IV = _ivBytes; rm.Mode = CipherMode.CBC; rm.Padding = PaddingMode.PKCS7; ICryptoTransform ict = rm.CreateEncryptor(); byte[] resultBytes = ict.TransformFinalBlock(contentBytes, 0, contentBytes.Length); //return Convert.ToBase64String(resultBytes, 0, resultBytes.Length); return resultBytes; } //解密数据 public byte[] Decipher(byte[] contentBytes) { RijndaelManaged rm = new RijndaelManaged(); rm.Key = _keyBytes; rm.IV = _ivBytes; rm.Mode = CipherMode.CBC; rm.Padding = PaddingMode.PKCS7; ICryptoTransform ict = rm.CreateDecryptor(); byte[] resultBytes = ict.TransformFinalBlock(contentBytes, 0, contentBytes.Length); //return Encoding.UTF8.GetString(resultBytes); return resultBytes; } } }
文件读取保存到本地部分:
//因为加密操作只需要在编辑器环境下运行一次,这里把加密环节单独放到一个场景,便于单独操作,打包的时候也不需要打包该场景
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using System; public class AESEncryption : MonoBehaviour { //在一帧内处理指定数量文件, 可根据单个Json数据量及电脑处理速度修改参数 [SerializeField] int OnceWriteFileMaxCount = 10; //读取的Json文件路径, 生成的加密文件路径, 从Asset下一级开始 [SerializeField] string jsonPath_file; [SerializeField] string dataPath_file; //读取的Json文件夹路径, 生成的加密文件夹路径, 从Asset下一级开始 [SerializeField] string jsonPath_folder; [SerializeField] string dataPath_folder; void Awake() { jsonPath_file = Application.dataPath + "/" + jsonPath_file; dataPath_file = Application.dataPath + "/" + dataPath_file; jsonPath_folder = Application.dataPath + "/" + jsonPath_folder; dataPath_folder = Application.dataPath + "/" + dataPath_folder; } void Start() { StartCoroutine(EncryptAll()); } IEnumerator EncryptAll() { yield return StartCoroutine(WriteEncryptFile(jsonPath_file, dataPath_file)); yield return new WaitForEndOfFrame(); yield return StartCoroutine(WriteEncryptFolder(jsonPath_folder, dataPath_folder)); } //删除原文件 public void DeletJsonFile(string jsonPath) { if (Directory.Exists(jsonPath)) { Directory.Delete(jsonPath, true); Debug.Log("文件夹已删除"); } else Debug.Log("文件夹不存在"); } //加密文件, 所有的Json在一个文件夹下 public void WriteEncryptFromFile(string jsonPath, string dataPath) { StartCoroutine(WriteEncryptFile(jsonPath, dataPath)); } //加密文件夹, Json路径文件夹下嵌套一层文件夹, 即所有的Json分类在多个子文件夹下 public void WriteEncryptFromFolder(string jsonPath, string dataPath) { StartCoroutine(WriteEncryptFolder(jsonPath, dataPath)); } // ------ IEnumerator WriteEncryptFile(string jsonPath, string dataPath) { //Debug.Log("<color=green> 开始加密文件 </color>"); if (!Directory.Exists(jsonPath)) { Debug.Log("<color=red> 该目录不存在 </color>" + jsonPath); yield break; } //删除原有文件夹及文件, 重新创建 if (Directory.Exists(dataPath)) { Directory.Delete(dataPath, true); //Debug.Log("文件夹存在,删除"); } Directory.CreateDirectory(dataPath); //Debug.Log("新建文件夹 " + dataPath); //生成新文件 //获取文件夹下所有文件 DirectoryInfo directoryInfo = new DirectoryInfo(jsonPath); FileInfo[] fileInfo = directoryInfo.GetFiles("*.json", SearchOption.AllDirectories); int count = 0; for (int i = 0; i < fileInfo.Length; i++) { //是否是Json if (fileInfo[i].Name.EndsWith(".json", StringComparison.Ordinal)) { //读取问价内容 byte[] bytes = File.ReadAllBytes(fileInfo[i].FullName); //加密文件内容 byte[] jsonBytes = AESEncrypt.AESEncryptTool.Instance.Encrypt(bytes); //生成加密后的新文件 FileStream fileStream = new FileStream(dataPath + "//" + fileInfo[i].Name.Replace("json", "bytes"), FileMode.Create); fileStream.Write(jsonBytes, 0, jsonBytes.Length); fileStream.Flush(); fileStream.Close(); fileStream.Dispose(); count++; //在一帧内处理指定数量文件, 可根据单个Json数据量及电脑处理速度修改参数 if (i % OnceWriteFileMaxCount == 0) yield return new WaitForEndOfFrame(); } } Debug.Log("<color=green> 文件加密完成,共有文件 :</color>" + count); } IEnumerator WriteEncryptFolder(string jsonPath, string dataPath) { //检查路径 if (!Directory.Exists(jsonPath)) { Debug.Log("<color=red> 该目录不存在 </color>" + jsonPath); yield break; } //删除原有加密文件夹及文件, 重新创建 if (Directory.Exists(dataPath)) { Directory.Delete(dataPath, true); //Debug.Log("文件夹存在,删除"); } Directory.CreateDirectory(dataPath); //Debug.Log("新建文件夹 " + dataPath); //获取指定路径下面的所有资源文件夹 //返回满足指定条件的所有文件和子目录的路径及名称 string[] directoryEntries = Directory.GetFileSystemEntries(jsonPath); string tmpPath; int i = 0, folderCount = 0; while (i < directoryEntries.Length) { tmpPath = directoryEntries[i]; //判断是否为文件夹 if (!tmpPath.Contains(".")) { //依次加密 yield return StartCoroutine(WriteEncryptFile(tmpPath, tmpPath.Replace(jsonPath, dataPath))); folderCount++; yield return new WaitForEndOfFrame(); } i++; } Debug.Log("<color=blue> 文件夹加密完成,共有文件夹 :</color>" + folderCount); } }
数据解密读取部分:
//在游戏运行过程中,数据读取
using System.Collections; using System.Collections.Generic; using UnityEngine; using Newtonsoft.Json; using System.Text; using System.IO; public class DataModel { public int level; public string str; } public class DataManager : MonoBehaviour { string resourcesPath01 = "File/FileData"; string resourcesPath02 = "Folder/FolderData"; //保存数据 Dictionary<int, DataModel> allDataDic = new Dictionary<int, DataModel>(); void Start () { DataModel dataModel = GetDataByLevel(1); Debug.Log(dataModel.level + " " + dataModel.str); } public DataModel GetDataByLevel(int level) { //是否已经读取过一次 if (allDataDic.ContainsKey(level)) { return allDataDic[level]; } else { //读取解密数据 string jsonStr = null; try { TextAsset textAsset = Resources.Load<TextAsset>(resourcesPath01 + "/level" + level); if (textAsset == null) return null; byte[] bytes = textAsset.bytes; byte[] txtBytes = AESEncrypt.AESEncryptTool.Instance.Decipher(bytes); jsonStr = Encoding.UTF8.GetString(txtBytes); } catch (FileNotFoundException error) { Debug.Log(error); Debug.Log("<color=red> 该文件不存在 </color>" + resourcesPath01 + "/level" + level); } DataModel dataModel = null; if (!string.IsNullOrEmpty(jsonStr)) { try { dataModel = JsonConvert.DeserializeObject<DataModel>(jsonStr); } catch (JsonReaderException error) { Debug.Log(error); Debug.Log("<color=red> Json数据解析错误 : level </color>" + "/level" + level); } } if (dataModel != null) { allDataDic.Add(level, dataModel); //Debug.Log("<color=green> Json 解密 解析 Success : level </color>" + "/level" + level); } else Debug.Log("<color=red> 未获取到Json数据 : level </color>" + "/level" + level); return dataModel; } } }
主要的代码就是以上三部分,下面是完整的Demo,方便大家理解整个过程~
链接:https://pan.baidu.com/s/1zR8jeyscwvJn71lZnt4Rlw 密码:uqi2