本文主要讲前端传输时的密码加密及后端收到密文后解密的过程,避免明文传输密码,文中内容都比较浅显,加密原理部分需要查阅其它资料。至于前端加密是否有意义,见仁见智。
环境
前端用的vue
,纯JS也可以,引入crypto-js
库,后端用java
。
加密方法、MODE、PADDING的选择
这几个参数之间相互有关联,且前后端加密、解密时的参数必须一致。
有的参数前端有A选项,但是后端没有对应的选项,反之也存在后端有B选项,但前端没有的,选择的时候只能选前后端都有的。
具体可以从下面两篇文档中看到:
三个参数前后端共有的选项分别是:
- 加密方法:
AES
、DES
、 Triple DES
(java对应DESede
)、RC4
- mode:
CBC
、CFB
、CTR
、OFB
、ECB
- padding:
NoPadding
、Iso10126
、Pkcs7
(java对应PKCS5Padding
)
选择的话,每一个方法、MODE、padding的实现都有点复杂,上面的java参考文档里有写具体的技术性文档链接,这里只说使用,不说原理。
- 加密方法通常选
AES
,参考:FIPS 197。 - mode:
crypto-js
默认是CBC
,参考:FIPS 81 - padding:如果上面选的是
AES/CBC
,那么padding
可以选Iso10126
或者Pkcs7
。- NoPadding不能用,因为AES只支持128bit、192bit、256bit几种大小的分块(block),取决于key的位数,除非要加密的密文正好是上面这个块的大小,否则就不能解密,这显然是不合理的。
- 前端的
Pkcs7
和java的PKCS5Padding
是对应的,参考wiki上的解释,两者基本等价:
PKCS#5 padding is identical to PKCS#7 padding, except that it has only been defined for block ciphers that use a 64-bit (8-byte) block size. In practice the two can be used interchangeably.
实现工具类
下面的例子采用AES/CBC/Pkcs7
这几项,java端是AES/CBC/PKCS5Padding
。
密钥key
和初始向量iv
都设置成固定值,前后端一致即可。
key
用于AES
方法加密,iv
用于CBC
MODE,iv
的大小要跟block
一样,在这里就是128bit,跟key
一样
前端引入crypto-js
:npm install crypto-js
前端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| //aesutils.js
import CryptoJs from 'crypto-js'
//把key、iv设置成固定值,前后端的值要一致
let key = CryptoJs.enc.Utf8.parse("onlinemenukey123");
let iv = CryptoJs.enc.Utf8.parse("onlinemenuiv1234");
/**
*
* @param word 原始文本
* @returns {*} Base64的文本
* @constructor
*/
export function Encrypt(word) {
let srcs = CryptoJs.enc.Utf8.parse(word);
var encrypted = CryptoJs.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJs.mode.CBC,
padding: CryptoJs.pad.Pkcs7
});
return CryptoJs.enc.Base64.stringify(encrypted.ciphertext);
}
|
后端
涉及到String
和byte[]
转换时,最好指定编码类型,可以避免
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
| package com.lyuww.bgmanager.Utils;
import org.apache.tomcat.util.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class AESUtils {
/**
* 解密前端发送的AES加密处理后的密码文本
* @param text 前端发送的加密后的密文
* @param key 跟前端设置的key一致,只能是128bit、192bit、256bit
* @param iv 初始向量,跟前端设置的iv一致
* @return
*/
private static final String key="onlinemenukey123";
private static final String iv="onlinemenuiv1234";
public static String DecoderAES(String text){
try{
byte[] encrypted=new Base64().decode(text);//把base64编码的文本转换成byte数组
Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding ");
SecretKeySpec secretKey=new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"AES");
IvParameterSpec ivParameter=new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE,secretKey,ivParameter);
byte[] decrypted=cipher.doFinal(encrypted);
String oriringalString=new String(decrypted,StandardCharsets.UTF_8);
return oriringalString;
}catch (Exception e){
//解密失败时的操作
return e.toString();
}
}
}
|
应用
主要步骤:
前端
引入工具类:
import {Decrypt, Encrypt} from "@/utils/aesutils";
用axios
等方法传输数据,比如密码,在传输前加工具类加密即可
Encrypt(this.$data.formData.password)
后端
版权声明
本博客使用CC BY-NC-SA 4.0许可协议(创意共享4.0:保留署名-非商业性使用-相同方式共享)。