title: node crypto.publicEncrypt returns different value each time it is used
body: <p>I'm trying to implement basic asymmetric encryption; one service has a public key and encrypts a value with that public key and then another service receives the encrypted message, decodes it using the private key, and does something with the decrypted data.</p>
  <p>The problem that I'm having is that every time I use the in-built <a href="https://nodejs.org/api/crypto.html#crypto_crypto_publicencrypt_key_buffer" rel="nofollow noreferrer">crypto.publicEncrypt</a> method, I get a different encrypted value returned. As far as I can tell, I'm using the same inputs, so as I understand it I should be seeing the same output. Perhaps I have misunderstood this?</p>
  <p>Here is my encryption utility;</p>
  <pre class="lang-ts prettyprint-override"><code>import { createPublicKey, createPrivateKey, privateDecrypt, publicEncrypt, constants } from &quot;crypto&quot;;
  
  const privateKeyPem = process.env.ENCRYPTION_PRIVATE_KEY;
  const privateKeyPemFixed = privateKeyPem.replace(/\\n/g, &quot;\n&quot;);
  const privateKey = createPrivateKey(privateKeyPemFixed);
  const publicKey = createPublicKey(privateKey);
  
  // const private1 = privateKey.export({
  //   type: 'pkcs1',
  //   format: 'pem',
  // }).toString(&quot;base64&quot;);
  
  // const public1 = publicKey.export({
  //   type: 'pkcs1',
  //   format: 'pem',
  // }).toString(&quot;base64&quot;);
  
  export const encrypt = (text: string): string =&gt; {
    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(&quot;base64&quot;));
    console.log(encrypted2.toString(&quot;base64&quot;));
  
    return encrypted1.toString(&quot;base64&quot;);
  }
  
  export const decrypt = (cipher: string): string =&gt; {
    const buffer = Buffer.from(cipher);
    const decrypted = privateDecrypt(privateKey, buffer);
    return decrypted.toString(&quot;utf8&quot;);
  }
  </code></pre>
  <p>I have a jest test which looks like this;</p>
  <pre class="lang-ts prettyprint-override"><code>import { encrypt } from &quot;./encryption&quot;;
  
  describe(&quot;encryption&quot;, () =&gt; {
  
    const helloWorld = &quot;Hello world&quot;;
    const encryptedHelloWorld = &quot;IIisobkVsZxKiR0e5nwyIHjsww/ebrKXI0hzDbdTdC8KMU2rc57IRX9krhVThVma2no7gZcMvbfwJsRjHz1s7NoBiT+BitgYlI/LE1jMpFd5Bmghy2S93F/wGFRWA4DMAqdw32I9s8CRKVvellxkh3ZlJ5NyzxWG8kVfc11CrEMD+1sqo2e9cFCcTdx5jEVYpCgITy7X2vDxUwOPQ7bK8K56kU5ivQhUfyoHjd9VclRUxfBaSzOwLJQqK6RJPbNwuUfILcCaR72GTf4zWMhQqIvs/zHhSu+S9QQYPVvmZ1SzqqJaCM9mM6Cvl8Gn2brwcMB003f0CFb8WFimOgM6lQ==&quot;;
  
    it(&quot;should encrypt text&quot;, () =&gt; {
      const received = encrypt(helloWorld);
      expect(received).toEqual(encryptedHelloWorld);
    });
  });
  
  </code></pre>
  <p>However it constantly fails as the result always seems to be different.</p>
  <p>I ran the encryption process twice in the <code>encrypt</code> function, to demonstrate the problem; the two values which it logs out are completely different and I don't understand why.</p>
  <pre><code>  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)
  </code></pre>
  <p>At first I wondered if there was a problem with my multiline private key in my <code>.env</code>, but I can export my private and public keys (see commented out code) and when I log them out, they look as I would expect, which I think means the <code>keyObjects</code> are being created successfully. If the keys were not created successfully, maybe it would create new keys each time and that would cause this failure? But as far as I can tell, they are created successfully.</p>
  <p>I also read <a href="https://stackoverflow.com/a/62224922/4293734">this answer</a> which suggested that there might be a problem with the OpenSSL implementation on MacOS - I'm on MacOs Big Sur, Node 14.16.0 (LTS). So, I <code>brew install openssl</code> and then linked it, and now I can see that I am using OpenSSL rather than LibreSSL by checking like so;</p>
  <pre class="lang-shell prettyprint-override"><code>➜  website git:(master) ✗ openssl version
  OpenSSL 1.1.1j  16 Feb 2021
  </code></pre>
  <p>However that doesn't seem to have made a difference.</p>
  <p>So, what can I do to make the encrypt function reliably return the same output, given the same input?</p>
  <p><strong>EDIT</strong></p>
  <p>I've updated my encryption util to the following and accepted that the result of the encryption will be different because it is encrypted with a unique session key as well as the public key, however all the output values decrypt correctly with the private key.</p>
  <pre class="lang-ts prettyprint-override"><code>import { createPublicKey, createPrivateKey, privateDecrypt, publicEncrypt } from &quot;crypto&quot;;
  
  const privateKeyPem = process.env.ENCRYPTION_PRIVATE_KEY;
  const privateKeyPemFixed = privateKeyPem.replace(/\\n/g, &quot;\n&quot;);
  const privateKey = createPrivateKey(privateKeyPemFixed);
  const publicKey = createPublicKey(privateKey);
  
  export const encrypt = (text: string): string =&gt; {
    const buffer = Buffer.from(text, &quot;utf8&quot;);
    const encrypted = publicEncrypt(publicKey, buffer);
    return encrypted.toString(&quot;base64&quot;);
  }
  
  export const decrypt = (cipher: string): string =&gt; {
    const buffer = Buffer.from(cipher, &quot;base64&quot;);
    const decrypted = privateDecrypt(privateKey, buffer);
    return decrypted.toString(&quot;utf8&quot;);
  }
  </code></pre>