dart和java数据交互通用加密方法

在开发app软件时:软件中的实时动态数据,会调用服务器项目api。这时软件不做防护的话,稍微抓个包,软件请求的api全部都会被获取,一览无余,然后再写个python脚本,循环遍历直接把数据库给掏空。别问我为啥知道的,可以看上一篇文章或进入爬虫专题。我都是这么干的,对于软件到服务器接口未加密的数据,直接抓包爬取屡试不爽。

所以数据的防护至关重要,常用的思路应该(没做过移动app开发,猜测)就是采用对称解密(AES、DES、RC6等),服务端加密数据通过接口给客户端,客户端根据秘钥进行解密,然后使用。反之亦然,客户端请求数据携带的参数也可以加密到后端,这样可以保证数据的安全性。或者数据不做加密处理,直接上SSL证书,抓过好多软件接口域名都是https的,数据全是乱码。

当然采用对称加密这种方式,也不是绝对的安全,对于技术高超会反编译没加固或加固简单的APP软件的极客来说,一样能破解,获取加/解密算法。然后本地模拟解密,然后写个python脚本,循环遍历直接……

不过,这样还是能防住一些“脚本小子”的,提高下门槛。下面使用的是异或加密,原始代码来源网络,代码改动较大。java、dart来回调试了用了我大半天。

原始代码遇到的坑:

  1. java端加密后,dart端只能解密非中文的字符,要不然解密报错/乱码。

  2. 源代码中异或操作前会把字符串URLEncoder转换一下,这样操作是把中文等转换成字母,流程没啥好说的。但是这地方浪费了我好长时间,java端加解密是没有问题,但是dart端解密某些中文字符同样会乱码。

后来经过我半天不断的调试,把加密前对字符串URLEncoder转换方式换成了Base64转换方式,最后证明不管是啥字符串都是可行的,详情见下方代码。

加密思路:对明文进行Base64加密一下,然后异或操作,然后为了更好的网络传输,再次进行Base64加密一下。得到最终的密文。

解密思路:对加密进行反方向解密一遍。密文进行Base64解密,然后异或操作解密,然后Base64再次解密得到明文。

关门放源码。

dart端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import 'dart:convert';

class XORCryptoUtil {
static const String keyStr = '709489b228199d46fa01f9dc59a9e9b5';
static int k = keyStr.length;

/// 异或加密
static String _coding(String message) {
int len = message.length;
String str = '';
for (int i = 0; i < len; i++) {
int msgCode = message.codeUnitAt(i);
int keyCode = keyStr.codeUnitAt(i % k);
var res = msgCode ^ keyCode;
str += String.fromCharCode(res);
}
return str;
}

/// 加密
static String encoding(String plaintext) {
return enBase64(_coding(enBase64(plaintext)));
}

/// 解密
static String decoding(String cipherText) {
return deBase64(_coding(deBase64(cipherText)));
}

static String enBase64(String content) {
var bytes = utf8.encode(content);
return base64Encode(bytes);
}

static String deBase64(String content) {
return utf8.decode(base64Decode(content));
}
}

void main() {
String name = "!!<>清?:{}(风)}1}ab明cd1234《月》?:!";
String eres = XORCryptoUtil.encoding(name);
String dres = XORCryptoUtil.decoding(eres);
print('加密后:$eres');
print('解密后:$dres');
}

java端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.wiung.api.utils;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class XORCryptoUtil {

private static byte[] keyBytes; // 密钥
private static int k;
private static String key = "709489b228199d46fa01f9dc59a9e9b5";

public XORCryptoUtil(String key) {
keyBytes = key.getBytes();
k = keyBytes.length;
}

private static String coding(String message) {
byte[] origin = message.getBytes();
byte[] master = new byte[origin.length];
for (int i = 0, len = origin.length; i < len; i++) {
master[i] = (byte) (origin[i] ^ keyBytes[i % k]);
}
return new String(master);
}

/// 加密
public static String encoding(String plaintext) {
XORCryptoUtil crypto = new XORCryptoUtil(key);
return enBase64(crypto.coding(enBase64(plaintext)));
}

/// 解密
public static String decoding(String cipherText) {
XORCryptoUtil crypto = new XORCryptoUtil(key);
return deBase64(crypto.coding(deBase64(cipherText))).replaceAll("\r|\n", "");
}

public static String enBase64(String content) {
/// Base64.getMimeEncoder()方式加解密
/// 字符串必须是4的倍数,要不然报错:Last unit does not have enough valid bits
/// Base64其他两种方式也试了也是各种错
if (content.length() % 4 != 0) {
for (int i = 0; i < content.length() % 4; i++) {
content += " ";
}
}
final byte[] textByte = content.getBytes(StandardCharsets.UTF_8);
return Base64.getMimeEncoder().encodeToString(textByte);
}

public static String deBase64(String content) {
return new String(Base64.getMimeDecoder().decode(content), StandardCharsets.UTF_8);
}

public static void main(String[] args) {
String content = "!!<>清?:{}(风)}1}ab明cd1234《月》?:!";

String cipherText = encoding(content);
String plaintext = decoding(cipherText);
System.out.println("加密:" + cipherText);
System.out.println("解密:" + plaintext);

System.out.println("b6加密:" + enBase64(content));
System.out.println("b6解密:" + deBase64(enBase64(content)));

// System.out.println("uri:" + URLEncoder.encode(content, "UTF-8"));
// System.out.println("uri:" + URLDecoder.decode(content, "UTF-8"));
}
}

异或加密原理(来源网络)

异或(XOR)运算加密一种简单高效、非常安全的加密方法。

异或(XOR)运算:

逻辑运算之中,除了 AND 和 OR,还有一种 XOR 运算,中文称为”异或运算”。

它的定义是:两个值相同时,返回false,否则返回true。也就是说,XOR可以用来判断两个值是否不同。

1
2
3
4
true XOR true // false
false XOR false // false
true XOR false // true
true XOR false // true

XOR运算有一个很奇妙的特点:如果对一个值连续做两次 XOR,会返回这个值本身。

1
2
3
4
5
// 第一次 XOR
1010 ^ 1111 // 0101

// 第二次 XOR
0101 ^ 1111 // 1010

上面代码中,原始值是1010,再任意选择一个值(上例是1111),做两次 XOR,最后总是会得到原始值1010。这在数学上是很容易证明的。

XOR 的这个特点,使得它可以用于信息的加密。

1
2
message XOR key // cipherText
cipherText XOR key // message

上面代码中,原始信息是message,密钥是key,第一次 XOR 会得到加密文本cipherText。对方拿到以后,再用key做一次 XOR 运算,就会还原得到message。

二战期间,各国为了电报加密,对密码学进行了大量的研究和实践,其中就包括 XOR 加密。

战后,美国数学家香农(Claude Shannon)将他的研究成果公开发表,证明了只要满足两个条件,XOR 加密是无法破解的。

  • key的长度大于等于message
  • key必须是一次性的,且每次都要随机产生

理由很简单,如果每次的key都是随机的,那么产生的CipherText具有所有可能的值,而且是均匀分布,无法从CipherText看出message的任何特征。也就是说,它具有最大的”信息熵”,因此完全不可能破解。这被称为 XOR 的”完美保密性”(perfect secrecy)。

满足上面两个条件的key,叫做 one-time pad(缩写为OTP),意思是”一次性密码本”,因为以前这样的key都是印刷成密码本,每次使用的时候,必须从其中挑选。

异或(XOR)运算加密/解密算法原理:

  从加密的主要方法看,换位法过于简单,特别是对于数据量少的情况很容易由密文猜出明文,而替换法不失为一种行之有效的简易算法。

  从各种替换法运算的特点看,异或运算最适合用于简易加解密运算,这种方法的原理是:当一个数A和另一个数B进行异或运算会生成另一个数C,如果再将C和B进行异或运算则C又会还原为A。

  相对于其他的简易加密算法,XOR算法的优点如下。

  (1)算法简单,对于高级语言很容易能实现。

  (2)速度快,可以在任何时候、任何地方使用。

  (3)对任何字符都是有效的,不像有些简易加密算法,只对西文字符有效,对中文加密后再解密无法还原为原来的字符。

碎碎念

至于为啥我要搞这个,那当然是为了独立开发者而奋进了。

其实关注flutter好久了,最近才下决心学习,下班回家都废寝忘食的。flutter真不错,google的,一套代码可以编译成多端app。前几年就听说google的新系统fuchsia,官方语言就是flutter,但是这个fuchsia好像有点神秘,也没啥新消息进展。

感觉开发移动端app,比开发网站用户多,而且使用起来也很方便。经常看见各种大佬开发的独立软件,使得他们都财务自由了。但愿30岁的时候自己能有一款用户日活跃过万的app。


-------------本文结束感谢您的阅读-------------
感觉文章不错,就赏个吧!
0%