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

最近在學習Vue+Node+Mongodb,索性就結合起來想做個個人博客,遇到的第一個坑就是如何在注冊和登錄的時候,給密碼加密傳給后臺,后臺解密。后續(xù)遇到的坑在慢慢梳理和總結哈
網(wǎng)上看了好多加密和解密的中間件,最終選擇了使用jsencrypt,后臺生成的公鑰和私鑰使用了node-rsa。簡述一下前后端在這加密和解密的大致流程哈(至于原理級別的,暫不太清楚):
后臺使用node-rsa生成公鑰和私鑰---->
后臺寫個獲取公鑰的接口(將公鑰轉化為指定格式)---->
前端調(diào)用接口獲取公鑰---->
前端使用JSEncrypt中間件設置公鑰,并加密密碼---->
前端將加密后的數(shù)據(jù)通過接口(如登錄接口)傳給后臺---->
后臺使用密鑰加密前端加密的數(shù)據(jù),在用解密后的數(shù)據(jù)查詢或者保存到數(shù)據(jù)庫中

1.安裝包

"jsencrypt": "^3.0.0-rc.1",
"node-rsa": "^1.0.5",
備注:使用Node開發(fā)時,建議安裝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,看文件名就知道誰是公鑰誰是密鑰了哈。

注意(此處有坑)---前端加密后的數(shù)據(jù)始終為false

之前自己也是在網(wǎng)上復制的代碼,有一個地方有問題,對比一下哈


1.png

在導出公鑰和密鑰的時候,exportKey的參數(shù)相同,都為pkcs1,這就導致了前端獲取到了公鑰加密之后的數(shù)據(jù)為始終為false。找了好久才知道問題所在,追溯在node-rsa中的源碼可以看到:


4.png

exportKey中的參數(shù)是有固定的值的,不然就會導致導出的公鑰或者私鑰有問題。

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.前端獲取公鑰并加密數(shù)據(jù)傳給后臺

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為游客,寫死,新建管理員數(shù)據(jù)庫直接改
                    });
                    newUser.save(function (err) {
                        if (err) {
                            res.send(err);
                        } else {
                            res.send({ 'status': 1, 'msg': '注冊成功' });
                        }
                    })
                }
            })
        }
    })
})

經(jīng)驗證,后臺解密后的密碼和前端加密前的數(shù)據(jù)一致,說明加密和解密的過程都OK。加密和解密有很多種方法的,以上總結是自己嘗過OK的一種,特此總結一下,希望能幫助到其他人。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容