node 第十七天 使用rsa非对称加密 实现前后端加密通信 JSEncrypt和node-rsa

  1. 什么是非对称加密

    加密过程需要两个钥匙, 公钥和私钥

    其中公钥用于加密明文, 私钥用于解密公钥加密的密文, 解密只可以用私钥

    公钥和私钥是一对一的关系

    公钥可以发送给用户, 不用担心泄露

    私钥需要保存在服务端, 不能泄露

    例如: 战场上,B要给A传递一条消息,内容为某一指令。
    RSA的加密过程如下:
    1.A生成一对密钥(公钥和私钥),私钥不公开,A自己保留。公钥为公开的,任何人可以获取。
    2.A传递自己的公钥给B,B用A的公钥对消息进行加密。
    3.A接收到B加密的消息,利用A自己的私钥对消息进行解密。
    在这个过程中,只有2次传递过程,第一次是A传递公钥给B,第二次是B传递加密消息给A,即使都被敌方截获,也没有危险性,因为只有A的私钥才能对消息进行解密,防止了消息内容的泄露。

  2. 在web业务中使用非对称加密

    比如用户登录接口, 我们可以对密码进行密文传输

    客户端在调用登录接口前先请求服务端获得公钥, 用公钥对密码进行加密

    服务端拿到加密后的密码, 用私钥进行解密

    这样的话即使密码泄露了, 泄露的也不是真实密码, 即使攻击者直接拿密文请求接口, 我们发现后也可以刷新私钥和公钥, 使之前的密文无效, 而不用修改用户的密码

  3. 客户端使用JSEncrypt库(客户端只用加密)

    html 复制代码
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <script src="./node_modules/axios/dist/axios.min.js"></script>
      <script src="./node_modules/jsencrypt/bin/jsencrypt.min.js"></script>
      <script>
        let key = '';
        function encrypt(publicKey, value) {
          let encrypt = new JSEncrypt();
          encrypt.setPublicKey(publicKey);
          return encrypt.encrypt(value);
        }
        const getPublicKey = () => {
          return axios
            .get('http://127.0.0.1:3000/public_key')
            .then(res => {
              key = res.data.key;
            })
            .catch(err => {
              console.log(err);
            });
        };
        const login = async () => {
          await getPublicKey();
          axios
            .post('http://127.0.0.1:3000/login', {
              user: 'gu',
              pwd: encrypt(key, '10086')
            })
            .then(res => {
              console.log(res);
            });
        };
      </script>
      <body>
        <button>登录</button>
      </body>
      <script>
        document.querySelector('button').onclick = login;
      </script>
    </html>

    值的注意的是encrypt加密的内容必须是一个字符串, 这一点在文档中没有写, 笔者是看了源码才发现, 毕竟是开源的东西, 作者也没有义务一定要把文档写的多好, ε=(´ο`*)))唉

  4. 服务端使用node-rsa

    生成秘钥方法

    js 复制代码
    const fs = require('fs');
    const path = require('path');
    const NodeRSA = require('node-rsa');
    const cerPath = path.join(process.cwd(), './auth');
    
    function generateKeys() {
      const newkey = new NodeRSA({ b: 512 });
      newkey.setOptions({ encryptionScheme: 'pkcs1' }); //因为jsencrypt自身使用的是pkcs1加密方案,只有从后台改咯
      let public_key = newkey.exportKey('pkcs8-public'); //公钥,
      let private_key = newkey.exportKey('pkcs8-private'); //私钥
      fs.writeFileSync(path.join(cerPath, 'private.cer'), private_key);
      fs.writeFileSync(path.join(cerPath, 'public.cer'), public_key);
    }
    // generateKeys(); //仅初始化执行一次

    加密解密方法(服务端只用解密)

    js 复制代码
    const fs = require('fs');
    const path = require('path');
    const NodeRSA = require('node-rsa');
    
    const cerPath = path.join(process.cwd(), './auth');
    
    function encrypt(plain) {
      let public_key = fs.readFileSync(path.join(cerPath, 'public.cer'), 'utf8'); //公钥
      const nodersa = new NodeRSA(public_key);
      nodersa.setOptions({ encryptionScheme: 'pkcs1' });
      return nodersa.encrypt(plain, 'base64');
    }
    
    function decrypt(cipher) {
      let private_key = fs.readFileSync(path.join(cerPath, 'private.cer'), 'utf8'); //私钥
      const nodersa = new NodeRSA(private_key);
      nodersa.setOptions({ encryptionScheme: 'pkcs1' });
      //decrypt(data: Buffer | string, encoding: NodeRSA.Encoding): string;
      //cipher必须是string或者Buffer
      return nodersa.decrypt(cipher, 'utf8');
    }
    
    module.exports = {
      encrypt,
      decrypt,
      cerPath
    };

    接口路由

    js 复制代码
    var express = require('express');
    const { decrypt, cerPath } = require('../core/rsa');
    const fs = require('fs');
    const path = require('path');
    
    var router = express.Router();
    
    /* GET home page. */
    router.get('/', (req, res, next) => {
      res.render('index', { title: 'Express' });
    });
    
    router.get('/public_key', (req, res, next) => {
      try {
        const publicKey = fs.readFileSync(path.join(cerPath, './public.cer'), 'utf8');
        res.send({
          key: publicKey
        });
      } catch (e) {
        console.log(e);
        next();
      }
    });
    
    router.post('/login', (req, res, next) => {
      let { user, pwd } = req.body;
      try {
        let { resUser, resPwd } = { resUser: user, resPwd: decrypt(pwd) };
        res.send({
          user: resUser,
          pwd: resPwd,
          msg: 'ok'
        });
      } catch (error) {
        res.send({
          msg: 'error'
        });
      }
    });
    
    module.exports = router;
  5. 结果示例

  6. 为什么前后端用不同的类库却能协作加密解密

    其实是因为, 底层使用的数学方法都是相同的, 毕竟加密是高等数学~😃

相关推荐
咖啡の猫2 小时前
Shell脚本-for循环应用案例
前端·chrome
lingggggaaaa2 小时前
小迪安全v2023学习笔记(六十二讲)—— PHP框架反序列化
笔记·学习·安全·web安全·网络安全·php·反序列化
Johny_Zhao3 小时前
基于 Docker 的 LLaMA-Factory 全流程部署指南
linux·网络·网络安全·信息安全·kubernetes·云计算·containerd·yum源·系统运维·llama-factory
百万蹄蹄向前冲4 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5815 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路5 小时前
GeoTools 读取影像元数据
前端
ssshooter5 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友5 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
黑客影儿6 小时前
Kali Linux 环境中的系统配置文件与用户配置文件大全
linux·运维·程序人生·安全·网络安全·系统安全·学习方法
Jerry6 小时前
Jetpack Compose 中的状态
前端