不废话了,直接上正文,年底心好累。。。
开发环境
客户端:web浏览器
服务端:node
工具库
jsencrypt.js
一个运行在浏览器端的JS库,能够进行OpenSSL RSA的加解密,以及公钥&私钥的生成。
node-rsa
一个运行在node环境下的RSA模块,若配合 browserify 也能够运行在浏览器端,与jsencrypt.js的作用相同。
适用场景
对前端敏感信息进行公钥加密,增加攻击成本。
兼容性
客户端:所有主流现代浏览器
服务端:同 node-rsa
准备工作
使用OpenSSL生成一对公钥和密钥。
拿基于Unix OS的终端举例,运行如下代码:
// 生成一把1024 bit的私钥
openssl genrsa -out rsa_1024_priv.pem 1024
// 根据该私钥再生成一把对应的公钥
openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem
到此为止,我们完成了公密钥的生成工作。
客户端代码实现
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实现的客户端数据加密,服务端数据解密</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>
加密前数据:<input id="J_Msg" type="text">
</div>
<div>
<button id="J_EncryptBtn" type="button">客户端加密</button>
<button id="J_DecryptBtn" type="button">服务端解密</button>
</div>
<hr>
<p>加密后数据:</p>
<div id="J_EncryptedMsg" class="encrypted-msg"></div>
<p>解密后后数据:</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 () {
// 使用公匙对明文进行加密
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 服务端解密
$('#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
服务端代码实现
考虑到文章内容的冗余度,直接进行接口的代码展示。
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;
}
// 创建私钥对象
var privateKey = new NodeRSA(data.toString());
privateKey.setOptions({
// 这里需要指定RSA padding模式为pkcs1,这是因为前端jsencrypt库采用了pkcs1,而后端node-rsa默认使用的pkcs1_oaep
// https://stackoverflow.com/questions/33837617/node-rsa-errors-when-trying-to-decrypt-message-with-private-key
encryptionScheme: 'pkcs1'
});
// 对数据进行解密
var decryptedMsg = privateKey.decrypt(body.msg, 'utf8');
res.json({
success: true,
encryptedMsg: body.msg,
decryptedMsg: decryptedMsg
});
});
});
module.exports = router;
总结
综上所述,实现了一个简洁的对明文数据进行加解密的过程,总结步骤如下:
- 生成两个配对的公钥(KE)和私钥(KD)
- 使用公钥对明文(S)进行加密,生成密文(R),算法描述如下:
R = E(S, KE)
- 使用私钥(KD)对密文(R)进行解密(即逆向计算),最终得到加密前的明文(S),算法描述如下:
S = D(R, KD)
参考文献
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