Search code examples
javascriptencryptionprivate-keycryptojscloudflare-workers

How to encrypt data and private key for Fastspring transactions?


I'm trying to encrypt payload and secure key for fastspring transactions.

I follow their documentation https://community.fastspring.com/s/article/Passing-Sensitive-Data-with-Secure-Requests

This is my code for encryption of data

async function encrypt(payload) {
    const aesKey = crypto.randomBytes(16);
    const iv = new Buffer("");
    const cipher = crypto.createCipheriv('aes-128-ecb', aesKey, iv);
        var encryptedPayload = cipher.update(new Buffer(JSON.stringify(payload), 'utf8'), 'utf8', 'base64');
    const securePayload = encryptedPayload + cipher.final('base64');
    const secureKey = crypto.privateEncrypt('MIIEpQIBA.....', iv).toString('base64');
    return {
       securePayload: securePayload,
       secureKey: secureKey
  };
  };

When I try to call function with payload

{
                "contact": {
                    "email": "xxxxxx",
                    "firstName": "xxxxxx",
                    "lastName": "zxxxx"
                },
                "items": "cart"
            }

I have error

TypeError: Cannot read properties of null (reading '2') at t.exports (worker.js:14:163836) at u (worker.js:8:103816) at t.exports [as publicEncrypt] (worker.js:14:210455) at e.privateEncrypt (worker.js:14:210199) at worker.js:10:297620 at worker.js:10:300887

Problem is in privateEncrpyt function I'm passing directly private key as string.. I would appreciate if someone knows how to solve it or give me more direction?

I use cloudflare workers to build this.

Thank you


Solution

  • If you used the instruction given in the FastSpring documentation for generating the RSA private key:

    openssl genrsa -out privatekey.pem 2048
    

    the generated key is a PEM encoded private RSA key in PKCS#1 format. This can be imported in the first parameter of privateEncrypt() as follows:

    var pkcs1pem = `-----BEGIN RSA PRIVATE KEY-----
    MII...
    -----END RSA PRIVATE KEY-----`;  
    ...
    const secureKey = crypto.privateEncrypt(pkcs1pem, aesKey).toString('base64');
    

    For completeness: crypto supports for private RSA keys beside the PKCS#1 format also the PKCS#8 format and beside the PEM encoding also the DER encoding.


    Note on the FastSpring encryption approach:

    Encrypting the AES key with privateEncrypt() and the private key as specified by FastSpring is actually the wrong approach. privateEncrypt() is intended for creating a signature (as part of a low level signing process). The purpose of a signature is not to keep data secret, but to verify the authenticity of a digital message. From the signature (at least as it is created here) the original data, i.e. the AES key, can easily be reconstructed using the public key. Since in general anyone can come into possession of the public key, the AES key is thus compromised.

    The basic problem of the FastSpring approach is that the actual public key must be kept secret, quote FastSpring: Create a 2048-bit RSA public key. Only share this key with FastSpring. The secrecy of the public key, however, is not guaranteed in practice (apart from the fact that it contradicts the concept of a public key). For instance, the public key could be intercepted during the transfer to FastSpring, or it could be accidentally leaked by FastSpring itself, etc.

    The correct way is to encrypt the AES key with publicEncrypt() and the public key of FastSpring. Then it is ensured that only FastSpring can decrypt the encrypted AES key, because only FastSpring has the private key.