一、前言

公司APP开发模式为手机端登录逻辑使用原生安卓开发,内部业务功能使用html5开发,客户安全要求明文不能传输敏感信息、传输数据不可修改,防止造成越权访问问题。基于这个要求我设计前后端交互对整个过程进行全部的加密处理,然后在加载和处理的时候进行解密处理。这个时候就必须设计一套java和js可以通用的加密和解密方式。这里我们采用了AES加解密。

二、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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import java.security.SecureRandom;

public class AESUtils {

private static final String KEY = "9936667890hijklm";

// 偏移量,AES 128位数据块对应偏移量为16位
public static final String VIPARA = "3424578890abcdef"; // AES 128位数据块对应偏移量为16位

// AES:加密方式 CBC:工作模式 PKCS5Padding:填充模式
private static final String CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";

private static final String AES = "AES";

// 编码方式
public static final String CODE_TYPE = "UTF-8";

/**
* AES 加密操作
*
* @param content
* 待加密内容
* @param key
* 加密密钥
* @return 返回Base64转码后的加密数据
*/
public static String encrypt(String content, String key) {

if (content == null || "".equals(content)) {
return content;
}

try {
/*
* 新建一个密码编译器的实例,由三部分构成,用"/"分隔,分别代表如下 1. 加密的类型(如AES,DES,RC2等) 2.
* 模式(AES中包含ECB,CBC,CFB,CTR,CTS等) 3. 补码方式(包含nopadding/PKCS5Padding等等)
* 依据这三个参数可以创建很多种加密方式
*/
Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);

// 偏移量
IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes(CODE_TYPE));

byte[] byteContent = content.getBytes(CODE_TYPE);

// 使用加密秘钥
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(CODE_TYPE), AES);
// SecretKeySpec skeySpec = getSecretKey(key);

cipher.init(Cipher.ENCRYPT_MODE, skeySpec, zeroIv);// 初始化为加密模式的密码器

byte[] result = cipher.doFinal(byteContent);// 加密

return Base64.encodeBase64String(result).replaceAll("\r\n", "");// 通过Base64转码返回
} catch (Exception ex) {
ex.printStackTrace();
}

return null;

}

/**
* AES 解密操作
*
* @param content
* @param key
* @return
*/
public static String decrypt(String content, String key) {
if (content == null || "".equals(content)) {
return content;
}

try {
// 实例化
Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes(CODE_TYPE));

SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(CODE_TYPE), AES);
// SecretKeySpec skeySpec = getSecretKey(key);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, zeroIv);

byte[] result = cipher.doFinal(Base64.decodeBase64(content));

return new String(result, CODE_TYPE);
} catch (Exception ex) {
ex.printStackTrace();
}

return null;
}

/**
* 生成加密秘钥
*
* @return
*/
private static SecretKeySpec getSecretKey(final String key) {
// 返回生成指定算法密钥生成器的 KeyGenerator 对象
KeyGenerator kg = null;

try {
kg = KeyGenerator.getInstance(AES);

// AES 要求密钥长度为 128
kg.init(128, new SecureRandom(key.getBytes()));

// 生成一个密钥
SecretKey secretKey = kg.generateKey();

return new SecretKeySpec(secretKey.getEncoded(), AES);// 转换为AES专用密钥
} catch (Exception ex) {
ex.printStackTrace();
}

return null;
}

public static String Encrypt(String content) {
String key = KEY;
String s = encrypt(content, key);
return s.replaceAll("/", "ut8fut8fut8f").replaceAll("[+]", "ut7fut7fut7f");
}

public static String Decrypt(String content) {
String key = KEY;
content = content.replaceAll("ut8fut8fut8f", "/").replaceAll("ut7fut7fut7f", "+");
return decrypt(content, key);
}

public static void main(String[] args) {
System.out.println(Encrypt("r6jjXhILAOds"));
System.out.println(Decrypt("72FJcuL7eXwNGLmNut8fut8fut8fLqQhA=="));

System.out.println(Encrypt("632600185488"));
System.out.println(Decrypt("qdlut8fut8fut8fo4uGeB0co849grtG3g=="));
System.out.println(Decrypt("qdlut8fut8fut8fo4uGeB0co849grtG3g=="));

}

}

三、js加解密工具

从官网下载 crypto-js.js ,改造工具类增加如下代码:

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
CryptoJS.wno = {
Encrypt: function(data){
var key = CryptoJS.enc.Utf8.parse('9936667890hijklm');
var iv = CryptoJS.enc.Utf8.parse('3424578890abcdef');
var encrypted =CryptoJS.AES.encrypt(data,key,
{
iv:iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
});
return encrypted.toString().replace(/[/]/g,'ut8fut8fut8f').replace(/[+]/g,'ut7fut7fut7f');
},
Decrypt: function(encrypted){
encrypted = encrypted.replace(/ut8fut8fut8f/g,'/').replace(/ut7fut7fut7f/g,'+');
var key = CryptoJS.enc.Utf8.parse('9936667890hijklm');
var iv = CryptoJS.enc.Utf8.parse('3424578890abcdef');
var decrypted =CryptoJS.AES.decrypt(encrypted,key,
{
iv:iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
}

}

四、测试

Java加密使用: AESUtils.Encrypt(String content);
Java解密使用: AESUtils.Decrypt(String content);

Js加密使用: CryptoJS.wno.Encrypt(String content);
Js解密使用: CryptoJS.wno.Decrypt(String content);

五、常用方法

5.1 java

1
2
3
4
5
6
7
解密
planId = AESUtils.Decrypt(planId);
args1 = AESUtils.Decrypt(paramterMap.get(key)[0]);

加密
result = "{\"info\":\""+AESUtils.Encrypt(result)+"\"}";
result = new String("{\"info\":\""+AESUtils.Encrypt(JSON.toJSONString(map))+"\"}");

5.2 js

1
2
3
4
5
6
解密
var info = data.info;
var data = JSON.parse(CryptoJS.wno.Decrypt(info));

加密
param = {"uid" : CryptoJS.wno.Encrypt('112233'), "dt_start" : CryptoJS.wno.Encrypt('2021-06-01')};