码迷,mamicode.com
首页 > 编程语言 > 详细

[转载]加密算法库Crypto——nodejs中间件系列

时间:2015-09-05 16:27:38      阅读:699      评论:0      收藏:0      [点我收藏+]

标签:

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 创业者,程序员(Java,R,Javascript)
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-crypto/

技术分享

前言

密码技术是互联网应用的一项最基本的技术之一,主要保证了数据的安全。安全定义是多维度的,通过不可逆的hash算法可以保证登陆密码的安全;通过非对称的加密算法,可以保证数据存储的安全性;通过数字签名,可以验证数据在传输过程中是否被篡改。

我们要做互联网应用,数据安全性一个是不容忽视的问题。不然可能会造成,如CSDN的100万用户明文密码被泄露事情;携程网,100万用户个人信息泄露事情等。

Node.js的Crypto库就提供各种加密算法,可以非常方便地让我们使用密码技术,解决应用开发中的问题。

目录

  1. Crypto介绍
  2. Hash算法
  3. Hmac算法
  4. 加密和解密算法
  5. 签名和验证算法
  6. salt算法
  7. 程序代码

1. Crypto介绍

Crypto库是随Nodejs内核一起打包发布的,主要提供了加密、解密、签名、验证等功能。Crypto利用OpenSSL库来实现它的加密技术,它提供OpenSSL中的一系列哈希方法,包括hmac、cipher、decipher、签名和验证等方法的封装。

Crypto官方文档:http://nodejs.org/api/crypto.html

2. Hash算法

哈希算法,是指将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的输入,在计算上是不可能的,所以数据的哈希值可以检验数据的完整性。一般用于快速查找和加密算法。

通常我们对 登陆密码,都是使用Hash算法进行加密,典型的哈希算法包括 ‘md5′,’sha’,’sha1′,’sha256′,’sha512′,’RSA-SHA’。下面我们就做一下算法的测试。

系统环境

  • Win7 64bit
  • Nodejs:v0.10.31
  • Npm:1.4.23

创建项目


~ cd D:\workspace\javascript>
~ D:\workspace\javascript>mkdir nodejs-crypto && cd nodejs-crypto

新建文件hash.js,打印出所支持的hash算法。


~ vi hash.js

var crypto = require(‘crypto‘);  # 加载crypto库
console.log(crypto.getHashes()); # 打印支持的hash算法

运行程序


~ node hash.js

[ ‘DSA‘,
  ‘DSA-SHA‘,
  ‘DSA-SHA1‘,
  ‘DSA-SHA1-old‘,
  ‘RSA-MD4‘,
  ‘RSA-MD5‘,
  ‘RSA-MDC2‘,
  ‘RSA-RIPEMD160‘,
  ‘RSA-SHA‘,
  ‘RSA-SHA1‘,
  ‘RSA-SHA1-2‘,
  ‘RSA-SHA224‘,
  ‘RSA-SHA256‘,
  ‘RSA-SHA384‘,
  ‘RSA-SHA512‘,
  ‘dsaEncryption‘,
  ‘dsaWithSHA‘,
  ‘dsaWithSHA1‘,
  ‘dss1‘,
  ‘ecdsa-with-SHA1‘,
  ‘md4‘,
  ‘md4WithRSAEncryption‘,
  ‘md5‘,
  ‘md5WithRSAEncryption‘,
  ‘mdc2‘,
  ‘mdc2WithRSA‘,
  ‘ripemd‘,
  ‘ripemd160‘,
  ‘ripemd160WithRSA‘,
  ‘rmd160‘,
  ‘sha‘,
  ‘sha1‘,
  ‘sha1WithRSAEncryption‘,
  ‘sha224‘,
  ‘sha224WithRSAEncryption‘,
  ‘sha256‘,
  ‘sha256WithRSAEncryption‘,
  ‘sha384‘,
  ‘sha384WithRSAEncryption‘,
  ‘sha512‘,
  ‘sha512WithRSAEncryption‘,
  ‘shaWithRSAEncryption‘,
  ‘ssl2-md5‘,
  ‘ssl3-md5‘,
  ‘ssl3-sha1‘,
  ‘whirlpool‘ ]

我们看到支持Hash真是不少,究竟怎么选择适合,我也说不清楚。以我对算法的理解,我会以加密计算时间和编码长度,做选择的依据。下面就简单地比较一下几种常见算法。

编辑hash.js文件


~ vi hash.js

///////////////////////////
// Hash算法
///////////////////////////
var crypto = require(‘crypto‘)
    ,fs = require(‘fs‘);

function hashAlgorithm(algorithm){
    var s1 = new Date();

    var filename = "package.json";
    var txt = fs.ReadStream(filename);

    var shasum = crypto.createHash(algorithm);
    txt.on(‘data‘, function(d) {
        shasum.update(d);
    });

    txt.on(‘end‘, function() {
        var d = shasum.digest(‘hex‘);
        var s2 = new Date();

        console.log(algorithm+‘,‘+(s2-s1) +‘ms,‘+ d);
    });
}

function doHash(hashs){
    hashs.forEach(function(name){
        hashAlgorithm(name);
    })
}

//var algs = crypto.getHashes();
var algs = [ ‘md5‘,‘sha‘,‘sha1‘,‘sha256‘,‘sha512‘,‘RSA-SHA‘,‘RSA-SHA1‘,‘RSA-SHA256‘,‘RSA-SHA512‘];
doHash(algs);

运行程序


~ node hash.js

md5,6ms,85cd416f811574bd4bdb61b241266670
sha,18ms,b1fc6647fa4fdb4b1b394f8dc7856ac140e2fbdb
sha1,20ms,0777e65066dca985569fa892fa88e21b45dc656d
sha256,21ms,5e4aea76f93ee87f422fcbd9458edad0e33ddf256d5d93bcc47977e33cb1654c
sha512,23ms,94249ec2f83b006354774dd8f8ec81125ea9e1e00f94393d8b20bbd7678e63db53fab6af125e139f9257fd7dbb6c69474e443d059903a9cb2dded03a94c8143
RSA-SHA,24ms,b1fc6647fa4fdb4b1b394f8dc7856ac140e2fbdb
RSA-SHA1,25ms,0777e65066dca985569fa892fa88e21b45dc656d
RSA-SHA256,26ms,5e4aea76f93ee87f422fcbd9458edad0e33ddf256d5d93bcc47977e33cb1654c
RSA-SHA512,26ms,94249ec2f83b006354774dd8f8ec81125ea9e1e00f94393d8b20bbd7678e63db53fab6af125e139f9257fd7dbb6c69474e4433d059903a9cb2dded03a94c8143

输出以逗号分隔,分别是算法名、时间、密文。最常见的md5,密文长度最短,计算时间也最少;sha和sha1比较接近;sha512密文最长,计算时间也最长。

由于md5已经有了大量的字典库,对于安全级别一般的网站用sha1吧;如果安全级别要求很高,CPU配置也很牛,可以考虑用sha512。

3. Hmac算法

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code),HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。HMAC可以有效防止一些类似md5的彩虹表等攻击,比如一些常见的密码直接MD5存入数据库的,可能被反向破解。

定义HMAC需要一个加密用散列函数(表示为H,可以是MD5或者SHA-1)和一个密钥K。我们用B来表示数据块的字节数。(以上所提到的散列函数的分割数据块字长B=64),用L来表示散列函数的输出数据字节数(MD5中L=16,SHA-1中L=20)。鉴别密钥的长度可以是小于等于数据块字长的任何正整数值。应用程序中使用的密钥长度若是比B大,则首先用使用散列函数H作用于它,然后用H输出的L长度字符串作为在HMAC中实际使用的密钥。一般情况下,推荐的最小密钥K长度是L个字节。

由于HMAC就是使用散列函数,所以我们同样选择上面的几种算法进行测试。新建文件hmac.js。


~ vi hmac.js

///////////////////////////
// Hmac算法
///////////////////////////

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

function hmacAlgorithm(algorithm,key){
    var s1 = new Date();

    var filename = "package.json";
    var txt = fs.ReadStream(filename);

    var shasum = crypto.createHmac(algorithm,key);
    txt.on(‘data‘, function(d) {
        shasum.update(d);
    });

    txt.on(‘end‘, function() {
        var d = shasum.digest(‘hex‘);
        var s2 = new Date();

        console.log(algorithm+‘,‘+(s2-s1) +‘ms,‘+ d);
    });
}

function doHmac(hashs,key){
    console.log("\nKey : %s", key);
    console.log("============================");
    hashs.forEach(function(name){
        hmacAlgorithm(name,key);
    })
}

//var algs = crypto.getHashes();
var algs = [ ‘md5‘,‘sha‘,‘sha1‘,‘sha256‘,‘sha512‘,‘RSA-SHA‘,‘RSA-SHA1‘,‘RSA-SHA256‘,‘RSA-SHA512‘];

// 短KEY的测试
setTimeout(function(){
    doHmac(algs,"abc");
},1)

// 长KEY的测试
setTimeout(function(){
    var key = "jifdkd;adkfaj^&fjdifefdafda,ijjifdkd;adkfaj^&fjdifefdafdaljifdkd;adkfaj^&fjdifefdafda";
    doHmac(algs,key);
},2*1000)

运行程序


~ node hmac.js
Key : abc
============================
md5,6ms,bf106a077abcfa0fffe6ec0da039545b
sha,6ms,a43a00981346ac64bb7b6fb0641b72a101fb04a5
sha1,6ms,aead69a72da77d0615a854dda1086d885807574a
sha256,7ms,98ac955cb2205ba01a6337951d0ed3fd9b68753544cf81275eced365da57fc5d
sha512,8ms,054f37e34b55a19e64a7f88fb60b1122dc0a30e9864ca28d01d61115b13c74de292ab66e85bf007e1a463a52d7c30fdff174618ef954401bc9c2c3318e762c8f
RSA-SHA,10ms,a43a00981346ac64bb7b6fb0641b72a101fb04a5
RSA-SHA1,11ms,aead69a72da77d0615a854dda1086d885807574a
RSA-SHA256,12ms,98ac955cb2205ba01a6337951d0ed3fd9b68753544cf81275eced365da57fc5d
RSA-SHA512,13ms,054f37e34b55a19e64a7f88fb60b1122dc0a30e9864ca28d01d61115b13c74de292ab66e85bf007e1a463a52d7c30fdff174618ef954401bc9c2c3318e762c8f

Key : jifdkd;adkfaj^&fjdifefdafda,ijjifdkd;adkfaj^&fjdifefdafdaljifdkd;adkfaj^&fjdifefdafda
============================
md5,5ms,164a8fee6e37bb3e40a9d5dff5c2fd66
sha,5ms,ba06f536856553c3756aa36254a63ef35e225d38
sha1,7ms,f3a89b0a5ee8a55c2bb6a861748d43e9d44dc489
sha256,7ms,f2df911f40e74b2b9bb3d53a7ca4b78d438d511e015d4b50431eaea65339380d
sha512,8ms,5b4b57386b1fcc4f1945c47788bf38c013e1cde356fc15e1f946e6bf6738b5dc52ecf17b3ddc80b2ff21f985a1a707df9357fe305e9aa143da073d2cafd794dc
RSA-SHA,11ms,ba06f536856553c3756aa36254a63ef35e225d38
RSA-SHA1,12ms,f3a89b0a5ee8a55c2bb6a861748d43e9d44dc489
RSA-SHA256,14ms,f2df911f40e74b2b9bb3d53a7ca4b78d438d511e015d4b50431eaea65339380d
RSA-SHA512,16ms,5b4b57386b1fcc4f1945c47788bf38c013e1cde356fc15e1f946e6bf6738b5dc52ecf17b3ddc80b2ff21f985a1a707df9357fe305e9aa143da073d2cafd794dc

通过比对短key和长key,在编码比较长的算法上面会有一些影响。由于Hmac有了第二参数key,所以会比单独的hash加密登陆密码,有更好的安全性上的保证。

对于网站登陆密码的设计,我们可以做成2个字段存储,用password字段存密文,passkey字段存储key,把算法直接封装到程序里面。


{
  username: ‘xxxx‘
  password: ‘aead69a72da77d0615a854dda1086d885807574a‘,
  passkey:‘abc‘
}

就算数据库被攻击,黑客也只是拿了密文和key,密码明文并没有被泄露。并且在不知道加密算法的情况下,也很难通过彩虹表进行攻击。

4. 加密和解密算法

对于登陆密码来说,是不需要考虑解密的,通常都会用不可逆的算法,像md5,sha-1等。但是,对于有安全性要求的数据来说,我们是需要加密存储,然后解密使用的,这时需要用到可逆的加密算法。对于这种基于KEY算法,可以分为对称加密和不对称加密。

  • 对称加密算法的原理很容易理解,通信一方用KEK加密明文,另一方收到之后用同样的KEY来解密就可以得到明文。
  • 不对称加密算法,使用两把完全不同但又是完全匹配的一对Key:公钥和私钥。在使用不对称加密算法加密文件时,只有使用匹配的一对公钥和私钥,才能完成对明文的加密和解密过程。

对于这种类型的操作,Crypto包也提供了大量的算法支持。新建文件cipher.js,打印出所支持的算法。


~ vi cipher.js

var crypto = require(‘crypto‘);
console.log(crypto.getCiphers());

运行程序


~ node cipher.js

[ ‘CAST-cbc‘,
  ‘aes-128-cbc‘,
  ‘aes-128-cbc-hmac-sha1‘,
  ‘aes-128-cfb‘,
  ‘aes-128-cfb1‘,
  ‘aes-128-cfb8‘,
  ‘aes-128-ctr‘,
  ‘aes-128-ecb‘,
  ‘aes-128-gcm‘,
  ‘aes-128-ofb‘,
  ‘aes-128-xts‘,
  ‘aes-192-cbc‘,
  ‘aes-192-cfb‘,
  ‘aes-192-cfb1‘,
  ‘aes-192-cfb8‘,
  ‘aes-192-ctr‘,
  ‘aes-192-ecb‘,
  ‘aes-192-gcm‘,
  ‘aes-192-ofb‘,
  ‘aes-256-cbc‘,
  ‘aes-256-cbc-hmac-sha1‘,
  ‘aes-256-cfb‘,
  ‘aes-256-cfb1‘,
  ‘aes-256-cfb8‘,
  ‘aes-256-ctr‘,
  ‘aes-256-ecb‘,
  ‘aes-256-gcm‘,
  ‘aes-256-ofb‘,
  ‘aes-256-xts‘,
  ‘aes128‘,
  ‘aes192‘,
  ‘aes256‘,
  ‘bf‘,
  ‘bf-cbc‘,
  ‘bf-cfb‘,
  ‘bf-ecb‘,
  ‘bf-ofb‘,
  ‘blowfish‘,
  ‘camellia-128-cbc‘,
  ‘camellia-128-cfb‘,
  ‘camellia-128-cfb1‘,
  ‘camellia-128-cfb8‘,
  ‘camellia-128-ecb‘,
  ‘camellia-128-ofb‘,
  ‘camellia-192-cbc‘,
  ‘camellia-192-cfb‘,
  ‘camellia-192-cfb1‘,
  ‘camellia-192-cfb8‘,
  ‘camellia-192-ecb‘,
  ‘camellia-192-ofb‘,
  ‘camellia-256-cbc‘,
  ‘camellia-256-cfb‘,
  ‘camellia-256-cfb1‘,
  ‘camellia-256-cfb8‘,
  ‘camellia-256-ecb‘,
  ‘camellia-256-ofb‘,
  ‘camellia128‘,
  ‘camellia192‘,
  ‘camellia256‘,
  ‘cast‘,
  ‘cast-cbc‘,
  ‘cast5-cbc‘,
  ‘cast5-cfb‘,
  ‘cast5-ecb‘,
  ‘cast5-ofb‘,
  ‘des‘,
  ‘des-cbc‘,
  ‘des-cfb‘,
  ‘des-cfb1‘,
  ‘des-cfb8‘,
  ‘des-ecb‘,
  ‘des-ede‘,
  ‘des-ede-cbc‘,
  ‘des-ede-cfb‘,
  ‘des-ede-ofb‘,
  ‘des-ede3‘,
  ‘des-ede3-cbc‘,
  ‘des-ede3-cfb‘,
  ‘des-ede3-cfb1‘,
  ‘des-ede3-cfb8‘,
  ‘des-ede3-ofb‘,
  ‘des-ofb‘,
  ‘des3‘,
  ‘desx‘,
  ‘desx-cbc‘,
  ‘id-aes128-GCM‘,
  ‘id-aes192-GCM‘,
  ‘id-aes256-GCM‘,
  ‘idea‘,
  ‘idea-cbc‘,
  ‘idea-cfb‘,
  ‘idea-ecb‘,
  ‘idea-ofb‘,
  ‘rc2‘,
  ‘rc2-40-cbc‘,
  ‘rc2-64-cbc‘,
  ‘rc2-cbc‘,
  ‘rc2-cfb‘,
  ‘rc2-ecb‘,
  ‘rc2-ofb‘,
  ‘rc4‘,
  ‘rc4-40‘,
  ‘rc4-hmac-md5‘,
  ‘seed‘,
  ‘seed-cbc‘,
  ‘seed-cfb‘,
  ‘seed-ecb‘,
  ‘seed-ofb‘ ]

同样地,在这么一大堆的算法面前,完全不知道如何去选择。我还是以加密和解密的计算时间为参考指标,选出几个常见的算法进行测试。


///////////////////////////
// 加密解密算法
///////////////////////////
var crypto = require(‘crypto‘)
    ,fs = require(‘fs‘);

//加密
function cipher(algorithm, key, buf ,cb){
    var encrypted = "";
    var cip = crypto.createCipher(algorithm, key);
    encrypted += cip.update(buf, ‘binary‘, ‘hex‘);
    encrypted += cip.final(‘hex‘);
    cb(encrypted);
}

//解密
function decipher(algorithm, key, encrypted,cb){
    var decrypted = "";
    var decipher = crypto.createDecipher(algorithm, key);
    decrypted += decipher.update(encrypted, ‘hex‘, ‘binary‘);
    decrypted += decipher.final(‘binary‘);
    cb(decrypted);
}

function cipherDecipherFile(filename,algorithm, key){
    fs.readFile(filename, "utf-8",function (err, data) {
        if (err) throw err;
        var s1 = new Date();

        cipher(algorithm, key,data,function(encrypted){
            var s2 = new Date();
            console.log(‘cipher:‘+algorithm+‘,‘+(s2-s1) +‘ms‘);

            decipher(algorithm, key,encrypted,function(txt){
                var s3 = new Date();
                console.log(‘decipher:‘+algorithm+‘,‘+(s3-s2) +‘ms‘);
//                console.log(txt);
            });
        });
    });
}

//console.log(crypto.getCiphers());
var algs = [‘blowfish‘,‘aes-256-cbc‘,‘cast‘,‘des‘,‘des3‘,‘idea‘,‘rc2‘,‘rc4‘,‘seed‘];
var key = "abc";
var filename = "book.pdf";//"package.json";
algs.forEach(function(name){
    cipherDecipherFile(filename,name,key);
})

运行程序


~ node cipher.js

cipher:blowfish,46ms
decipher:blowfish,95ms
cipher:des,67ms
decipher:des,104ms
cipher:idea,54ms
decipher:idea,88ms
cipher:rc4,16ms
decipher:rc4,44ms
cipher:des3,158ms
decipher:des3,193ms
cipher:aes-256-cbc,19ms
decipher:aes-256-cbc,47ms
cipher:cast,46ms
decipher:cast,82ms
cipher:seed,64ms
decipher:seed,98ms
cipher:rc2,104ms
decipher:rc2,99ms

输出一共3列,第一列,cipher(加密),decipher(解密);第二列,是算法名称;第三列是计算时间。

在选中的这几个算法中,rc4和aes-256-cbc是表现不错的算法,加密和解密时间都比较短,加密时间:解密时间=1:3;其他的算法,总体时间相对较长,有的加密时间:解密时间=1:1。所以,怎么选算法,另外的一个标准就要看业务需求了。如果业务上,解密操作的次数远大于加密操作的次数,而且是在服务器计算,那么我们最好找到,加密时间:解密时间=N:1,N>1的算法;如果加密在服务器端,解密在客户端完成,那么aes-256-cbc算法的计算时间比例就非常适合了。

5. 签名和验证算法

我们除了对数据进行加密和解密,还需要判断数据在传输过程中,是否真实际和完整,是否被篡改了。那么就需要用到签名和验证的算法,利用不对称加密算法,通过私钥进行数字签名,公钥验证数据的真实性。

数字签名的制作和验证过程,如下图所示。

技术分享

下面我们用程序,来现实图中的操作流程,由于证书是我们自己制作的,不打算对外公开,没有到CA进行认证,所以下过程将不包括公钥伪造,再到CA认证的过程。

首先,我们要用openSSL命令先生成,私钥server.pem和公钥cert.pem。


# 生成私钥
~ D:\workspace\javascript\nodejs-crypto>openssl genrsa  -out server.pem 1024
Generating RSA private key, 1024 bit long modulus
..................++++++
..................++++++
e is 65537 (0x10001)

# 生成公钥
~ D:\workspace\javascript\nodejs-crypto>openssl req -key server.pem -new -x509 -out cert.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.‘, the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:
Email Address []:

接下来,我们利用生成私钥生成数学签名,然后再用公钥验证数据,是否被篡改。新建文件signer.js。


~ vi signer.js

///////////////////////////
// 签名验证算法
// openssl genrsa  -out server.pem 1024
// openssl req -key server.pem -new -x509 -out cert.pem
///////////////////////////

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

function signer(algorithm,key,data){
    var sign = crypto.createSign(algorithm);
    sign.update(data);
    sig = sign.sign(key, ‘hex‘);
    return sig;
}

function verify(algorithm,pub,sig,data){
    var verify = crypto.createVerify(algorithm);
    verify.update(data);
    return verify.verify(pubkey, sig, ‘hex‘)
}

var algorithm = ‘RSA-SHA256‘;
var data = "abcdef";   //传输的数据
var privatePem = fs.readFileSync(‘server.pem‘);
var key = privatePem.toString();
var sig = signer(algorithm,key,data); //数字签名

var publicPem = fs.readFileSync(‘cert.pem‘);
var pubkey = publicPem.toString();
console.log(verify(algorithm,pubkey,sig,data));         //验证数据,通过公钥、数字签名 =》是原始数据
console.log(verify(algorithm,pubkey,sig,data+"2"));    //验证数据,通过公钥、数字签名  =》不是原始数据

运行程序


~ node signer.js
true
false

两行输出结果,第一行验证的结果是true,表示数据在传输过程中,没有被篡改;第二行验证的结果是false,表示数据在传输过程中被篡改,不是原始的数据。当然,如何保证私钥和公钥的匹配,需要CA第三方来认证,与Crypto库无关本文不再介绍。

6. salt算法

我们知道,如果直接对密码进行散列,那么黑客可以对通过获得这个密码散列值,然后通过查散列值字典(例如MD5密码破解网站),得到某用户的密码。

盐(Salt),在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。加盐后的散列值,可以极大的降低由于用户数据被盗而带来的密码泄漏风险,即使通过彩虹表寻找到了散列后的数值所对应的原始内容,但是由于经过了加盐,插入的字符串扰乱了真正的密码,使得获得真实密码的概率大大降低。

加盐的实现过程通常是在需要散列的字段的特定位置增加特定的字符,打乱原始的字串,使其生成的散列结果产生变化。比如,用户使用了一个密码:


123465

经过MD5散列后,可以得出结果:


3d9188577cc9bfe9291ac66b5cc872b7

但是由于用户密码位数不足,短密码的散列结果很容易被彩虹表破解,因此,在用户的密码末尾添加特定字串:


123465abcdefghijklmnopqrstuvwxyz

因此,加盐后的密码位数更长了,散列的结果也发生了变化:


27e20c64ccb8cce9ad68b8ccff6252cf

新建文件salt.js,实现上面的程序。


~ vi salt.js

//////////////////////////////
// salt算法
//////////////////////////////
var crypto = require(‘crypto‘);
var md5 = crypto.createHash(‘md5‘);
var txt = "123465";

md5.update(txt);
console.log(md5.digest(‘hex‘));

md5 = crypto.createHash(‘md5‘);
var salt = "abcdefghijklmnopqrstuvwxyz";
md5.update(txt+salt);
console.log(md5.digest(‘hex‘));

我们可以不用自己动手加盐,而使用crypto.pbkdf2()函数,默认会调用hmac算法,用sha1的散列函数,并且可以设置迭代次数和密文长度。具体的使用代码如下。


~ vi salt.js

var crypto = require(‘crypto‘);
var txt = "123465";
var salt = "abcdefghijklmnopqrstuvwxyz";

// 生成密文,默认HMAC函数是sha1算法
crypto.pbkdf2(txt, salt, 4096, 256, function (err,hash) {
    if (err) { throw err; }
    console.log(hash.toString(‘hex‘));
})

运行程序,生成256位的密文。


~ node salt.js

29c7de002d942ebcbf3afe05f3eb0ff620f0515fe6b8f19176736273cd70805afdfcc828a6f227152dfa4d0c4f96da184fbd060d4d4c86a5deb8d704699c3e8653acdb0e5bc3e584e0890a44206bb2926f0289fc8e0abe49fd1876461fcc50f06dc7991c4b93cc4e80076529c73b2f2c56f16b5b319368edf017f3d3583a33aa44fd30f89801f0d8877eb8262925f5fdc40a5c57f1b275e5674784dca635c75bc58b6c22264e0f29e363eb25dedf1a242429084e3e17d344b59cab3b9723db03ee4838b632786d1a9eb968f2523404286e5d0a41a1707577650cc3cc2f1ab65714a4cb31f068e4aefa259c6be68174e0a475d5610168305a4935a14bb221a516

如果加盐每次都是固定的值也会不安全,我们还可以利用随机randomBytes()函数,配合pbkdf2()函数,让每次都是不同的salt,生成安全级别更高的密文。


~ vi salt.js

//通过伪随机码生成salt,进行加密
crypto.randomBytes(128, function (err, salt) {
    if (err) { throw err;}
    salt = salt.toString(‘hex‘);
    console.log(salt); //生成salt

    crypto.pbkdf2(txt, salt, 4096, 256, function (err,hash) {
        if (err) { throw err; }
        hash = hash.toString(‘hex‘);
        console.log(hash);//生成密文
    })
})

运行程序


~ node salt.js

# 随机生成的salt
78e59de99f16697e3eb684dcfa8efa086db0940c7cd47d33f9311e3bfcf9d58bf30915f54b3f72793b5c8568d32f1f15c55cc87affd043d96f1ed1f56c25a8054b3d83a306636f3b9e3bc9e48c3303aff54da006f92e370023165857fce0a1d1ff0b89178ae8c1416747275daba25652ea864d52a80427658ea69dbe500a7261

# 通过salt生成的密文
48943eb51ea702b436f95bf1dacc7f64bc41cf7cfa4cb40d101d5550c28caafc58ca720934352238430634f21fd5a6a4ef63fe5828c2665362e9902adc0305f93d2523fbd28521ad00947a74ff8229f63ad5796f2e12677cbed6af02b9973ee0187a69ad67e86790471d95f18d6d2c43ef904f7d17a5d8264f8236f227363a016ae2c14559c17236d540e06c5fd443af740721897f76bdbd9711c8499d7a34cae2e917f900fc364f72f9afaf301845c6e0b5c37def949b4af62336a39dbd1e829405d6189536092c7769a5d7e427b8e97419988da4e1bad49c69f25ac4e96f74a0ce3eab9e1433277568105b1dcc0cf9e1f9c91a7ed391c5825eefcd71ef5ca1

我们把salt和密文一起进行存储,保证用户密码的安全性。

7. 程序代码

本文的程序代码,可以直接从Github上面下载本文项目中的源代码,按照片文章中的介绍学习Crypto,下载地址:https://github.com/bsspirit/nodejs-crypto

也可以直接用github命令行来下载:


~ git clone git@github.com:bsspirit/nodejs-crypto.git   # 下载github项目
~ cd nodejs-crypto                                      # 进入下载目录

最后要声明一下:本人对密码技术理解不深,本文所涉及的密码技术仅仅是Crypto库在应用上的,文中观点不对或描述不清楚的地方,还请专家来指正!!

参考文章:

转载请注明出处:
http://blog.fens.me/nodejs-crypto

[转载]加密算法库Crypto——nodejs中间件系列

标签:

原文地址:http://www.cnblogs.com/sunws/p/4783358.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!