vue+koa中使用aes和rsa混合加密

本文详细介绍了如何在Vue前端和Node.js后端使用RSA非对称加密与AES对称加密进行混合加密通信。前端利用crypto-js库进行AES加密,通过jsencrypt库使用RSA公钥加密AES密钥,后端使用node-rsa库解密AES密钥并解密AES加密的数据。整个流程包括随机生成AES密钥、获取服务端公钥、加密传输、解密响应等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先需要了解以下内容:

Vue中使用crypto-js进行AES对称加密:https://blog.youkuaiyun.com/qq_40323256/article/details/123688626

Vue中使用jsencrypt进行RSA非对称加密:https://blog.youkuaiyun.com/qq_40323256/article/details/124006449 

生成RSA密钥对在线工具:http://web.chacuo.net/netrsakeypair

RSA公钥加密解密:http://tool.chacuo.net/cryptrsapubkey

AES加密解密:http://tool.chacuo.net/cryptaes 

混合加密流程:

  1. 前端每次请求都要随机生成一个秘钥字符串key_aes(秘钥字符串length推荐为16,可大小写+数字)。然后AES用这个秘钥key_aes加密明文data。
  2. 请求服务端获取公钥的接口,获取服务端通过RSA生成的公钥。(服务端只需要生成一次公钥和私钥,然后持久保存在服务端即可,不用每次都生成,否则多了难以维护)
  3. 前端使用获取到的公钥通过RSA对key_aes进行非对称加密。
  4. 前端将对称加密后的data和非对称加密后的秘钥key_aes传输到服务端
  5. 服务端用刚刚通过RSA生成的私钥解密key_aes,获取解密后的key_aes。再用解密后的key_aes通过AES解密data,获取data明文
  6. 经过一轮以上步骤,此时前后端都保存有相同的秘钥key_aes。服务端给前端响应数据时,就可用该秘钥AES加密响应的数据,前端AES解密数据即可。

整个过程,需要服务端提供2个接口,一个是获取公钥的接口,一个是传送数据的接口 (包括对称加密的明文data数据和非对称加密的aes秘钥)

随机生成aes密钥:

密钥字符串的length最好为16。当然也可以为32,但是在工作中发现用32的话,后端可能会因为密钥长度相对较长而导致密钥被截断一部分,因此选择用16

    //生成指定长度随机字符串
    generateRandomStr(strLength = 16) {
      let code = '';
      const chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
      const charsArr = chars.split('');
 
      for (let i = 0; i < strLength; i++) {
        const num = Math.floor(Math.random() * charsArr.length);
        code += charsArr[num];
      }
      return code;
    }

 客户端:

<!--main-->
<template>
  <div>
    <el-button @click="test()">test</el-button>
  </div>
</template>
<script>
import { JSEncrypt } from "jsencrypt";
import CryptoJS from "crypto-js";
import axios from "axios";
export default {
  methods: {
    async test() {
      //待加密传输的data数据
      const data = {
        username: "李疆",
        password: 123,
      };
      console.log("aes加密前的data数据:", data);

      //aes密钥
      let key_aes = "385f33cb91484b04a177828829081ab7";
      console.log("aes密钥:", key_aes);

      //aes加密data数据
      const data_encrypted_by_aes = CryptoJS.AES.encrypt(
        JSON.stringify(data),
        CryptoJS.enc.Utf8.parse(key_aes),
        {
          mode: CryptoJS.mode.ECB,
          padding: CryptoJS.pad.Pkcs7,
        }
      ).ciphertext.toString();
      console.log("aes加密后的data数据:", data_encrypted_by_aes);

      //从服务端获取公钥
      let { data: publicKey } = await axios.get("/api/test/publicKey");
      console.log("从服务端获取的rsa公钥:", publicKey);

      //rsa公钥加密aes密钥
      let encryptor = new JSEncrypt();
      encryptor.setPublicKey(publicKey);
      let aeskey_encrypted_by_rsa = encryptor.encrypt(key_aes);
      console.log("被rsa公钥加密后的aes密钥:", aeskey_encrypted_by_rsa);

      //发送aes加密后的data数据和被rsa公钥加密的aes密钥到服务端
      let result = await axios.post("/api/test/decryption", {
        data_encrypted_by_aes,
        aeskey_encrypted_by_rsa,
      });
      console.log("result:", result);
    },
  },
};
</script>

服务端:

const router = require('koa-router')()
const CryptoJS = require("crypto-js");
const NodeRSA = require('node-rsa');

router.prefix('/test')

router.get('/', (ctx, next) => {
    ctx.body = 'this is a users response!'
})

const key_rsa = new NodeRSA({ b: 1024 });//密钥长度1024位
key_rsa.setOptions({ encryptionScheme: 'pkcs1' });//指定加密格式.不改格式得话可能会报错
router.get('/publicKey', async (ctx, next) => {
    let publicKey = key_rsa.exportKey('public');//生成公钥
    // let privateKey = key_rsa.exportKey('private');//生成私钥
    ctx.body = publicKey;
});
router.post('/decryption', async (ctx, next) => {
    let { data_encrypted_by_aes, aeskey_encrypted_by_rsa, } = ctx.request.body;

    console.log("aes加密后的data数据:", data_encrypted_by_aes)
    console.log("被rsa公钥加密的aes密钥:", aeskey_encrypted_by_rsa)

    //rsa私钥解密出aes密钥
    const aeskey_decrypted_by_rsa = key_rsa.decrypt(aeskey_encrypted_by_rsa, 'utf8');
    console.log('被rsa私钥解密出的aes密钥: ', aeskey_decrypted_by_rsa);

    // aes密钥解密出data数据
    let data_decrypted_by_aes = JSON.parse(CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(
        CryptoJS.format.Hex.parse(data_encrypted_by_aes),
        CryptoJS.enc.Utf8.parse(aeskey_decrypted_by_rsa),
        {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        }
    )))
    console.log('被aes密钥解密出的data数据:', data_decrypted_by_aes);

    ctx.body = { message: "success" };

});

module.exports = router

结果:

上面的公钥和私钥是通过node-rsa生成的,如果想自己手动指定公钥和私钥,可通过如下方式:(客户端代码不变,只需要改服务端代码)

const router = require('koa-router')()
const CryptoJS = require("crypto-js");
const NodeRSA = require('node-rsa');

router.prefix('/test')

router.get('/', (ctx, next) => {
    ctx.body = 'this is a users response!'
})

router.get('/publicKey', async (ctx, next) => {
    let publicKey = `-----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDARcNxlvPXZQSup9yPV6iVzqMI
    eBr2Rztd4zxYUCwL2pqaig3ZP17eFpzfiI+aBf2F4qp/QHPMVnEQmx33/fGlssvc
    cqplP7gvk7BKporUWtnyqYKqy/fAE07c/Yxpe5WZoMo2pRyaBDMLMZ49MpfAzHXS
    pTzUY9W4F49C0Y8SPQIDAQAB
    -----END PUBLIC KEY-----`
    ctx.body = publicKey;
});
router.post('/decryption', async (ctx, next) => {
    let { data_encrypted_by_aes, aeskey_encrypted_by_rsa, } = ctx.request.body;

    console.log("aes加密后的data数据:", data_encrypted_by_aes)
    console.log("被rsa公钥加密的aes密钥:", aeskey_encrypted_by_rsa)

    let privateKey = `-----BEGIN PRIVATE KEY-----
    MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAMBFw3GW89dlBK6n
    3I9XqJXOowh4GvZHO13jPFhQLAvampqKDdk/Xt4WnN+Ij5oF/YXiqn9Ac8xWcRCb
    Hff98aWyy9xyqmU/uC+TsEqmitRa2fKpgqrL98ATTtz9jGl7lZmgyjalHJoEMwsx
    nj0yl8DMddKlPNRj1bgXj0LRjxI9AgMBAAECgYEAsjd34y+yMTKXVnSNQehUgRcZ
    kt4Qz7pS0sGvy4P0b8BekWPpVjcg7eYz3hYsVO28gn8I3g6Vxw9Qmb3N13HwqrPt
    1z6pGkr54FtgsxPDaS0Cu86gt9G+MfAHMbYHtCd3VEhfXmj17/NeV/nFUG0mlgGZ
    bZY0wLlDNYX6t+clYQECQQDphC6/5xhdWr1yelNO5fybt/1p9Cp5q3nRR1NjEwCY
    77WE1VMwbdSYkj9whSL9EcnEGFL6/PgcWjkXuY4Jpg9hAkEA0sj79IVYt7L0xnTO
    tNg6RBoF9vnt9T0HUAqU0Q9YHbV+SzEMUOKepMW5XwSlPomeOpOQEYot7SmXuUL5
    aBj8XQJBAKNj+dIMwN+RPj17mg76nWXXt1kLnyzC7tmgLpiEE0bvcvMe0LZyIu8e
    ZYU1ouWwLJ0o5+b2WiR8fLY8/0WBDAECQQCa7mXbfiQOqjgmovbIkGKLagFWXrBc
    YKY+W/i0ja278IpK5FtkHJ51CGxPfg+jnu2xqoLrkYBeYQMHhkXiEPN1AkEAmjlL
    3AWqcl6I0vsvt1cK4XiQmez8L6JomMddjF9vi96mNy8Be8+7OCxWv+aZ2P8AhZKD
    WrHvhaHSs389XzS3HQ==
    -----END PRIVATE KEY-----
    `
    const key_rsa = new NodeRSA(privateKey);
    key_rsa.setOptions({ encryptionScheme: 'pkcs1' }); // 因为jsencrypt自身使用的是pkcs1加密方案, nodejs需要修改成pkcs1。
    const aeskey_decrypted_by_rsa = key_rsa.decrypt(aeskey_encrypted_by_rsa, 'utf8');
    console.log('被rsa私钥解密出的aes密钥: ', aeskey_decrypted_by_rsa);

    let data_decrypted_by_aes = JSON.parse(CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(
        CryptoJS.format.Hex.parse(data_encrypted_by_aes),
        CryptoJS.enc.Utf8.parse(aeskey_decrypted_by_rsa),
        {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        }
    )))
    console.log('被aes密钥解密出的data数据:', data_decrypted_by_aes);

    ctx.body = { message: "success" };
});

module.exports = router

注意:我尝试过代码中不直接展示公钥和私钥,而是通过fs.readFile()的方式读取本地的公钥和私钥文件,虽然可以读取,但是最后加密解密失败。

把公钥私钥文件放到public/files文件夹中,这样读取路径是可以的:fs.readFile('public/files/xxx.txt',(err,data)=>{})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值