节点 crypto.publicEncrypt 每次使用时返回不同的值

sau*_*imo 3 encryption-asymmetric node.js node-crypto

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

\n

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

\n

这是我的加密实用程序;

\n
import { createPublicKey, createPrivateKey, privateDecrypt, publicEncrypt, constants } from "crypto";\n\nconst privateKeyPem = process.env.ENCRYPTION_PRIVATE_KEY;\nconst privateKeyPemFixed = privateKeyPem.replace(/\\\\n/g, "\\n");\nconst privateKey = createPrivateKey(privateKeyPemFixed);\nconst publicKey = createPublicKey(privateKey);\n\n// const private1 = privateKey.export({\n//   type: \'pkcs1\',\n//   format: \'pem\',\n// }).toString("base64");\n\n// const public1 = publicKey.export({\n//   type: \'pkcs1\',\n//   format: \'pem\',\n// }).toString("base64");\n\nexport const encrypt = (text: string): string => {\n  const buffer = Buffer.from(text);\n\n  const encrypted1 = publicEncrypt(   {\n      key: publicKey,\n      oaepHash: \'sha256\',\n      padding: constants.RSA_PKCS1_OAEP_PADDING,\n  }, buffer);\n\n  const encrypted2 = publicEncrypt({\n    key: publicKey,\n    oaepHash: \'sha256\',\n    padding: constants.RSA_PKCS1_OAEP_PADDING,\n  }, buffer);\n\n  console.log(encrypted1.toString("base64"));\n  console.log(encrypted2.toString("base64"));\n\n  return encrypted1.toString("base64");\n}\n\nexport const decrypt = (cipher: string): string => {\n  const buffer = Buffer.from(cipher);\n  const decrypted = privateDecrypt(privateKey, buffer);\n  return decrypted.toString("utf8");\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我有一个笑话测试,如下所示;

\n
import { encrypt } from "./encryption";\n\ndescribe("encryption", () => {\n\n  const helloWorld = "Hello world";\n  const encryptedHelloWorld = "IIisobkVsZxKiR0e5nwyIHjsww/ebrKXI0hzDbdTdC8KMU2rc57IRX9krhVThVma2no7gZcMvbfwJsRjHz1s7NoBiT+BitgYlI/LE1jMpFd5Bmghy2S93F/wGFRWA4DMAqdw32I9s8CRKVvellxkh3ZlJ5NyzxWG8kVfc11CrEMD+1sqo2e9cFCcTdx5jEVYpCgITy7X2vDxUwOPQ7bK8K56kU5ivQhUfyoHjd9VclRUxfBaSzOwLJQqK6RJPbNwuUfILcCaR72GTf4zWMhQqIvs/zHhSu+S9QQYPVvmZ1SzqqJaCM9mM6Cvl8Gn2brwcMB003f0CFb8WFimOgM6lQ==";\n\n  it("should encrypt text", () => {\n    const received = encrypt(helloWorld);\n    expect(received).toEqual(encryptedHelloWorld);\n  });\n});\n\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
  console.log\n    aDWDWcE+Zs92/rp2DLJN8UTgwHPTg6TDqFPIrC3ODVIfZgo5uaQV0NTSESPPPAGHhHeKiWB8JFnVewJaEN7iz9StzRepaL3+DFpD/CvhA8L7o8CQ5CTeScqL9HedVkM7O4MziMHkTJy0Li7EjP/6xdp8Caw+m6EsqvQ9Yd3qN4OTwrsMWmItLIaAHmkB/4UPhMqVnddVnwBUVb7toJ5rvGc/uktZkZPuHdzJRI0XSW//ltHHFCi3zneoJ92v/myYZOtWTyBDTmrgUtzC5fHbsSVdnD9IyWTRf72fz1Hjf2z8xFdFsdugo/+0qzOwE77K4BkgukeIDwhAxmdIr5yo4w==\n\n      at encrypt (utils/encryption.ts:33:11)\n\n  console.log\n    LROC3KIjXJVoQVawJYZUYqT7rhXC8enb6O9ipY9VnOFMilFM00NHGiF3FHJQLWqac5zWFFZg2ofygANqT7Y5rQRtePcUEM5bLEUHvMaDdOAEXSdOK4PTbiCqZCAIPd79VVsW9gk2+vhKHbsq78AXhycCgUiOVjv25ooluDvqj3CQ+sTR+5cbatYO5kpXWwpu/BmPlRZYwsLUldpCuUPAYbkItKmQmiq/FWw1+z9Vx8mMKYhPtLuSTxnRrJ2Hn1eQm2EkuEeWQAEp+TJYaBsi93NalqmcWDo5swNe5HFPUH4hV7xtMtTZv82Wu9uNJ+ADUTD1B2mKDzKr0M0yNEYcGA==\n\n      at encrypt (utils/encryption.ts:34:11)\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
  console.log\n    aDWDWcE+Zs92/rp2DLJN8UTgwHPTg6TDqFPIrC3ODVIfZgo5uaQV0NTSESPPPAGHhHeKiWB8JFnVewJaEN7iz9StzRepaL3+DFpD/CvhA8L7o8CQ5CTeScqL9HedVkM7O4MziMHkTJy0Li7EjP/6xdp8Caw+m6EsqvQ9Yd3qN4OTwrsMWmItLIaAHmkB/4UPhMqVnddVnwBUVb7toJ5rvGc/uktZkZPuHdzJRI0XSW//ltHHFCi3zneoJ92v/myYZOtWTyBDTmrgUtzC5fHbsSVdnD9IyWTRf72fz1Hjf2z8xFdFsdugo/+0qzOwE77K4BkgukeIDwhAxmdIr5yo4w==\n\n      at encrypt (utils/encryption.ts:33:11)\n\n  console.log\n    LROC3KIjXJVoQVawJYZUYqT7rhXC8enb6O9ipY9VnOFMilFM00NHGiF3FHJQLWqac5zWFFZg2ofygANqT7Y5rQRtePcUEM5bLEUHvMaDdOAEXSdOK4PTbiCqZCAIPd79VVsW9gk2+vhKHbsq78AXhycCgUiOVjv25ooluDvqj3CQ+sTR+5cbatYO5kpXWwpu/BmPlRZYwsLUldpCuUPAYbkItKmQmiq/FWw1+z9Vx8mMKYhPtLuSTxnRrJ2Hn1eQm2EkuEeWQAEp+TJYaBsi93NalqmcWDo5swNe5HFPUH4hV7xtMtTZv82Wu9uNJ+ADUTD1B2mKDzKr0M0yNEYcGA==\n\n      at encrypt (utils/encryption.ts:34:11)\n
Run Code Online (Sandbox Code Playgroud)\n

然而,这似乎并没有产生什么影响。

\n

那么,在给定相同的输入的情况下,我该怎么做才能使加密函数可靠地返回相同的输出呢?

\n

编辑

\n

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

\n
\xe2\x9e\x9c  website git:(master) \xe2\x9c\x97 openssl version\nOpenSSL 1.1.1j  16 Feb 2021\n
Run Code Online (Sandbox Code Playgroud)\n

sau*_*imo 5

事实证明我的假设crypto.PublicEncrypt是错误的。引用这个答案

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

在进行非对称加密时这是不可能的,因为每次操作都会生成随机会话密钥。使用公钥对会话密钥进行加密,然后使用会话密钥对有效负载进行加密。返回的值通常只是两个值的编码版本:(1) pubkey 加密的会话密钥,以及 (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");
  });
});
Run Code Online (Sandbox Code Playgroud)

现在它正在发挥作用。