RSA+AES实现混合加密

为什么使用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<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> 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<? extends HttpMessageConverter<?>> 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<String,String> map = new Gson().fromJson(requestData,new TypeToken<Map<String,String>>() {
                }.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<String, String> 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<String,Object> info) {
    String content = "内容";
    info.put("name",nameTest);
    return R.ok("请求成功").put("info",info);
}

  • 12
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
混合加密是指使用多种加密算法对数据进行加密,以达到更高的安全性。在实际应用中,常常使用AESRSA两种加密算法进行混合加密。 具体实现步骤如下: 1. 生成AES密钥,并用该密钥对明文进行加密。 2. 生成RSA密钥对,并使用公钥加密AES密钥。 3. 将经过AES加密的密文和RSA加密AES密钥一起发送给接收方。 4. 接收方使用自己的RSA私钥解密AES密钥。 5. 使用解密后的AES密钥对密文进行解密,得到原始明文。 Java代码实现如下: ```java import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; public class HybridEncryptUtil { // 生成AES密钥 public static SecretKey generateAESKey() throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); return keyGenerator.generateKey(); } // 生成RSA密钥对 public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(1024); return keyPairGenerator.generateKeyPair(); } // 使用AES密钥加密数据 public static byte[] encryptAES(byte[] data, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, key); return cipher.doFinal(data); } // 使用AES密钥解密数据 public static byte[] decryptAES(byte[] data, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, key); return cipher.doFinal(data); } // 使用RSA公钥加密数据 public static byte[] encryptRSA(byte[] data, PublicKey publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data); } // 使用RSA私钥解密数据 public static byte[] decryptRSA(byte[] data, PrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } // 使用AES密钥加密数据,并使用RSA公钥加密AES密钥 public static byte[][] hybridEncrypt(byte[] data, PublicKey publicKey) throws Exception { SecretKey secretKey = generateAESKey(); byte[] encryptedData = encryptAES(data, secretKey); byte[] encryptedKey = encryptRSA(secretKey.getEncoded(), publicKey); return new byte[][]{encryptedData, encryptedKey}; } // 使用RSA私钥解密AES密钥,并使用AES密钥解密数据 public static byte[] hybridDecrypt(byte[][] encryptedData, PrivateKey privateKey) throws Exception { byte[] encryptedContent = encryptedData[0]; byte[] encryptedKey = encryptedData[1]; byte[] decryptedKey = decryptRSA(encryptedKey, privateKey); SecretKey secretKey = new SecretKeySpec(decryptedKey, "AES"); return decryptAES(encryptedContent, secretKey); } } ``` 使用示例: ```java import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import javax.crypto.SecretKey; public class HybridEncryptTest { public static void main(String[] args) throws Exception { String content = "Hello, World!"; // 生成RSA密钥对 KeyPair keyPair = HybridEncryptUtil.generateRSAKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); // 混合加密 byte[][] encryptedData = HybridEncryptUtil.hybridEncrypt(content.getBytes(), publicKey); // 混合解密 byte[] decryptedData = HybridEncryptUtil.hybridDecrypt(encryptedData, privateKey); String result = new String(decryptedData); System.out.println(result); } } ``` 注意事项: 1. AES密钥长度必须为128位,否则无法与RSA密钥进行混合加密。 2. RSA加密的数据长度不能超过密钥长度减去11,否则无法加密,因此在实际应用中需要对数据进行分段加密。 3. 在使用密钥加密和解密数据时,需要使用相同的算法和模式,否则无法正确解密数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值