实现AES CBC模式后端加密 前端解密
1.封装加密返回体
package com.xxx.common.core.core.domain;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
@Data
@NoArgsConstructor
public class EncryptedR<T> {
private int code;
private String msg;
private String data; // 加密后的数据
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static final String SECRET_KEY = "rK7CLfWzZqFmYlJQ6oVpXQ=="; // 必须是16字节
private static final String IV = "aB3#cD9!xY7@kL2$"; // 必须是16字节
public static EncryptedR error(String msg,int code) {
EncryptedR result = new EncryptedR<>();
result.code = code;
result.msg = msg;
return result;
}
/**
* 加密布尔值
*/
public static String encryptBoolean(boolean value) {
return encrypt(Boolean.toString(value));
}
/**
* 加密字符串(使用AES/CBC/PKCS5Padding)
*/
public static String encrypt(String plainText) {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("加密失败", e);
}
}
/**
* 解密字符串(使用AES/CBC/PKCS5Padding)
*/
public static String decrypt(String encryptedText) {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);
byte[] decrypted = cipher.doFinal(decodedBytes);
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("解密失败", e);
}
}
public static void main(String[] args) throws NoSuchAlgorithmException {
String plainText = "123456";
String encrypted = encrypt(plainText);
System.out.println("加密结果:" + encrypted); // 输出类似:eYIQuRPS54iO2+qLft7GVQ==
System.out.println("解密结果:" + decrypt(encrypted));
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, new SecureRandom()); // AES-128
SecretKey secretKey = kg.generateKey();
String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded());
System.out.println("Base64 Encoded SecretKey (16 bytes): " + base64Key);
// IV 可以随机生成
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
String ivBase64 = Base64.getEncoder().encodeToString(iv);
System.out.println("Base64 Encoded IV (16 bytes): " + ivBase64);
}
public static <T> EncryptedR<T> ok(T data) {
EncryptedR<T> result = new EncryptedR<>();
result.code = 200;
result.msg = "success";
try {
if (data instanceof Boolean) {
result.data = encryptBoolean((Boolean) data);
} else {
String jsonData = new ObjectMapper().writeValueAsString(data);
result.data = encrypt(jsonData);
}
} catch (JsonProcessingException e) {
throw new RuntimeException("序列化数据失败", e);
}
return result;
}
}
2. 前端页面案例
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>AES CBC 解密测试</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script> <style> body { font-family: Arial, sans-serif; padding: 20px; } input, button { padding: 8px; margin-top: 10px; width: 100%; max-width: 400px; box-sizing: border-box; } #result { margin-top: 20px; white-space: pre-wrap; color: green; } #error { margin-top: 20px; color: red; } </style> </head> <body> <h2>前端 AES CBC 解密测试页面</h2> <label for="encrypted">请输入加密数据 (Base64):</label><br/> <input type="text" id="encrypted" value="eYIQuRPS54iO2+qLft7GVQ==" /> <br/> <button onclick="decrypt()">解密</button> <div id="result"></div> <div id="error"></div> <script> const SECRET_KEY = "rK7CLfWzZqFmYlJQ6oVpXQ=="; // 必须是16字节 const IV = "aB3#cD9!xY7@kL2$"; // 必须是16字节 function decrypt() { const encrypted = document.getElementById("encrypted").value.trim(); const resultDiv = document.getElementById("result"); const errorDiv = document.getElementById("error"); resultDiv.textContent = ""; errorDiv.textContent = ""; if (!encrypted) { errorDiv.textContent = "请输入加密字符串"; return; } try { const key = CryptoJS.enc.Utf8.parse(SECRET_KEY); const iv = CryptoJS.enc.Utf8.parse(IV); const decrypted = CryptoJS.AES.decrypt(encrypted, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); const decryptedText = decrypted.toString(CryptoJS.enc.Utf8); if (!decryptedText) { throw new Error("解密结果为空,请检查密钥、IV或加密数据是否正确"); } resultDiv.textContent = "解密成功:" + decryptedText; } catch (e) { errorDiv.textContent = "解密失败:" + e.message; } } </script> </body> </html>
3.接口

4.需要注意的是我使用的是AES-128 加密算法,密钥长度必须是16 字节(128 位)

为什么 IV 也必须是 16 字节?
AES 是一种 分组加密算法(Block Cipher)
使用 CBC 模式时,每个明文块会与前一个密文块进行异或操作
第一个块没有前一块,所以需要一个初始化向量(IV)来替代
AES 的块大小固定为 128 位(16 字节)
所以:IV 必须等于块大小,也就是 16 字节

浙公网安备 33010602011771号