前后端请求AES加解密

AES前后端加解密

前端

安装依赖

1
npm install --save crypto-js

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
const CryptoJS = require("crypto-js");

exports.aes = {
// 加密
encrypt: function (str, key) {
return CryptoJS.AES.encrypt(str, key).toString();
},
//解密
decrypt: function (str, key) {
let bytes = CryptoJS.AES.decrypt(str, key);
return bytes.toString(CryptoJS.enc.Utf8);
},
};

加解密测试

1
2
3
4
5
let key = "psvmc.cn";
let encrypt_str = aes.encrypt("123456", key);
console.info("encrypt_str:", encrypt_str);
let decrypt_str = aes.decrypt(encrypt_str, key);
console.info("decrypt_str:", decrypt_str);

结果

encrypt_str: U2FsdGVkX1/QM9zoNjeuJ4AHYhjME01+XQLEOGkO3ns=
decrypt_str: 123456

后端

安装依赖

1
npm install --save crypto-js

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
const CryptoJS = require("crypto-js");

exports.aes = {
// 加密
encrypt: function (str, key) {
return CryptoJS.AES.encrypt(str, key).toString();
},
//解密
decrypt: function (str, key) {
let bytes = CryptoJS.AES.decrypt(str, key);
return bytes.toString(CryptoJS.enc.Utf8);
},
};

加解密测试

1
2
3
4
5
6
const { aes } = require("./utils/aes");
let key = "psvmc.cn";
let encrypt_str = aes.encrypt("123456", key);
console.info("encrypt_str:", encrypt_str);
let decrypt_str = aes.decrypt(encrypt_str, key);
console.info("decrypt_str:", decrypt_str);

结果

encrypt_str: U2FsdGVkX1875i8Vc3AbUur+Ycyw1VNODq7BW+OFaNI=
decrypt_str: 123456

注意

这里前后端加密后的字符串是不一样的,不用担心,他们都可以解密回原来的字符串,经过测试发现,同样的字符串每次加密都会生成不一样的字符串,但是都可以解密回原来的字符串。

项目对接

前端

设置请求拦截器

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
const http = axios.create({
timeout: 1000 * 30, // 请求超时时间30秒(单位:毫秒)
validateStatus: function (status) {
return status >= 200 && status <= 500; // 默认的
},
headers: {
"Content-Type": "application/json",
},
});

http.interceptors.request.use(
(config) => {
if (
config.headers["Content-Type"] &&
config.headers["Content-Type"].indexOf("application/json") !== -1 &&
config.method === "post" &&
config.data
) {
let mdata = {};
mdata.encrypt = aes.encrypt(JSON.stringify(config.data), "psvmc.cn");
config.data = mdata;
}
return config;
},
(err) => {
console.log(err);
}
);

Vue.prototype.$axios = http;

后端

我这里后端使用的是Koa框架,新定义了一个属性保存了加密后的请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app.use(cors());

app.use(bodyParser());
app.use(async (ctx, next) => {
let content_type = ctx.request.headers["content-type"];
if (content_type && content_type.indexOf("application/json") !== -1) {
if (ctx.request.body && ctx.request.body.encrypt) {
let encrypt = ctx.request.body.encrypt;
ctx.request.body = JSON.parse(aes.decrypt(encrypt, key));
}
}
await next();
});

const controller = require("./controller");
app.use(controller());

注意

解密代码要放在bodyParser()之后,Controller之前

后端使用Java

前端库地址:crypto-js

安装依赖

1
npm install --save crypto-js

或者下载后引用

1
2
<script src="./js/crypto-js.js"></script>
<script src="./js/aes.js"></script>

使用CBC模式

前端

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
// 字符串转hex
let string_to_hex = function (str) {
let tempstr = "";
for (let i = 0; i < str.length; i++) {
if (tempstr === "") tempstr = str.charCodeAt(i).toString(16);
else tempstr += str.charCodeAt(i).toString(16);
}
return tempstr;
};

let keystr = "0123456789ABCDEF";
let ivstr = "0123456789101112";
const src = "Hello World";

console.log("密钥:", keystr);
console.log("偏移量:", ivstr);
console.log("原字符串:", src);

let key = string_to_hex(keystr);

key = CryptoJS.enc.Hex.parse(key);
ivstr = string_to_hex(ivstr);
let iv = CryptoJS.enc.Hex.parse(ivstr);

const enc = CryptoJS.AES.encrypt(src, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});

const enced = enc.ciphertext.toString();
console.log("加密:", enced);

const dec = CryptoJS.AES.decrypt(CryptoJS.format.Hex.parse(enced), key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});

const decstr = CryptoJS.enc.Utf8.stringify(dec);
console.log("解密:", decstr);

结果

密钥: 0123456789ABCDEF
偏移量: 0123456789101112
原字符串: Hello World
加密: 4b20efdf7aceb95c099b7df542673256
解密: Hello World

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
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {

private static String Algorithm = "AES";
private static String AlgorithmProvider = "AES/CBC/PKCS5Padding"; //算法/模式/补码方式

public static byte[] generatorKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm);
keyGenerator.init(256);//默认128,获得无政策权限后可为192或256
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}

public static IvParameterSpec getIv(String ivstr) throws UnsupportedEncodingException {
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivstr.getBytes("utf-8"));
return ivParameterSpec;
}

public static String encrypt(String src, String keystr, IvParameterSpec iv) throws
NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
IllegalBlockSizeException,
BadPaddingException,
UnsupportedEncodingException,
InvalidAlgorithmParameterException {
byte[] key = keystr.getBytes("utf-8");
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
IvParameterSpec ivParameterSpec = iv;
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
return byteToHexString(cipherBytes);
}

public static String decrypt(String src, String keystr, IvParameterSpec iv) throws Exception {
byte[] key = keystr.getBytes("utf-8");
SecretKey secretKey = new SecretKeySpec(key, Algorithm);

IvParameterSpec ivParameterSpec = iv;
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] hexBytes = hexStringToBytes(src);
byte[] plainBytes = cipher.doFinal(hexBytes);
return new String(plainBytes, "utf-8");
}

/**
* 将byte转换为16进制字符串
*
* @param src
* @return
*/
public static String byteToHexString(byte[] src) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xff;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
sb.append("0");
}
sb.append(hv);
}
return sb.toString();
}

/**
* 将16进制字符串装换为byte数组
*
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] b = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return b;
}

private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}

public static void main(String[] args) {
try {
// 密钥必须是16的倍数
String keystr = "0123456789ABCDEF";
String ivstr = "0123456789101112";
String src = "Hello World";

System.out.println("密钥:" + keystr);
System.out.println("偏移量:" + ivstr);
System.out.println("原字符串:" + src);
IvParameterSpec iv = getIv(ivstr);
String enc = encrypt(src, keystr, iv);
System.out.println("加密:" + enc);

String dec = decrypt(enc, keystr, iv);
System.out.println("解密:" + dec);
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}

结果

密钥:0123456789ABCDEF
偏移量:0123456789101112
原字符串:Hello World
加密:4b20efdf7aceb95c099b7df542673256
解密:Hello World

使用ECB模式

前端

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
// 字符串转hex
let string_to_hex = function (str) {
let tempstr = "";
for (let i = 0; i < str.length; i++) {
if (tempstr === "") tempstr = str.charCodeAt(i).toString(16);
else tempstr += str.charCodeAt(i).toString(16);
}
return tempstr;
};

let keystr = "0123456789ABCDEF";
const src = "Hello World";

console.log("密钥:", keystr);
console.log("原字符串:", src);

let key = string_to_hex(keystr);

key = CryptoJS.enc.Hex.parse(key);
const enc = CryptoJS.AES.encrypt(src, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});

const enced = enc.ciphertext.toString();
console.log("加密:", enced);

const dec = CryptoJS.AES.decrypt(CryptoJS.format.Hex.parse(enced), key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});

let decstr = CryptoJS.enc.Utf8.stringify(dec);
console.log("解密:", decstr);

结果

密钥: 0123456789ABCDEF
原字符串: Hello World
加密: 21f41dde54d0c9703c730fd14ce47cfe
解密: Hello World

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
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {

private static String Algorithm = "AES";
private static String AlgorithmProvider = "AES/ECB/PKCS5Padding"; // 算法/模式/补码方式

public static byte[] generatorKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm);
keyGenerator.init(256);//默认128,获得无政策权限后可为192或256
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}

public static String encrypt(String src, String keystr) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
byte[] key = keystr.getBytes("utf-8");
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
return byteToHexString(cipherBytes);
}

public static String decrypt(String src, String keystr) throws Exception {
byte[] key = keystr.getBytes("utf-8");
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] hexBytes = hexStringToBytes(src);
byte[] plainBytes = cipher.doFinal(hexBytes);
return new String(plainBytes, "utf-8");
}

/**
* 将byte转换为16进制字符串
*
* @param src
* @return
*/
public static String byteToHexString(byte[] src) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xff;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
sb.append("0");
}
sb.append(hv);
}
return sb.toString();
}

/**
* 将16进制字符串装换为byte数组
*
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] b = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return b;
}

private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}

public static void main(String[] args) {
try {
// 密钥必须是16的倍数
String keystr = "0123456789ABCDEF";
String src = "Hello World";

System.out.println("密钥:" + keystr);
System.out.println("原字符串:" + src);

String enc = encrypt(src, keystr);
System.out.println("加密:" + enc);

String dec = decrypt(enc, keystr);
System.out.println("解密:" + dec);
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}

结果

密钥:0123456789ABCDEF
原字符串:Hello World
加密:21f41dde54d0c9703c730fd14ce47cfe
解密:Hello World