AES 算法
防止信息泄露
防止数据篡改
简单使用
一、AES -> ECB
这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。
1.JS -> CryptoJS
CryptoJS 包
2.Vue -> CryptoJS
npm install crypto-js
// const CryptoJS = require('./crypto-js.js'); // js
import CryptoJS from 'crypto-js'; // vue
const key = '16位'
// 加密
function ECBEncryt (plaint) {
const aesKey = CryptoJS.enc.Base64.parse(key);
const aesPlaint = CryptoJS.enc.Utf8.parse(plaint);
const encryted = CryptoJS.AES.encrypt(aesPlaint, aesKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
return encryted.toString();
}
// 解密
function ECBDecrypt (cipher) {
const aesKey = CryptoJS.enc.Base64.parse(key);
const aesDecrypt = CryptoJS.AES.decrypt(cipher, aesKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return CryptoJS.enc.Utf8.stringify(aesDecrypt)
}
2.JavaScript -> CryptoJS
二、AES -> CBC
这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。
// CBC 加密
CBCEncrypt: function (key, iv, data) {
// 统一将传入的字符串转成UTF8编码
const dataHex = CryptoJS.enc.Utf8.parse(data); // 需要加密的数据
const keyHex = CryptoJS.enc.Utf8.parse(key); // 秘钥
const ivHex = CryptoJS.enc.Utf8.parse(iv); // 偏移量
const encrypted = CryptoJS.AES.encrypt(dataHex, keyHex, {
iv: ivHex,
mode: CryptoJS.mode.CBC, // 加密模式
padding: CryptoJS.pad.Pkcs7,
});
let encryptedVal = encrypted.ciphertext.toString();
return encryptedVal; // 返回加密后的值
},
// CBC 解密
CBCDecrypt: function (key, iv, encryptedVal) {
// 传入的key和iv需要和加密时候传入的key一致
// 统一将传入的字符串转成UTF8编码
let encryptedHexStr = CryptoJS.enc.Hex.parse(encryptedVal);
let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
const keyHex = CryptoJS.enc.Utf8.parse(key); // 秘钥
const ivHex = CryptoJS.enc.Utf8.parse(iv); // 偏移量
let decrypt = CryptoJS.AES.decrypt(srcs, keyHex, {
iv: ivHex,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
return decryptedStr.toString();
},
三、AES -> GCM
GCM是认证加密模式中的一种,能同时确保数据的保密性、完整性及真实性
1. 使用node.js crypto模块
import crypto from 'crypto';
const keyStr = '82c22b07c5654dcf';
const gcmIv = 'qwerty1234rfidjs';
const gcmType = {
'16': 'aes-128-gcm',
'24': 'aes-192-gcm',
'32': 'aes-256-gcm',
}
// 加密
export function encrypt(word) {
if (!word) return '';
if (typeof word != 'string') word = JSON.stringify(word);
const algorithm = gcmType[keyStr.length];
const cipher = crypto.createCipheriv(algorithm, keyStr, gcmIv);
const encrypted = cipher.update(word, 'utf8');
const finalstr = cipher.final();
const tag = cipher.getAuthTag();
const res = Buffer.concat([encrypted, finalstr, tag]);
return res.toString('base64');
}
// 解密
export function decrypt(word) {
if (!word) return '';
const algorithm = aesObj[keyStr.length];
const decipher = crypto.createDecipheriv(algorithm, keyStr, gcmIv);
const b = Buffer.from(word, 'base64');
decipher.setAuthTag(b.subarray(b.length - 16));
const str = decipher.update(Buffer.from(b.subarray(0, b.length - 16), 'hex'));
const fin = decipher.final();
const decryptedStr = new TextDecoder('utf8').decode(Buffer.concat([str, fin]))
try {
return JSON.parse(decryptedStr);
} catch (e) {
return decryptedStr
}
}
上面的方法 key 值,没有进行md5加密,会导致后台解析失败。 最终版:
import crypto from 'crypto' //crypto是nodejs内置模块
const iv = "0123456789ABCDEF";
//加密方法
function encodeAes(word, aesKey) {
if (!word) return '';
if (typeof word != 'string') word = JSON.stringify(word);
const md5 = crypto.createHash('md5');
const result = md5.update(aesKey).digest();
const cipher = crypto.createCipheriv('aes-128-gcm', result, iv);
const encrypted = cipher.update(word, 'utf8');
const finalstr = cipher.final();
const tag = cipher.getAuthTag();
const res = Buffer.concat([encrypted, finalstr, tag]);
return res.toString('base64');
}
//解密方法
function decodeAes(word, aesKey) {
if (!word) return '';
const md5 = crypto.createHash('md5');
const result = md5.update(aesKey).digest();
const decipher = crypto.createDecipheriv('aes-128-gcm', result, iv);
const b = Buffer.from(word, 'base64')
decipher.setAuthTag(b.subarray(b.length - 16));
const str = decipher.update(Buffer.from(b.subarray(0, b.length - 16), 'hex'));
const fin = decipher.final();
const decryptedStr = new TextDecoder('utf8').decode(Buffer.concat([str, fin]))
try {
return JSON.parse(decryptedStr);
} catch (e) {
return decryptedStr
}
}
export default {
encodeParams(origin, aesKey) {
if (!origin) origin = {};
encodeAes(origin, aesKey);
},
decodeParams(parameters, aesKey) {
if (!parameters) return "";
return decodeAes(parameters, aesKey);
}
}
import java.security.MessageDigest;
import java.security.Security;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class GCMUtil {
private static final String IV = "0123456789ABCDEF";
private static final String ALGORITHMSTR = "AES/GCM/NoPadding";
private static final String DEFAULT_CODING = "utf-8";
/**
* 如果报错java.security.NoSuchProviderException: no such provider: BC,那么需要加上这一段,同时需要bcprov-jdk15on.jar
*/
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* 加密
* @param content
* @param encryptKey
* @param iv
* @return
* @throws Exception
*/
public static String aesEncrypt(String content, String encryptKey) throws Exception {
byte[] input = content.getBytes(DEFAULT_CODING);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(encryptKey.getBytes(DEFAULT_CODING));
SecretKeySpec skc = new SecretKeySpec(thedigest, "AES");
IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes(DEFAULT_CODING));
Cipher cipher = Cipher.getInstance(ALGORITHMSTR, "BC");
cipher.init(Cipher.ENCRYPT_MODE, skc, ivspec);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
return Base64.getEncoder().encodeToString(cipherText);
}
/**
* 解密
* @param tmp
* @param decryptKey
* @param iv
* @return
* @throws Exception
*/
public static String aesDecrypt(String tmp, String decryptKey) throws Exception {
byte[] keyb = decryptKey.getBytes(DEFAULT_CODING);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes(DEFAULT_CODING));
Cipher dcipher = Cipher.getInstance(ALGORITHMSTR, "BC");
dcipher.init(Cipher.DECRYPT_MODE, skey, ivspec);
byte[] clearbyte = dcipher.doFinal(Base64.getDecoder().decode(tmp));
return new String(clearbyte, DEFAULT_CODING);
}
public static void main(String[] args) throws Exception {
final String key = "alckdirtjgfl0tig";
String origin = "abcdefggghhhiiiijjjjjj中文测试";
String encryptstr = aesEncrypt(origin, key);
System.out.println(encryptstr);
String decryptstr= aesDecrypt(encryptstr, key);
System.out.println(decryptstr);
}
}
2. 使用node-forge库
由于纯HTML中无法使用ndeJs中的api
这时候我们需要用到一个库:node-forge(js, vue中都可使用)
npm地址:node-forge - npm
github地址:github.com/digitalbaza…
html/js引用
<script src ="https://unpkg.com/node-forge@1.0.0/dist/forge.min.js "></script>
// 加密
function encrypt(someBytes) {
// 生成随机iv 12字节
var iv = forge.random.getBytesSync(12);
// 生成AES-GCM模式的cipher对象 并传入密钥
var cipher = forge.cipher.createCipher('AES-GCM', keyStr);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(forge.util.encodeUtf8(someBytes)));
cipher.finish();
var encrypted = cipher.output;
var tag = cipher.mode.tag;
return btoa(iv+encrypted.data+tag.data)
}
// 解密
function decrypt(someBytes) {
someBytes = atob(someBytes)
const iv = someBytes.slice(0, 12)
const tag = someBytes.slice(-16)
const data = someBytes.slice(12, someBytes.length - 16)
var decipher = forge.cipher.createDecipher('AES-GCM', keyStr)
decipher.start({iv: iv, tag: tag });
decipher.update(forge.util.createBuffer(data))
const pass = decipher.finish()
if (pass) return decipher.output.toString();
}
vue引用
// 安装
npm install node-forge
// 引入
import forge from 'node-forge'
// 加密
export function encrypt(word) {
// 生成随机iv 12字节
var iv = forge.random.getBytesSync(12);
// 生成AES-GCM模式的cipher对象 并传入密钥
var cipher = forge.cipher.createCipher('AES-GCM', keyStr);
cipher.start({iv: iv})
cipher.update(forge.util.createBuffer(forge.util.encodeUtf8(word)))
cipher.finish()
var encrypted = cipher.output
var tag = cipher.mode.tag
return window.btoa(iv + encrypted.data + tag.data)
}
// 解密
export function decrypt(datamsg) {
datamsg = window.atob(datamsg)
const iv = datamsg.slice(0, 12)
const tag = datamsg.slice(-16)
const data = datamsg.slice(12, datamsg.length - 16)
var decipher = forge.cipher.createDecipher('AES-GCM', keyStr)
decipher.start({ iv: iv, tag: tag })
decipher.update(forge.util.createBuffer(data))
const pass = decipher.finish()
if (pass) return decipher.output.toString();
}