Node.js 使用 RSA 做加密

0 人评论
  作者: Avrit   分类:学习   浏览:910
RSA
RSA加密算法是一种非对称加密算法。

假设 A 与 B 通信。A 和 B 都提供一个公开的公钥。A 把需要传递的信息,先用自己的私钥签名,再用 B 的公钥加密。B 接收到这串密文后,用自己的私钥解密,用 A 提供的公钥验签。

为什么要先签名后加密?如果你先加密后签名,非法用户通过获取的公钥就可以破解签名,破解之后就可以替换签名。

详细的原理可以参考以下文档:
RSA算法原理(一)
RSA算法原理(二)

node-rsa
在 node.js 中使用 rsa 算法,我们使用的是 node-rsa 这个包。

const NodeRSA = require('node-rsa');

const a_public_key_data = '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----';
const a_private_key_data = '-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----';

const b_public_key_data = '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----';
const b_private_key_data = '-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----';

// 生成 A 的公私钥对象
const a_public_key = new NodeRSA(a_public_key_data);
const a_private_key = new NodeRSA(a_private_key_data);

// 生成 B 的公私钥对象
const a_public_key = new NodeRSA(a_public_key_data);
const a_private_key = new NodeRSA(a_private_key_data);

const text = 'Hello RSA!';

// 加签并加密
const sign = a_private_key.sign(text, 'base64', 'utf8');
console.log('A 私钥加签:', sign);

const encrypted = a_public_key.encrypt(sign, 'base64');
console.log('B 公钥加密:', encrypted);

// 解密并验签
const decrypted = a_public_key.decrypt(encrypted, 'utf8');
console.log('B 私钥解密:', decrypted);

const verify = a_public_key.verify(text, decrypted, 'utf8', 'base64');
console.log('A 公钥验签:', verify);
serialize
接口传递的一般是复杂的对象,所以我们需要把对象按一定的顺序排列并序列化成字符串再进行签名加密的操作

const serialize = (obj) => {
  const str = [];
  Object.keys(obj).sort().forEach((key) => {
    if (obj.hasOwnProperty(key)) {
      str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
    }
  });
  return str.join('&');
};
注意
RSA 算法有一定的计算量,加上 Node 不适合做计算密集型的操作。当接口被频繁调用可能会占用主线程,阻塞其他接口,使用了 RSA 的接口并发量会下降十倍左右。如非必要,谨慎在 Node 里使用 RSA。

node.js rsa加解密

0 人评论
  作者: Avrit   分类:学习   浏览:930
Node自带加解密的库crypto,不用安装第三方库就能实现加解密

util.js:

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

function generateKeys() {
  const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
    modulusLength: 4096,
    publicKeyEncoding: {
      type: 'pkcs1',
      format: 'pem',
    },
    privateKeyEncoding: {
      type: 'pkcs1',
      format: 'pem',
      cipher: 'aes-256-cbc',
      passphrase: '',
    },
  });
  fs.writeFileSync('private.cer', privateKey);
  fs.writeFileSync('public.cer', publicKey);
}

function encrypt(plain, pathToPublicKey) {
  const publicKey = fs.readFileSync(path.resolve(__dirname, pathToPublicKey), 'utf8');
  const buffer = Buffer.from(plain, 'utf8');
  return crypto.publicEncrypt(publicKey, buffer).toString('base64');
}

function decrypt(cipher, pathToPrivateKey) {
  const privateKey = fs.readFileSync(path.resolve(__dirname, pathToPrivateKey), 'utf8');
  const buffer = Buffer.from(cipher, 'base64');
  const plain = crypto.privateDecrypt({
    key: privateKey.toString(),
    passphrase: ''
  }, buffer);
  return plain.toString('utf8')
}


module.exports = {
  generateKeys,
  encrypt,
  decrypt
};
index.js:

const { generateKeys, encrypt, decrypt } = require('./utils');

generateKeys();
const cypher = encrypt('hello', 'public.cer');
console.log(cypher);
const plain = decrypt(cypher, 'private.cer');
console.log(plain);
输出:

> node index.js

VDWO5U8gdNmbS5/euzhr/Yx8NC8HBkHR40obxMm+EtnXXw2dq+ojmbet1mxYg/x8mSrkMHV6a+zbmjkF0y9XP7euZnNJug1SiDCgfTF2ctcjjnJVWREsuub/JORcIHTqpHLnYCBuCyceazP5KFyJk1IRSDev09/8Hn5wWjLzcOHEU/qR5QANFPQ0
skyQqRhvkyKwWLSODosycJebqrC8xvfcFPpDSRUe64qNv5EIU/sB9RYfqbjNa5psyVrcjnaL7oSPDwINxtlqNcL6a3D1GKOdJJWbluuxllKEia9m1tsufpoh4abvigIFCBXWrTuMKHQuBbBifRWjQdFbqlLkb6aTvlzHDiCbuYg0FU3jEps7FSwk
utOjREDRMCmCAsoMt8trJuP9hkpZa4q18o3YLKvGYQLwFrbw5WrWSauAuxTz9Wb7T9WZXJOgrP+bymB7rwZxMvBhkXxtQtbB8kuGwoXu6o6e/0mXKsaaOKzQs9g7EyrUwVs8ane5J1OI9n5F76MfPWbz0NbfbIwRv0V2HoSAvJY+oWPrYjvyPkou
0KwsnmG9egSGCIkglqyrbfQ1pmLoxzOzjFrrKEpkXWuN9NZv8Inucb87VcDr23XkLo72Rh05mYn8YSECF/2GIREcDadU+gVSaoRl2v+K+iv4XYeePg/M4SVCsj4oc0aAgaE=
hello