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":"中国"}