来源:Node.js

crypto

crypto是Nodejs的一个密码学模块,能提供通用的加密和哈希算法

用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢

而Nodejs用C/C++实现这些算法后,通过crypto这个模块暴露为JavaScript接口,这样方便快捷

密码学

密码学是计算机科学中的一个重要领域,它涉及到加密、解密、哈希函数和数字签名等技术

Nodejs提供了强大的密码学模块,使开发人员能够轻松地在其应用程序中实现各种密码学功能

对称加密

对称加密简单快速,使用相同的密钥(对称密钥)进行加密和解密,即发送者和接收者在加密和解密过程中都使用相同的密钥,适合对大量数据进行加密和解密操作

然而,对称密钥的安全性难以保证,因为需要确保发送者和接收者都安全地共享密钥,否则有被未授权的人获取密钥并解密数据的风险

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const crypto = require('node:crypto')

// 对称加密算法

// 加密-----------------------------------------------------

// 生成随机密钥
const key = crypto.randomBytes(32)

// 生成随机IV
// IV:初始向量,通常是用于加密每条信息的固定长度随机值
// 保证每次生成的密文不一样,缺位时还能进行补码,增加安全性
const iv = Buffer.from(crypto.randomBytes(16))

// 参数1:使用的算法 参数2:密钥,32位 参数3:IV
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv)

// 开始加密
// 参数1:被加密的字符串 参数2:加密格式 参数3:输出格式
cipher.update('野兽先辈', 'utf8', 'hex')

// 输出一个16进制的密文
const secret = cipher.final('hex')

console.log(secret) // -> ff68a9ebf32d12cfea137516efa32e13

// 解密-----------------------------------------------------

// 解密需要使用相同的算法、相同的key和相同的iv
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv)

// 开始解密
decipher.update(secret, 'hex', 'utf8')

// 解密后的字符串
console.log(decipher.final('utf8')) // -> 野兽先辈

非对称加密

非对称加密使用一对密钥,分别是公钥和私钥:发送者使用接收者的公钥进行加密,接收者使用自己的私钥进行解密

公钥可以自由分享给任何人,而私钥必须保密,所以非对称加密算法提供了更高的安全性,因为即使公钥泄露,只有持有私钥的接收者才能解密数据

然而非对称加密算法的加密速度相对较慢,不适合加密大量数据,因此,实际通常使用非对称加密交换对称密钥,然后使用对称加密算法加密实际的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const crypto = require('node:crypto')

// 非对称加密算法

// 加密-----------------------------------------------------

// 生成RSA密钥对
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048, // 密钥长度,长度越长越安全,但运算速度也越慢
})

// 使用公钥加密
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from('野兽先辈', 'utf8'))

// 获取密文
const secret = encrypted.toString('hex')

console.log(secret) // ->一个长度为2048的密文

// 解密-----------------------------------------------------

// 使用私钥解密
const decrypted = crypto.privateDecrypt(privateKey, encrypted)

console.log(decrypted.toString('utf8')) // ->野兽先辈

哈希函数

哈希函数是单向的,意味着几乎不可能从哈希值推导出原始输入数据,即使输入数据发生微小的变化,其哈希值也会完全不同

哈希函数具有较低的碰撞概率,即不同的输入数据生成相同的哈希值的可能性应该非常小,几乎确保哈希值能够唯一地标识输入数据

无论输入数据的大小,哈希函数的输出长度是固定的,常见的哈希函数如MD5和SHA-256生成的哈希值长度分别为128位和256位

在避免密码明文传输,或验证文件完整性时,可以使用哈希函数:读取内容生成哈希,如果前端上传的哈希和后端读取的哈希匹配,说明验证成功

但哈希不见得非常安全,因为它们具有唯一性,有几率撞库碰到真实内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const crypto = require('node:crypto')

// 哈希函数

// 指定哈希函数为sha256
const hash = crypto.createHash('sha256')

// 加密
hash.update('野兽先辈')

// 获取哈希
const secret = hash.digest('hex')

console.log(secret) // ->279d970609356d29a806019daec23d7c137e73e9fc479120aa7d993ae4217e2c

// 指定哈希函数为sha256
const secret2 = crypto.createHash('md5').update('野兽先辈').digest('hex')

console.log(secret2) // ->ec43c6d33baf97bfb44e1827aa44cd83