为什么使用RSA + AES混合加密

1.加密介绍

RSA加密: 属于非对称加密,公钥用于对数据进行加密,私钥对数据进行解密,两者不可逆。公钥和私钥是同时生成的,且一一对应。比如:客户端拥有公钥,服务端拥有公钥和私钥。客户端将数据通过公钥进行加密后,发送密文给服务端,服务端可以通过私钥和公钥进行解密。AES加密: 属于对称加密,简单点说就是,客户端用密码对数据进行AES加密后,服务端用同样的密码对密文进行AES解密。

2.加密思路

利用 RSA 来加密传输 AES的密钥,用 AES的密钥 来加密数据。既利用了 RSA 的灵活性,可以随时改动 AES 的密钥;又利用了 AES 的高效性,可以高效传输数据。

3.混合加密原因

单纯的使用 RSA(非对称加密)方式,效率会很低,因为非对称加密解密方式虽然很保险,但是过程复杂,耗费时间长,性能不高;RSA 优势在于数据传输安全,且对于几个字节的数据,加密和解密时间基本可以忽略,所以用它非常适合加密 AES 秘钥(一般16个字节);单纯的使用 AES(对称加密)方式的话,非常不安全。这种方式使用的密钥是一个固定的密钥,客户端和服务端是一样的,一旦密钥被人获取,那么,我们所发的每一条数据都会被都对方破解;AES有个很大的优点,那就是加密解密效率很高,而我们传输正文数据时,正好需要这种加解密效率高的,所以这种方式适合用于传输量大的数据内容;

基于以上特点,就有了我们混合加密的思路

时序图

前端代码:

创建aesUtils.js

import CryptoJS from 'crypto-js'

import {JSEncrypt} from 'jsencrypt'

import cryptoJS from './CryptoJSUtils'

/**

* 创建密钥

* @returns AES密钥

*/

export function createAesKey () {

const expect = 16

let str = Math.random().toString(36).substr(2)

while (str.length < expect) {

str += Math.random().toString(36).substr(2)

}

str = str.substr(0, 16)

return str

}

/**

* AES加密

* @param {*} word 加密字段

* @param {*} keyStr AES密钥

* @returns

*/

export function AESencrypt (word, keyStr) {

keyStr = keyStr || 'abcdefgabcdefg12'

var key = CryptoJS.enc.Utf8.parse(keyStr) // Latin1 w8m31+Yy/Nw6thPsMpO5fg==

var srcs = CryptoJS.enc.Utf8.parse(word)

var encrypted = CryptoJS.DES.encrypt(srcs, key, {

mode: CryptoJS.mode.ECB,

padding: CryptoJS.pad.Pkcs7

})

return encrypted.ciphertext.toString()

}

/**

* RSA加密算法

* @param {*} pas

* @returns

*/

export function RSAencrypt (pas, publickey) {

let jse = new JSEncrypt()

jse.setPublicKey(publickey)

return jse.encrypt(pas)

}

/**

* 获取16位随机数,当做aes秘钥key,进行加密操作

* @constructor

*/

export function RsaEncryptData (data) {

// 此处生成十六位随机数进行aes对称加密密钥准备

var randomStr = Math.random().toString().substr(0, 16)

// aes加密

var datas = cryptoJS.encrypt(JSON.stringify(data), randomStr)

datas = datas.toString()

// 声明rsa加密规则

var encrypt = new JSEncrypt()

// 将rsa公钥进行保存

encrypt.setPublicKey('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCI2zy2kkLxdhx31pu2gRB95QCx5aOvw5yTt44glEPIWhaoqXVeTch9dwAjaoInm6a1BiQHEtE/ccWTPmM7Iktrjcw3siC3dV2/QJkpk8/b52TMCw9R55qXL1+Y1f0z7BCu3ikCfyTw5cxAh5pa3r0YhYmeC+E6J3crmBPzImfYCwIDAQAB')

// 使用公钥对aes的密钥进行加密

var encrypted = encrypt.encrypt(randomStr)

// 创建json对象

let json = {

'requestData': datas,

'encrypted': encrypted

}

return json

}

/**

* 将返回的数据进行解密操作

* @constructor

*/

export function RsaDecryptsData (result) {

// rsa解密,获取到aes秘钥

var decrypt = new JSEncrypt()

decrypt.setPrivateKey('MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALs0Ufy3++1luZf7XtsQiPXORRuv5KC6ec7pkApNc3ckFQfpWjhAiM0yn7tqGl8y1zRRR8/g8dsUCofTLOL1EJB+7xEvUCSmjB6RDowtVOxA9vRCrrxwVoNY881x94GE/Ln2A64xVtbFspq0s9hpP4GU0QXWIHKMV/SzB7DsN37PAgMBAAECgYEArS6VukkqUlANBcCR2+7MBTmxTQ/HXbmk/fmsOxuzecBzhEIoKGnrJIl0o5hglTkfRVL8MB9VHurHYyfFGqDDlJB70FVrCPBrtxPoUtB5aI0SLSkDHX3EWjlOBlCQkMiFhx9cS9PCloDSA2Ahzga3y8Bg3LaXhoZediPgz4PmBaECQQD5+mPahaPnpJcR5CKCjryXlpqic+s0cE33ZtYPwKe163KHsSdCErOsFQ9k01JpHbCZmipzRRC+xT0CZ7DfKLRjAkEAv7bOVC+HO9YM5Qf2eYW2kUv4ssG9c8NhsXBSnKQfaFEKM4xLPtulj16YQevHpjgzr2BIg5arVWW81Nu1YLlJpQJAW7cHXcx8d2fG2ZSXKMmP3houf/4BxMqTgHrlfQAVSESrT6eqnK5Z54AOltKFwPVYrvKGMqabXzLkkHZUyXuYuwJAEkhywOCPewtcy3LI9Knl0VF3dES5tpKJfIyDtGCKhj5ERMo6WtJDpbqVtqOvtJBjjXQXNkVmLYy4R2x0jbbd6QJARGMQhsPUTkac/xf956UBZNkP8Xn/rokR3M2fm+HNPZ9t0EOzdfdIYk7aUUoLqR73v9o9YiSGy5NSwOT+33MCpw==')

var aesKey = decrypt.decrypt(result.encrypted)

// 用aes秘钥进行解密

return eval('(' + cryptoJS.decrypt(result.requestData, aesKey) + ')')

}

 创建CryptoJSUtils.js

import CryptoJS from 'crypto-js'

export default {

/**

@param {*需要加密的字符串 注:对象转化为json字符串再加密} word

@param {*aes加密需要的key值,这个key值后端同学会告诉你} keyStr

*/

encrypt (word, keyStr) { // 加密

let key = CryptoJS.enc.Utf8.parse(keyStr)

let srcs = CryptoJS.enc.Utf8.parse(word)

let encrypted = CryptoJS.AES.encrypt(srcs, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}) // 加密模式为ECB,补码方式为PKCS5Padding(也就是PKCS7)

return encrypted.toString()

},

decrypt (word, keyStr) { // 解密

let key = CryptoJS.enc.Utf8.parse(keyStr)

let decrypt = CryptoJS.AES.decrypt(word, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7})

return CryptoJS.enc.Utf8.stringify(decrypt).toString()

}

}

java后台

创建DecodeRequestBodyAdvice.java

package com.wisdom.iotSystem.exception;

import com.google.gson.Gson;

import com.google.gson.reflect.TypeToken;

import com.wisdom.iotSystem.annotations.SecurityParameter;

import com.wisdom.iotSystem.utils.AesEncryptUtils;

import com.wisdom.iotSystem.utils.RSAUtils;

import org.apache.commons.io.IOUtils;

import org.apache.commons.lang.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.core.MethodParameter;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpInputMessage;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.IOException;

import java.io.InputStream;

import java.lang.reflect.Type;

import java.util.Map;

/**

* @author wzw

* @desc 请求数据解密

* @date 2018/10/29 20:17

*/

@ControllerAdvice(basePackages = "com.wisdom.iotSystem.controller")

public class DecodeRequestBodyAdvice implements RequestBodyAdvice {

private static final Logger logger = LoggerFactory.getLogger(DecodeRequestBodyAdvice.class);

// @Value("${server.private.key}")

private String SERVER_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIjbPLaSQvF2HHfWm7aBEH3lALHlo6/DnJO3jiCUQ8haFqipdV5NyH13ACNqgiebprUGJAcS0T9xxZM+YzsiS2uNzDeyILd1Xb9AmSmTz9vnZMwLD1HnmpcvX5jV/TPsEK7eKQJ/JPDlzECHmlrevRiFiZ4L4TondyuYE/MiZ9gLAgMBAAECgYBT5QnH5ctx1/TFpeKYs2/XrT2K0HpScfiXOSvAXwNaW5eOVyti3w3rk7qa+1zESQ+d4yDM0UVCvkze4ZzVEEXoyGV4q7HkaGhBYJeE9guWi81G4arsso8er5SvIcirAYGQykn9WvVssbsUjGe0P2Fan05RbGy9JnRpWXagyKMuqQJBAL4OgvQwoJ/djBgx5zRrJZzHySAP+Vr06ZF+6q2N+hBKG4g35Qqi7QWJxvJznlhNqqH58eWxl5Ypr0AmvjUKbl8CQQC4V0r4VBEvtorsCnilkoyiaNhITj/i3plKouKiwUf6Xvgkw2J1iqOKkZ1qlXxyUIzIeuasIIXnkJxjwN4xzt3VAkBW4X1dsYkL65QqT024+a4lAHNhs8uyl7jaKSGQmxGQNsBlQd/zP82INZZ7qPzeswpop0C8VrXMEFwrwEo9JvqTAkEAmVAwj/wLFy2wuMO0t6/8uw6L4wcBZ0RPJZ328/ngTUEzDBBcEPovLg4RaBXPnJuVmx9sPfgGpiLFjslXgwFTyQJAYzUrnA9eqmnLjTHLziCpuDuvaKa0Y/WMbieIGxnsIjXbMX1vtP3zIwr/ykrao5Ln+wv2yHwUSHeHlaxfO35xlg==";

@Override

public boolean supports(MethodParameter methodParameter, Type type, Class> aClass) {

return true;

}

@Override

public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class> aClass) {

return body;

}

@Override

public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type, Class> aClass) throws IOException {

try {

boolean encode = false;

if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {

//获取注解配置的包含和去除字段

SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);

//入参是否需要解密

encode = serializedField.inDecode();

}

if (encode) {

return new MyHttpInputMessage(inputMessage);

}else{

return inputMessage;

}

} catch (Exception e) {

e.printStackTrace();

logger.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:"+e.getMessage());

return inputMessage;

}

}

@Override

public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class> aClass) {

return body;

}

class MyHttpInputMessage implements HttpInputMessage {

private HttpHeaders headers;

private InputStream body;

public MyHttpInputMessage(HttpInputMessage inputMessage) throws Exception {

this.headers = inputMessage.getHeaders();

this.body = IOUtils.toInputStream(easpString(IOUtils.toString(inputMessage.getBody(),"utf-8")));

}

@Override

public InputStream getBody() throws IOException {

return body;

}

@Override

public HttpHeaders getHeaders() {

return headers;

}

/**

*

* @param requestData

* @return

*/

public String easpString(String requestData) {

if(requestData != null && !requestData.equals("")){

Map map = new Gson().fromJson(requestData,new TypeToken>() {

}.getType());

// 密文

String data = map.get("requestData");

// 加密的aes秘钥

String encrypted = map.get("encrypted");

if(StringUtils.isEmpty(data) || StringUtils.isEmpty(encrypted)){

throw new RuntimeException("参数【requestData】缺失异常!");

}else{

String content = null ;

String aseKey = null;

try {

aseKey = RSAUtils.decryptDataOnJava(encrypted,SERVER_PRIVATE_KEY);

}catch (Exception e){

throw new RuntimeException("参数【aseKey】解析异常!");

}

try {

content = AesEncryptUtils.decrypt(data, aseKey);

}catch (Exception e){

throw new RuntimeException("参数【content】解析异常!"+e);

}

if (StringUtils.isEmpty(content) || StringUtils.isEmpty(aseKey)){

throw new RuntimeException("参数【requestData】解析参数空指针异常!");

}

return content;

}

}

throw new RuntimeException("参数【requestData】不合法异常!");

}

}

}

创建EncodeResponseBodyAdvice.java

package com.wisdom.iotSystem.exception;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.wisdom.iotSystem.annotations.SecurityParameter;

import com.wisdom.iotSystem.utils.AesEncryptUtils;

import com.wisdom.iotSystem.utils.RSAUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.core.MethodParameter;

import org.springframework.http.MediaType;

import org.springframework.http.server.ServerHttpRequest;

import org.springframework.http.server.ServerHttpResponse;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

import java.util.Map;

import java.util.Random;

/**

* @author monkey

* @desc 返回数据加密

* @date 2018/10/25 20:17

*/

@ControllerAdvice(basePackages = "com.wisdom.iotSystem.controller")

public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {

private final static Logger logger = LoggerFactory.getLogger(EncodeResponseBodyAdvice.class);

// @Value("${client.public.key}")

private String CLIENT_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7NFH8t/vtZbmX+17bEIj1zkUbr+SgunnO6ZAKTXN3JBUH6Vo4QIjNMp+7ahpfMtc0UUfP4PHbFAqH0yzi9RCQfu8RL1AkpowekQ6MLVTsQPb0Qq68cFaDWPPNcfeBhPy59gOuMVbWxbKatLPYaT+BlNEF1iByjFf0swew7Dd+zwIDAQAB";

@Override

public boolean supports(MethodParameter methodParameter, Class aClass) {

return true;

}

@Override

public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {

boolean encode = false;

if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {

//获取注解配置的包含和去除字段

SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);

//出参是否需要加密

encode = serializedField.outEncode();

}

if (encode) {

// logger.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");

ObjectMapper objectMapper = new ObjectMapper();

try {

String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);

// 生成aes秘钥

String aseKey = getRandomString(16);

// rsa加密

String encrypted = RSAUtils.encryptedDataOnJava(aseKey, CLIENT_PUBLIC_KEY);

// aes加密

String requestData = AesEncryptUtils.encrypt(result, aseKey);

Map map = new HashMap<>();

map.put("encrypted", encrypted);

map.put("requestData", requestData);

return map;

} catch (Exception e) {

e.printStackTrace();

logger.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());

}

}

return body;

}

/**

* 创建指定位数的随机字符串

* @param length 表示生成字符串的长度

* @return 字符串

*/

public static String getRandomString(int length) {

String base = "abcdefghijklmnopqrstuvwxyz0123456789";

Random random = new Random();

StringBuffer sb = new StringBuffer();

for (int i = 0; i < length; i++) {

int number = random.nextInt(base.length());

sb.append(base.charAt(number));

}

return sb.toString();

}

}

创建接口:SecurityParameter

package com.wisdom.iotSystem.annotations;

import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

/**

* @author wzw

* @desc 请求数据解密

* @date 2018/10/25 20:17

*/

@Target({ElementType.METHOD,ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Mapping

@Documented

public @interface SecurityParameter {

/**

* 入参是否解密,默认解密

*/

boolean inDecode() default true;

/**

* 出参是否加密,默认加密

*/

boolean outEncode() default true;

}

测试

/**

* RSA+AES双重加密测试

*

* @return object

*/

@RequestMapping("/testEncrypt")

@ResponseBody

// 数据加密注解

@SecurityParameter

public R testEncrypt(@RequestBody Map info) {

String content = "内容";

info.put("name",nameTest);

return R.ok("请求成功").put("info",info);

}

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: