RSA加密的一个前后端尝试,同时解决不支持node20+版本的问题

989 阅读2分钟

前端对密码加密以及后端解密的一个尝试

前言:

前端对敏感信息进行加密已经是一个屡见不鲜的事情了,但是现在很对B端项目还在进行明文传输密码等敏感信息,这是一个很不安全的行为。

加密方式

  • 对称加密
    • 密钥:同一个密钥。加密方和解密方必须事先共享同一个密钥,并且保证这个密钥的安全
    • 加密速度:通常更快,因为它使用较简单的算法来处理大量数据
    • 安全性:虽然加密算法难以破解,但是密钥可能会泄露
    • 场景:数据库,文件加密
    • 常用加密算法:AESDES,SM1
  • 非对称加密
    • 密钥:公钥和私钥。使用公钥加密 ,私钥解密
    • 加密速度:相对较慢
    • 安全性:更安全,只要保存好私钥,即使有了公钥也无法解密
    • 场景:密码,数字签名
    • 常见加密算法:RSAECC SM2

本篇主要介绍RSA加密在前(vue、react)后端(node)的一个过程

image.png 接下来看下具体的一个代码

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

const generateKeyPem = () => {
  // 生成 RSA 密钥对
  crypto.generateKeyPair(
    'rsa',
    {
      modulusLength: 2048, // 密钥长度
      publicKeyEncoding: {
        type: 'pkcs1',
        format: 'pem', // 输出格式
      },
      privateKeyEncoding: {
        type: 'pkcs1',
        format: 'pem', // 输出格式
      },
    },
    (err, publicKey, privateKey) => {
      if (err) {
        console.error('生成密钥对时发生错误:', err)
        return
      }

      // 将公钥写入文件
      fs.writeFileSync('public_key.pem', publicKey)

      // 将私钥写入文件
      fs.writeFileSync('private_key.pem', privateKey)
    },
  )
}

接下来我们看下前端加密和后端解密的一个过程

import JSEncrypt from "jsencrypt";
const encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
const encryptedPassword = encrypt.encrypt(password);

后端解密

const privateKey = fs.readFileSync('private_key.pem', 'utf8')
    const decryptedPassword = crypto.privateDecrypt(
      {
        key: privateKey,
        padding: crypto.constants.RSA_PKCS1_PADDING,
      },

      Buffer.from(encryptedPassword, 'base64'),
    )
    const newPassword = decryptedPassword.toString()

以上的代码在node16.20版本是可以运行的,但是在node20+的版本会报错,报错这个错误

RSA_PKCS1_PADDING is no longer supported for private decryption, this can be reverted with --security-revert=CVE-2023-46809 

这个消息是与OpenSSL相关的更新。OpenSSL在新的版本中由于安全原因已经停止支持RSA_PKCS1_PADDING用于私钥解密操作,这是因为存在安全漏洞(CVE-2023-46809)

为了兼容我们只能修改我们的一个加密和解密方式

这个经过查询,得到一个结果说是我们可以使用OAEP 的方式对密钥加密进行一个填充

经过修改,前后端加密解密的过程为

import forge from "node-forge";
const publicKeyPem = forge.pki.publicKeyFromPem(publicKey);
// 使用OAEP填充进行加密
const encrypted = publicKeyPem.encrypt(password, "RSA-OAEP", {
    md: forge.md.sha256.create(), // 使用SHA-256作为哈希函数
    mgf1: {
       md: forge.md.sha256.create() // 使用SHA-256作为MGF1的哈希函数
    }
});
// 将加密数据转换为Base64格式,以便传输
const encryptedPassword = forge.util.encode64(encrypted);
const privateKey = fs.readFileSync('private_key.pem', 'utf8')
const decryptedPassword = crypto.privateDecrypt(
      {
        key: privateKey,
        padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
        oaepHash: 'sha256',
      },
      Buffer.from(encryptedPassword, 'base64'),
    )
const newPassword = decryptedPassword.toString('utf8')