你真的了解NodeJS的加密模块crypto吗?本文通过crypto的api深入了解加密算法
当发送方A向接收方B发送数据时,需要考虑的问题有:
保证安全性
对称密钥加密又叫专用密钥加密或共享密钥加密,即发送和接收数据的双方必使用相同的密钥对明文进行加密和解密运算。
优点:速度快
缺点:密钥被盗就被破解、密钥管理不方便(每个用户都要对应一个密钥)
实现算法有:凯撒密码,AES(Advanced Encryption Standard)、DES(Data Encryption Standard)、动态口令等。
推荐:AES
分组长度是128bit,也就是16字节。
解密的过程就是加密的逆向过程。
对称密钥算法DES、AES都属于分组密码,分组密码的特点是分组的长度是固定的。但是由于明文的长度不固定且基本超过分组长度,所以就需要进行多轮的迭代加密。模式就是指的多轮迭代的方式。
// 加密
const crypto = require("crypto");
const algorithm = "aes-192-cbc";
const password = "Password used to generate key";
// Use the async `crypto.scrypt()` instead.
const key = crypto.scryptSync(password, "salt", 24);
// Use `crypto.randomBytes` to generate a random iv instead of the static iv
// shown here.
const iv = Buffer.alloc(16, 0); // Initialization vector.
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update("some clear text data", "utf8", "hex");
encrypted += cipher.final("hex");
console.log(encrypted);
// Prints: e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa
// 解密
const crypto = require("crypto");
const algorithm = "aes-192-cbc";
const password = "Password used to generate key";
// Use the async `crypto.scrypt()` instead.
const key = crypto.scryptSync(password, "salt", 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.
const decipher = crypto.createDecipheriv(algorithm, key, iv);
// Encrypted using same algorithm, key and iv.
const encrypted =
"e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa";
let decrypted = decipher.update(encrypted, "hex", "utf8");
decrypted += decipher.final("utf8");
console.log(decrypted);
// Prints: some clear text data
openssl list -cipher-algorithms 可以列出支持的加密算法
Cipher
类的创建创建 Cipher
类可以使用crypto模块的 crypto.createCipher()
和 crypto.createCipheriv()
方法。OpenSSL推荐使用 pbkdf2
来替换 EVP_BytesToKey
,因此在创建 Cipher
类时,建议使用 crypto.pbkdf2
来派生 key
和 iv
,并使用 createCipheriv()
来创建加密流。
crypto.createCipher(algorithm, password)
:用给定的算法和密钥,创建并返回一个 Cipher
加密算法的对象。参数:algorithm
算法是依赖OpenSSL库支持的算法, 例如: 'aes192'
算法等, password
是用来派生 key
和 iv
的,它必须是一个 'binary'
二进制格式的字符串或者是一个 Buffer
。crypto.createCipheriv(algorithm, key, iv)
:用给定的算法、密钥和向量,创建并返回一个 Cipher
加密算法的对象。参数:algorithm
与 createCipher
方法相同, key
密钥是一个被算法使用的原始密钥, iv
是一个 初始化向量 。key
密钥和 iv
向量必须是 'binary'
二进制格式的字符串或者是一个 Buffer
。Cipher
类加密数据Cipher
加密对象是一个可读写的 Stream
流。可以使用 Cipher
类中的 update
方法写入纯文本的数据,数据输入完成后通过 final
方法返回加密后的数据。
cipher.update(data, [input_encoding], [output_encoding])
:更新 Cipher
类的加密数据。data
:要更新的 Cipher
加密对象的数据,编码 input_encoding
可以是:'utf8'
、 'ascii'
、 'binary'
。如果没有编码参数,那么 data
必须是一个 Buffer
。output_encoding
指定加密数据的输出编码,可以是:'binary'
、 'base64'
或 'hex'
,如果未设置这个参数,将会返回一个 Buffer
。cipher.final([output_encoding])
:返回加密后的内容, output_encoding
为:'binary'
、 'base64'
或 'hex'
。如果没有提供编码格式,如果未设置这个参数,将会返回一个 Buffer
。final()
后不能再用 Cipher
对象cipher.setAutoPadding(auto_padding=true)
:设置输入数据自动填充到块大小功能,这个函数必须在 cipher.final
之前调用。如果 auto_padding
是false,那么整个输入数据的长度必须是加密器的块大小的整倍数,否则 final
会失败。这对非标准的填充很有用,例如:使用 0x0
而不是 PKCS
的填充。cipher.setAuthTag(buffer)
:加密认证模式(目前支持:GCM),这个方法返回经过计算的认证标志Buffer
。必须在使用 final
方法完成加密后调用。cipher.setAAD(buffer)
:对于加密认证模式(目前支持:GCM),用这个方法设置附加认证数据( AAD )。相关API资料:
Node.js的加密模块crypto之使用Cipher类加密数据:https://itbilu.com/nodejs/core/EJOj6hBY.html
Node.js的加密模块crypto之使用Decipher类解密数据:https://itbilu.com/nodejs/core/4ySMqlUF.html
非对称加密算法需要两个密钥:公开密钥 (publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
优点:安全性高、密钥管理方便
缺点:加密速度慢、无法防止中间人攻击,A 不知道收到的公钥是否是来自 B
实现算法有:RSA 算法、椭圆曲线加密算法等
推荐:RSA
// 公钥加密
let encryptString = crypto.publicEncrypt(
{
key: publicKey,
padding: crypto.constants.RSA_NO_PADDING
},
Buffer.from("需要加密的内容")
);
encryptString = encryptString.toString("base64");
// 私钥加密
let encryptString = crypto.privateEncrypt(
{
key: privateKey,
padding: crypto.constants.RSA_NO_PADDING
},
Buffer.from("需要加密的内容")
);
encryptString = encryptString.toString("base64");
// 私钥解密
crypto.privateDecrypt(privateKey, buffer);
// 公钥解密
crypto.publicDecrypt(key, buffer);
1024位的证书,加密时最大支持117个字节,解密时为128;2048位的证书,加密时最大支持245个字节,解密时为256。所以在加密和解密较大内容的过程中需要分块进行。推荐使用node-rsa库。
密钥配送问题:如何安全地把密钥给到接受者?
优点:DH 利用“离散对数问题”解决中间人攻击
实现算法有:DH、ECDH
推荐:ECDH > DH
ECDH
是基于ECC(Elliptic Curve Cryptosystems,椭圆曲线密码体制,参看ECC)的Diffie-Hellma密钥交换算法。交换双方可以在不共享任何秘密的情况下协商出一个密钥。与 Diffie-Hellman
相比ECDH具有ECC的高强度、短密钥长度、计算速度快等优点。Node.js的crypto模块,封装了 ECDH
类,可以用这个类来生成EC Diffie-Hellman交换密钥。
const crypto = require('crypto');
const assert = require('assert');
// Generate Alice's keys...
const alice = crypto.createECDH('secp521r1');
const aliceKey = alice.generateKeys();
// Generate Bob's keys...
const bob = crypto.createECDH('secp521r1');
const bobKey = bob.generateKeys();
// Exchange and generate the secret...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);
assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
// OK
相关API资料:
Node.js的加密模块crypto之使用DiffieHellman类生成交换密钥:https://itbilu.com/nodejs/core/EknZWVKt.html
Node.js的加密模块crypto之使用ECDH类生成EC Diffie-Hellman交换密钥:https://itbilu.com/nodejs/core/41iXzVot.html
常用密钥交换算法:https://chziyue.com/post/57.html
DH需要和数字签名一起使用才安全,相关:https://www.slideshare.net/dganesan11/active-attacks-on-dh-key-exchange-143931907
优点:使用处理速度快的对称密钥加密数据同时保证对称密钥的安全性。HTTPS 的 TLS 加密就是用的混合加密。