Params Encryption

For additional security, it is possible to encrypt Advocate, Friend, and Loyalty member email, as well as Custom User Data on back-end. This can be done by using 2048-bit key generated by Talkable for your site. So, instead of sending params in plain text, you can send them encrypted.

Site public encryption key

To get your unique site public encryption key, go to All Site Settings → Integrations → API integration → Public encryption key for email addresses.

Passing email as a GET parameter to Standalone Campaign

Also it’s possible to pass encrypted email as a GET parameter (e.g. for CTA links that point to standalone invite page). But to do that encrypted email should be URL-encoded.

Note

It’s recommended to use Optimal Asymmetric Encryption Padding (OAEP) padding scheme together with RSA encryption.

Ruby Example

require 'openssl'
require 'base64'

def key
  @key ||= begin
    key_content = File.read('talkable_your_site_slug_public_key.pem')
    OpenSSL::PKey::RSA.new(key_content)
  end
end

def encode_param_for_talkable(param)
  encrypted_param = key.public_encrypt(param, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
  Base64.strict_encode64(encrypted_param)
end

puts encode_param_for_talkable("email_to_encrypt@example.com")

Java Example

This example uses Bouncy Castle library and has been tested on:

  • bcprov-jdk18on-1.78.1.jar (Provider)

  • bcpkix-jdk18on-1.78.1.jar (PKIX/CMS/EAC/PKCS/OCSP/TSP/OPENSSL)

that can be downloaded from Bouncy Castle Latest Releases.

Note

Please note that loading a Security Provider and a key into memory takes more time than encrypting. So it is recommended to store the key in memory instead of loading it each time to avoid performance overhead.

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.FileReader;
import java.io.IOException;
import java.security.*;
import java.util.Base64;

import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

public class EncryptionDemo {
    static class ParamEncryptor {
        private Cipher cipher;
        private Key publicKey;

        private void initPublicKey() throws IOException {
            PEMParser pemParser = new PEMParser(new FileReader("talkable_your_site_slug_public_key.pem"));
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
            SubjectPublicKeyInfo publicKeyInfo = (SubjectPublicKeyInfo) pemParser.readObject();
            publicKey = converter.getPublicKey(publicKeyInfo);
        }

        private void initCipher() throws GeneralSecurityException {
            cipher = Cipher.getInstance("RSA/ECB/OAEPPadding", "BC");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        }

        public ParamEncryptor() throws IOException, GeneralSecurityException {
            initPublicKey();
            initCipher();
        }

        public String encryptParam(String param) throws BadPaddingException, IllegalBlockSizeException {
            byte[] input = param.getBytes();
            byte[] cipherText = cipher.doFinal(input);
            byte[] encodedBytes = Base64.getEncoder().encode(cipherText);
            return new String(encodedBytes);
        }
    }

    static ParamEncryptor paramEncryptor;

    static {
        Security.addProvider(new BouncyCastleProvider());
        try {
            paramEncryptor = new ParamEncryptor();
        } catch (IOException | GeneralSecurityException e) {
            e.printStackTrace();
        }
    }

    public static String encryptParam(String param) throws BadPaddingException, IllegalBlockSizeException {
        return paramEncryptor.encryptParam(param);
    }

    public static void main(String[] args) throws Exception {
        String email = "email_to_encrypt@example.com";
        System.out.println(encryptParam(email));
    }
}

Node.js Example

const fs = require('fs');
const crypto = require('crypto');

// Read the public key from file
const publicKey = fs.readFileSync('talkable_your_site_slug_public_key.pem', 'utf8');

const encryptData = (data) => {
  const encryptedData = crypto.publicEncrypt(
    {
      key: publicKey,
      padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
      oaepHash: 'sha1',
    },
    Buffer.from(data)
  );
  return encryptedData.toString('base64');
};

// Example usage
const encryptedEmail = encryptData('email_to_encrypt@example.com');
console.log('Encrypted email:', encryptedEmail);

Front-end Part

Please modify the front-end using this pseudo code example:

<script>
  _talkableq.push(['authenticate_customer', {
    email: '<%= to_json(TalkableEncryptionService.encrypt(current_user.email)) %>',
    phone_number: '<%= to_json(TalkableEncryptionService.encrypt(current_user.phone_number)) %>',
    first_name: '<%= to_json(current_user.first_name) %>',
    last_name: '<%= to_json(current_user.last_name) %>',
    person_custom_properties: {
      secret: '<%= to_json(TalkableEncryptionService.encrypt(current_user.secret)) %>'
    }
  }]);
</script>