AES加密解密

774 阅读4分钟

个人笔记-仅供参考

AES 算法

防止信息泄露
防止数据篡改
简单使用

一、AES -> ECB

这种模式是将整个明文分成若干段相同的小段,然后对每一小段进行加密。

image.png

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

这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

image.png

// 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();
}

引用文章:blog.csdn.net/You_got_thi…