原文地址:http://www.moye.me/2015/12/19/protect_jsapp_tsl_by_using_node-forge/

引子

半年前的最后一次更新(惭愧  ),提到了对称与非对称的混合加解密系统,点到为止而未涉及实践,今次将就此展开,再续爱丽丝与鲍伯的前缘。

为什么

Javascript应用的安全传输,这事不是有SSL吗,为什么要多此一举呢? 好问题,自己实现TSL是因为:

  • SSL是基于浏览器的,是浏览器负责的安全性,这意味着,非浏览器应用得不到保护,即使你的服务器上安装了证书
  • SSL需要有明确的CA受信端,自颁发证书的对等端应用,显然是使用不能

那么,这样的场景是需要自实现 TSL 的:一台node服务器 和 一个electron桌面应用 之间的安全通信

怎么做

SSL是怎么做的呢? 它的流程和之前提到的混合密码系统是一样:公私钥和对称算法混合应用,在安全和性能之间取得平衡(更为具体的流程请参见 HTTPS/SSL原理及Ruby实现)。

用啥做

既然要保障的是Javascript应用的传输安全,那自然需要在npm上网罗一下,感谢社区,让我找到了 node-forge 。它是这么介绍自己的:

JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.

功能似乎很全,然后,关于性能,这货在评测中表现颇为优秀:

任何加解密都需要操作 bytes,在node-forge里,也提供了一个byte buffer的实现:ByteStringBuffer,inspect一下,是不是很眼熟:

更多的介绍可参见 官方repo文档 和 Node.js加解密

动手实践

需求

有一个页面,通过ajax的方式与node的后端通信,我希望能把各种请求(GET/POST/PUT/DELETE)里的某个参数加密(假设为id好了)

准备工作

颁发一对公私钥:

openssl genrsa -out prv.key
openssl rsa -in prv.key -pubout > pub.key

公钥给页面用来做加密:

-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKPLxKHePPC3OusMSYqPymDuuUpojM00
OF66DkWizWoein2b7n/lTUqwtiEkwY0ZZkDvktfjijpiDJMp4xscN18CAwEAAQ==
-----END PUBLIC KEY-----

私钥给后端用来解密:

-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAKPLxKHePPC3OusMSYqPymDuuUpojM00OF66DkWizWoein2b7n/l
TUqwtiEkwY0ZZkDvktfjijpiDJMp4xscN18CAwEAAQJBAJsMZ3zmV39xoxcekXrV
dDhfogw6fZY96WJZ8uqeGp5o+E8kIiwSvVPJJ/ktntSeGdz82BKip6CB7Pw28iuM
CiECIQDZESt+cflsbSLydOX8Ioo4PGDw7ftJT4YMlUHvIaOZDwIhAMEsm4v0CSm5
4sXODT546WrnrCECk7Yi1pAwqcSmIlyxAiAyvejE7i+4QOricqEwh4J4EuU2bOtI
/+X+GwYGuH5d0QIhAITnDMk4B4nWoweWIRSHGYh8hbdcT4Xy6A3h/RsXdfKxAiEA
luBD4h2dSlbNwjFyb3bRW+1Kc4PbMFOPCX6ip5PGFQ4=
-----END RSA PRIVATE KEY-----

页面端

先clone一份node-forge源码,安装完依赖,再生成bundle:

git clone https://github.com/digitalbazaar/forge && cd forge
npm install
npm run bundle

然后就能得到一个完整的forge包:js/forge.bundle.js,在页面中引用它:

<script src="js/forge.bundle.js"></script>
<script src="js/jquery.min.js"></script> //jQuery 假定你也是用的

一如之前设计的,我们要山寨的TSL是基于RSA+AES的混合方案,纵然有node-forge这么老卵的包,也还是需要自己写点工具方法的:

//aes对称加密,返回: 密文/key/iv
function _encrypt_by_aes(message) {
var key = forge.random.getBytesSync(32);
var iv = forge.random.getBytesSync(32);
var cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(message));
cipher.finish();
var encrypted = cipher.output;
return {encrypted: encrypted, key: key, iv: iv};
}
//rsa公钥加密,传入: 公钥PEM形式
function _encrypt_by_rsa(message, pubkey) {
var pki = forge.pki;
var publicKey = pki.publicKeyFromPem(pubkey);
return publicKey.encrypt(message);
}
//序列化:把密文binary形式转成能够传输的hex形式
function _serialize(obj) {
return forge.util.bytesToHex(obj);
}

页面与后端的传输逻辑也就相对容易实施了:

function _normalize(url, kvset) {
var api_url = url + '?';
for (var k in kvset) {
api_url += k + '=' + kvset[k] + '&';
}
return api_url;
}
function _transfer_wrapper(type) {
return function(id, pubkey, url, cb_success, cb_err) {
var _cipher = _encrypt_by_aes(id);
var _cipher_id = _serialize(_cipher.encrypted);
//后端对称解密id原文需要用到的的key和iv,被rsa加密后序列化传输:
var _cipher_key = _serialize(_encrypt_by_rsa(_serialize(_cipher.key), pubkey));
var _cipher_iv = _serialize(_encrypt_by_rsa(_serialize(_cipher.iv), pubkey)); var api_url = _normalize(access_url, {
key: _cipher_key,
iv: _cipher_iv,
id: _cipher_id
}); $.ajax({type: type, url: api_url, data: {}})
.done(cb_success)
.fail(cb_err);
};
} $.fn.API_GET = _transfer_wrapper('GET');
$.fn.API_POST = _transfer_wrapper('POST');
$.fn.API_PUT = _transfer_wrapper('PUT');
$.fn.API_DELETE = _transfer_wrapper('DELETE');

页面上如此调用:

var pubkey = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKPLxKHePPC3OusMSYqPymDuuUpojM00
OF66DkWizWoein2b7n/lTUqwtiEkwY0ZZkDvktfjijpiDJMp4xscN18CAwEAAQ==
-----END PUBLIC KEY-----`;
var id = 'b5a98f0a-73a2-403a-b6fe-a7cc712169a8'; //被加密的id
var url = 'http://example.org/api'; function success(data){
console.log('SUCCESS', data);
}
function error(err){
console.log('ERR', err);
} $.fn.API_POST(id, pubkey, url, success, error);

node.js 后端

后端一切从简,假定web框架用的express 4.x,依然需要先安装node-forge包:

npm install node-forge --save

纵然老卵,工具方法还是要自己写的:

var forge = require('node-forge');

function _decrypt_by_aes(encrypted, key, iv) {
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
decipher.update(encrypted);
decipher.finish();
return decipher.output;
}
function _decrypt_by_rsa(encrypted, prvkey) {
var pki = forge.pki;
var privateKey = pki.privateKeyFromPem(prvkey);
return privateKey.decrypt(encrypted);
}
function _deserialize(hex) {
var buffer = forge.util.hexToBytes(hex);
return forge.util.createBuffer(buffer, 'raw');
} var private_key = `-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAKPLxKHePPC3OusMSYqPymDuuUpojM00OF66DkWizWoein2b7n/l
TUqwtiEkwY0ZZkDvktfjijpiDJMp4xscN18CAwEAAQJBAJsMZ3zmV39xoxcekXrV
dDhfogw6fZY96WJZ8uqeGp5o+E8kIiwSvVPJJ/ktntSeGdz82BKip6CB7Pw28iuM
CiECIQDZESt+cflsbSLydOX8Ioo4PGDw7ftJT4YMlUHvIaOZDwIhAMEsm4v0CSm5
4sXODT546WrnrCECk7Yi1pAwqcSmIlyxAiAyvejE7i+4QOricqEwh4J4EuU2bOtI
/+X+GwYGuH5d0QIhAITnDMk4B4nWoweWIRSHGYh8hbdcT4Xy6A3h/RsXdfKxAiEA
luBD4h2dSlbNwjFyb3bRW+1Kc4PbMFOPCX6ip5PGFQ4=
-----END RSA PRIVATE KEY-----`;

写个中间件,尝试解密请求并判定是否合法:

function _check_api_request(req, res, next) {
//...blabla
try { //出错必定为非法请求
var key = _deserialize(req.query.key);
var iv = _deserialize(req.query.iv);
var cipher_id = _deserialize(req.query.id);
var cipher_k = _deserialize(_decrypt_by_rsa(key.data, private_key));
var cipher_v = _deserialize(_decrypt_by_rsa(iv.data, private_key));
//注入id
req.id = _decrypt_by_aes(cipher_id, cipher_k, cipher_v);
} catch(err) { return res.sendStatus(401); } return next();
}

小结

至此,关于Javascript的TSL实践告一段落,其实上面的例子可以做得更鲁棒一些,加上CheckSum也是好的选择,有时间的话,择日将再整理一版跨语言的加解密方案。

参考

  1. Node.js加解密 http://www.jianshu.com/p/85f152944527
  2. HTTPS/SSL原理及Ruby实现 http://foobar.me/2011/05/19/https-ssl-yuan-li-ji-ruby-shi-xian/

更多文章请移步我的blog新地址: http://www.moye.me/

[Node.js] 使用node-forge保障Javascript应用的传输安全的更多相关文章

  1. node.js 使用 UglifyJS2 高效率压缩 javascript 文件

    UglifyJS2 这个工具使用很长时间了,但之前都是在 gulp 自动构建 时用到了 UglifyJS 算法进行压缩. 最近玩了一下 UglifyJS2 ,做了一个 在线压缩javascript工具 ...

  2. 一起来学node.js吧 node school简介

    node.js这几年火爆的简直丧心病狂,去lagou.com查查node.js的职位,那叫一个多. 要说火爆到什么程度,竟然有一个网站专门去教大家学习node.js, Node School. 进去逛 ...

  3. .NET程序员也学Node.js——初识Node.js

    清明在石门休了八天假,一眨眼,4月又到中旬了...看到.NET在天朝彻底沦陷而又无能为力,我开始尝试去学习一些新的东西来充实自己,我自然是打死不会去学java的,没有为什么,于是乎,最近开始学习一些前 ...

  4. 【Node.js】Node.js的安装

    Node.js的简介 简单的说,Node.js 是运行在服务端的 JavaScript. Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台. Node.js是一个事件 ...

  5. Node.js实战(五)之必备JavaScript基础

    阅读本章的话,个人觉得之前使用过JavaScript,完全轻松. Node.js的核心类型有:number.boolean.string以及object.另外两种类型分别是函数合数组,其实它们你可以理 ...

  6. Node.js入门-Node.js 介绍

    Node.js 是什么 Node.js 不是一种独立的语言,与 PHP,Python 等"既是语言优势平台"不同,它也不是一个 JavaScrip 框架,不同于 CakePHP,D ...

  7. 初识Node.js之Node与java作为后台服务器的对比

    > 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. ![file](https://img2018.cnblogs.com/blog/830272/20 ...

  8. 【Node.js】Node.js的调试

    目录结构: contents structure [-] 使用console.log() 使用Chrome DevTools 使用Visual Studio Code 与JavaScript运行在浏览 ...

  9. 极简 Node.js 入门 - Node.js 是什么、性能有优势?

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

随机推荐

  1. 解决eclipse-helios中Errors running builder JavaScript Validator的问题(转)

    原文地址:http://hi.baidu.com/%B3%BF%D1%F4%C2%FE%B2%BD/blog/item/2528f6de3ca90955ccbf1a3f.html 最近下载了eclip ...

  2. HDU1575Tr A(矩阵相乘与快速幂)

    Tr A hdu1575 就是一个快速幂的应用: 只要知道怎么求矩阵相乘!!(比赛就知道会超时,就是没想到快速幂!!!) #include<iostream> #include<st ...

  3. Flask的socket.error:10053

    一脸懵逼: 学习python一段时间,最近使用flask搭建了一个服务器,然后使用phantom(相当于浏览器)发送请求发送了几条flask就挂掉了,报错信息如下: 由于个人python经验不是很足, ...

  4. linux系统用户以及用户组管理

    本系列的博客来自于:http://www.92csz.com/study/linux/ 在此,感谢原作者提供的入门知识 这个系列的博客的目的在于将比较常用的liunx命令从作者的文章中摘录下来,供自己 ...

  5. 强大好用的"文本"编辑器

    1 editplugs 说明:EditPlus是一款由韩国 Sangil Kim (ES-Computing)出品的小巧但是功能强大的可处理文本.HTML和程序语言的Windows编辑器,你甚至可以通 ...

  6. 利用jmSlip写一个移动端顶部日历选择组件

    可滚动选日期,并限制哪些日期可选和不可选. 主要用来根据后台返回生成一个日期选择器. 具体实现可关注jmslip: https://github.com/jiamao/jmSlip 示例:http:/ ...

  7. OWIN的理解和实践(三) –Middleware开发入门

    上篇我们谈了Host和Server的建立,但Host和Server无法产出任何有实际意义的内容,真正的内容来自于加载于Server的Middleware,本篇我们就着重介绍下Middleware的开发 ...

  8. 【Bugly 技术干货】Android开发必备知识:为什么说Kotlin值得一试

    1.Hello, Kotlin Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 1. ...

  9. Dojo动画原理解析

    dojo中动画部分分为两部分:dojo/_base/fx, dojo/fx.dojo/_base/fx部分是dojo动画的基石,里面有两个底层API:animateProperty.anim和两个常用 ...

  10. Matrix Admin 后台模板笔记

    一个后台模板用久了就想换一个.上次找到了Matrix Admin.和ACE一样都是Bootstrap风格,比较容易上手.Matrix要更健壮些.感觉拿去做用户界面也是可以的. 整体风格: 1.表单验证 ...