美文网首页
2019-07-17 jsencrypt加密和解密的那点事

2019-07-17 jsencrypt加密和解密的那点事

作者: 快乐女孩筱梅 | 来源:发表于2019-07-17 15:34 被阅读0次

    最近在学习Vue+Node+Mongodb,索性就结合起来想做个个人博客,遇到的第一个坑就是如何在注册和登录的时候,给密码加密传给后台,后台解密。后续遇到的坑在慢慢梳理和总结哈
    网上看了好多加密和解密的中间件,最终选择了使用jsencrypt,后台生成的公钥和私钥使用了node-rsa。简述一下前后端在这加密和解密的大致流程哈(至于原理级别的,暂不太清楚):
    后台使用node-rsa生成公钥和私钥---->
    后台写个获取公钥的接口(将公钥转化为指定格式)---->
    前端调用接口获取公钥---->
    前端使用JSEncrypt中间件设置公钥,并加密密码---->
    前端将加密后的数据通过接口(如登录接口)传给后台---->
    后台使用密钥加密前端加密的数据,在用解密后的数据查询或者保存到数据库中

    1.安装包

    "jsencrypt": "^3.0.0-rc.1",
    "node-rsa": "^1.0.5",
    备注:使用Node开发时,建议安装nodemon,可以热加载后台改的代码,超级好用,只需要安装一下包,在启动的使用nodemon app.js即可

    2.后台生成公钥和密钥

    const NodeRSA = require('node-rsa');
    const fs = require('fs');
    //生成公钥
    function generator() {
        var key = new NodeRSA({ b: 512 })
        key.setOptions({ encryptionScheme: 'pkcs1' })
    
        var privatePem = key.exportKey('pkcs1-private-pem')
        var publicPem = key.exportKey('pkcs8-public-pem')
    
        fs.writeFile('./pem/public.pem', publicPem, (err) => {
            if (err) throw err
            console.log('公钥已保存!')
        })
        fs.writeFile('./pem/private.pem', privatePem, (err) => {
            if (err) throw err
            console.log('私钥已保存!')
        })
    }
    generator();
    

    这样就可以在pem目录下生成两个文件:private.pem和public.pem,看文件名就知道谁是公钥谁是密钥了哈。

    注意(此处有坑)---前端加密后的数据始终为false

    之前自己也是在网上复制的代码,有一个地方有问题,对比一下哈


    1.png

    在导出公钥和密钥的时候,exportKey的参数相同,都为pkcs1,这就导致了前端获取到了公钥加密之后的数据为始终为false。找了好久才知道问题所在,追溯在node-rsa中的源码可以看到:


    4.png
    exportKey中的参数是有固定的值的,不然就会导致导出的公钥或者私钥有问题。

    3.后台设置获取公钥接口供前端使用

    router.get('/api/getPublicKey', (req, res) => {
        try {
            let publicKey = fs.readFileSync('./pem/public.pem', 'utf-8');
            console.log('publicKey', publicKey)
            res.send({ 'status': 0, 'msg': '公钥获取成功', 'resultmap': publicKey });
        } catch (err) {
            res.send(err);
        }
    })
    

    我使用的是Express中间件哈,使用Koa或者其他中间件的自行修改。

    4.前端获取公钥并加密数据传给后台

    import { JSEncrypt } from "jsencrypt";
    
      that.$axios
            .get(webUrl + "getPublicKey")
            .then(res => {
              if (res.data.status === 0) {
                let encryptor = new JSEncrypt();  //实例化
                encryptor.setPublicKey(res.data.resultmap); //设置公钥
                console.log(that.password);
                console.log(encryptor.encrypt(a));
                let data = {
                  name: that.name,
                  password: encryptor.encrypt(that.password), //加密
                  nickName: that.nickName
                };
                that.$axios
                  .post(webUrl + "admin/signUp", data)
                  .then(response => {
                    that.$message({
                      type: "success",
                      message: response.data.msg
                    });
                    if (response.data.status == 1) {
                      that.back();
                    }
                  })
                  .catch(reject => {
                    console.log(reject);
                  });
              }
            })
            .catch(err => {
              console.log(err);
            });
    

    5.后台用私钥解密

    // 注册
    router.post('/api/admin/signUp', (req, res) => {
        //是否重名
        db.User.find({ name: req.body.name }, (err, docs) => {
            if (err) {
                res.send(err);
                return
            }
            if (docs.length > 0) {
                res.send({ 'status': 0, 'msg': '用户名已注册' });
            } else {
                db.User.find({ nickName: req.body.nickName }, (err, docs) => {
                    if (err) {
                        res.send(err);
                        return
                    }
                    if (docs.length > 0) {
                        res.send({ 'status': 0, 'msg': '昵称已注册' });
                    } else {
                        const privateKey = fs.readFileSync('./pem/private.pem', 'utf8'); //读取私钥
                        let buffer1 = Buffer.from(req.body.password, 'base64'); //转化格式
                        let password = crypto.privateDecrypt({
                            key: privateKey,
                            padding: crypto.constants.RSA_PKCS1_PADDING // 注意这里的常量值要设置为RSA_PKCS1_PADDING
                        },  buffer1).toString('utf8');
                        console.log('解密之后的密码', password);
                        let newUser = new db.User({
                            name: req.body.name,
                            password: password,
                            nickName: req.body.nickName,
                            avatar: null,
                            // type: req.body.type
                            type: 2//1为管理员,2为游客,写死,新建管理员数据库直接改
                        });
                        newUser.save(function (err) {
                            if (err) {
                                res.send(err);
                            } else {
                                res.send({ 'status': 1, 'msg': '注册成功' });
                            }
                        })
                    }
                })
            }
        })
    })
    

    经验证,后台解密后的密码和前端加密前的数据一致,说明加密和解密的过程都OK。加密和解密有很多种方法的,以上总结是自己尝过OK的一种,特此总结一下,希望能帮助到其他人。

    相关文章

      网友评论

          本文标题:2019-07-17 jsencrypt加密和解密的那点事

          本文链接:https://www.haomeiwen.com/subject/vgbqlctx.html