【前后端接口AES+RSA混合加解密详解(vue+SpringBoot)附完整源码】

news/2024/7/9 23:54:42 标签: vue, 接口加解密, 前后端接口加密, RSA, AES

前后端接口AES+RSA混合加解密详解(vue+SpringBoot)

  • 前后端接口AES+RSA混合加解密
    • 一、AES加密原理和为什么不使用AES加密
    • 二、RSA加密原理和为什么不使用rsa加密
    • 三、AESRSA混合加密的原理
    • 四、代码样例
      • 前端
        • 1. 请求增加加密标识
        • 2. 前端加密工具类
        • 3.前端axios请求统一封装,和返回统一封装
      • 后端
        • 1.后端加密工具类
        • 2. 请求的返回实体封装
        • 3. 配置过滤器对请求进行加解密操作
        • 4.过滤器注册
        • 5. 获取RSA公钥接口
    • 总结:

AESRSA_2">前后端接口AES+RSA混合加解密

     为什么需要对前后端接口加解密?主要是甲方要求。

AESAES_6">一、AES加密原理和为什么不使用AES加密

  AES是最常见的对称加密算法,加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合,且加密长文本比较方便。缺点是密钥的传输比较麻烦,只能将一把公钥分别在前后端代码中各存放一份,还有一个遇到的问题下面会讲到,那就是美国对AES加密密钥长度的限制。优点是加密效率高,缺点是安全性低。

RSArsa_10">二、RSA加密原理和为什么不使用rsa加密

RSA也就是非对称加密算法,是使用不同密钥进行加密和解密的算法,也称为公私钥加密。公钥和私钥是同时生成的,公钥用来加密,私钥用来解密,加解密的密钥是成对出现。但是不推荐用RSA加密请求报文,因为RSA加密后的报文会很长,经常会出现超过请求体长度限制。优点是安全性高,缺点是RSA的加密效率低。

AESRSA_14">三、AESRSA混合加密的原理

我们可以结合两者的优点,AES的加密效率高,那我们可以使用aes来加密报文,而RSA的灵活性和安全性来加密aes的密钥。总的思路就是利用RSA来加密传输AES的密钥,用 AES的密钥来加密请求报文。

四、代码样例

前端

1. 请求增加加密标识
  首先这个功能我们的出发点是可以灵活配置,所以我们在需要加密的请求头添加一个加密标识代码如下:
import axios from '@common/plugins/Axios'

saveStudent: data => {
   
        return axios.request({
   
            url: `/demo/saveStudent`,
            method: 'post',
            headers: {
   
            //需要加密的请求在头部塞入标识
                isEncrypt: 1
            },
            data
        })
    }
2. 前端加密工具类

import JSEncrypt from 'jsencrypt'
import CryptoJS from 'crypto-js'

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

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

export function aesEncrypt(aeskey, Str) {
   
    // 设置一个默认值,如果第二个参数为空采用默认值,不为空则采用新设置的密钥
    var key = CryptoJS.enc.Utf8.parse(aeskey)
    var srcs = CryptoJS.enc.Utf8.parse(Str)
    var encrypted = CryptoJS.AES.encrypt(srcs, key, {
   
    // 切记   需要和后端算法模式一致
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    })

    return encrypted.toString()
}

export function aesDecrypt(aeskey, Str) {
   
    var key = CryptoJS.enc.Utf8.parse(aeskey)
    var decrypt = CryptoJS.AES.decrypt(Str, key, {
   
     // 切记   需要和后端算法模式一致
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    })
    return CryptoJS.enc.Utf8.stringify(decrypt).toString()
}
/**
 * 获取16位随机码AES
 * @returns {string}
 */
export function get16RandomNum() {
   
    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 = ''
    //这个地方切记要选择16位,因为美国对密钥长度有限制,选择32位的话加解密会报错,需要根据jdk版本去修改相关jar包,有点恼火,选择16位就不用处理。
    for (var i = 0; i < 16; i++) {
   
        var id = parseInt(Math.random() * 61)
        nums += chars[id]
    }
    return nums
}
//获取rsa密钥对
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
}
3.前端axios请求统一封装,和返回统一封装
import Axios from 'axios'
import {
    getRsaKeys, rsaEncrypt, rsaDecrypt, aesDecrypt, aesEncrypt, get32RandomNum } from '@common/util'
/**
 * axios实例
 * @type {AxiosInstance}
 */
const instance = Axios.create({
   
    headers: {
   
        x_requested_with: 'XMLHttpRequest'
    }
})
let frontPrivateKey
/**
 * axios请求过滤器
 */
instance.interceptors.request.use(
    async config => {
   
        if (sessionStorage.getItem('X-Access-Token')) {
   
            // 判断是否存在token,如果存在的话,则每个http header都加上token
            config.headers['X-Access-Token'] = sessionStorage.getItem('X-Access-Token')
        }
        if (config.headers['isEncrypt']) {
   
            config.headers['Content-Type'] = 'application/json;charset=utf-8'
            if (config.method === 'post' || config.method === 'put') {
   
                const {
    privateKey, publicKey } = await getRsaKeys()
                let afterPublicKey = sessionStorage.getItem('afterPublicKey')
                frontPrivateKey = privateKey
                //每次请求生成aeskey
                let aesKey = get16RandomNum()
               //用登陆后后端生成并返回给前端的的RSA密钥对的公钥将AES16位密钥进行加密
                let aesKeyByRsa = rsaEncrypt(aesKey, afterPublicKey)
               //使用AES16位的密钥将请求报文加密(使用的是加密前的aes密钥)
                if (config.data) {
   
                    let data = aesEncrypt(aesKey, JSON.stringify(config.data))
                    config.data = {
   
                        data: data,
                        aeskey: aesKeyByRsa,
                        frontPublicKey: publicKey
                    }
                }

                if (config.params) {
   
                    let data = aesEncrypt(aesKey, JSON.stringify(config.params))
                    config.params = {
   
                        params: data,
                        aeskey: aesKeyByRsa,
                        frontPublicKey: publicKey
                    }
                }
            }
        }

        config.url ="你的后端接口请求地址" + config.url

        return config
    },
    err => {
   
        return Promise.reject(err)
    }
)
/**
 * axios响应过滤器
 */
instance.interceptors.response.use(
    response => {
   
        //后端返回的通过rsa加密后的aes密钥
        let aesKeyByRsa = response.data.aesKeyByRsa
       
        if (aesKeyByRsa) {
   
        //通过rsa的私钥对后端返回的加密的aeskey进行解密
            let aesKey = rsaDecrypt(aesKeyByRsa, frontPrivateKey)
            //使用解密后的aeskey对加密的返回报文进行解密
            response.data.data = JSON.parse(JSON.parse(aesDecrypt(aesKey, response.data.data)))
         
            return response.data
        }else{
    
           return response
           }      
        }
    },
    error => {
   
        if (error.response.status === 500) {
   
            const {
    data } = erro

http://www.niftyadmin.cn/n/5370891.html

相关文章

【第三十五节】idea项目的创建以及setting和Project Structure的设置

项目创建 Project Structure的设置 点击file ~ Project Structure 进入 进入view/Appearance 选中Toolbar 就会出现状态栏

2.8作业

1、选择题 1.1、以下选项中,不能作为合法常量的是 ___b_______ A&#xff09;1.234e04 B&#xff09;1.234e0.4 C&#xff09;1.234e4 D&#xff09;1.234e0 1.2、以下定义变量并初始化错误的是______d_______。 A) char c1 ‘H’ &#xff1b; B) char c…

有道ai写作,突破免费限制,无限制使用

预览效果 文末提供源码包及apk下载地址 有道ai写作python版 import hashlib import time import json import ssl import base64 import uuidfrom urllib.parse import quote import requests from requests_toolbelt.multipart.encoder import MultipartEncoder from Crypto…

Kafka 下载与启动

目录 一. 前言 二. 版本下载 2.1. 版本说明 三. 快速启动 3.1. 下载解压 3.2. 启动服务 3.3. 创建一个主题&#xff08;Topic&#xff09; 3.4. 发送消息 3.5. 消费消息 3.6. 使用 Kafka Connect 来导入/导出数据 3.7. 使用 Kafka Stream 来处理数据 3.8. 停止 Kaf…

P2957

题目描述 The cows enjoy mooing at the barn because their moos echo back, although sometimes not completely. Bessie, ever the excellent secretary, has been recording the exact wording of the moo as it goes out and returns. She is curious as to just how mu…

Android Studio安装过程遇到SDK无法安装问题解决

首次打开studio遇到该类问题&#xff0c;需要下载SDK文件&#xff0c;后又发现SDK由于是Google源&#xff0c;无法进行正常安装&#xff0c;故转而进行SDK的镜像安装。 一、下载SDK Tools 地址&#xff1a;AndroidDevTools - Android开发工具 Android SDK下载 Android Studio…

centos ssh 默认端口 修改

centos7更改SSH端口-腾讯云开发者社区-腾讯云 (tencent.com) linux centos修改ssh端口号_semanage port -a -t ssh_port_t -p tcp-CSDN博客

攻防世界 CTF Web方向 引导模式-难度1 —— 1-10题 wp精讲

目录 view_source robots backup cookie disabled_button get_post weak_auth simple_php Training-WWW-Robots view_source 题目描述: X老师让小宁同学查看一个网页的源代码&#xff0c;但小宁同学发现鼠标右键好像不管用了。 不能按右键&#xff0c;按F12 robots …