Decrypting Email Addresses

This page explains how to decrypt an email address returned by the Notifications Subscriptions API.

Introduction

To protect user privacy, email addresses will not be communicated in plain text. Instead, when responding to requests for opt-out or opt-in lists and in response to a successful submission, the Notifications Subscriptions API will send the email addresses in a format following the Advanced Encryption Standard (AES).

Email encryption details

AES is an NIST and US government approved standard for encrypting sensitive data based on the Rijndael algorithm. AES is a symmetrical algorithm (meaning it can be reversed using a shared key), offers a 128-bit block encryption and supports a shared key size of 128, 192, or 256 bits. It is also royalty free and supported by most major programming platforms.

AlgorithmAES
Key size128 bit
ModeECB
PaddingPKCS5/PKCS7

Decryption steps

Although every programming platform will vary in its implementation of AES, the following general steps will need to be performed to decipher an encrypted user email address:

  1. Hex decode encrypted email address string & shared secret key
  2. Initialize your decryption library using details above as appropriate
  3. Decrypt encrypted email address string
  4. Remove excess padding as necessary

Continue reading for code samples demonstrating user email address decryption.

Code samples

πŸ“˜

The code samples below are for educational purposes only. They are not intended to be used in a production environment and are provided "as is" without warranty of any kind.

The follow code samples demonstrate how to decrypt the AES encrypted user email addresses returned by the Notifications Subscriptions API. Refer to your programming language's documentation for the exact implementation.

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.IllegalBlockSizeException;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import java.security.NoSuchProviderException;

// Example usage: $ java decrypt ENCRYPTED_EMAIL_FROM_BAZAARVOICE YOUR_SHARED_SECRET_KEY
public class DecryptEmailExample {

    static public void main(String args[]) {

        // Hex decode the encrypted email
        byte[] encryptedEmailAddress = DatatypeConverter.parseHexBinary(args[0]);
        // Hex decode shared secret key
        byte[] sharedSecret = DatatypeConverter.parseHexBinary(args[1]);

        try {
            // http://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SunJCEProvider
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
            SecretKeySpec key = new SecretKeySpec(sharedSecret, "AES");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decrypted = cipher.doFinal(encryptedEmailAddress);
            System.out.println(new String(decrypted));
        }
        catch (BadPaddingException e) {
            System.out.println(e);
        }
        catch (NoSuchPaddingException e) {
            System.out.println(e);
        }
        catch (IllegalBlockSizeException e) {
            System.out.println(e);
        }
        catch (NoSuchProviderException e) {
            System.out.println(e);
        }
        catch (NoSuchAlgorithmException e) {
            System.out.println(e);
        }
        catch (InvalidKeyException e) {
            System.out.println(e);
        }
    }
}
        
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

// Example usage: $ decrypt.exe ENCRYPTED_EMAIL_FROM_BAZAARVOICE YOUR_SHARED_SECRET_KEY
namespace BazaarvoiceNotificationsApiExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // Hex decode the encrypted email
            byte[] bytEncryptedEmail = FromHex(args[0]);

            // Hex decode shared secret key
            byte[] bytSharedSecretKey = FromHex(args[1]);

            // Initialize encryption class
            var rijAlg = new RijndaelManaged();
            rijAlg.KeySize = 128;
            rijAlg.Mode = CipherMode.ECB;
            rijAlg.Padding = PaddingMode.PKCS7;
            rijAlg.Key = bytSharedSecretKey;

            // Perform decryption
            string decrypted = Decrypt(rijAlg, bytEncryptedEmail);
            Console.Write(decrypted);
            Console.ReadLine();
        }

        public static string Decrypt(RijndaelManaged rijAlg, byte[] bytEncryptedEmail)
        {
            using (var decryptor = rijAlg.CreateDecryptor())
            {
                using (MemoryStream msDecrypt = new MemoryStream(bytEncryptedEmail))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                           return srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
        }

        public static byte[] FromHex(string hex)
        {
            byte[] bytToken = new byte[hex.Length / 2];
            for (int i = 0; i < hex.Length; i += 2)
            {
                bytToken[i / 2] = System.Convert.ToByte(hex.Substring(i, 2), 16);
            }
            return bytToken;
        }
    }
}
        
// Example usage: $ node decrypt.js ENCRYPTED_EMAIL_FROM_BAZAARVOICE YOUR_SHARED_SECRET_KEY

// Requires https://github.com/evanvosberg/crypto-js
var CryptoJS = require("crypto-js");
var args = process.argv.splice(2);

// Hex decode the encrypted email
var encryptedEmail = CryptoJS.enc.Hex.parse(args[0]);

// Hex decode shared secret key
var sharedSecretKey = CryptoJS.enc.Hex.parse(args[1]);

// Initialize CryptoJS Arguments
var config = {
   mode: CryptoJS.mode.ECB,
   padding: CryptoJS.pad.Pkcs7
};
var ciperParams = CryptoJS.lib.CipherParams.create({
   ciphertext: encryptedEmail
});

// Perform decryption
var decrypted = CryptoJS.AES.decrypt(ciperParams, sharedSecretKey, config);

console.log( decrypted.toString(CryptoJS.enc.Utf8) );
        
# Example usage: $ python decrypt.py ENCRYPTED_EMAIL_FROM_BAZAARVOICE YOUR_SHARED_SECRET_KEY

# Requires https://github.com/dlitz/pycrypto
import sys
from Crypto.Cipher import AES

# Hex decode the encrypted email
encryptedEmail = sys.argv[1].decode("hex")

# Hex decode shared secret key
sharedSecretKey = sys.argv[2].decode("hex")

# Initialize Crypt_AES class
cypher = AES.new(sharedSecretKey, AES.MODE_ECB)

# Perform decryption
decrypted = cypher.decrypt(encryptedEmail)

print decrypted
<?php
// Example usage: $ php decrypt.php ENCRYPTED_EMAIL_FROM_BAZAARVOICE YOUR_SHARED_SECRET_KEY

// Requries https://github.com/phpseclib/phpseclib
require_once(dirname(__FILE__) . '/phpseclib-1.0.5/phpseclib/Crypt/AES.php');

function hexToStr($hex){
    $string = '';
    for ($i = 0; $i < strlen($hex) - 1; $i += 2){
        $string .= chr(hexdec($hex[$i].$hex[$i + 1]));
    }
    return $string;
}

// Hex decode the encrypted email
$encryptedEmail = hexToStr($argv[1]);

// Hex decode shared secret key
$sharedSecretKey = hexToStr($argv[2]);

// Initialize Crypt_AES class
$cipher = new Crypt_AES(CRYPT_AES_MODE_ECB);
$cipher->setKeyLength(128);
$cipher->setKey($sharedSecretKey);

// Perform decryption
$decrypted = $cipher->decrypt($encryptedEmail);

echo $decrypted . "\n";