node_05 基本模塊

crypto

crypto模塊的目的是為了提供通用的加密和哈希算法当娱。用純JavaScript代碼實(shí)現(xiàn)這些功能不是不可能誊册,但速度會非常慢仙蛉。Nodejs用C/C++實(shí)現(xiàn)這些算法后材彪,通過cypto這個(gè)模塊暴露為JavaScript接口您市,這樣用起來方便觉痛,運(yùn)行速度也快。

MD5和SHA1

MD5是一種常用的哈希算法茵休,用于給任意數(shù)據(jù)一個(gè)“簽名”薪棒。這個(gè)簽名通常用一個(gè)十六進(jìn)制的字符串表示:

    const crypto = require('crypto');

    const hash = crypto.createHash('md5');

    // 可任意多次調(diào)用update():
    hash.update('Hello, world!');
    hash.update('Hello, nodejs!');
    //相當(dāng)於字符串疊加

    console.log(hash.digest('hex')); // 7e1977739c748beac0c0fd14fd26a544

update()方法默認(rèn)字符串編碼為UTF-8,也可以傳入Buffer榕莺。

如果要計(jì)算SHA1俐芯,只需要把'md5'改成'sha1',就可以得到SHA1的結(jié)果1f32b9c9932c02227819a4151feed43e131aca40钉鸯。

還可以使用更安全的sha256和sha512吧史。

Hmac

Hmac算法也是一種哈希算法,它可以利用MD5或SHA1等哈希算法唠雕。不同的是贸营,Hmac還需要一個(gè)密鑰:

    const crypto = require('crypto');

    const hmac = crypto.createHmac('sha256', 'secret-key');

    hmac.update('Hello, world!');
    hmac.update('Hello, nodejs!');

    console.log(hmac.digest('hex')); // 80f7e22570...

只要密鑰發(fā)生了變化,那么同樣的輸入數(shù)據(jù)也會得到不同的簽名岩睁,因此钞脂,可以把Hmac理解為用隨機(jī)數(shù)“增強(qiáng)”的哈希算法。

AES

AES是一種常用的對稱加密算法捕儒,加解密都用同一個(gè)密鑰冰啃。crypto模塊提供了AES支持,但是需要自己封裝好函數(shù)刘莹,便于使用:

    const crypto = require('crypto');

    function aesEncrypt(data, key) {
        const cipher = crypto.createCipher('aes192', key);
        var crypted = cipher.update(data, 'utf8', 'hex');
        crypted += cipher.final('hex');
        return crypted;
    }

    function aesDecrypt(encrypted, key) {
        const decipher = crypto.createDecipher('aes192', key);
        var decrypted = decipher.update(encrypted, 'hex', 'utf8');
        decrypted += decipher.final('utf8');
        return decrypted;
    }

    var data = 'Hello, this is a secret message!';
    var key = 'Password!';
    var encrypted = aesEncrypt(data, key);
    var decrypted = aesDecrypt(encrypted, key);

    console.log('Plain text: ' + data);
    console.log('Encrypted text: ' + encrypted);
    console.log('Decrypted text: ' + decrypted);

運(yùn)行結(jié)果如下:

Plain text: Hello, this is a secret message!
Encrypted text: 8a944d97bdabc157a5b7a40cb180e7...
Decrypted text: Hello, this is a secret message!

注意到AES有很多不同的算法阎毅,如aes192,aes-128-ecb点弯,aes-256-cbc等扇调,AES除了密鑰外還可以指定IV(Initial Vector),不同的系統(tǒng)只要IV不同蒲拉,用相同的密鑰加密相同的數(shù)據(jù)得到的加密結(jié)果也是不同的肃拜。加密結(jié)果通常有兩種表示方法:hex和base64痴腌,這些功能Nodejs全部都支持雌团,但是在應(yīng)用中要注意,如果加解密雙方一方用Nodejs士聪,另一方用Java锦援、PHP等其它語言,需要仔細(xì)測試剥悟。如果無法正確解密灵寺,要確認(rèn)雙方是否遵循同樣的AES算法曼库,字符串密鑰和IV是否相同,加密后的數(shù)據(jù)是否統(tǒng)一為hex或base64格式略板。

Diffie-Hellman

DH算法是一種密鑰交換協(xié)議毁枯,它可以讓雙方在不泄漏密鑰的情況下協(xié)商出一個(gè)密鑰來。DH算法基于數(shù)學(xué)原理叮称,比如小明和小紅想要協(xié)商一個(gè)密鑰种玛,可以這么做:

小明先選一個(gè)素?cái)?shù)和一個(gè)底數(shù),例如瓤檐,素?cái)?shù)p=23赂韵,底數(shù)g=5(底數(shù)可以任選),再選擇一個(gè)秘密整數(shù)a=6挠蛉,計(jì)算A=g^a mod p=8祭示,然后大聲告訴小紅:p=23,g=5谴古,A=8质涛;

小紅收到小明發(fā)來的p,g讥电,A后蹂窖,也選一個(gè)秘密整數(shù)b=15,然后計(jì)算B=g^b mod p=19恩敌,并大聲告訴小明:B=19瞬测;

小明自己計(jì)算出s=B^a mod p=2,小紅也自己計(jì)算出s=A^b mod p=2纠炮,因此月趟,最終協(xié)商的密鑰s為2。

在這個(gè)過程中恢口,密鑰2并不是小明告訴小紅的孝宗,也不是小紅告訴小明的,而是雙方協(xié)商計(jì)算出來的耕肩。第三方只能知道p=23因妇,g=5,A=8猿诸,B=19婚被,由于不知道雙方選的秘密整數(shù)a=6和b=15,因此無法計(jì)算出密鑰2梳虽。

用crypto模塊實(shí)現(xiàn)DH算法如下:

    const crypto = require('crypto');

    // xiaoming's keys:
    var ming = crypto.createDiffieHellman(512);
    var ming_keys = ming.generateKeys();

    var prime = ming.getPrime();
    var generator = ming.getGenerator();

    console.log('Prime: ' + prime.toString('hex'));
    console.log('Generator: ' + generator.toString('hex'));

    // xiaohong's keys:
    var hong = crypto.createDiffieHellman(prime, generator);
    var hong_keys = hong.generateKeys();

    // exchange and generate secret:
    var ming_secret = ming.computeSecret(hong_keys);
    var hong_secret = hong.computeSecret(ming_keys);

    // print secret:
    console.log('Secret of Xiao Ming: ' + ming_secret.toString('hex'));
    console.log('Secret of Xiao Hong: ' + hong_secret.toString('hex'));

運(yùn)行后址芯,可以得到如下輸出:

    Prime: 80f65a4455ecfc93b472be8a67fbe5edd2836de38885ae779db15a1e86449cd84138df156d5cb2dcdab680da8822431e5642d22614a55390bd665313a62b540b
    Generator: 02
    Secret of Xiao Ming: 5518afe72bde63dad7493804ae2e556f2e2df4aef34a761f089fd3c996e8d753fbc822d3e75a59530a4e28a55733f90f2f1d18f2eff3ef005a6ed3e643ba0853
    Secret of Xiao Hong: 5518afe72bde63dad7493804ae2e556f2e2df4aef34a761f089fd3c996e8d753fbc822d3e75a59530a4e28a55733f90f2f1d18f2eff3ef005a6ed3e643ba0853
RSA

RSA算法是一種非對稱加密算法,即由一個(gè)私鑰和一個(gè)公鑰構(gòu)成的密鑰對,通過私鑰加密谷炸,公鑰解密北专,或者通過公鑰加密,私鑰解密旬陡。其中拓颓,公鑰可以公開,私鑰必須保密描孟。

RSA算法是1977年由Ron Rivest录粱、Adi Shamir和Leonard Adleman共同提出的,所以以他們?nèi)说男帐系念^字母命名画拾。

當(dāng)小明給小紅發(fā)送信息時(shí)啥繁,可以用小明自己的私鑰加密,小紅用小明的公鑰解密青抛,也可以用小紅的公鑰加密旗闽,小紅用她自己的私鑰解密,這就是非對稱加密蜜另。相比對稱加密适室,非對稱加密只需要每個(gè)人各自持有自己的私鑰,同時(shí)公開自己的公鑰举瑰,不需要像AES那樣由兩個(gè)人共享同一個(gè)密鑰捣辆。

在使用Node進(jìn)行RSA加密前,我們先要準(zhǔn)備好私鑰和公鑰此迅。

首先汽畴,在命令行執(zhí)行以下命令以生成一個(gè)RSA密鑰對:

openssl genrsa -aes256 -out rsa-key.pem 2048

根據(jù)提示輸入密碼,這個(gè)密碼是用來加密RSA密鑰的耸序,加密方式指定為AES256忍些,生成的RSA的密鑰長度是2048位。執(zhí)行成功后坎怪,我們獲得了加密的rsa-key.pem文件罢坝。

第二步,通過上面的rsa-key.pem加密文件搅窿,我們可以導(dǎo)出原始的私鑰嘁酿,命令如下:

openssl rsa -in rsa-key.pem -outform PEM -out rsa-prv.pem

輸入第一步的密碼,我們獲得了解密后的私鑰男应。

類似的闹司,我們用下面的命令導(dǎo)出原始的公鑰:

openssl rsa -in rsa-key.pem -outform PEM -pubout -out rsa-pub.pem

這樣,我們就準(zhǔn)備好了原始私鑰文件rsa-prv.pem和原始公鑰文件rsa-pub.pem殉了,編碼格式均為PEM开仰。

下面拟枚,使用crypto模塊提供的方法薪铜,即可實(shí)現(xiàn)非對稱加解密众弓。

首先,我們用私鑰加密隔箍,公鑰解密:

    const
        fs = require('fs'),
        crypto = require('crypto');

    // 從文件加載key:
    function loadKey(file) {
        // key實(shí)際上就是PEM編碼的字符串:
        return fs.readFileSync(file, 'utf8');
    }

    let
        prvKey = loadKey('./rsa-prv.pem'),
        pubKey = loadKey('./rsa-pub.pem'),
        message = 'Hello, world!';

    // 使用私鑰加密:
    let enc_by_prv = crypto.privateEncrypt(prvKey, Buffer.from(message, 'utf8'));
    console.log('encrypted by private key: ' + enc_by_prv.toString('hex'));


    let dec_by_pub = crypto.publicDecrypt(pubKey, enc_by_prv);
    console.log('decrypted by public key: ' + dec_by_pub.toString('utf8'));

執(zhí)行后谓娃,可以得到解密后的消息,與原始消息相同蜒滩。

接下來我們使用公鑰加密滨达,私鑰解密:

// 使用公鑰加密:
let enc_by_pub = crypto.publicEncrypt(pubKey, Buffer.from(message, 'utf8'));
console.log('encrypted by public key: ' + enc_by_pub.toString('hex'));

// 使用私鑰解密:
let dec_by_prv = crypto.privateDecrypt(prvKey, enc_by_pub);
console.log('decrypted by private key: ' + dec_by_prv.toString('utf8'));

執(zhí)行得到的解密后的消息仍與原始消息相同。

如果我們把message字符串的長度增加到很長俯艰,例如1M捡遍,這時(shí),執(zhí)行RSA加密會得到一個(gè)類似這樣的錯誤:data too large for key size竹握,這是因?yàn)镽SA加密的原始信息必須小于Key的長度画株。那如何用RSA加密一個(gè)很長的消息呢?實(shí)際上啦辐,RSA并不適合加密大數(shù)據(jù)谓传,而是先生成一個(gè)隨機(jī)的AES密碼,用AES加密原始信息芹关,然后用RSA加密AES口令续挟,這樣,實(shí)際使用RSA時(shí)侥衬,給對方傳的密文分兩部分诗祸,一部分是AES加密的密文,另一部分是RSA加密的AES口令轴总。對方用RSA先解密出AES口令贬媒,再用AES解密密文,即可獲得明文肘习。

證書

crypto模塊也可以處理數(shù)字證書际乘。數(shù)字證書通常用在SSL連接,也就是Web的https連接漂佩。一般情況下脖含,https連接只需要處理服務(wù)器端的單向認(rèn)證,如無特殊需求(例如自己作為Root給客戶發(fā)認(rèn)證證書)投蝉,建議用反向代理服務(wù)器如Nginx等Web服務(wù)器去處理證書养葵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘩缆,隨后出現(xiàn)的幾起案子关拒,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件着绊,死亡現(xiàn)場離奇詭異谐算,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)归露,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門洲脂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人剧包,你說我怎么就攤上這事恐锦。” “怎么了疆液?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵一铅,是天一觀的道長。 經(jīng)常有香客問我堕油,道長馅闽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任馍迄,我火速辦了婚禮福也,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘攀圈。我一直安慰自己暴凑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布赘来。 她就那樣靜靜地躺著现喳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪犬辰。 梳的紋絲不亂的頭發(fā)上嗦篱,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音幌缝,去河邊找鬼灸促。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涵卵,可吹牛的內(nèi)容都是我干的浴栽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼轿偎,長吁一口氣:“原來是場噩夢啊……” “哼典鸡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起坏晦,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤萝玷,失蹤者是張志新(化名)和其女友劉穎嫁乘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體球碉,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜓斧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汁尺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡多律,死狀恐怖痴突,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狼荞,我是刑警寧澤辽装,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站相味,受9級特大地震影響拾积,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丰涉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一拓巧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧一死,春花似錦肛度、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至伪煤,卻和暖如春加袋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抱既。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工职烧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人防泵。 一個(gè)月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓阳堕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親择克。 傳聞我的和親對象是個(gè)殘疾皇子恬总,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容