前后端(JAVA)实现AES对称加解密方式
文章目录
前后端(JAVA)实现AES对称加解密方式
1 对称加密分类以及概括
文章《加解密篇 - 对称加密算法 (DES、3DES、AES、RC)》有对这几种对称加密的说明,我大概概括一下。
1.1 加密安全等级 DES < 3DES < AES < RC
依次是DES < 3DES < AES < RC
1.2 DES
DES全称 Data Encryption Standard,是一种使用密钥加密的块算法,该加密算法运用非常普遍,是一种标准的加密算法。现在认为是一种不安全的加密算法,因为现在已经有用穷举法攻破 DES 密码的报道了,所以诞生了3DES。
1.3 3DES
3DES(或称为 Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于是对每个数据块应用三次 DES 加密算法。
1.4 AES
AES 加密算法的安全性要高于 DES 和 3DES,所以 AES 已经成为了主要的对称加密算法。AES 加密算法就是众多对称加密算法中的一种,它的英文全称是 Advanced Encryption Standard,翻译过来是高级加密标准,它是用来替代之前的 DES 加密算法的。
AES 一共有四种加密模式,分别是 ECB(电子密码本模式)、CBC(密码分组链接模式)、CFB、OFB,我们一般使用的是 CBC 模式。四种模式中除了 ECB 相对不安全之外,其它三种模式的区别并没有那么大。
1.5 RC
RC加密包括RC2,RC4,RC5,RC2 是由著名密码学家 Ron Rivest 设计的一种传统对称分组加密算法,它可作为 DES 算法的建议替代算法。它的输入和输出都是64bit。密钥的长度是从1字节到128字节可变,但目前的实现是8字节(1998年)。
参考:
加解密篇 - 对称加密算法 (DES、3DES、AES、RC)
2 前后端实现AES对称加解密方式
-
AES为对称加密算法,顾名思义,如果是前后端加解密场景,那前端需要保存一份秘钥,后端也需要保存一份秘钥,这两个秘钥是相同的,才可以实现加解密。
-
AES的秘钥默认长度为16位,初始向量 IV也是16位,这两个默认长度一定要遵守,否则会有很多不可未知的错误。如果需要增加秘钥的长度增加复杂性,则推荐使用RC加密算法,因为该算法的秘钥长度可变。
-
待解密长度需要为16的倍数,否则会报以下错误,常用的解决办法为加密后使用Base64包装密文,则会自动补齐为16的倍数,解密时先使用Base64解密,则密文一定是16的整数倍。
Input length must be multiple of 16 when decrypting with padded cipher
3 后端AES对称加解密(ECB和CBC模式)工具类
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* @author
* @description AES加解密(ECB和CBC模式)
* @date 2023/12/8 15:08
*/
public class AESEncryptUtil {
/**
* 加密模式
* ECB: AES/ECB/PKCS5Padding
* CBC: AES/CBC/NoPadding
*/
private static final String[] TRANSFORM_ALGORITHM = new String[]{"AES/ECB/PKCS5Padding", "AES/CBC/NoPadding"};
/**
* 初始向量样式 IV
*/
private static String iv = "HBJNRU56MDk4NzK6";
private static final String ALGORITHM = "AES";
private static final String CHARSET_NAME = "UTF-8";
/**
* AES加解key样式
*/
public static String ENCRYPT_KEY = "7CC408B24462ABD1";
/**
* AES的ECB模式加解
* @param data 待加密参数
* @param key 加密key
* @return
*/
public static String encryptECB(String data, String key) {
if (StringUtils.isEmpty(key)) {
throw new IllegalArgumentException("加密失败,加密key为空");
}
SecretKeySpec aesKey = new SecretKeySpec(key.getBytes(Charset.forName(CHARSET_NAME)), ALGORITHM);
try {
Cipher cipher = Cipher.getInstance(TRANSFORM_ALGORITHM[0]);
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(data.getBytes(Charset.forName(CHARSET_NAME)));
// 使用Base64来包装是规避报错Input length must be multiple of 16 when decrypting with padded cipher
// 解密的字节数组必须是16的倍数
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new IllegalArgumentException("加密失败: "+ e.getMessage());
}
}
/**
* AES的ECB模式解密
* @param data 待解密参数
* @param key 解密key
* @return
*/
public static String decryptECB(String data, String key) {
if (StringUtils.isEmpty(key)) {
throw new IllegalArgumentException("解密失败,解密key为空");
}
byte[] decode = Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8));
SecretKeySpec aesKey = new SecretKeySpec(key.getBytes(Charset.forName(CHARSET_NAME)), ALGORITHM);
try {
Cipher cipher = Cipher.getInstance(TRANSFORM_ALGORITHM[0]);
cipher.init(Cipher.DECRYPT_MODE, aesKey);
return new String(cipher.doFinal(decode));
} catch (Exception e) {
throw new IllegalArgumentException("解密失败: "+ e.getMessage());
}
}
/**
* AES的CBC模式加密
* @param data 要加密的数据
* @param key 加密key
* @return 加密的结果
*/
public static String encryptCBC(String data, String key) {
try {
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance(TRANSFORM_ALGORITHM[1]);
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyStr = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivStr = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.ENCRYPT_MODE, keyStr, ivStr);
byte[] encrypted = cipher.doFinal(plaintext);
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new IllegalArgumentException("加密失败: "+ e.getMessage());
}
}
/**
* AES的CBC模式解密
* @param data 要解密的数据
* @param key 解密key
* @return 解密的结果
*/
public static String decryptCBC(String data, String key) {
try {
byte[] encrypted1 = Base64.getDecoder().decode(data);
Cipher cipher = Cipher.getInstance(TRANSFORM_ALGORITHM[1]);
SecretKeySpec keyStr = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivStr = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, keyStr, ivStr);
byte[] original = cipher.doFinal(encrypted1);
return new String(original, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new IllegalArgumentException("解密失败: "+ e.getMessage());
}
}
public static void main(String[] args) throws Exception {
String data = "hello Test symmetric encry";
String keyStr = "7CC408B24462ABD1";
String encryDataStr = encryptECB(data, keyStr);
System.out.println("encryptECB = " + encryDataStr);
System.out.println("decryptECB = " + decryptECB(encryDataStr, keyStr));
encryDataStr = encryptCBC(data, keyStr);
System.out.println("encryptCBC = " + encryDataStr);
System.out.println("decryptCBC = " + decryptCBC(encryDataStr, keyStr));
}
}
4 前端(VUE)AES对称加解密(CBC模式)工具类
前端其他工程配置请看文章《java前后端参数和返回加密解密AES+CBC》
npm install crypto-js
4.1 aseKeConfig.js AES+CBC配置
export const AES_KEY = 'MTIzNDU2Nzg5MEFC'
export const AES_IV = 'QUJDRURGMDk4NzY1'
// 参数是否进行加密设置,需要与后端配置保持一致
export const PARAM_ENCRYPT_ABLE = true
// 结果是否进行加密
export const RESULT_ENCRYPT_ABLE = true
// 需要排除的不进行加密的接口,正则匹配
export const EXCLUE_PATH = ['.*/orc/.*', '.*/fastdfs/.*', '.*/eempFastdfs/.*', ',.*/homepage/preview', '.*oauth/getClickApplicationInfo', '.*/common/defaultKaptcha.*', '.*/autoKeyword$', '.*/getLayerCount$', '.*/getLayerInfoListByPage$',
'.*/epUiImgSaveAndAnalysisMultipartFile$', '/v7/weather']
4.2 加密解密工具类 aesSecretUtil.js
import CryptoJS from 'crypto-js'
import {
AES_KEY,
AES_IV,
PARAM_ENCRYPT_ABLE,
EXCLUE_PATH,
RESULT_ENCRYPT_ABLE
}
from '../config/aesKeyConfig.js'
const key = CryptoJS.enc.Utf8.parse(AES_KEY) // 16位
const iv = CryptoJS.enc.Utf8.parse(AES_IV)
const excluePath = EXCLUE_PATH
const paramEncryptAble = PARAM_ENCRYPT_ABLE
const resultEncryptAble = RESULT_ENCRYPT_ABLE
/**
*Description AES CBC BASE64加密解密
*@author
*@date 13:38 2022/3/31
*/
export default {
// aes加密
encrypt(word) {
let encrypted = ''
if (typeof word === 'string') {
const srcs = CryptoJS.enc.Utf8.parse(word)
encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
})
} else if (typeof word === 'object') {
// 对象格式的转成json字符串
const data = JSON.stringify(word)
const srcs = CryptoJS.enc.Utf8.parse(data)
encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
})
}
return CryptoJS.enc.Base64.stringify(encrypted.ciphertext)
},
// aes解密
decrypt(word) {
if (word) {
let base64 = CryptoJS.enc.Base64.parse(word)
let src = CryptoJS.enc.Base64.stringify(base64)
var decrypt = CryptoJS.AES.decrypt(src, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
})
var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
return decryptedStr.toString()
} else {
return word
}
},
// 判断url是否在匹配的正则表达式上,匹配则不进行加密,不配则需要加密
checkIsExcluePath(url) {
// 如果包含需要排除加密的接口返回true
let flag = false
for (let i = 0; i < excluePath.length; i++) {
if (new RegExp('^' + excluePath[i]).test(url)) {
flag = true
break
} else {
flag = false
}
}
return flag
},
// 判断是否请求需要进行加密,配置值true的时候需要加密否则不需要
checkParamEncryptAble() {
// console.log(encryptAble)
return paramEncryptAble
},
// 判断是否只对结果进行加密
checkResultEncryptAble() {
// console.log(encryptAble)
return resultEncryptAble
}
}
参考:
java前后端参数和返回加密解密
对称加密( 共享密钥密码 ) —用相同的密钥进行加密和解密
手写一个java加密工具类Securit
Java中的AES加解密(CBC模式)
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!