java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > JavaWe RSA+AES混合加密

JavaWeb实现RSA+AES混合加密

作者:YO_RUI

RSA+AES的混合加密时,AES用于给传输的数据加密,然后通过RSA给AES的秘钥加密,本文就来详细的介绍一下如何实现,具有一定的参考价值,感兴趣的可以了解一下

一、前言

RSA与AES加密的详细介绍这里就不写了,网上很多博客,这里就只是简单说明一下:

二、整体构思

RSA+AES的混合加密时,AES用于给传输的数据加密,然后通过RSA给AES的秘钥加密,所以接收到数据后,就需要先解密得到AES的秘钥,然后通过AES秘钥再去解密得到数据。
下面简单说下demo中加密解密的实现过程:

这里面提到的存储秘钥的方式只是在demo中作为演示使用,可以采用更合理、更安全的方式是实现!

这样,前后端都拥有的对方RSA的公钥,后面在同一个会话中具体的请求数据时,每次各自都会生成新的AES秘钥(AES的算法也需要前后端能匹配上),RSA的秘钥则在响应位置去取就可以了。

在这里插入图片描述

在这里插入图片描述

三、主要代码

1、服务端

两个加密解密工具类,里面部分有使用第三方jar(hutool-all.jar)。

package com.lr.demo.util;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;

public class AESUtil {
    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法

    public static String getKey(int len){
        if(len % 16 != 0){
            System.out.println("长度要为16的整数倍");
            return null;
        }

        char[] chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
        char[] uuid = new char[len];

        if (len > 0) {
            for (int i = 0; i < len; i++) {
                int x = (int) (Math.random() * (len - 0 + 1) + 0);
                uuid[i] = chars[x % chars.length];
            }
        }

        return new String(uuid);
    }


    public static String byteToHexString(byte[] bytes){
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String strHex=Integer.toHexString(bytes[i]);
            if(strHex.length() > 3){
                sb.append(strHex.substring(6));
            } else {
                if(strHex.length() < 2){
                    sb.append("0" + strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return  sb.toString();
    }

    /**
     * AES 加密操作
     *
     * @param content 待加密内容
     * @param key 加密密码
     * @return 返回Base64转码后的加密数据
     */
    public static String encrypt(String content, String key) {
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器

            byte[] byteContent = content.getBytes("utf-8");

            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器

            byte[] result = cipher.doFinal(byteContent);// 加密

            return org.apache.commons.codec.binary.Base64.encodeBase64String(result);//通过Base64转码返回
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    /**
     * AES 解密操作
     *
     * @param content
     * @param key
     * @return
     */
    public static String decrypt(String content, String key) {

        try {
            //实例化
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

            //使用密钥初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));

            //执行操作
            byte[] result = cipher.doFinal(org.apache.commons.codec.binary.Base64.decodeBase64(content));

            return new String(result, "utf-8");
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    private static SecretKeySpec getSecretKey(final String key) throws UnsupportedEncodingException {
        //返回生成指定算法密钥生成器的 KeyGenerator 对象
        KeyGenerator kg = null;

        try {
            kg = KeyGenerator.getInstance(KEY_ALGORITHM);

            //AES 要求密钥长度为 128
            kg.init(128, new SecureRandom(key.getBytes()));

            //生成一个密钥
            SecretKey secretKey = kg.generateKey();

            return new SecretKeySpec(Arrays.copyOf(key.getBytes("utf-8"), 16), KEY_ALGORITHM);// 转换为AES专用密钥
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        }

        return null;
    }

}

package com.lr.demo.util;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import org.springframework.util.Base64Utils;

import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;

public class RSAUtil {

    public static final String PB_KEY = "pb_key";
    public static final String PR_KEY = "pr_key";

    public static final String CLI_PB_KEY = "cli_pb_key";

    /**
     * 获取公私秘钥对
     * @return
     */
    public static Map<String, Key> getRSAKey(){
        KeyPair pair = SecureUtil.generateKeyPair("RSA");
        PrivateKey privateKey = pair.getPrivate();
        PublicKey publicKey = pair.getPublic();

        Map<String, Key> keys = new HashMap<>();
        keys.put(PR_KEY,privateKey);
        keys.put(PB_KEY,publicKey);

        return keys;
    }

    /**
     * 公钥加密
     * @param pbKey
     * @param content
     * @return
     */
    public static String encByPbKey(String pbKey,String content){
        try {
            byte[] bytes = Base64Utils.decode(pbKey.getBytes("UTF-8"));
            RSA rsa = new RSA(null,bytes);
            byte[] enc = rsa.encrypt(content.getBytes("UTF-8"), KeyType.PublicKey);
            String s = new String(Base64Utils.encode(enc));
            return s;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 公钥加密
     * @param pbKey
     * @param content
     * @return
     */
    public static String encByPbKey(PublicKey pbKey,String content){
        try {
            RSA rsa = new RSA(null,pbKey);
            byte[] enc = rsa.encrypt(content.getBytes("UTF-8"), KeyType.PublicKey);
            String s = new String(Base64Utils.encode(enc));
            return s;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私钥解密
     * @param prKey
     * @param content
     * @return
     */
    public static String dencByPrKey(String prKey,String content){
        try {
            byte[] bytes = Base64Utils.decode(prKey.getBytes("UTF-8"));
            RSA rsa = new RSA(bytes,null);
            byte[] denc = rsa.decrypt(Base64Utils.decode(content.getBytes("UTF-8")), KeyType.PrivateKey);
            String s = new String(Base64Utils.encode(denc));
            return s;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私钥解密
     * @param prKey
     * @param content
     * @return
     */
    public static String dencByPrKey(PrivateKey prKey,String content){
        try {
            RSA rsa = new RSA(prKey,null);
            byte[] denc = rsa.decrypt(Base64Utils.decode(content.getBytes("UTF-8")), KeyType.PrivateKey);
            String s = new String(denc);
            return s;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

}

l两个Controller,一个是初始化是秘钥交换的,一个用于测试

package com.lr.demo.controller;

import com.lr.demo.commons.Result;
import com.lr.demo.util.RSAUtil;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;
import java.security.Key;
import java.security.PrivateKey;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("secret")
public class SecretController {

    /**
     * 返回服务端的RSA公钥
     * @param session
     * @return
     */
    @RequestMapping("getKey")
    public Result getKey(HttpSession session){
        Map<String, Key> rsaKey = (Map<String, Key>) session.getAttribute("keys");
        if(rsaKey == null){
            rsaKey = RSAUtil.getRSAKey();
            session.setAttribute("keys",rsaKey);
        }

        byte[] encode = Base64Utils.encode(rsaKey.get(RSAUtil.PB_KEY).getEncoded());
        return Result.success(new String(encode));

    }

    /**
     * 分段解密发送过来的客户端RSA公钥
     * @param map
     * @param session
     * @return
     */
    @RequestMapping("acceptKey")
    public Result acceptKey(@RequestBody Map<String,Object> map, HttpSession session){

        List<String> clientKeys = (List<String>) map.get("clientKey");
        System.out.println("clientKey:" + clientKeys);
        Map<String, Key> rsaKey = (Map<String, Key>) session.getAttribute("keys");

        String cli_key = "";
        if(clientKeys != null){
            for (String item : clientKeys) {
                cli_key += RSAUtil.dencByPrKey((PrivateKey) rsaKey.get(RSAUtil.PR_KEY), item);
            }
        }
        session.setAttribute(RSAUtil.CLI_PB_KEY,cli_key);
        System.out.println("解密后客户端公钥:" + cli_key);
        return Result.success();
    }

}

package com.lr.demo.controller;


import com.alibaba.fastjson.JSON;
import com.lr.demo.commons.Constant;
import com.lr.demo.commons.Result;
import com.lr.demo.util.AESUtil;
import com.lr.demo.util.RSAUtil;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.security.Key;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("sys")
public class SystemController {

    @RequestMapping("login")
    public Result login(@RequestBody String data, HttpServletRequest request,HttpServletResponse response){
        String plaintext = dencrypt(request, data);
        return Result.success(encrypt("登录成功啦",response));
    }


    private String dencrypt(HttpServletRequest request,String data){
        // 从session中获取服务端RSA的私钥
        HttpSession session = request.getSession();
        Map<String, Key> rsaKey = (Map<String, Key>) session.getAttribute("keys");

        HashMap<String,String> hashMap = JSON.parseObject(data, HashMap.class);
        // 获取客户端发送的加密数据
        String enc_data = hashMap.get(Constant.ENCRYPT_DATA);
        System.out.println("获取请求数据---->:" + enc_data);
        // 获取发送过来的AES秘钥
        String enc_aes_key = request.getHeader(Constant.ENCRYPT_AES_KEY);
        // 解密AES秘钥
        String aes_key = RSAUtil.dencByPrKey((PrivateKey) rsaKey.get(RSAUtil.PR_KEY), enc_aes_key);
        // AES解密
        String plaintext = AESUtil.decrypt(enc_data, aes_key);

        System.out.println("解密数据---->:" + plaintext);
        return plaintext;
    }

    public Map<String, String> encrypt(String data, HttpServletResponse response){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        String cliKey = (String) session.getAttribute(RSAUtil.CLI_PB_KEY); // 获取客户端RSA公钥
        String aesKey = AESUtil.getKey(16); // 获取AES秘钥
        // RSA加密AES秘钥
        String encrypt_aes_key = RSAUtil.encByPbKey(cliKey, aesKey);
        // AES加密返回数据
        String encrypt_data = AESUtil.encrypt(data, aesKey);
        // 添加响应头(AES秘钥)
        response.addHeader(Constant.ENCRYPT_AES_KEY, encrypt_aes_key);
        Map<String,String> map = new HashMap<>();
        map.put(Constant.ENCRYPT_DATA,encrypt_data);
        return map;
    }
}

2、前端

前端涉及的文件较多,这里就只展示下页面,其余详细代码可以下载源码后查看。

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
    <script src="static/js/aes_v1.0.js"></script> 
    <script src="static/js/rsa.js"></script>
    <script src="static/js/crypto-js.js"></script>
    <script src="static/js/demo.js"></script>
    <script src="static/js/jquery-3.5.1.js"></script>
  </head>
  <body>

  <form action="<%=request.getContextPath()%>/sys/login" id="loginForm">
      <input type="text" name="username" value="">
      <input type="password" name="password" value="">
      <input type="button" value="登录" id="loginBtn">
  </form>

  <script>
      // 首页加载时,秘钥生成和交换
      getRsaKeys(f)
  </script>
  
  <script>

      function getFormJson(formJqueryObj) {
          var o = {};
          var a = formJqueryObj.serializeArray();
          $.each(a, function () {
              if (o[this.name]) {
                  if (!o[this.name].push) {
                      o[this.name] = [o[this.name]];
                  }
                  o[this.name].push(this.value || '');
              } else {
                  o[this.name] = this.value || '';
              }
          });
          return o;
      }


      $('#loginBtn').click(function () {
          var json = getFormJson($('#loginForm'))
          // demo.js封装的函数
          request(json,'<%=request.getContextPath()%>/sys/login',function (res) {
              console.log(res)
          })
      })
  </script>
  </body>
</html>

四、测试

启动项目,打开首页:

在这里插入图片描述

服务端日志:

在这里插入图片描述

当交换完秘钥后,进行登录测试:

在这里插入图片描述

服务端就收时的日志输出:

在这里插入图片描述

响应后页面的输出:

在这里插入图片描述

在这里插入图片描述

以上就是一个简单的demo,源码点击下载

到此这篇关于JavaWeb实现RSA+AES混合加密的文章就介绍到这了,更多相关JavaWe RSA+AES混合加密内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文