0


flutter 加密安全

前言:数据安全
  1. 数据的**加密解密操**作在 日常网络交互中经常会用到,**现在密码的安全主要在于**** 秘钥的安全****,如论 DES 3DES AES 还是 RSA, 秘钥的算法(****计算秘钥不固定****) 保存,都决定了你的数据安全;但是常见的逆向操作 ** 比如 **hook 加密算法**都很容易拿到 秘钥; 这个时候我们可以 **回溯到 之前的 古典密码学**(**依赖算法本身**),基本思路 ** 置换 移位 编码** 等等手段 来配合 **加密算法一起使用,提高我们应用的安全**;

密码学概论_在传统的密码学中,加解密基础操作包括移位置换替换编码-CSDN博客文章浏览阅读201次。密码学基础_在传统的密码学中,加解密基础操作包括移位置换替换编码https://blog.csdn.net/nicepainkiller/article/details/132978492?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170902384916777224453245%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170902384916777224453245&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-132978492-null-null.nonecase&utm_term=%E5%AF%86%E7%A0%81&spm=1018.2226.3001.4450

android frida 逆向 自吐加密算法_frida 自吐算法 教程-CSDN博客文章浏览阅读1.8k次。frida hook android Android 逆向神器_frida 自吐算法 教程https://blog.csdn.net/nicepainkiller/article/details/132554698?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170902437216800182198144%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170902437216800182198144&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-132554698-null-null.nonecase&utm_term=hook&spm=1018.2226.3001.4450

flutter 数据加密

如果是 android 原生应用,可以 hook 系统加密代码,来获取加密秘钥,而在 flutter 中,dart 是被直接编译为 .so 文件,也就是汇编,对于低级汇编语言 可读性大大降低。当然也是可以下断点动态调试的;但是相对于难度 大大增加,我们可以结合 古典密码学 主要特点 数据安全基于算法的保密,算法不公开, 设计繁琐的算法过程,增加汇编可读性难度;

有个业务需求是:相当于是一个签到功能,每天可以领取,这里防止脚本调用使用两层 基于算法加密 和 AES加密 相结合的方式;(当然还用到了第三方的的安全软件)

以下是我上个版本加密算法设计:整体思路就是:

  • 按照规则 移动字符 替换字符
  • 阶段一主要是 按规则打乱字符(密文)位置
  • 阶段二主要是 指定位置(密文)插入无关字符

match_request_data.dart

  1. import 'dart:convert';
  2. import 'package:crypto/crypto.dart';
  3. import 'package:encrypt/encrypt.dart' as encrypt;
  4. ///匹配接口加密工具类
  5. ///整体加密思路:按照规则 移动字符 替换字符
  6. ///阶段一主要是 按规则打乱字符位置
  7. ///阶段二主要是 指定位置插入无关字符
  8. class MatchRequestData {
  9. final String gameId;
  10. final String chatSign;
  11. final String nickName;
  12. late List<int> _gameIdSort;
  13. MatchRequestData(
  14. {required this.gameId, required this.chatSign, required this.nickName});
  15. String generateCode(String datum) {
  16. String idStr = '${int.parse(gameId)}';
  17. List<int> searchKeywords =
  18. List<int>.generate(idStr.length, (index) => int.parse(idStr[index]));
  19. searchKeywords.sort();
  20. _gameIdSort = searchKeywords;
  21. String base64 = _encodeBase64(datum);
  22. String base64Step0 =
  23. base64.substring(0, _gameIdSort[_gameIdSort.length - 1]) +
  24. _stepOne((base64.substring(
  25. _gameIdSort[_gameIdSort.length - 1],
  26. base64.length -
  27. _gameIdSort[3] -
  28. _gameIdSort[_gameIdSort.length - 1]))) +
  29. base64.substring(base64.length -
  30. _gameIdSort[3] -
  31. _gameIdSort[_gameIdSort.length - 1]);
  32. String _strHex = _strToHex(base64Step0);
  33. final key = encrypt.Key.fromUtf8(_generateMd5(chatSign + nickName));
  34. final iv = encrypt.IV.fromUtf8(_ivStepOne().substring(4, 20).toUpperCase());
  35. final encrypter =
  36. encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
  37. final encrypted = encrypter.encrypt(_strHex, iv: iv);
  38. final encryptCode = _stepTwo(encrypted.base64);
  39. idStr = _strHex = base64 = base64Step0 = '';
  40. return encryptCode;
  41. }
  42. String _stepOne(String str) {
  43. String res = '';
  44. str = _inverse(str);
  45. int lastPosition = _gameIdSort[_gameIdSort.length - 1];
  46. int count = 0;
  47. if (lastPosition % 2 == 0) {
  48. count = _gameIdSort[_gameIdSort.length - 2];
  49. } else {
  50. count = _gameIdSort[_gameIdSort.length - 3];
  51. }
  52. if (count == 0) {
  53. count = lastPosition;
  54. }
  55. int step = str.length ~/ count;
  56. List<String> base64Parts = [];
  57. for (int i = 0; i < count; i++) {
  58. if (i % 2 == 1) {
  59. base64Parts.add(_inverse(str.substring(step * i, step * (i + 1))));
  60. } else {
  61. base64Parts.add(str.substring(step * i, step * (i + 1)));
  62. }
  63. }
  64. if (step * count < str.length) {
  65. if (lastPosition % 2 == 0) {
  66. base64Parts.insert(0, str.substring(step * count));
  67. } else {
  68. base64Parts.insert(base64Parts.length, str.substring(step * count));
  69. }
  70. }
  71. if (lastPosition % 2 == 1) {
  72. for (int i = 0; i < base64Parts.length; i++) {
  73. res = res + base64Parts[i];
  74. }
  75. } else {
  76. for (int i = base64Parts.length - 1; i >= 0; i--) {
  77. res = res + base64Parts[i];
  78. }
  79. }
  80. str = '';
  81. lastPosition = count = step = -1;
  82. return res;
  83. }
  84. String _stepTwo(String data) {
  85. String res = "";
  86. String _strHex = _strToHex(data);
  87. String _code = _inverse(_strHex);
  88. final key = encrypt.Key.fromUtf8(_generateMd5(gameId + chatSign));
  89. final iv = encrypt.IV.fromUtf8(_ivStepTwo().substring(14, 30));
  90. final encrypter =
  91. encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
  92. final encrypted = encrypter.encrypt(_code, iv: iv);
  93. res = encrypted.base64;
  94. int maxLength = res.length;
  95. int indexSub = 0;
  96. int insertPos = 0;
  97. String insertStr = '';
  98. for (int i = 1; i < _gameIdSort.length; i++) {
  99. indexSub = _gameIdSort[i] + 1;
  100. insertPos = _magic(indexSub + i) + i * 11 + i - 1;
  101. // insertStr = chatSign.substring(1,indexSub);
  102. insertStr = chatSign[indexSub];
  103. //前面插入
  104. if (insertPos > res.length) {
  105. insertPos = maxLength;
  106. }
  107. res =
  108. '${res.substring(0, insertPos)}$insertStr${res.substring(insertPos)}';
  109. }
  110. _strHex = _code = '';
  111. return res;
  112. }
  113. String _stepThree(String str) {
  114. return str;
  115. }
  116. String _inverse(String tag) {
  117. String res = '';
  118. List<String> searchKeywords =
  119. List<String>.generate(tag.length, (index) => tag[index]);
  120. Iterable<String> array = searchKeywords.reversed;
  121. for (var e in array) {
  122. res = '$res$e';
  123. }
  124. return res;
  125. }
  126. String _ivStepOne() {
  127. String res = '';
  128. String map = _generateMd5(chatSign) + _generateMd5(nickName);
  129. int index = _gameIdSort[_gameIdSort.length - 2];
  130. while (res.length < 50) {
  131. res += map[index];
  132. index++;
  133. }
  134. index = 0;
  135. return res;
  136. }
  137. String _ivStepTwo() {
  138. String res = '';
  139. String map = _generateMd5(_inverse(chatSign)) + _generateMd5(chatSign);
  140. int index = _gameIdSort[_gameIdSort.length - 1];
  141. while (res.length < 50) {
  142. res += map[index];
  143. index++;
  144. }
  145. index = 0;
  146. return _inverse(res);
  147. }
  148. /// 字符串转 十六进制
  149. String _strToHex(String str) {
  150. List<int> charCodes = str.runes.toList();
  151. return charCodes.map((code) => code.toRadixString(16)).join('');
  152. }
  153. /// 字符串转 base64
  154. String _encodeBase64(String data) {
  155. return base64Encode(utf8.encode(data));
  156. }
  157. /// base64转 普通字符
  158. String _decodeBase64(String data) {
  159. return String.fromCharCodes(base64Decode(data));
  160. }
  161. String _generateMd5(String str) {
  162. return md5.convert(utf8.encode(str)).toString();
  163. }
  164. int _magic(int num) {
  165. if (num < 3) {
  166. return 1;
  167. } else {
  168. return _magic(num - 1) + _magic(num - 2);
  169. }
  170. }
  171. }

调用的地方:

  1. MatchRequestData data = MatchRequestData (
  2. gameId: userArray[i]['gameID'],
  3. chatSign: userArray[i]['chatSign'],
  4. nickName: userArray[i]['nickName'],
  5. );
  6. //需要传递给后台的 内容
  7. Map datum = {
  8. 'inTrust': 'TRUE',
  9. 'time': DateTime.now().millisecondsSinceEpoch,
  10. 'GameID': userArray[i]['gameID'],
  11. 'nickName': userArray[i]['nickName'],
  12. 'MachineCode':
  13. md5.convert(utf8.encode(userArray[i]['gameID'])).toString(),
  14. 'sign': md5
  15. .convert(
  16. utf8.encode(userArray[i]['gameID'] + userArray[i]['chatSign']))
  17. .toString(),
  18. };
  19. String res = data.generateCode(jsonEncode(datum));
服务端的数据解密:

服务端为 .net 框架:

对应于加密算法写的解密算法:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Security.Cryptography;
  4. using System.Text;
  5. namespace ToMatch
  6. {
  7. public class MatchEncrypt
  8. {
  9. private string gameID;
  10. private string chatSign;
  11. private string nickName;
  12. private List<int> idSort;
  13. /// <summary>
  14. /// 匹配构造函数
  15. /// </summary>
  16. /// <param name="gameId">GameID</param>
  17. /// <param name="chatSign">签名</param>
  18. /// <param name="nickName">昵称</param>
  19. public MatchEncrypt(string gameId, string chatSign, string nickName) {
  20. this.gameID = gameId;
  21. this.chatSign = chatSign;
  22. this.nickName = nickName;
  23. this.idSort = new List<int>();
  24. string idStr = int.Parse(this.gameID).ToString();
  25. for (int i = 0; i < idStr.Length; i++)
  26. {
  27. this.idSort.Add((int)Char.GetNumericValue(idStr[i]));
  28. }
  29. this.idSort.Sort();
  30. }
  31. private String IvStepOne {
  32. get {
  33. String res = "";
  34. String map = Md5Hash(chatSign) + Md5Hash(nickName);
  35. int index = idSort[idSort.Count - 2];
  36. while (res.Length < 50)
  37. {
  38. res += map[index];
  39. index++;
  40. }
  41. return res;
  42. }
  43. }
  44. private String IvStepTwo
  45. {
  46. get
  47. {
  48. String res = "";
  49. String map = Md5Hash(AESHelper.Inverse(chatSign),false) + Md5Hash(chatSign,false);
  50. int index = idSort[idSort.Count - 1];
  51. while (res.Length < 50)
  52. {
  53. res += map[index];
  54. index++;
  55. }
  56. return AESHelper.Inverse(res);
  57. }
  58. }
  59. /// <summary>
  60. /// 解密客户端内容
  61. /// </summary>
  62. /// <param name="code">密文</param>
  63. /// <returns></returns>
  64. public string Resolver(string code) {
  65. //第一阶段解密内容
  66. string resStepOne = StepOne(code);
  67. if (resStepOne.Length > 0)
  68. {
  69. //Console.WriteLine("第一解密 result:" + resStepOne);
  70. //第二阶段解密
  71. string resSteptwo = Steptwo(resStepOne);
  72. //Console.WriteLine("第二解密 result:" + resSteptwo);
  73. //Console.WriteLine(AESHelper.FromBase64(resSteptwo));
  74. if (resSteptwo.Length > 0)
  75. {
  76. return AESHelper.FromBase64(resSteptwo);
  77. }
  78. else
  79. {
  80. return "解密失败——请记录日志.Step-2";
  81. }
  82. }
  83. else {
  84. return "解密失败——请记录日志.Step-1";
  85. }
  86. }
  87. private string StepOne(string code)
  88. {
  89. // 1.先移除插入的字符
  90. // 2.再进行解密操作
  91. int maxlength = code.Length - idSort.Count - 1;
  92. int indexSub = 0;
  93. int insertPos = 0;
  94. for (int i = 1; i < idSort.Count; i++)
  95. {
  96. indexSub = idSort[i] + 1;
  97. insertPos = magic(indexSub + i) + i * 11;
  98. //前面插入
  99. //Console.WriteLine("前面 索引:" + i);
  100. //Console.WriteLine("前面插入位置:" + insertPos);
  101. //Console.WriteLine("前面插入字符:" + insertStr + "");
  102. if (insertPos > code.Length) {
  103. //Console.WriteLine("修正Length:" + code.Length);
  104. Console.WriteLine("修正insertPos:" + insertPos);
  105. //Console.WriteLine("----code.Length:" + (code.Length -maxlength ));
  106. //Console.WriteLine("----code.Length:" + ( idSort.Count-1 - i));
  107. //Console.WriteLine("----code.Length:" + ((code.Length - maxlength - (idSort.Count - 1 - i))+1));
  108. insertPos = maxlength -4;
  109. insertPos = maxlength - ((code.Length - maxlength - (idSort.Count - 1 - i)) + 1);
  110. //Console.WriteLine("*******code.Length:" + ((code.Length - maxlength - (idSort.Count - 1 - i)) + 1));
  111. //Console.WriteLine("*******code.Length:" + (code.Length - maxlength - (idSort.Count - i)) );
  112. //Console.WriteLine("*******code.Length:" + (code.Length - maxlength - idSort.Count - i ));
  113. //Console.WriteLine("code.Length:" + code.Length);
  114. //Console.WriteLine("maxlength:" + maxlength);
  115. //Console.WriteLine("idSort.Count:" + idSort.Count);
  116. //Console.WriteLine("idSort.Count - i:" + (idSort.Count - i));
  117. //Console.WriteLine("修正插入位置i:" + i);
  118. //Console.WriteLine("修正插入位置:" + insertPos);
  119. insertPos = maxlength - ((code.Length - maxlength - (idSort.Count - 1 - i)) + 1);
  120. }
  121. code = code.Substring(0, insertPos) + code.Substring(insertPos + 1);
  122. }
  123. //Console.WriteLine("整理后的:" + code);
  124. //Console.WriteLine("整理后的Length:" + code.Length);
  125. string key = Md5Hash(this.gameID + this.chatSign, false);
  126. string iv = IvStepTwo.Substring(14, 16);
  127. //第一次解密是 16进制字符串
  128. string result = AESHelper.Decrypt(code, key, iv);
  129. return AESHelper.HexStringToString(AESHelper.Inverse(result), Encoding.UTF8);
  130. }
  131. private string Steptwo(string code) {
  132. string key = Md5Hash(this.chatSign + this.nickName, false);
  133. string iv = IvStepOne.Substring(4, 16);
  134. string base64 = AESHelper.HexStringToString(AESHelper.Decrypt(code, key, iv), Encoding.UTF8);
  135. string source = base64.Substring(0, idSort[idSort.Count - 1]) + generateMid(base64.Substring(idSort[idSort.Count - 1], base64.Length - idSort[3] - idSort[idSort.Count - 1] * 2)) + base64.Substring(base64.Length - idSort[3] - idSort[idSort.Count - 1]);
  136. //第二次解密是 base64
  137. return source ;
  138. }
  139. private string generateMid(string str) {
  140. string res = "";
  141. List<String> base64Parts = new List<string>();
  142. int lastPosition = this.idSort[this.idSort.Count - 1];
  143. string subBefore = "";
  144. int count = 0;
  145. if (lastPosition % 2 == 0)
  146. {
  147. count = idSort[idSort.Count - 2];
  148. }
  149. else
  150. {
  151. count = idSort[idSort.Count - 3];
  152. }
  153. if (count == 0) {
  154. count = lastPosition;
  155. }
  156. int step = str.Length / count;
  157. int subLength = str.Length - step * count;
  158. if (lastPosition % 2 == 0)
  159. {
  160. for (int i = 0; i < count; i++)
  161. {
  162. if (i % 2 == 1)
  163. {
  164. base64Parts.Add(AESHelper.Inverse(str.Substring(step * (count - i - 1), step)));
  165. }
  166. else
  167. {
  168. //base64Parts.Add(v.Substring(step * i, step));
  169. //Console.WriteLine(i + "不需要翻转原始:" + str.Substring(step * (count - i - 1), step));
  170. base64Parts.Add(str.Substring(step * (count - i - 1), step));
  171. }
  172. }
  173. for (int i = 0; i < base64Parts.Count; i++)
  174. {
  175. //Console.WriteLine("偶数项目:" + i + " " + base64Parts[i]);
  176. res = res + base64Parts[i];
  177. }
  178. subBefore = str.Substring(step * count);
  179. res += subBefore;
  180. }
  181. else
  182. {
  183. for (int i = 0; i < count; i++)
  184. {
  185. if (i % 2 == 1)
  186. {
  187. base64Parts.Add(AESHelper.Inverse(str.Substring(step * i, step)));
  188. }
  189. else
  190. {
  191. base64Parts.Add(str.Substring(step * i, step));
  192. }
  193. }
  194. subBefore = str.Substring(step * count);
  195. base64Parts.Add(subBefore);
  196. for (int i = 0; i < base64Parts.Count; i++)
  197. {
  198. //Console.WriteLine("奇数项目:" + i + " " + base64Parts[i]);
  199. res = res + base64Parts[i];
  200. }
  201. }
  202. return AESHelper.Inverse(res);
  203. }
  204. private static int magic(int num)
  205. {
  206. if (num < 3)
  207. {
  208. return 1;
  209. }
  210. else
  211. {
  212. return magic(num - 1) + magic(num - 2);
  213. }
  214. }
  215. private string Md5Hash(string sourceText, bool toUpper = true)
  216. {
  217. StringBuilder result = new StringBuilder();
  218. using (MD5 md5 = new MD5CryptoServiceProvider())
  219. {
  220. byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(sourceText));
  221. if (toUpper)
  222. for (int i = 0; i < data.Length; i++)
  223. result.Append(data[i].ToString("X2"));
  224. else
  225. for (int i = 0; i < data.Length; i++)
  226. result.Append(data[i].ToString("x2"));
  227. }
  228. return result.ToString();
  229. }
  230. }
  231. }
总结

数据安全不是绝对的,只能说我们多设置些障碍,对于逆向的难度对增大,你挖的坑多远。逆向时候就越困难,当然也可以借助一些第三方安全软件来增加我们数据的安全性。在数据安全的道路上 始终是此消彼长的状态


本文转载自: https://blog.csdn.net/nicepainkiller/article/details/136326876
版权归原作者 坎大哈 所有, 如有侵权,请联系我们删除。

“flutter 加密安全”的评论:

还没有评论