• Unity制作影视互动游戏视频加密方案分享


    前言

    随着《完蛋!我被美女包围!》和《美女,别影响我学习》等影视互动游戏的爆火 公司最近也有款影视互动项目 于是乎就接到了对视频加密的任务 毕竟谁也不想直接被拿到几十个G的视频原文件看完直接退款吧 于是乎研究了一下当前的加密方案(仅限用Unity原生 VideoPlayer播放)

     

    一、Assetbundle打包

    这种方式就不做过多介绍了 接触过热更新的小伙伴们基本手拿把掐 本质就是把视频源文件打成ab包 要播放视频的时候在异步解包播放

    优点:操作简单方便 加载也比较快

    缺点:有可能会被解包 防君子不防小人

    二、字节加密

    这种方式本质就是将任意文件转换成字节数组 然后对字节数组进行加密解密操作 这个方式不限于视频文件 任何想要加密的文件都可以

    加密方式也有许多 比如 将所有字节取相反的二进制 把所以字节移位、截取数组开头的一段数组放到数组末尾、自定义个字符串转换成数组拼接到数组开头 等等...

    然后把加密后的字节数组在通过File.WriteAllBytes方法写成文件 解密的时候在进行加密时候的逆向操作即可

    以取相反二进制为例 

    1. //加密资源
    2. public static void BuildVideoByTurnByte()
    3. {
    4. //要加密的视频资源目录
    5. string videoPath = Application.streamingAssetsPath + "/Test/";
    6. //加密后的视频资源目录
    7. string videoOutPath = Application.streamingAssetsPath + "/Test2/";
    8. //示例 选取目录下的所有文件
    9. var files = Directory.GetFiles(videoPath, "*", SearchOption.AllDirectories);
    10. List<string> list = new List<string>();
    11. for (int i = 0; i < files.Length; i++)
    12. {
    13. //筛选掉.meta文件 不做操作
    14. string ext = Path.GetExtension(files[i]);
    15. if (ext == ".meta")
    16. continue;
    17. //获取文件名
    18. string fileName = Path.GetFileNameWithoutExtension(files[i]);
    19. //将文件转换为字节数组
    20. byte[] bytes = File.ReadAllBytes(files[i]);
    21. //加密
    22. byte[] buffer_ed = TurnByte(bytes);
    23. //输出加密后的文件 后缀名随你喜欢取 这里以哥哥为例
    24. File.WriteAllBytes(videoOutPath + fileName + ".cxk", buffer_ed);
    25. }
    26. Logger.Log("资源加密成功");
    27. }
    28. public static byte[] TurnByte(byte[] input)
    29. {
    30. for (int i = 0; i < input.Length; i++)
    31. {
    32. //隐式类型转换
    33. byte currentByte = input[i];
    34. //去相反的二进制
    35. byte shiftedValue = (byte)(~currentByte);
    36. //替换
    37. input[i] = shiftedValue;
    38. }
    39. return input;
    40. }

    以上操作便可把.mp4文件写入成.cxk的文件 此时如果直接把更改后缀回.mp4 播放 会发现无法播放

    Unity里播放如下

    1. public void PlayVideo(string name)
    2. {
    3. //VideoPlayer组件
    4. VideoPlayer mPlayer = gameObject.GetComponent();
    5. //加密后的视频路径
    6. string resourceUrl = Application.streamingAssetsPath + "/Test2/";
    7. //解密后的临时视频路径
    8. string tempUrl = Path.GetTempPath() + "Temp/";
    9. //临时目录空的话就创建
    10. if (Directory.Exists(tempUrl))
    11. {
    12. //删除临时目录下的所有文件
    13. foreach (string filePath in Directory.GetFiles(tempUrl))
    14. {
    15. File.Delete(filePath);
    16. }
    17. }
    18. else
    19. {
    20. //创建目录
    21. Directory.CreateDirectory(tempUrl);
    22. }
    23. //如果加密路径下没有对应名字的加密文件 或者 已经临时目录下已有解密的视频文件 则不执行
    24. if (File.Exists(resourceUrl + name + ".cxk") && !File.Exists(tempUrl + name + ".mp4"))
    25. {
    26. //将文件转换成字节数组
    27. byte[] bytes = File.ReadAllBytes(resourceUrl + name + ".cxk");
    28. //取相反的二进制
    29. byte[] buffer_ed = TurnByte(bytes);
    30. //将字节数组写入到临时目录下
    31. File.WriteAllBytes(tempUrl + name + ".mp4", buffer_ed);
    32. }
    33. //给VideoPlayer组件赋值并播放
    34. mPlayer.url = tempUrl + name + ".mp4";
    35. mPlayer.Play();
    36. }
    37. public static byte[] TurnByte(byte[] input)
    38. {
    39. for (int i = 0; i < input.Length; i++)
    40. {
    41. byte currentByte = input[i];
    42. byte shiftedValue = (byte)(~currentByte);
    43. // 将移位后的字节添加到加密byte数组中
    44. input[i] = shiftedValue;
    45. }
    46. return input;
    47. }

    这个时候运行游戏就可以正常播放视频啦

    基本上就可以满足加密的需求了 毕竟别人也很难猜到你对字节做了什么羞羞的事情 

    优点:视频加密解密速度快 

    缺点:有被破解的风险(不过谁会这么无聊呢)

     

    三、AES字节数组加密

    这种方式基本上很难破解了 因为基于AES加密的方式 安全性可以保障 具体的加密方式方法可以点最下方的链接去到原作者的博客 里面有非常详细的关于AES加密的介绍 不过加密解密的过程也会比较耗时 同样的也是可以把任何格式的文件都加密 毕竟本质上也是对字节数组进行操作 只不过是方法二字节加密的Plus版本罢了

    下面是代码

    VideoDecryptTools.cs  加密方法类

    1. using System.IO;
    2. using System.Security.Cryptography;
    3. using System.Text;
    4. using System;
    5. public class VideoDecryptTools
    6. {
    7. //读取视频文件
    8. public static void LoadVideo(string dataUrl, string res, string tempUrl, string password)
    9. {
    10. //获取对应的视频文件转换为字节数组
    11. byte[] bytes = AuthGetFileData(dataUrl + res + ".cxk");
    12. byte[] buffer_ed = DecryptByte(bytes, password);
    13. if (buffer_ed != null)
    14. {
    15. File.WriteAllBytes(tempUrl + res + ".mp4", buffer_ed);
    16. }
    17. }
    18. //将文件转换成字节数组
    19. public static byte[] AuthGetFileData(string fileUrl)
    20. {
    21. FileStream fs = new FileStream(fileUrl, FileMode.Open, FileAccess.Read);
    22. byte[] buffur = new byte[fs.Length];
    23. fs.Read(buffur, 0, buffur.Length);
    24. fs.Close();
    25. return buffur;
    26. }
    27. //加密
    28. public static byte[] DecryptByte(byte[] buffer, string password)
    29. {
    30. byte[] decrypted;
    31. using (Aes aes = Aes.Create())
    32. {
    33. //设定密钥和向量
    34. (aes.Key, aes.IV) = GenerateKeyAndIV(password);
    35. //设定运算模式和填充模式
    36. aes.Mode = CipherMode.CBC;
    37. aes.Padding = PaddingMode.PKCS7;
    38. //创建解密器对象(加解密方法不同处仅仅这一句话)
    39. var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
    40. using (MemoryStream msDecrypt = new MemoryStream(buffer))
    41. {
    42. try
    43. {
    44. //选择Read模式
    45. using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
    46. {
    47. //创建临时数组,用于包含可用字节+无用字节
    48. byte[] buffer_T = new byte[buffer.Length];
    49. //对加密数组进行解密,并通过i确定实际多少字节可用
    50. int i = csDecrypt.Read(buffer_T, 0, buffer.Length);
    51. //使用Read模式不能有此句,但write模式必须要有。
    52. //csDecrypt.FlushFinalBlock();
    53. //创建只容纳可用字节的数组
    54. decrypted = new byte[i];
    55. //从bufferT拷贝出可用字节到decrypted
    56. Array.Copy(buffer_T, 0, decrypted, 0, i);
    57. }
    58. return decrypted;
    59. }
    60. catch (CryptographicException)
    61. {
    62. Logger.Log("线程未加载完成视频后 切换线程 可忽略!");
    63. return null;
    64. }
    65. }
    66. }
    67. }
    68. //解密
    69. public static byte[] EncryptByte(byte[] buffer, string password)
    70. {
    71. byte[] encrypted;
    72. using (Aes aes = Aes.Create())
    73. {
    74. //设定密钥和向量
    75. (aes.Key, aes.IV) = GenerateKeyAndIV(password);
    76. //设定运算模式和填充模式
    77. aes.Mode = CipherMode.CBC;
    78. aes.Padding = PaddingMode.PKCS7;
    79. //创建加密器对象(加解密方法不同处仅仅这一句话)
    80. var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
    81. using (MemoryStream msEncrypt = new MemoryStream())
    82. {
    83. using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))//选择Write模式
    84. {
    85. //对原数组加密并写入流中
    86. csEncrypt.Write(buffer, 0, buffer.Length);
    87. //使用Write模式需要此句,但Read模式必须要有。
    88. csEncrypt.FlushFinalBlock();
    89. //从流中写入数组(加密之后,数组变长,详见方法AesCoreSingleTest内容)
    90. encrypted = msEncrypt.ToArray();
    91. }
    92. }
    93. }
    94. return encrypted;
    95. }
    96. public static (byte[] Key, byte[] IV) GenerateKeyAndIV(string password)
    97. {
    98. byte[] key = new byte[32];
    99. byte[] iv = new byte[16];
    100. byte[] hash = default;
    101. if (string.IsNullOrWhiteSpace(password))
    102. throw new ArgumentException("必须输入口令!");
    103. using (SHA384 sha = SHA384.Create())
    104. {
    105. byte[] buffer = Encoding.UTF8.GetBytes(password);
    106. hash = sha.ComputeHash(buffer);
    107. }
    108. //用SHA384的原因:生成的384位哈希值正好被分成两段使用。(32+16)*8=384。
    109. Array.Copy(hash, 0, key, 0, 32);//生成256位密钥(32*8=256)
    110. Array.Copy(hash, 32, iv, 0, 16);//生成128位向量(16*8=128)
    111. return (Key: key, IV: iv);
    112. }
    113. }

    参数:

    name:为你要加密的文件名

    password:为你的加密秘钥 

    加密:

    1. //加密资源
    2. public static void BuildVideoByTurnByte()
    3. {
    4. //要加密的视频资源目录
    5. string videoPath = Application.streamingAssetsPath + "/Test/";
    6. //加密后的视频资源目录
    7. string videoOutPath = Application.streamingAssetsPath + "/Test2/";
    8. //示例 选取目录下的所有文件
    9. var files = Directory.GetFiles(videoPath, "*", SearchOption.AllDirectories);
    10. List<string> list = new List<string>();
    11. for (int i = 0; i < files.Length; i++)
    12. {
    13. //筛选掉.meta文件 不做操作
    14. string ext = Path.GetExtension(files[i]);
    15. if (ext == ".meta")
    16. continue;
    17. //获取文件名
    18. string fileName = Path.GetFileNameWithoutExtension(files[i]);
    19. //将文件转换为字节数组
    20. byte[] bytes = VideoDecryptTools.AuthGetFileData(files[i]);
    21. //加密 秘钥为 jinitaimei
    22. byte[] buffer_ed = VideoDecryptTools.EncryptByte(bytes, "jinitaimei");
    23. }
    24. Logger.Log("资源加密成功");
    25. }

    播放视频:

    1. public void PlayVideo(string name,string password)
    2. {
    3. //password 为上段代码片段的 "jinitiaomei"
    4. //VideoPlayer组件
    5. VideoPlayer mPlayer = gameObject.GetComponent();
    6. //加密后的视频路径
    7. string resourceUrl = Application.streamingAssetsPath + "/Test2/";
    8. //解密后的临时视频路径
    9. string tempUrl = Path.GetTempPath() + "Temp/";
    10. //临时目录空的话就创建
    11. if (Directory.Exists(tempUrl))
    12. {
    13. //删除临时目录下的所有文件
    14. foreach (string filePath in Directory.GetFiles(tempUrl))
    15. {
    16. File.Delete(filePath);
    17. }
    18. }
    19. else
    20. {
    21. //创建目录
    22. Directory.CreateDirectory(tempUrl);
    23. }
    24. //如果加密路径下没有对应名字的加密文件 或者 已经临时目录下已有解密的视频文件 则不执行
    25. if (File.Exists(resourceUrl + name + ".cxk") && !File.Exists(tempUrl + name + ".mp4"))
    26. {
    27. VideoDecryptTools.LoadVideo(resourceUrl, name, tempUrl, password);
    28. }
    29. //给VideoPlayer组件赋值并播放
    30. mPlayer.url = tempUrl + name + ".mp4";
    31. mPlayer.Play();
    32. }

    以上整个加密解密的流程就完毕了

    需要注意的是 :

    视频解密同样是跟方法二一样将视频缓存到本地的缓存目录 如果视频文件很大 解密的过程会比较慢 就会导致点击播放视频 要卡个几秒才会播放视频 对于游戏的体验观感十分不友好 如果用这种方式的话 建议提前缓存下一个视频

    优点:安全 还是安全

    缺点:加密解密的时间损耗比较大

    总结

    以上就是本篇的全部内容了 如果有帮到你的话还请给我点个免费的赞 

    当然以上内容只是本人新手的一些见解 如果有大佬还请勿喷 多多指出问题 谢谢嘻嘻

    AES加密参考:https://www.cnblogs.com/syzcyyx/articles/13657222.html

  • 相关阅读:
    银河麒麟4.0Kylin桌面版安装Java环境
    【2024】springboot校服订购系统设计与实现
    解决 node-sass 安装时出现的各种报错
    JAVA mybatis操作mysql——批量增加,批量删除,多条件模糊搜索,新增时返回id和常用的增删查改
    算法训练(leetcode)第四十六天 | 110. 字符串接龙、105. 有向图的完全可达性、106. 岛屿的周长
    【Web前端】标签大全HTML/CSS/JavaScript
    653. 两数之和 IV - 输入二叉搜索树
    TabLayout的一种圆角背景
    make编译出错Relocations in generic ELF (EM: 62)
    PostgreSQL 16 发布,更可靠更稳健
  • 原文地址:https://blog.csdn.net/2401_83152164/article/details/139620771