【问题标题】:node crypto.publicEncrypt returns different value each time it is used节点 crypto.publicEncrypt 每次使用都会返回不同的值
【发布时间】:2021-03-01 13:49:20
【问题描述】:

我正在尝试实现基本的非对称加密;一个服务有一个公钥并使用该公钥加密一个值,然后另一个服务接收加密的消息,使用私钥对其进行解码,并对解密的数据进行处理。

我遇到的问题是,每次我使用内置的crypto.publicEncrypt 方法时,都会返回一个不同的加密值。据我所知,我使用的是相同的输入,所以据我所知,我应该看到相同的输出。也许我误解了这一点?

这是我的加密工具;

import { createPublicKey, createPrivateKey, privateDecrypt, publicEncrypt, constants } from "crypto";

const privateKeyPem = process.env.ENCRYPTION_PRIVATE_KEY;
const privateKeyPemFixed = privateKeyPem.replace(/\\n/g, "\n");
const privateKey = createPrivateKey(privateKeyPemFixed);
const publicKey = createPublicKey(privateKey);

// const private1 = privateKey.export({
//   type: 'pkcs1',
//   format: 'pem',
// }).toString("base64");

// const public1 = publicKey.export({
//   type: 'pkcs1',
//   format: 'pem',
// }).toString("base64");

export const encrypt = (text: string): string => {
  const buffer = Buffer.from(text);

  const encrypted1 = publicEncrypt(   {
      key: publicKey,
      oaepHash: 'sha256',
      padding: constants.RSA_PKCS1_OAEP_PADDING,
  }, buffer);

  const encrypted2 = publicEncrypt({
    key: publicKey,
    oaepHash: 'sha256',
    padding: constants.RSA_PKCS1_OAEP_PADDING,
  }, buffer);

  console.log(encrypted1.toString("base64"));
  console.log(encrypted2.toString("base64"));

  return encrypted1.toString("base64");
}

export const decrypt = (cipher: string): string => {
  const buffer = Buffer.from(cipher);
  const decrypted = privateDecrypt(privateKey, buffer);
  return decrypted.toString("utf8");
}

我有一个看起来像这样的笑话测试;

import { encrypt } from "./encryption";

describe("encryption", () => {

  const helloWorld = "Hello world";
  const encryptedHelloWorld = "IIisobkVsZxKiR0e5nwyIHjsww/ebrKXI0hzDbdTdC8KMU2rc57IRX9krhVThVma2no7gZcMvbfwJsRjHz1s7NoBiT+BitgYlI/LE1jMpFd5Bmghy2S93F/wGFRWA4DMAqdw32I9s8CRKVvellxkh3ZlJ5NyzxWG8kVfc11CrEMD+1sqo2e9cFCcTdx5jEVYpCgITy7X2vDxUwOPQ7bK8K56kU5ivQhUfyoHjd9VclRUxfBaSzOwLJQqK6RJPbNwuUfILcCaR72GTf4zWMhQqIvs/zHhSu+S9QQYPVvmZ1SzqqJaCM9mM6Cvl8Gn2brwcMB003f0CFb8WFimOgM6lQ==";

  it("should encrypt text", () => {
    const received = encrypt(helloWorld);
    expect(received).toEqual(encryptedHelloWorld);
  });
});

但是它总是失败,因为结果似乎总是不同。

我在encrypt 函数中运行了两次加密过程,以演示问题;它注销的两个值完全不同,我不明白为什么。

  console.log
    aDWDWcE+Zs92/rp2DLJN8UTgwHPTg6TDqFPIrC3ODVIfZgo5uaQV0NTSESPPPAGHhHeKiWB8JFnVewJaEN7iz9StzRepaL3+DFpD/CvhA8L7o8CQ5CTeScqL9HedVkM7O4MziMHkTJy0Li7EjP/6xdp8Caw+m6EsqvQ9Yd3qN4OTwrsMWmItLIaAHmkB/4UPhMqVnddVnwBUVb7toJ5rvGc/uktZkZPuHdzJRI0XSW//ltHHFCi3zneoJ92v/myYZOtWTyBDTmrgUtzC5fHbsSVdnD9IyWTRf72fz1Hjf2z8xFdFsdugo/+0qzOwE77K4BkgukeIDwhAxmdIr5yo4w==

      at encrypt (utils/encryption.ts:33:11)

  console.log
    LROC3KIjXJVoQVawJYZUYqT7rhXC8enb6O9ipY9VnOFMilFM00NHGiF3FHJQLWqac5zWFFZg2ofygANqT7Y5rQRtePcUEM5bLEUHvMaDdOAEXSdOK4PTbiCqZCAIPd79VVsW9gk2+vhKHbsq78AXhycCgUiOVjv25ooluDvqj3CQ+sTR+5cbatYO5kpXWwpu/BmPlRZYwsLUldpCuUPAYbkItKmQmiq/FWw1+z9Vx8mMKYhPtLuSTxnRrJ2Hn1eQm2EkuEeWQAEp+TJYaBsi93NalqmcWDo5swNe5HFPUH4hV7xtMtTZv82Wu9uNJ+ADUTD1B2mKDzKr0M0yNEYcGA==

      at encrypt (utils/encryption.ts:34:11)

起初我想知道我的.env 中的多行私钥是否有问题,但我可以导出我的私钥和公钥(请参阅注释掉的代码),当我将它们注销时,它们看起来就像我想的那样期望,我认为这意味着 keyObjects 正在成功创建。如果没有成功创建密钥,也许它每次都会创建新的密钥,这会导致失败?但据我所知,它们已成功创建。

我还阅读了this answer,它表明 MacOS 上的 OpenSSL 实现可能存在问题 - 我在 MacOs Big Sur,节点 14.16.0 (LTS) 上。所以,我 brew install openssl 然后链接它,现在我可以通过这样的检查看到我使用的是 OpenSSL 而不是 LibreSSL;

➜  website git:(master) ✗ openssl version
OpenSSL 1.1.1j  16 Feb 2021

但这似乎并没有什么不同。

那么,在给定相同输入的情况下,如何使加密函数可靠地返回相同的输出?

编辑

我已将我的加密工具更新为以下内容,并接受加密结果会有所不同,因为它是使用唯一会话密钥和公钥加密的,但是所有输出值都使用私钥正确解密键。

import { createPublicKey, createPrivateKey, privateDecrypt, publicEncrypt } from "crypto";

const privateKeyPem = process.env.ENCRYPTION_PRIVATE_KEY;
const privateKeyPemFixed = privateKeyPem.replace(/\\n/g, "\n");
const privateKey = createPrivateKey(privateKeyPemFixed);
const publicKey = createPublicKey(privateKey);

export const encrypt = (text: string): string => {
  const buffer = Buffer.from(text, "utf8");
  const encrypted = publicEncrypt(publicKey, buffer);
  return encrypted.toString("base64");
}

export const decrypt = (cipher: string): string => {
  const buffer = Buffer.from(cipher, "base64");
  const decrypted = privateDecrypt(privateKey, buffer);
  return decrypted.toString("utf8");
}

【问题讨论】:

  • 对于您的加密,您使用的是“RSA_PKCS1_OAEP_PADDING”,它具有重要的安全性功能 - 它添加了一些随机数据,因此最后每个密文看起来不同,使用相同的密钥和输入.还有其他填充模式,例如 PKCS#1.5,但一些库将其标记为“已弃用”,因为它会生成在很多环境中不安全的确定性签名(每次运行看起来都一样)。
  • @MichaelFehr 谢谢你,这对我的理解很有用

标签: node.js encryption-asymmetric node-crypto


【解决方案1】:

事实证明,我对crypto.PublicEncrypt 的假设是不正确的。引用this answer

纯函数准则 1:调用具有相同值的函数必须始终产生相同的返回值

在进行非对称加密时这是不可能的,因为每次操作都会生成一个随机会话密钥。会话密钥用公钥加密,然后会话密钥用于加密有效载荷。返回的值通常只是两个值的编码版本:(1) 公钥加密的会话密钥,以及 (2) 会话密钥加密的有效负载。

每次调用函数时,这两个值都会不同,因为会话密钥每次都会不同。

但是,尽管返回值不相等,但我认为它们在语义上是相等的——也就是说,如果您使用匹配的私钥解密每个值,解密后的值将比较相等。

所以我将测试更新为;

import { decrypt, encrypt } from "./encryption";

describe("encryption", () => {

  it("should encrypt and decrypt text", () => {
    const encrypted = encrypt("Hello World");
    const decrypted = decrypt(encrypted);
    expect(decrypted).toEqual("Hello World");
  });
});

现在它正在工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-07
    • 1970-01-01
    • 2023-03-06
    • 2021-11-06
    • 2019-02-26
    • 2020-09-30
    • 2016-01-11
    相关资源
    最近更新 更多