Encrypting and Decrypting on Salesforce and Node.js

Need to send secure messages back and forth between Salesforce and Node.js?  Whether it’s Heroku, AWS, or somewhere else, Node.js is an important platform to be able to communicate securely with Salesforce on.

Salesforce provides a fairly robust Crypto class.  I like the AES256 with Managed initialization vector (aka IV) that Salesforce provides.  The IV is 16 bytes of random/pseudorandom data that salts the encryption so the output is different every time even if the input is the same.  This prevents hackers from being able to spot patterns in the encrypted data, and makes it very difficult to brute force.  The IV gets prepended to the encrypted message so it is available on the other end when decrypting.

In Salesforce, Crypto.encryptWithManagedIV() and Crypto.decryptWithManagedIV() handle creating the IV, and prepending and parsing of the IV.

It’s pretty simple to build encryption and decryption using these methods on the Salesforce platform, see the example here.

But when it comes times to send and receive these encrypted messages on another platform, Node.js in this case, it becomes more difficult.  Managing the IV must be handled differently.  I found an article on how to do this with Java and a partial answer for Node.js.  But I couldn’t seem to find a complete, working example for Salesforce <-> Node.js.  So after I figured out how to make this work on my own, I thought I should blog about it to save others the trouble!

 

Saleforce

Here is example code of encrypt and decrypt, with some values from Node.js also included to be decrypted on Saleforce.

public class Encrypt {

    private static final Blob KEY = EncodingUtil.base64Decode('LHDK5bekAHJOFfXXzkd5uR/AoLBNPDNLIMAK8M0xss8=');    

/*

//execute anonymous example:

String secret = 'Shhhh..  This is a secret.';
System.debug('1: ' + secret);

String encrypted = Encrypt.encryptString(secret);
System.debug('2: ' + encrypted);

String encryptedAgain = Encrypt.encryptString(secret);
System.debug('3: ' + encryptedAgain);

System.debug('4: ' + Encrypt.decryptString(encrypted));
System.debug('5: ' + Encrypt.decryptString(encryptedAgain));

String encryptedfromNodeJS = 'cLaipdqsdGM/z+e+QjpqjUKeeQR26XSJdpUvgNE1areZHhB8DeAA+9xOgZO+wEe9';
System.debug('6: ' + encryptedfromNodeJS);

String encryptedfromNodeJSagain = '1NfNnjF5ROu3W9O8G14yzpjbpLDlEYjOjg/v1or5f7OgNZj+p/v3gdj5+NCR6olD';
System.debug('7: ' + encryptedfromNodeJSagain);

System.debug('8: ' + Encrypt.decryptString(encryptedfromNodeJS));
System.debug('9: ' + Encrypt.decryptString(encryptedfromNodeJSagain));

*/
    
    public static String encryptString(String clearText) {
        String encryptedText = null;
        Blob encryptedBlob = Crypto.encryptWithManagedIV('AES256', KEY, Blob.valueOf(clearText));
        encryptedText = EncodingUtil.base64Encode(encryptedBlob); 
        return encryptedText;
    }
    
    public static String decryptString(String encryptedText) {
        String clearText = null;
        Blob encryptedBlob = EncodingUtil.base64Decode(encryptedText);
        Blob decryptedBlob = Crypto.decryptWithManagedIV('AES256', KEY, encryptedBlob);
        clearText = decryptedBlob.toString();
        return clearText;
    }            
    
}

 

We can run this code in execute anonymous and we can see that we encrypt the same value twice, get two different encrypted values which decrypt to the same value.  We also decrypt two different encrypted values which came from Node.js and they decrypt to the same value.

 

Node.js

Here is example code of encrypt and decrypt, with some values from Salesforce also included to be decrypted on Node.js.

var KEY = Buffer.from('LHDK5bekAHJOFfXXzkd5uR/AoLBNPDNLIMAK8M0xss8=', 'base64');
const crypto = require('crypto');

var secret = 'Shhhh..  This is a secret.';
console.log('1: ' + secret);

var encrypted = encryptString(secret);
console.log('2: ' + encrypted);

var encryptedAgain = encryptString(secret);
console.log('3: ' + encryptedAgain);

console.log('4: ' + decryptString(encrypted));
console.log('5: ' + decryptString(encryptedAgain));

var encryptedfromSFDC = 'eFSWOcUjUiXKcfM+szX9HnjOZNTTCTUrxu82cwV0KR6AHNmp4X9PmNX8eQf4H1fG';
console.log('6: ' + encryptedfromSFDC);

var encryptedfromSFDCagain = 'Ed2oiBbyhZYm7P3kDvT7jYkg1p5e6Tb4xEEGaOPe/UznnZxEbr9pEmY6WVTWlWL6';
console.log('7: ' + encryptedfromSFDCagain);

console.log('8: ' + decryptString(encryptedfromSFDC));
console.log('9: ' + decryptString(encryptedfromSFDCagain));

function encryptString(clearText) {
	var encryptedText = null;

	var textBuffer = new Buffer(clearText, 'utf-8');
	var iv = crypto.randomBytes(16);

	var cipher = crypto.createCipheriv('aes-256-cbc', KEY, iv);
	var encryptedBuffer = cipher.update(textBuffer);
	encryptedText = Buffer.concat([iv, encryptedBuffer, cipher.final()]).toString('base64');

	return encryptedText;	
}

function decryptString(encryptedText) {
	var clearText = null;

	var encryptedBlob = new Buffer(encryptedText, 'base64');
	var iv = encryptedBlob.slice(0, 16);
	var textBuffer = encryptedBlob.toString('base64', 16);

	var decipher = crypto.createDecipheriv('aes-256-cbc', KEY, iv);
	clearText = decipher.update(textBuffer,'base64','utf-8');
	clearText += decipher.final('utf-8'); 
	
	return clearText;
}

 

We can run this code via command line and see that we encrypt the same value twice, get two different encrypted values which decrypt to the same value.  We also decrypt two different encrypted values which came from Salesforce and they decrypt to the same value.

Now you can talk securely back and forth between Salesforce and Node.js to your heart’s content!

Here is the full code on GitHub: https://github.com/danieljpeter/SalesforceNodeEncryption