不廢話了,直接上正文懂鸵,年底心好累偏螺。。匆光。
開(kāi)發(fā)環(huán)境
客戶端:web瀏覽器
服務(wù)端:node
工具庫(kù)
jsencrypt.js
一個(gè)運(yùn)行在瀏覽器端的JS庫(kù)套像,能夠進(jìn)行OpenSSL RSA的加解密,以及公鑰&私鑰的生成殴穴。
node-rsa
一個(gè)運(yùn)行在node環(huán)境下的RSA模塊凉夯,若配合 browserify 也能夠運(yùn)行在瀏覽器端货葬,與jsencrypt.js的作用相同采幌。
適用場(chǎng)景
對(duì)前端敏感信息進(jìn)行公鑰加密,增加攻擊成本震桶。
兼容性
客戶端:所有主流現(xiàn)代瀏覽器
服務(wù)端:同 node-rsa
準(zhǔn)備工作
使用OpenSSL生成一對(duì)公鑰和密鑰休傍。
拿基于Unix OS的終端舉例,運(yùn)行如下代碼:
// 生成一把1024 bit的私鑰
openssl genrsa -out rsa_1024_priv.pem 1024
// 根據(jù)該私鑰再生成一把對(duì)應(yīng)的公鑰
openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem
到此為止蹲姐,我們完成了公密鑰的生成工作磨取。
客戶端代碼實(shí)現(xiàn)
rsa.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>基于RSA實(shí)現(xiàn)的客戶端數(shù)據(jù)加密人柿,服務(wù)端數(shù)據(jù)解密</title>
<style>
body {
margin: 20% 30%;
font-size: 24px;
font-weight: bold;
}
.encrypted-msg {
word-break: break-all;
}
</style>
</head>
<body>
<!-- 公鑰rsa_1024_pub.pem中的key字串 -->
<textarea name="" id="J_PublicKey" cols="30" rows="10" hidden>
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8asrfSaoOb4je+DSmKdriQJKW
VJ2oDZrs3wi5W67m3LwTB9QVR+cE3XWU21Nx+YBxS0yun8wDcjgQvYt625ZCcgin
2ro/eOkNyUOTBIbuj9CvMnhUYiR61lC1f1IGbrSYYimqBVSjpifVufxtx/I3exRe
ZosTByYp4Xwpb1+WAQIDAQAB
-----END PUBLIC KEY-----
</textarea>
<div>
加密前數(shù)據(jù):<input id="J_Msg" type="text">
</div>
<div>
<button id="J_EncryptBtn" type="button">客戶端加密</button>
<button id="J_DecryptBtn" type="button">服務(wù)端解密</button>
</div>
<hr>
<p>加密后數(shù)據(jù):</p>
<div id="J_EncryptedMsg" class="encrypted-msg"></div>
<p>解密后后數(shù)據(jù):</p>
<div id="J_DecryptedMsg" class="decrypted-msg"></div>
<script src="/jquery/dist/jquery.js"></script>
<script src="./jsencrypt.js"></script>
<script src="./rsa.js"></script>
</body>
</html>
rsa.js
//#region 客戶端加密
$('#J_EncryptBtn').click(function () {
// 使用公匙對(duì)明文進(jìn)行加密
var encrypt = new JSEncrypt();
var publicKey = $.trim($('#J_PublicKey').val());
var msg = $.trim($('#J_Msg').val());
if (!msg) {
return;
}
encrypt.setPublicKey(publicKey);
var encryptedMsg = encrypt.encrypt(msg);
$('#J_EncryptedMsg').html(encryptedMsg);
});
//#endregion
//#region 服務(wù)端解密
$('#J_DecryptBtn').click(function () {
$.ajax({
method: 'POST',
url: '/rsa/decrypt',
data: {
msg: $('#J_EncryptedMsg').html()
}
}).then(function (res) {
if (!res || !res.success) {
return;
}
$('#J_DecryptedMsg').html(res.decryptedMsg);
}, function () {
});
});
//#endregion
服務(wù)端代碼實(shí)現(xiàn)
考慮到文章內(nèi)容的冗余度,直接進(jìn)行接口的代碼展示忙厌。
var express = require('express');
var router = express.Router();
var NodeRSA = require('node-rsa');
var fs = require('fs');
var path = require('path');
router.post('/decrypt', function (req, res, next) {
var body = req.body;
console.log(body);
// 讀取私鑰
fs.readFile(path.join(__dirname, "./private.pem"), (err, data) => {
if (err) {
res.status(500).json({
success: false
});
return;
}
// 創(chuàng)建私鑰對(duì)象
var privateKey = new NodeRSA(data.toString());
privateKey.setOptions({
// 這里需要指定RSA padding模式為pkcs1凫岖,這是因?yàn)榍岸薺sencrypt庫(kù)采用了pkcs1,而后端node-rsa默認(rèn)使用的pkcs1_oaep
// https://stackoverflow.com/questions/33837617/node-rsa-errors-when-trying-to-decrypt-message-with-private-key
encryptionScheme: 'pkcs1'
});
// 對(duì)數(shù)據(jù)進(jìn)行解密
var decryptedMsg = privateKey.decrypt(body.msg, 'utf8');
res.json({
success: true,
encryptedMsg: body.msg,
decryptedMsg: decryptedMsg
});
});
});
module.exports = router;
總結(jié)
綜上所述逢净,實(shí)現(xiàn)了一個(gè)簡(jiǎn)潔的對(duì)明文數(shù)據(jù)進(jìn)行加解密的過(guò)程哥放,總結(jié)步驟如下:
- 生成兩個(gè)配對(duì)的公鑰(KE)和私鑰(KD)
- 使用公鑰對(duì)明文(S)進(jìn)行加密,生成密文(R)爹土,算法描述如下:
R = E(S, KE)
- 使用私鑰(KD)對(duì)密文(R)進(jìn)行解密(即逆向計(jì)算)甥雕,最終得到加密前的明文(S),算法描述如下:
S = D(R, KD)
參考文獻(xiàn)
https://github.com/travist/jsencrypt
https://www.npmjs.com/package/node-rsa
http://www.cnblogs.com/leoo2sk/archive/2010/10/01/hash-and-encrypt.html
https://www.cnblogs.com/o--ok/p/4827580.html