Flutter 版的 authcode 加密/解密方法
2020-06-19 18:45 浏览(3067 更新于 2020-09-10 23:24

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


评论(5)
发布评论
回复X
聊天室(0