说明:使用aes对数据进行加密解密,对aes的秘钥进行rsa加密解密。前端获取后台公钥(调用获取公钥接口,后台会自动生成)并保存;前端加密:前台发起请求如果要加密的话在headers中加上isEncrypt属性,经过axios请求拦截器生成rsa公钥私钥队,并将公钥传给后端(后端使用前端公钥进行加密),然后再生成aes秘钥,使用aes秘钥对参数进行加密传给后端,使用之前获取的后端公钥对aes秘钥进行加密并传给后端;前端解密:使用前端私钥对后端传来解密aes秘钥解密,在使用aes秘钥对数据进行解密。

一、vue 前端aes和rsa生成工具类

import JSEncrypt from 'jsencrypt'
import CryptoJS from 'crypto-js'
import get from 'core-js/library/fn/reflect/get'
import { resolve } from 'core-js/library/es6/promise'
import { reject } from 'core-js/fn/promise'

// 加密
export function rsaEncrypt(txt,afterPublicKey) {
	
	const encryptor = new JSEncrypt()
	encryptor.setPublicKey(afterPublicKey) // 设置公钥
	return encryptor.encrypt(txt) // 对数据进行加密
}

// 解密
export function rsaDecrypt(txt,frontPrivateKey) {
	const encryptor = new JSEncrypt()
	encryptor.setPrivateKey(frontPrivateKey) // 设置私钥
	return encryptor.decrypt(txt) // 对数据进行解密
}

export function aesEncrypt(keyStr, word) {
	// 设置一个默认值,如果第二个参数为空采用默认值,不为空则采用新设置的密钥
	var key = CryptoJS.enc.Utf8.parse(keyStr);
	var srcs = CryptoJS.enc.Utf8.parse(word);
	var encrypted = CryptoJS.AES.encrypt(srcs, key, {
		mode: CryptoJS.mode.ECB,
		padding: CryptoJS.pad.Pkcs7
	});
	return encrypted.toString();
}

export function aesDecrypt(keyStr, word) {
	var key = CryptoJS.enc.Utf8.parse(keyStr);
	var decrypt = CryptoJS.AES.decrypt(word, key, {
		mode: CryptoJS.mode.ECB,
		padding: CryptoJS.pad.Pkcs7
	});
	return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}

/**
 * 获取32位随机码AES
 * @returns {string}
 */
export function get32RandomNum() {
	var chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
		'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e',
		'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
	];
	var nums = "";
	for (var i = 0; i < 32; i++) {
		var id = parseInt(Math.random() * 61);
		nums += chars[id];
	}
	return nums;
}

//获取密钥对
export  function getRsaKeys(){
	return new Promise((resolve,reject)=>{
		window.crypto.subtle.generateKey(
		    {
		        name: "RSA-OAEP",
		        modulusLength: 2048, //can be 1024, 2048, or 4096
		        publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
		        hash: {name: "SHA-512"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
		    },
		    true, //whether the key is extractable (i.e. can be used in exportKey)
		    ["encrypt", "decrypt"] //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
		).then(function(key){
		    window.crypto.subtle.exportKey(
		        "pkcs8", 
		        key.privateKey 
		    ).then(function(keydata1){
		        window.crypto.subtle.exportKey(
		            "spki",
		            key.publicKey 
		        ).then(function(keydata2){
		            var privateKey = RSA2text(keydata1,1);
		            var publicKey = RSA2text(keydata2);
		            resolve({privateKey,publicKey})
		        }).catch(function(err){
		            reject(err)
		        });
		    })
		    .catch(function(err){
		         reject(err)
		    });
		})
		.catch(function(err){
		     reject(err)
		});
	})
   
}
function RSA2text(buffer,isPrivate=0) {
        var binary = '';
        var bytes = new Uint8Array(buffer);
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        var base64 = window.btoa(binary);

       let text =base64.replace(/[^\x00-\xff]/g,"$&\x01").replace(/.{64}\x01?/g,"$&\n");
    
        return text;
}

二、 axios 配置拦截

import axios from 'axios'
import {
	getRsaKeys,
	rsaEncrypt,
	rsaDecrypt,
	aesDecrypt,
	aesEncrypt,
	get32RandomNum
} from '../utils/jsEncryptUtils'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
let frontPrivateKey
const request = axios.create({
	baseURL: 'http://localhost:8090'

})
request.interceptors.request.use(async config => {
	if (config.method !== 'post' && config.method !== 'put') {
		return config
	}

	if (!config.headers["isEncrypt"]) {
		return config
	}
	const {
		privateKey,
		publicKey
	} = await getRsaKeys()
	frontPrivateKey = privateKey
	let afterPublicKey = localStorage.getItem("afterPublicKey")
	let aesKey = get32RandomNum()
	let aesKeyByRsa = rsaEncrypt(aesKey, afterPublicKey)
	let data = aesEncrypt(aesKey, JSON.stringify(config.data))
	config.data = {
		data: data,
		aeskey: aesKeyByRsa,
		frontPublicKey: publicKey
	}

	return config


})
request.interceptors.response.use(res => {
	let aesKeyByRsa = res.data.data.aesKeyByRsa
	if (!aesKeyByRsa) {
		return res
	}
	let aesKey = rsaDecrypt(aesKeyByRsa, frontPrivateKey)
	res.data.data = JSON.parse(aesDecrypt(aesKey,res.data.data.data));
	return res
})
export default request

三、后台公钥获取接口

 @GetMapping("/getPublicKey")
    public R<?> getPublicKey() throws Exception {
        String publicKey = RSAUtils.getPublicKey();
        if (publicKey == null) {
            return R.ok(RSAUtils.genKeyPair().get("publicKey"));
        }
      return R.ok(publicKey);
    }

四、aesutils

package com.qi.demo1.utils;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Random;

/**
 * @author qyb
 * @version 1.0
 * @date 2022/8/16-16:14
 */
public class AESUtils {
    /**
     * 加密算法AES
     */
    private static final String KEY_ALGORITHM = "AES";

    /**
     * key的长度,Wrong key size: must be equal to 128, 192 or 256
     * 传入时需要16、24、36
     */
    private static final int KEY_LENGTH = 32 * 8;

    /**
     * 算法名称/加密模式/数据填充方式
     * 默认:AES/ECB/PKCS5Padding
     */
    private static final String ALGORITHMS = "AES/ECB/PKCS5Padding";

    /**
     * 后端AES的key,由静态代码块赋值
     */
    public static String key;


    static {
        key = getKey();
    }

    /**
     * 获取key
     */
    public static String getKey() {
        int length = KEY_LENGTH / 8;
        StringBuilder uid = new StringBuilder(length);
        //产生32位的强随机数
        Random rd = new SecureRandom();
        for (int i = 0; i < length; i++) {
            //产生0-2的3位随机数
            switch (rd.nextInt(3)) {
                case 0:
                    //0-9的随机数
                    uid.append(rd.nextInt(10));
                    break;
                case 1:
                    //ASCII在65-90之间为大写,获取大写随机
                    uid.append((char) (rd.nextInt(26) + 65));
                    break;
                case 2:
                    //ASCII在97-122之间为小写,获取小写随机
                    uid.append((char) (rd.nextInt(26) + 97));
                    break;
                default:
                    break;
            }
        }
        return uid.toString();
    }

    /**
     * AES 加密
     *
     * @param content    加密的字符串
     * @param encryptKey key值
     */
    public static String encrypt(String content, String encryptKey) throws Exception {
        //设置Cipher对象
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM));

        //调用doFinal
        // 转base64
        return Base64.encodeBase64String(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));

    }

    /**
     * AES 解密
     *
     * @param encryptStr 解密的字符串
     * @param decryptKey 解密的key值
     */
    public static String decrypt(String encryptStr, String decryptKey) throws Exception {
        //base64格式的key字符串转byte
        byte[] decodeBase64 = Base64.decodeBase64(encryptStr);


        //设置Cipher对象
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM));
        //调用doFinal解密
        return new String(cipher.doFinal(decodeBase64));
    }
}

五、rsautils

package com.qi.demo1.utils;


import org.apache.commons.codec.binary.Base64;


import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author qyb
 * @version 1.0
 * @date 2022/8/16-17:13
 */
public class RSAUtils {


    /**
     * 加密算法RSA
     */
    private static final String KEY_ALGORITHM = "RSA";

    /**
     * 算法名称/加密模式/数据填充方式
     * 默认:RSA/ECB/PKCS1Padding
     */
    private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding";

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 245;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 256;

    /**
     * RSA 位数 如果采用2048 上面最大加密和最大解密则须填写:  245 256
     */
    private static final int INITIALIZE_LENGTH = 2048;

    /**
     * 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值
     */
    private static final Map<String, String> map = new LinkedHashMap<>(2);

    /**
     * 生成密钥对(公钥和私钥)
     */

    public static Map<String,String> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(INITIALIZE_LENGTH);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 获取公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 获取私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到公钥字符串
        String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
        // 得到私钥字符串
        String privateKeyString = Base64.encodeBase64String((privateKey.getEncoded()));
        map.put("publicKey",publicKeyString);
        map.put("privateKey",privateKeyString);
        return map;
    }
    public static  String getPrivateKey(){
        return map.get("privateKey");
    }
    public  static  String getPublicKey(){
        return  map.get("publicKey");
    }
    /**
     * RSA私钥解密
     * @param data BASE64编码过的密文
     * @param privateKey 私钥(BASE64编码)
     * @return utf-8编码的明文
     */
    public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        //base64格式的key字符串转Key对象
        Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.DECRYPT_MODE, privateK);

        //分段进行解密操作
        return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
    }

    /**
     * RSA公钥加密
     * @param data BASE64编码过的密文
     * @param publicKey 公钥(BASE64编码)
     * @return utf-8编码的明文
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        //base64格式的key字符串转Key对象
        Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        //分段进行加密操作
        return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
    }

    /**
     * RSA公钥解密
     * @param data BASE64编码过的密文
     * @param publicKey RSA公钥
     * @return utf-8编码的明文
     */
    public static byte[] pubKeyDec(byte[] data, String publicKey) throws Exception {
        //base64格式的key字符串转Key对象
        Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.DECRYPT_MODE, privateK);

        //分段进行解密操作
        return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
    }


    /**
     * RSA私钥加密
     * @param data 待加密的明文
     * @param privateKey RSA私钥
     * @return  经BASE64编码后的密文
     */
    public static byte[] privKeyEnc(byte[] data, String privateKey) throws Exception {

        //base64格式的key字符串转Key对象
        Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.ENCRYPT_MODE, publicK);

        //分段进行加密操作
        return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 分段进行加密、解密操作
     */
    private static byte[] encryptAndDecryptOfSubsection(byte[] data, Cipher cipher, int encryptBlock) throws Exception {
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > encryptBlock) {
                cache = cipher.doFinal(data, offSet, encryptBlock);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * encryptBlock;
        }
        out.close();
        return out.toByteArray();
    }

}

六、自定义加密解密注解

package com.qi.demo1.annotation;

import org.springframework.data.elasticsearch.annotations.Document;

import java.lang.annotation.*;

/**
 * @author qyb
 * @version 1.0
 * @date 2022/8/17-9:51
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptAndDecrypt {
    /**
     * 是否加密 默认为加密
     *
     */
    boolean isEncrypt() default true;

    /**
     *
     * 是否解密 默认为解密
     */
    boolean isDecrypt() default  true;
}

七、aop 拦截处理加密解密(前后端都是使用application/json)

package com.qi.demo1.aspect;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.qi.demo1.annotation.EncryptAndDecrypt;

import com.qi.demo1.common.ApiEncryptRes;
import com.qi.demo1.common.R;
import com.qi.demo1.utils.ApiSecurityUtils;
import com.qi.demo1.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

/**
 * @author qyb
 * @version 1.0
 * @date 2022/8/17-9:54
 */
@Slf4j
@Component
@Aspect
public class EncryptAndDecryptAspect {
    @Around("@annotation(encryptAndDecrypt)")
    public Object doAround(ProceedingJoinPoint point, EncryptAndDecrypt encryptAndDecrypt) throws Throwable {
        HttpServletRequest request = ServletUtils.getRequest();
        String method = request.getMethod().toUpperCase();
        Object[] args = point.getArgs();
        String frontPublicKey="";
        if ("POST".equals(method) || "PUT".equals(method)) {
//            解密
            if (encryptAndDecrypt.isDecrypt()) {
                if (args.length > 0) {
                    // TODO: 2022/8/17 解密
                    String bodyString = getBodyString(request);
                    JSONObject jsonObject = JSONObject.parseObject(bodyString);
                    String aesKey = (String) jsonObject.get("aeskey");
                    frontPublicKey = (String) jsonObject.get("frontPublicKey");
                    String data = (String) jsonObject.get("data");
                    if (StringUtils.isNotEmpty(aesKey)) {
                        String decrypt = ApiSecurityUtils.decrypt(aesKey, data);
                        args[0] = JSON.parseObject(decrypt, args[0].getClass());
                        log.info("解密成功");
                    } else {
                        args[0] = JSON.parseObject(data, args[0].getClass());
                    }
                }
            }
        }
//       执行方法
        Object object = point.proceed(args);
        if (encryptAndDecrypt.isEncrypt()) {
            R<?> r = (R<?>) object;
            Object data = r.getData();
            if (StringUtils.isEmpty(frontPublicKey) || data == null) {
                return object;
            }
            ApiEncryptRes apiEncryptRes = ApiSecurityUtils.encrypt(JSON.toJSONString(data), frontPublicKey);
            log.info("加密成功");
            return R.ok(apiEncryptRes);
        }
        return object;
    }

    private String getBodyString(HttpServletRequest request) {
        BufferedReader bufferedReader;
        StringBuilder sb = new StringBuilder();
        try (InputStream inputStream = request.getInputStream()) {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
}

八、apiSecurityUtils

package com.qi.demo1.utils;

import com.qi.demo1.common.ApiEncryptRes;
import org.apache.commons.codec.binary.Base64;


/**
 * @author qyb
 * @version 1.0
 * @date 2022/8/17-15:40
 */
public class ApiSecurityUtils {
    /**
     *
     * @param aesKeyByRsa 经过rsa加密的aeskey
     * @param decryptStr 经过aes加密的数据
     * @return 解密后的数据
     */
    public  static String decrypt(String aesKeyByRsa,String decryptStr) throws Exception {
        byte[] bytes = RSAUtils.decryptByPrivateKey(Base64.decodeBase64(aesKeyByRsa), RSAUtils.getPrivateKey());
        String aesKey = new String(bytes);
       return AESUtils.decrypt(decryptStr, aesKey);
    }

    /**
     *
     * @param encryptStr 要加密的数据
     * @param frontPublicKey  前端公钥
     * @return 加密后的数据
     */
    public static ApiEncryptRes encrypt(String encryptStr, String frontPublicKey) throws Exception {
        String aesKey = AESUtils.getKey();
        String data = AESUtils.encrypt(encryptStr, aesKey);
        ApiEncryptRes apiEncryptRes = new ApiEncryptRes();
        String aesKeyByRsa = Base64.encodeBase64String(RSAUtils.encryptByPublicKey(aesKey.getBytes(), frontPublicKey));
        apiEncryptRes.setAesKeyByRsa(aesKeyByRsa);
        apiEncryptRes.setData(data);
        return apiEncryptRes;
    }
}

九、相关请求api

import request from '../utils/request'

export function saveGoods(data) {
	return request({
		url: '/goods/saveGoods',
		method: 'post',
		data,
		headers: {
			isEncrypt: 1
		}
	})
}
export function getPublicKey() {
	return request({
		url: '/goods/getPublicKey',
		method: 'get',
		headers: {
			isEncrypt: 0
		}
	})
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐