本文详解前端如何使用crypto-js库进行AES加解密,涵盖安装、CBC/ECB模式配置及填充处理。结合JSEncrypt库实现RSA密钥生成与交换,并通过封装Axios拦截器,在请求头中传递加密后的AES密钥与数据,实现前后端安全通信的完整流程。
引言
CryptoJS 为 JavaScript 加密算法库,旨在客户端浏览器内执行加解密任务。其涵盖 AES、DES、Triple DES、Rabbit、RC4、MD5、SHA-1 等常见算法。
AES
机制解析
AES(高级加密标准)属对称加密算法,加解密共用同一密钥。支持 128、192、256 位数据块加密,采用 128 位密钥。该算法具备固定块长与密钥长,广泛见于 SSL/TLS、SSH、IPSec 等安全协议。
AES 加密时,明文被划分为 128 位数据块,各块均使用同一密钥处理。具体流程如下:
密钥扩展:依据算法需求,将原始密钥扩展为多轮密钥。
初始轮:将明文分块后,与首轮密钥执行异或运算。
多轮加密:对初始轮结果进行多轮迭代加密,每轮应用不同轮密钥。
最终轮:末轮加密后直接输出密文,不再进行后续轮次处理。
解密流程与加密逆向对应,但需使用相同的密钥及轮密钥。鉴于 AES 为块加密算法,加密时需对数据进行填充,以保证数据块大小达到 128 位。
AES 算法优势
高安全性:AES 为可靠加密方案,广泛应用于数据传输、文件加密及网络安全领域。
高执行效率:AES 采用对称加密机制,加解密共用密钥。相较于非对称加密,对称算法效率更高,故 AES 具备更优性能。
应用普及:AES 在数据传输、文件加密及网络安全中应用广泛。数据传输时加密可保障数据安全;文件加密可保护文件内容;网络安全领域加密网络数据可维护网络环境安全。
github 地址: https://github.com/brix/crypto-js
cryptojs 文档: https://cryptojs.gitbook.io/docs/#encoders
在线 aes 加密解密工具: http://tool.chacuo.net/cryptaes
安装配置
script 标签嵌入方式
<script src="https://cdn.bootcdn.net/ajax/libs/crypto-js/4.1.1/aes.min.js"></script>
npm 或 yarn 安装方式
npm install crypto-js
yarn add crypto-js
CommonJS 模块引用
const CryptoJS = require('crypto-js');
ES module 模块引用:
import CryptoJS from 'crypto-js';
加解密功能封装
import CryptoJS from 'crypto-js'
// ------------AES-------------
function getAesString(data, key, iv) { //加密
let keys = CryptoJS.enc.Utf8.parse(key)
let vis = CryptoJS.enc.Utf8.parse(iv)
let encrypt = CryptoJS.AES.encrypt(data, keys, {
iv: vis, //iv偏移量 CBC需加偏移量
mode: CryptoJS.mode.CBC, //CBC模式
// mode: CryptoJS.mode.ECB, //ECB模式
padding: CryptoJS.pad.Pkcs7 //padding处理
});
// debugger
return encrypt.toString(); //加密完成后,转换成字符串
}
function getDAesString(encrypted, key, iv) { // 解密
var key = CryptoJS.enc.Utf8.parse(key);
var iv = CryptoJS.enc.Utf8.parse(iv);
var decrypted =CryptoJS.AES.decrypt(encrypted,key,{
iv:iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
// AES 对称秘钥加密
const aes = {
en: (data, key) => getAesString(data, key.key, key.iv),
de: (data, key) => getDAesString(data, key.key, key.iv)
};
// BASE64
const base64 = {
en: (data) => CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(data)),
de: (data) => CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8)
};
// SHA256
const sha256 = (data) => {
return CryptoJS.SHA256(data).toString();
};
// MD5
const md5 = (data) => {
return CryptoJS.MD5(data).toString();
};
export { aes, md5, sha256, base64 };利用 JSEncrypt 生成密钥
JSEncet 是基于 JavaScript 的 RSA 加密库,支持浏览器端 RSA 加解密操作。其提供易用 API,简化客户端加密流程。
JSEncrypt 支持以下功能:
生成密钥对:利用 JSEncrypt 生成 RSA 公钥与私钥对。
加密:使用公钥加密数据,确保仅持有私钥的服务器可解密。
解密:使用私钥解密由公钥加密的数据。
密钥格式:JSEncrypt 兼容多种密钥格式,含 PEM 格式(基于 Base64 编码)。
使用 JSEncrypt 进行 RSA 加密的基本步骤如下:
引入 JSEncrypt 库:在 HTML 页面中加载 JSEncrypt 脚本文件。
创建 JSEncrypt 对象:通过构造函数实例化新的 JSEncrypt 对象。
生成密钥对:封装 getKey() 方法生成 RSA 密钥对,并将生成的私钥加密后传输至后端。
加密数据:调用 encrypt.encrypt(plainText) 方法,对明文数据进行加密处理。
解密数据:在服务器端使用私钥进行解密,还原原始数据。
需注意,JSEncrypt 在客户端执行,密钥传输过程可能存在安全风险。为确保数据安全,建议客户端与服务器间使用安全通信协议传输数据,并在服务器端进行额外安全验证与处理。
import JSEncrypt from 'jsencrypt'
const encrypt = new JSEncrypt();
let publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeCDcnFrS7DIRbvZLHreVUzaMbAFy2DYmioxBK606urY4rVR8IgLgUhnyw2/GQ99pyr8lGtqPeOoapantw1XwEVyi74MDxs4UDL8j4OZR1Es7HVGOB0GwKWobdU9cm/1iDwGyouSmijxKyAePg6KsLNgbjDPYZRS11bYEuZ8/RLQIDAQAB';
encrypt.setPublicKey('-----BEGIN PUBLIC KEY-----' + publicKey + '-----END PUBLIC KEY-----')
const random = (length) => {
var str = Math.random().toString(36).substr(2);
if (str.length>=length) {
return str.substr(0, length);
}
str += random(length-str.length);
return str;
}
export const rsaEncode = (src)=>{
let data = encrypt.encrypt(src); // 加密
return data
};
export const getKey = ()=>{
let key = { key: random(16), iv: random(16) }; // 生成密钥
let code = rsaEncode(key.key + ',' + key.iv) // 给密钥加密,在将加密后的密钥传给后端
window.codeArr = window.codeArr || {}
codeArr[code] = key
return {
key, code
}
};
export const getAesKey = (aes)=>{
let key = JSON.parse(JSON.stringify(codeArr[aes]))
delete codeArr[aes]
return key
};
window.getKey = getKey
window.rsaEncode = rsaEncode
publicKey 可通过请求后端获取,亦可自行定义,需确保前后端一致。
封装 axios 拦截器以执行加解密
/**
*
* http配置
*
*/
// 引入axios以及element ui中的loading和message组件
import { aes } from "@/util/encrypt.js";
import { getKey, getAesKey } from "@/config/key.js";
import axios from "axios";
import store from "../store";
import router from "../router/router";
import { Loading, Message } from "element-ui";
import { getSessStore, setSessStore } from "@/util/store";
// 超时时间
if (store.online) axios.defaults.timeout = 20000;
else axios.defaults.timeout = 0;
//跨域请求,允许保存cookie
axios.defaults.withCredentials = true;
// 统一加解密
const Unify = {
// 统一加密方法
en(data, key) {
// 1.aes加密
let aesStr = aes.en(JSON.stringify(data), key);
return aesStr;
},
// 统一解密
de(aesStr, key) {
// 1.aes解密
let dataStr = aes.de(aesStr, key);
// 3.转json对象
let data = JSON.parse(dataStr);
return data;
},
};
let loadinginstace;
let cfg, msg;
msg = "服务器君开小差了,请稍后再试";
function ens(data) {
// debugger
let src = [...data];
src = JSON.stringify(src);
let dataJm = aes.en(src);
return dataJm;
}
function des(data) {
// debugger
let src = [...data];
let dataJm = aes.de(src);
dataJm = JSON.parse(dataJm);
return dataJm;
}
const cancelToken = axios.CancelToken
const source = cancelToken.source()
//HTTPrequest拦截
axios.interceptors.request.use(
function (config) {
console.log(config.data, "加密前入参---");
config.cancelToken = source.token; // 全局添加cancelToken
loadinginstace = Loading.service({
fullscreen: true,
});
if (store.getters.token) {
let info = getSessStore("token");
// console.log("info", info);
config.headers["Authorization"] = "Bearer " + info; // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
}
const contenttype = config.headers.common["Accept"];
let types = contenttype.includes("application/json");
let key = getKey(); // 获取密钥
config.headers["aes"] = key.code; // 将 aes 的 code 设置到请求头,传给后端解密
config.headers["name"] = "send";
if (types) {
if (config.method == "post" || config.method == "put") {
if (config.data) {
config.headers["crypto"] = true;
config.headers["content-type"] = "application/json";
let data = {
body: config.data,
};
let dataJm = Unify.en(data, key.key); // 加密 post 请求参数
config.data = dataJm;
}
}
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
//HTTPresponse拦截
axios.interceptors.response.use(
(response) => {
loadinginstace.close();
let res = response.data || {};
if (response.headers["date"]) {
store.commit("setServiceTime", response.headers.date);
}
if (res.crypto) {
try {
let key = getAesKey(response.headers.aes); /// 拿到公钥加密字符串
if (!key) {
message("获取密钥异常", "error");
return Promise.reject(res);
}
// debugger
res = Unify.de(res.body, key);
response.data = res;
} catch (err) {
message("系统异常:" + err.message, "error");
return Promise.reject(err);
}
}
// debugger
if (res.code === 1) {
message(res.msg, "error");
return Promise.reject(res);
}
console.log(response, "解密后response");
return response;
},
(error) => {
console.log("错误信息", error);
loadinginstace.close();
const res = error.response || {};
if (res.status === 478 || res.status === 403 || res.status === 401) {
let resMsg = res.data.msg ? res.data.msg : res.data.data
if (res.status === 403) {
message('服务授权失败,请联系管理添加权限!', "error");
} else {
message(resMsg, "error");
}
let flg = res.data.msg.includes('当前登录状态已失效')
if (res.status === 478 && flg) {
//token失效
source.cancel('登录信息已过期'); // 取消其他正在进行的请求
store.dispatch("FedLogOut").then(() => {
router.push("/login"); ///test
});
}
} else if (res.status === 400) {
message(res.data.error_description, "error");
} else if (res.status === 202) {
//三方未绑定
this.$router.push({
path: "/",
});
} else if (res.status === 503 || res.status === 504) {
//服务异常
message(res.data, "error");
} else if (
(res.status === 401 && res.statusText == "Unauthorized") ||
res.data.error == "invalid_token" ||
res.data.error == "unauthorized"
) {
//token失效
store.dispatch("FedLogOut").then(() => {
router.push("/login"); ///test
});
} else {
message(res.data.message, "error");
}
return Promise.reject(error);
}
);
export function message(text, type) {
let t = text ? text : "服务或网络异常!"
Message({
message: t,
type: type,
duration: 30 * 1000,
center: true,
showClose: true
});
}
export default axios;总结
加密:使用 JSEncrypt 生成私钥 key 并将密钥 key 加密得到 code, 使用 CryptoJS.AES.encrypt() 和 key 加密请求数据,将 加密后的 code 设置在请求头,后端获取加密后 code 进行解密得到私钥 key ,再对请求数据解密得到原始数据
解密:前端获取响应头的key,通过解密 JSEncrypt 解密得到私钥 key, 使用 CryptoJS.AES.decrypt() 方法对响应数据进行解密,得到原始数据
至此,关于前端使用 crypto-js 库 aes 加解密的文章介绍完毕,更多相关前端 crypto-js 库 aes 加解密内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!