• flutter 加密安全


    前言:数据安全

            数据的加密解密操作在 日常网络交互中经常会用到,现在密码的安全主要在于 秘钥的安全,如论 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. }

     总结

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

  • 相关阅读:
    STM32玩矩阵开关(输入和输出)
    神经网络实现AND门:逻辑运算的智能化飞跃
    回调函数的简单使用0717
    微服务框架 SpringCloud微服务架构 17 初识ES 17.6 安装IK 分词器
    蓝桥杯官网练习题(谈判)
    波兰计算器的实现(代码实现) [数据结构][Java]
    PGSQL的on conflict
    Day.js 常用方法 以后再也不用new Date( )啦
    聊聊redis分布式锁的8大坑
    Apache OFBiz 路径遍历导致RCE漏洞复现(CVE-2024-36104)
  • 原文地址:https://blog.csdn.net/nicepainkiller/article/details/136326876