2012 Jan 14

Crypto Module and Security In NodeJS

NodeJS已经在各个领域都有应用了,大部分敏感数据在公共网络上传播时都会遇到各种安全问题,在分布式系统上尤为明显。J2EE等主流应用框架都已经对HTTPS、各种加密算法有了良好支持,但是对于新型的NodeJS引擎,其版本号甚至没有达到“1”却已经掀起了业界的改革狂潮。周末抽空,来整整NodeJS里面加密和HTTPS的相关只是,希望对大家有用。

其实NodeJS的相关实现就是把C的库套了一层壳,那么JS下面的openssl等模块其实就代表了NodeJS相关的功能和性能了。编译NodeJS时务必要安装openssl模块,否则crypto模块基本不可用。

该文章的所有代码实例均是Node 0.6.4、OpenSSL 0.9.8r on MacOS Lion 64bit。演示代码的源代码都在Github上:https://github.com/RobinQu/crypto-demo

openssl-commands

Hash算法

先从最基本的Hash算法说起。以当前平台为例,支持的Digest算法不少,最常用的MD5、SHA1等。大家都知道我们这用的Hash都是不可逆的,所以这是少数几个不成对出现的方法之一,详细文档:http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createHash

var crypto = require("crypto");

var md5hash, result;
//Warning: MD5 collision is made easier and easier. Use SHA1 instead!
md5hash = crypto.createHash("md5");
md5hash.update("hello world!");
md5hash.update("hello nodejs!");
result = md5hash.digest("hex");
console.log(result); //7d962c953bb09058460f3f47650b1ab2

HMAC算法

以前我也不怎么用HMAC,所以临时wiki了一下。HMAC结果通常是带私钥两次hash后的一串字符,用来同时验证数据完整性(data integrity)和真实性(authenticity)。wiki里面的图表什么的已经说的相当详细了。NodeJS里面的相关文档:http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createHmac

var crypto = require("crypto");

var hmac, result;
hmac = crypto.createHmac("sha1", "i'm a secret!");

hmac.update("hello world!");
hmac.update("hello nodejs!");
result = hmac.digest("hex");
console.log(result); //9e7b9239f03d2e03cb041a8518977ac84ab4c9b9


对称加密算法

如果跟我一样不是安全专家的话,建议读一下MS上的一篇启蒙文章

从这里开始,NodeJS的方法都是成对出现了。NodeJS相关文档:http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createCipher

这里的加密操作要注意输入、输出的编码;早期版本的node对于base64编码的输出有bug。所以,请尽量使用hex编码输出。

API文档里要求password是binary格式,但是我直接给了一个utf8的字符串也没事⋯⋯

var crypto = require("crypto");

var cipher, ciphered, decipher, deciphered, password;

password = "i'm the password";

cipher = crypto.createCipher("aes-256-ecb", password);
//important! Update cipher content in "utf8" encoding; To be transformed to a "hex" string!
ciphered = cipher.update("hello world!", "utf8", "hex");
ciphered += cipher.update("hello nodejs!", "utf8", "hex");
ciphered += cipher.final("hex");
console.log(ciphered);//6cc3c04d5fc076ba32b742e1f3439880adbe251e87fe0be9f42a16be4d69a4b8

decipher = crypto.createDecipher("aes-256-ecb", password);
//important! Update decipher content is "hex" string; To be transfromed to a "utf8" result!
deciphered = decipher.update(ciphered, "hex", "utf8");
deciphered += decipher.final("utf8");
console.log(deciphered);//hello world!hello nodejs!

这一对方法有一个小细节,就是还有一对createCipheriv和createDecipheriv方法。后者需要多传一个iv。什么是iv呢?果断wiki啊:Initialization Vector。简单的说,这个iv就是一组随即信息,根据这组信息生成的key在对称加密过程中更加安全。事实上,createCipher和createDecipher的第二个参数password并不是直接作为对称加密的key,而是用来生成iv的。

签名算法

这个可能大家更常用一些,这个能够提供相比对称加密更高的安全级别。之前说的HMAC也是一种签名算法,但前者不涉及到非对称加密、CA等问题。NodeJS里面的createSign和createVerifier方法是需要配合公钥、私钥、CA一起使用的。

接下来的一些操作需要自己生成公钥、私钥。可能需要参考一些openssl命令:http://www.madboa.com/geek/openssl/

以下例子将对内容进行RSA-SHA256算法进行签名,该算法足以应付大部分的安全需求。该算法以SHA256进行digest,再将结果以RSA算法进行加密,得到的密文作为签名。

openssl genrsa -out mykey.pem 1024
openssl rsa -in mykey.pem -pubout > mypub.pem

这两个命令生成了一个1024位的公钥、私钥对。Node里面对内容签名的流程很清晰,diegest后用私钥加密,密文为签名。验证流程需要传入正文内容、公钥、签名,verify方法返回一个标识验证是否成功的布尔值标识签名和内容是否匹配。

var crypto = require("crypto"),
    fs = require("fs");

var signer, sign, verifier, privateKey, publicKey, result;

privateKey = fs.readFileSync("mykey.pem", "utf8");
publicKey = fs.readFileSync("mypub.pem", "utf8");

signer = crypto.createSign("RSA-SHA256");
signer.update("hello world!");
signer.update("hello node!");
sign = signer.sign(privateKey, "hex");
console.log(sign);//82071cf4bf80e8c0445351102444ce144017054c25e4832cc050a580aa13035526d432d74a33ebc8aee81376d51e0372a75bc796ece3cadea16bff58b15eccc4e7399c21e0391353c7855391e19cf48aefa148aeb73d1ad4e4e2af156d14da2a84bbf616d18353b0bb4d3570f513056c2edb20af9dfcba71695221f16f4ef182

verifier = crypto.createVerify("RSA-SHA256");
verifier.update("hello world!");
verifier.update("hello node!");
result = verifier.verify(publicKey, sign, "hex");
console.log(result);//true

HTTPS模块

打开node的https模块的源码,你会发现,这是tls模块的一个轻度封装。

最新版的Node已经对tls服务的支持相当不错了(node 0.4左右版本就是惨不忍睹的),文档里已经有非常丰富的例子:http://nodejs.org/docs/v0.6.7/api/tls.html

大家可以根据里面的代码利用X509自签名证书来做出来一个tls服务器、客户端的例子。这里就不在赘述。

One Trackback

  1. By More About Crypto Module In NodeJS – PageTalks on January 18, 2012 at 1:31 pm

    [...] 在上一篇文章里讨论了crypto模块里的一些常用方法。在0.6系列以后,crypto模块的改动非常大,增添了DiffieHellman、pbkdf2、randomByes三大块。这三个也是非常有趣的东西,在这里和大家分享一下。 [...]

Post a Comment

Your email is never shared. Required fields are marked *

*
*