php 有个加密解密的 authcode 方法,在写 Flutter 的时候涉及到数据加密,就写了个 Flutter 版的 authcode
特点
1、可设置加密 key
2、可设置过期时间
加密原理
根据加密key,获取 ascii 码映射表 box(打乱)
前十位为时间戳,主要用来判断是否过期,中间16位为加密的值,后面就是原始数据,然后求出每个字符的 255 位 ascii 码值,并和 box 获取的值进行异或位运算,最后用base64加密一下
Flutter 坑点
其实也不算是坑,应该只是我不熟 dart 语言而已
convert 包中的 ascii.decode 方法只能转换 127 的 ascii ,所以要使用字符串的 codeUnits
base64Decode 解密的字符串必须是 4 的倍数,否则会报错,因为加密返回时,去掉了 = 号,所以在解密时要补全
对于加密中文
加密中文时,必须使用 urlencode(Uri.encodeComponent(url)) 转换一下,当然解出来后也要 urldecode(Uri.decodeComponent(encodedUrl)) 一下
源码如下
import 'dart:convert' as convert; import 'package:crypto/crypto.dart'; import 'dart:typed_data'; class AuthCode { //加密key static const String KEY = '123456'; //过期时间,秒 static const int EXPIRY = 0; //加密 static String encode(String str ,{String key : KEY, int expiry : EXPIRY}){ return _authcode(str,operation:'ENCODE',key: key,expiry: expiry); } //解密 static String decode(String str ,{String key : KEY, int expiry : EXPIRY}){ return _authcode(str,operation:'DECODE',key: key,expiry: expiry); } static String _authcode(String str,{String operation : 'DECODE', String key : KEY, int expiry : EXPIRY}){ int ckey_length = 4; key = md5.convert(convert.utf8.encode(key)).toString(); String keya = md5.convert(convert.utf8.encode(key.substring(0,16))).toString(); String keyb = md5.convert(convert.utf8.encode(key.substring(16,32))).toString(); String keyc = ''; if(operation == 'DECODE'){ keyc = str.substring(0,ckey_length); }else{ //微秒时间戳 String tmpMd5 = md5.convert(convert.utf8.encode(getAuthMicrosecond())).toString(); keyc = tmpMd5.substring(tmpMd5.length - ckey_length,tmpMd5.length); } String cryptkey = keya + md5.convert(convert.utf8.encode(keya + keyc)).toString(); int key_length = cryptkey.length; if(operation == 'DECODE'){ String codeStr = str.substring(ckey_length,str.length); int mod4 = codeStr.length % 4; if (mod4 > 0 ) { codeStr += '==='.substring(0,4 - mod4); } Uint8List tmpStrBytes = convert.base64Decode(codeStr) ; str = String.fromCharCodes(tmpStrBytes); }else{ //过期时间,若为0 ,则加 10位前导0 int tmpExpiry = expiry > 0 ? expiry + getAuthTimestamp() : 0; String preStr = tmpExpiry > 0 ? tmpExpiry.toString() : '0000000000'; String middleStr = md5.convert(convert.utf8.encode(str + keyb)).toString().substring(0,16); str = preStr + middleStr + str; } int string_length = str.length; String result = ''; //映射ASCII 表 Map box = {}; for (int i = 0; i <= 255; i++) { box[i] = i; } Map rndkey = {}; String tmp = ''; List cryptkeyList = cryptkey.split(''); for (int i = 0; i <= 255; i++) { //获取加密串的 ASCII 值 tmp = cryptkeyList[i % key_length]; rndkey[i] = convert.ascii.encode(tmp)[0]; } int tmp1 = 0; for (int j = 0, i = 0; i < 256; i++) { j = (j + box[i] + rndkey[i]) % 256; tmp1 = box[i]; box[i] = box[j]; box[j] = tmp1; } int tmp2 = 0; for (int a = 0, j = 0 ,i = 0; i < string_length; i++) { a = (a + 1) % 256; j = (j + box[a]) % 256; tmp2 = box[a]; box[a] = box[j]; box[j] = tmp2; int s1 = str[i].codeUnits[0]; int s2 = box[(box[a] + box[j]) % 256]; Uint8List bytes = Uint8List.fromList([s1 ^ s2]); result += String.fromCharCodes(bytes); } if (operation == 'DECODE') { int result10 = int.parse(result.substring(0,10)); String result16 = result.substring(10,26); String result26 = result.substring(26,result.length); String tmpKey = md5.convert((result26 + keyb).codeUnits).toString().substring(0,16); int nowTime = getAuthTimestamp(); if ((result10 == 0 || result10 - nowTime > 0) && result16 == tmpKey) { return result.substring(26,result.length); } else { return ''; } } else { return keyc + convert.base64Encode(result.codeUnits).replaceAll('=', ''); } } //获取微秒时间戳,格式如 0.78677000 1592544251 static String getAuthMicrosecond(){ int microseconds = DateTime.now().microsecondsSinceEpoch; String seconds = microseconds.toString().substring(0,10); String micro = microseconds.toString().substring(10,16); return '0.'+micro+'00 '+seconds; } //10位秒时间戳 static int getAuthTimestamp() { String tmp = (DateTime.now().millisecondsSinceEpoch / 1000).toString(); return int.parse(tmp.substring(0, 10)); } }
使用如下
Map data = { 'mobile': '15088888888', 'email': 'jam00@vip.qq.com', 'name':'中国', }; String jsonStr = convert.jsonEncode(data); String authStr = AuthCode.encode(Uri.encodeComponent(jsonStr)); print(authStr); print(Uri.decodeComponent(AuthCode.decode(authStr)));
结果如下
I/flutter (16152): 07701fBXi+L0f0wm5r8MEjS1HtqfvEFMKjIaLpemLlCfYDAZH6fHLeH15BterTFVeEJCRQEW36hT3mg+oxsHmcMU+WAiPqWy0ad90Y69KKmDjo+7tTXAfLLlSQPcunsAh3wV0b9VRKJtGRie4bmEC+V0rX7yj9IMNLt3oHxfkquMR/4OQASPCan4e2KvuBHtP9aVLQ I/flutter (16152): {"mobile":"15088888888","email":"jam00@vip.qq.com","name":"中国"}