前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NodeJS加密算法(一)

NodeJS加密算法(一)

作者头像
皮小蛋
发布2021-02-08 09:38:15
2.2K0
发布2021-02-08 09:38:15
举报
文章被收录于专栏:前端皮小蛋

你真的了解NodeJS的加密模块crypto吗?本文通过crypto的api深入了解加密算法

安全加密

当发送方A向接收方B发送数据时,需要考虑的问题有:

  1. 数据的安全性。
  2. 数据的完整性,即数据不被篡改。
  3. 数据的真实性,即数据确实来自于发送方,传输过程中没有被替换。
  4. 数据的不可否认性,即验证发送方确实发送了数据。

保证安全性

对称密钥加密 Symmetric Encryption

对称密钥加密又叫专用密钥加密或共享密钥加密,即发送和接收数据的双方必使用相同的密钥对明文进行加密和解密运算。

流程

  1. A 使用密钥加密数据
  2. A 将密文发送给 B
  3. B 收到密文后,使用相同的密钥对其进行解密,取得原始数据
代码语言:javascript
复制
优点:速度快
缺点:密钥被盗就被破解、密钥管理不方便(每个用户都要对应一个密钥)
实现算法有:凯撒密码,AES(Advanced Encryption Standard)、DES(Data Encryption Standard)、动态口令等。
推荐:AES

AES加解密特点

分组长度是128bit,也就是16字节。

  1. 加密第一步,针对每个分组逐个字节的进行SubBytes的操作。笼统的说,就是每个字节根据256个值的替换表,将当前字节替换成另外一个字节。
  2. 加密第二步,以单个字节为单位进行ShiftRows处理,就是将字节有规律的打乱。
  3. 加密第三步,再以4字节为单位进行MixColumns处理,就是进行比特运算变成另外的4个字节。
  4. 加密第四步,还是4字节为单位与轮密钥进行XOR运算。至此一轮运算就结束了。

解密的过程就是加密的逆向过程。

分组密码的模式

对称密钥算法DES、AES都属于分组密码,分组密码的特点是分组的长度是固定的。但是由于明文的长度不固定且基本超过分组长度,所以就需要进行多轮的迭代加密。模式就是指的多轮迭代的方式。

  • ECB模式:Electronic CodeBook mode(电子密码本模式)
  • CBC模式:Cipher Block Chaining mode(密码分组链接模式)推荐使用
  • CFB模式:Cipher FeedBack mode(密文反馈模式)
  • OFB模式:Output FeedBack mode(输出反馈模式)
  • CTR模式:CounTeR mode(计数器模式)推荐使用

NodeJS示例:Cipher、Decipher

代码语言:javascript
复制
// 加密
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
代码语言:javascript
复制
// 解密
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 来派生 keyiv ,并使用 createCipheriv() 来创建加密流。

  • crypto.createCipher(algorithm, password) :用给定的算法和密钥,创建并返回一个 Cipher 加密算法的对象。参数:algorithm 算法是依赖OpenSSL库支持的算法, 例如: 'aes192' 算法等, password 是用来派生 keyiv 的,它必须是一个 'binary' 二进制格式的字符串或者是一个 Buffer
  • crypto.createCipheriv(algorithm, key, iv) :用给定的算法、密钥和向量,创建并返回一个 Cipher 加密算法的对象。参数:algorithmcreateCipher 方法相同, 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

非对称密钥加密 Asymmetric Encryption

非对称加密算法需要两个密钥:公开密钥 (publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

公开密钥加密

流程

  1. 首先由接收方 B 生成公钥和私钥
  2. B 把公钥发送给 A
  3. A 使用 B 发来的公钥加密数据,然后发送给 B
  4. B 使用私钥对密文进行解密,得到原始数据
代码语言:javascript
复制
优点:安全性高、密钥管理方便
缺点:加密速度慢、无法防止中间人攻击,A 不知道收到的公钥是否是来自 B
实现算法有:RSA 算法、椭圆曲线加密算法等 
推荐:RSA

NodeJS示例:privateEncrypt、privateDecrypt、publicEncrypt、publicDecrypt

代码语言:javascript
复制
// 公钥加密
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库。

非对称密钥概览

密钥配送

密钥配送问题:如何安全地把密钥给到接受者?

配送问题解决办法

  • 事先共享密钥,缺点是需要很多密钥,且管理麻烦。
  • 通过密钥中心分配,缺点是全部人员所有通信密钥都产生于一个地方,这个地方的压力就相当大,而且一旦此服务器坏掉,则所有人员无法通信。
  • 公钥密码解决办法,缺点如果得到服务器私钥就可以解密所有数据。
  • Diffie-Hellman密钥交换解决办法:SSL通信使用的就是这种技术。

密钥交换协议

流程

  1. A 生成密钥 P
  2. A 把密钥 P 发送给 B
  3. A 和 B 各自准备自己的私钥 SA 和 SB
  4. A 利用密钥 P 和私钥 SA 合成新的密钥 P-SA
  5. B 也利用密钥 P 和私有密钥 SB 合成新的密钥 P-SB
  6. A 将密钥 P-SA 发送给 B,B 也将密钥 P-SB 发送给 A
  7. A 将私有密钥 SA 和收到的密钥 P-SB 合成新的密钥 SA-P-SB(合成结果和合成顺序无关,合成密钥无法被分解) h)同样地,B 也将私钥 SB 和收到的密钥 P-SA 合成新的密钥 P-SA-SB。于是 A 和 B 都得到了密钥 P-SA-SB。这个密钥将作为“加密密钥”和“解密密钥”使用。
代码语言:javascript
复制
优点:DH 利用“离散对数问题”解决中间人攻击
实现算法有:DH、ECDH 
推荐:ECDH > DH

NodeJS示例:DiffieHellman、DiffieHellmanGroup、ECDH

ECDH 是基于ECC(Elliptic Curve Cryptosystems,椭圆曲线密码体制,参看ECC)的Diffie-Hellma密钥交换算法。交换双方可以在不共享任何秘密的情况下协商出一个密钥。与 Diffie-Hellman 相比ECDH具有ECC的高强度、短密钥长度、计算速度快等优点。Node.js的crypto模块,封装了 ECDH 类,可以用这个类来生成EC Diffie-Hellman交换密钥。

代码语言:javascript
复制
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

混合加密

流程

  1. 接收方 B 事先生成公钥和私钥
  2. B 将公钥发送给 A
  3. A 使用收到的公钥对共享密钥(对称密钥)进行加密,并发送给 B
  4. B 使用私钥解密,得到共享密钥
  5. 接下来 A 只要使用对称密钥加密好数据发送给 B 即可

优点:使用处理速度快的对称密钥加密数据同时保证对称密钥的安全性。HTTPS 的 TLS 加密就是用的混合加密。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端皮小蛋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安全加密
    • 对称密钥加密 Symmetric Encryption
      • 流程
      • AES加解密特点
      • 分组密码的模式
      • NodeJS示例:Cipher、Decipher
    • 非对称密钥加密 Asymmetric Encryption
      • 公开密钥加密
      • 流程
      • NodeJS示例:privateEncrypt、privateDecrypt、publicEncrypt、publicDecrypt
      • 非对称密钥概览
    • 密钥配送
      • 配送问题解决办法
      • 密钥交换协议
      • 流程
      • NodeJS示例:DiffieHellman、DiffieHellmanGroup、ECDH
    • 混合加密
      • 流程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档