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解密
const 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代码来的方便。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。