NodeJS中的Crypto使用
在爬蟲JS解密的時候經常會遇到常見的加密,例如:MD5,Sha1,Sha256,AES,RSA等加密算法,這些可以在Python中調用,當然有時候采用NodeJS調用也很方便,熟悉NodeJS常見的加密算法對逆向JS很有幫助。NodeJS 中的 Crypto 模塊提供了加密功能,包括對 OpenSSL 的哈希、HMAC、加密、解密、簽名、以及驗證功能的一整套封裝。
Hash
將任意長度的二進制值串映射為固定長度的二進制值串,這個映射的規則就是哈希算法,而通過原始數據映射之后得到的二進制值串就是哈希值(散列值).一個優秀的哈希算法需要滿足:
- 從哈希值不能反向推導出原始數據(所以哈希算法也叫單向哈希算法);
- 對輸入數據非常敏感,哪怕原始數據只修改了一個 Bit,最后得到的哈希值也大不相同;
- 散列沖突的概率要很小,對於不同的原始數據,哈希值相同的概率非常小;
- 哈希算法的執行效率要盡量高效,針對較長的文本,也能快速地計算出哈希值。
哈希算法嚴格來說並不屬於加密算法,傳統意義上的 加密 是與 解密 相配對的。哈希算法只能加密不能反向解密。
MD5
在NodeJS中 MD5 Hash 函數的用法如下:
const crypto = require('crypto'); const hash = crypto.createHash('md5'); console.log(hash.update('666666').digest('hex')) // f379eaf3c831b04de153469d1bec345e const hash2 = crypto.Hash('md5'); console.log(hash2.update('666666').digest('hex')) // f379eaf3c831b04de153469d1bec345e
其中摘要的編碼方式有如下三種:
- latin1
- hex
- base64
Sha1
const crypto = require('crypto'); const hash = crypto.createHash('sha1'); console.log(hash.update('666666').digest('hex')) // 1411678a0b9e25ee2f7c8b2f7ac92b6a74b3f9c5 const hash2 = crypto.Hash('sha1'); console.log(hash2.update('666666').digest('hex')) // 1411678a0b9e25ee2f7c8b2f7ac92b6a74b3f9c5
Base64
百度百科中對Base64有一個很好的解釋:“Base64是網絡上最常見的用於傳輸8Bit字節碼的編碼方式之一,Base64就是一種基於64個可打印字符來表示二進制數據的方法”。什么是“可打印字符”呢?為什么要用它來傳輸8Bit字節碼呢?在回答這兩個問題之前我們有必要來思考一下什么情況下需要使用到Base64?Base64一般用於在HTTP協議下傳輸二進制數據,由於HTTP協議是文本協議,所以在HTTP協議下傳輸二進制數據需要將二進制數據轉換為字符數據。然而直接轉換是不行的。因為網絡傳輸只能傳輸可打印字符。什么是可打印字符?在ASCII碼中規定,0~31、127這33個字符屬於控制字符,32~126這95個字符屬於可打印字符,也就是說網絡傳輸只能傳輸這95個字符,不在這個范圍內的字符無法傳輸。那么該怎么才能傳輸其他字符呢?其中一種方式就是使用Base64。
Base64,就是使用64個可打印字符來表示二進制數據的方法。Base64的索引與對應字符的關系如下表所示:
更加詳細的解釋可以參考:什么是Base64?
嚴格來說Base64也不是加密算法,但是爬蟲中也會經常遇到,所以在此給出在NodeJS中的Base64的"加密"與"解密"。在瀏覽器中加密與解密分別對應 btoa
和 atob
。
Base64加密
對應瀏覽器中的 btoa
:
const data = '666666'; const encodedData = Buffer.from(data, 'utf-8').toString('base64'); // 輸入編碼為utf8,輸出為base64 console.log(encodedData); // NjY2NjY2
Base64解密
對應瀏覽器中的 atob
:
const data = 'NjY2NjY2'; const decodedData = Buffer.from(data, 'base64').toString('utf8'); // 輸入編碼為base64,輸出編碼為utf8 console.log(decodedData); // 666666
DES
DES 是 Data Encryption Standard(數據加密標准)的縮寫。它是由IBM公司研制的一種對稱密碼算法,美國國家標准局於1977年公布把它作為非機要部門使用的數據加密標准,它一直活躍在國際保密通信的舞台上,扮演了十分重要的角色。
DES是一個分組加密算法,典型的DES以64位為分組對數據加密,加密和解密用的是同一個算法。它的密鑰長度是56位(因為每個第8 位都用作奇偶校驗),密鑰可以是任意的56位的數,而且可以任意時候改變。其中有極少數被認為是易破解的弱密鑰,但是很容易避開它們不用。所以保密性依賴於密鑰。
DES加密
const crypto = require('crypto'); const cipher = crypto.createCipheriv('des-cbc', '01234567', '01234567') var crypted = cipher.update('666666', 'utf8', 'base64'); crypted += cipher.final('base64'); console.log(crypted); // 83OX84xg+iM=
DES解密
const crypto = require('crypto'); const cipher = crypto.createDecipheriv('des-cbc', '01234567', '01234567') var crypted = cipher.update('83OX84xg+iM=', 'base64', 'utf8'); crypted += cipher.final('utf8'); console.log(crypted); // 666666
AES
高級加密標准(AES,Advanced Encryption Standard)為最常見的對稱加密算法(微信小程序加密傳輸就是用這個加密算法的)。對稱加密算法也就是加密和解密用相同的密鑰,具體的加密流程如下圖:
AES加密
const crypto = require('crypto'); const cipher = crypto.createCipheriv('aes128', '0123456789abcdef', '0123456789abcdef') var crypted = cipher.update('666666', 'utf8', 'hex'); crypted += cipher.final('hex'); console.log(crypted); // c319a2c27be50284fec9fc95d7045737
說明:
createCipheriv
原型如下:
crypto.createCipheriv(algorithm,key,iv [,options])
iv
是初始化向量,可以 為空 或者 16 字節的字符串key
是加密密鑰,根據選用的算法不同,密鑰長度也不同,對應關系如下:aes128
對應16位
長度密鑰aes192
對應24位
長度秘鑰aes256
對應32位
長度密鑰
AES解密
onst crypto = require('crypto'); const cipher = crypto.createDecipheriv('aes128', '0123456789abcdef', '0123456789abcdef') var data = cipher.update('c319a2c27be50284fec9fc95d7045737', 'hex', 'utf8'); // 輸入數據編碼為hex(16進制),輸出為utf8 data += cipher.final('utf8'); console.log(data); // 666666
RSA
RSA公開密鑰密碼體制是一種使用不同的加密密鑰與解密密鑰,也就是在計算上由已知加密密鑰推導出解密密鑰是不可行的 。在公開密鑰密碼體制中,加密密鑰(即公開密鑰)PK是公開信息,而解密密鑰(即秘密密鑰)SK是需要保密的。加密算法E和解密算法D也都是公開的。雖然解密密鑰SK是由公開密鑰PK決定的,但卻不能根據PK計算出SK。正是基於這種理論,1978年出現了著名的RSA算法,它通常是先生成一對RSA密鑰,其中之一是保密密鑰,由用戶保存;另一個為公開密鑰,可對外公開,甚至可在網絡服務器中注冊。為提高保密強度,RSA密鑰至少為500位長,一般推薦使用1024位。這就使加密的計算量很大。為減少計算量,在傳送信息時,常采用傳統加密方法與公開密鑰加密方法相結合的方式,即信息采用改進的DES或IDEA對話密鑰加密,然后使用RSA密鑰加密對話密鑰和信息摘要。對方收到信息后,用不同的密鑰解密並可核對信息摘要。
RSA加解密
const crypto = require('crypto'); const {privateKey, publicKey} = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, }); const encodedData = crypto.privateEncrypt(privateKey, Buffer.from('666666','utf8')); // 傳入utf8編碼的數據 console.log(encodedData.toString('hex')); const rawData = crypto.publicDecrypt(publicKey, Buffer.from(encodedData, 'hex')); // 傳入hex(16進制)數據 console.log(rawData.toString('utf8'));
總結
常見的加密方案采用Python模塊或者NodeJS自帶的Crypto模塊就可以解決,但是在工作中還是推薦先扣JS加密代碼,如果難度比較大,或者需要耗時,那么就可以采用Python或者NodeJS來模擬加密方案。在部分網站中還會有自己創立的算法,這種一般不建議用Python或者NodeJS模擬實現,還是直接扣JS代碼來的方便。
轉自:https://mp.weixin.qq.com/s/s7R0HlsX12w4sEfatFXPLw