-
Notifications
You must be signed in to change notification settings - Fork 0
20210325关于加密解密的技术选型,我可能会使用authcode(包含具体的源代码)
zqy edited this page Mar 25, 2021
·
1 revision
首先是authcode的dart实现:
https://blog.jam00.com/article/info/68.html
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': '[email protected]',
'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":"[email protected]","name":"中国"}
然后是authcode的golang实现:
https://github.com/starten/go-authcode/blob/master/authcode.go
package authcode
import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"fmt"
"strings"
"time"
"strconv"
"log"
)
// base64 加密
func Base64_encode(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
}
// base64 解密
func Base64_decode(s string) string {
sByte, err := base64.StdEncoding.DecodeString(s)
if err == nil {
return string(sByte)
} else {
return ""
}
}
//生成32位md5字串
func GetMd5String(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
/**
* $string 明文或密文
* $operation 加密ENCODE或解密DECODE
* $key 密钥
* $expiry 密钥有效期
*/
func AuthCode(str, operation, key string, expiry int64) string {
// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
// 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
// 取值越大,密文变动规律越大,密文变化 = 16 的 ckey_length 次方
// 当此值为 0 时,则不产生随机密钥
ckey_length := 4;
// 密匙
if key == "" {key = "#@!.5ebcQJx2Lz6GmcsqNiNHW.!@#"}
key = GetMd5String(key)
// 密匙a会参与加解密
keya := GetMd5String(key[:16])
// 密匙b会用来做数据完整性验证
keyb := GetMd5String(key[16:])
// 密匙c用于变化生成的密文
keyc := ""
if ckey_length != 0 {
if operation == "DECODE" {
keyc = str[:ckey_length]
} else {
sTime := GetMd5String(time.Now().String())
sLen := 32 - ckey_length
keyc = sTime[sLen:]
}
}
// 参与运算的密匙
cryptkey := fmt.Sprintf("%s%s", keya, GetMd5String(keya + keyc))
key_length := len(cryptkey)
// 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
// 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
if operation == "DECODE" {
str = strings.Replace(str, "*", "+", -1)
str = strings.Replace(str, "_", "/", -1)
strByte, err := base64.StdEncoding.DecodeString(str[ckey_length:])
if err != nil {
log.Fatal(err)
}
str = string(strByte)
} else {
if expiry != 0 {expiry = expiry + time.Now().Unix()}
tmpMd5 := GetMd5String(str + keyb)
str = fmt.Sprintf("%010d%s%s", expiry, tmpMd5[:16], str)
}
string_length := len(str)
resdata := make([]byte, 0, string_length)
var rndkey, box [256]int
// 产生密匙簿
j := 0;a := 0;i := 0;tmp := 0
for i = 0; i < 256; i++ {
rndkey[i] = int(cryptkey[i % key_length])
box[i] = i
}
// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上并不会增加密文的强度
for i = 0; i < 256; i ++ {
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp
}
// 核心加解密部分
a = 0;j = 0;tmp = 0
for i = 0; i < string_length; i++ {
a = ((a + 1) % 256)
j = ((j + box[a]) % 256)
tmp = box[a]
box[a] = box[j]
box[j] = tmp
// 从密匙簿得出密匙进行异或,再转成字符
resdata = append(resdata, byte(int(str[i]) ^ box[(box[a] + box[j]) % 256]))
}
result := string(resdata)
if operation == "DECODE" {
// substr($result, 0, 10) == 0 验证数据有效性
// substr($result, 0, 10) - time() > 0 验证数据有效性
// substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
// 验证数据有效性,请看未加密明文的格式
frontTen, _ := strconv.ParseInt(result[:10], 10, 0)
if (frontTen == 0 || frontTen - time.Now().Unix() > 0) && result[10:26] == GetMd5String(result[26:]+keyb)[:16] {
return result[26:]
} else {
return "";
}
} else {
// 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
// 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
result = keyc + base64.StdEncoding.EncodeToString([]byte(result))
result = strings.Replace(result, "+", "*", -1)
result = strings.Replace(result, "/", "_", -1)
return result
}
}
感觉还行,有现成的终归是最好的,谢谢作者!