📘

Credentials Required

In order to use the Quick Quotes via Encrypted URLs functionality, you will require a Partner ID and encryption keys provided by the Aventus Platform. Please contact us to get these.

The step-by-step example below shows the necessary steps in encrypting, signing & formatting the payload with the expected outcome at each step (base64-encoded) for prepa

This example uses the following keys, you must replace these with your own keys as provided by Aventus Platform:

Encryption keyDbU0fQQc5IbF4v9a/KUvMrzF2EWGhnp7Ussb4GvWk9M=
Authentication keyHPqKD+TDmsZPuKSZHSmpZT+Y2nGsZ7uijy1NesJWbyU=
Random 16 bytes Initialisation Vector (IV)6AimI+hLvyAuxLlTlGN0pg==

The process

StepDescriptionOutcome
1Construct the JSON payload{"firstName":"John","lastName":"Doe","dateOfBirth":"1970-01-01T00:00:00Z","emailAddress":"[email protected]","phoneNumber":"07000123456","address1": "1 Anchor Road","address2":"Kingsclere","town":"Newbury","postalCode":"RG20 5NE","partnerReference":"01F0B37A-E056-4415-93D9-03F75506EE17","nonce":"u_KDhf2j79jCjDZ-ArFqHXOKqaC.B9sZ","timestamp","2018-10-19T11:35:20Z"}
2Encrypt the JSON with encryption key + IVFYOX4sfwwshBs9sxACOP8W2Fez9shCP4eYUB0oTJf7/KbD5vh92G3Y/f/u3rzebHRO60guu9qY17Z+vnQMREZPR0EzzGa5yyzvD9UuHoPR/MiiLyLcAHphAts6QWhpuosfY8mCoSd9p+Sp365LZkjXAIa/JWSMVa981Ykriedr7Pz2bLtsbkc0JF7M2Yq483mOIZtCKq997UsAEulXNQ2y75hpNu+g1898/65YThcd7YDJ8d/0QQ/69JRgTFPtVak4EyIhUTUViW7FhuifYDw4sjhmlBxclyy/zsRGdQrG13GAtDKdgl2Upg/QKWsMiNKhSnKtQ/cDDjJqs8y+MQ/XrKPrNEpSor4ltLIC1b/Sj5Z/0I+DPh0F3Z9wcqBObjVOjN7mFHsQDcZRLG6xWt2V9QqhFF7Ka6Gi0f6BXVlWQx8X2umOjrDD+M+8t8i/aVMGkYQiag0bUkdY77qXjvVCe4Ke/dpGyJZyuHy7YC19o=
3.1Concatenate IV + cipher6AimI+hLvyAuxLlTlGN0phWDl+LH8MLIQbPbMQAjj/FthXs/bIQj+HmFAdKEyX+/ymw+b4fdht2P3/7t683mx0TutILrvamNe2fr50DERGT0dBM8xmucss7w/VLh6D0fzIoi8i3AB6YQLbOkFoabqLH2PJgqEnfafkqd+uS2ZI1wCGvyVkjFWvfNWJK4nna+z89my7bG5HNCRezNmKuPN5jiGbQiqvfe1LABLpVzUNsu+YaTbvoNfPfP+uWE4XHe2AyfHf9EEP+vSUYExT7VWpOBMiIVE1FYluxYbon2A8OLI4ZpQcXJcsv87ERnUKxtdxgLQynYJdlKYP0ClrDIjSoUpyrUP3Aw4yarPMvjEP16yj6zRKUqK+JbSyAtW/0o+Wf9CPgz4dBd2fcHKgTm41Toze5hR7EA3GUSxusVrdlfUKoRReymuhotH+gV1ZVkMfF9rpjo6ww/jPvLfIv2lTBpGEImoNG1JHWO+6l471QnuCnv3aRsiWcrh8u2Atfa
3.2Compute hashcTwKkvrFuYjZHEpCtAWocg9+1KslEka0zIbw+K0D6Wc=
3.3Concatenate IV + cipher + hash6AimI+hLvyAuxLlTlGN0phWDl+LH8MLIQbPbMQAjj/FthXs/bIQj+HmFAdKEyX+/ymw+b4fdht2P3/7t683mx0TutILrvamNe2fr50DERGT0dBM8xmucss7w/VLh6D0fzIoi8i3AB6YQLbOkFoabqLH2PJgqEnfafkqd+uS2ZI1wCGvyVkjFWvfNWJK4nna+z89my7bG5HNCRezNmKuPN5jiGbQiqvfe1LABLpVzUNsu+YaTbvoNfPfP+uWE4XHe2AyfHf9EEP+vSUYExT7VWpOBMiIVE1FYluxYbon2A8OLI4ZpQcXJcsv87ERnUKxtdxgLQynYJdlKYP0ClrDIjSoUpyrUP3Aw4yarPMvjEP16yj6zRKUqK+JbSyAtW/0o+Wf9CPgz4dBd2fcHKgTm41Toze5hR7EA3GUSxusVrdlfUKoRReymuhotH+gV1ZVkMfF9rpjo6ww/jPvLfIv2lTBpGEImoNG1JHWO+6l471QnuCnv3aRsiWcrh8u2AtfacTwKkvrFuYjZHEpCtAWocg9+1KslEka0zIbw+K0D6Wc=
4URL encode6AimI%2bhLvyAuxLlTlGN0phWDl%2bLH8MLIQbPbMQAjj%2fFthXs%2fbIQj%2bHmFAdKEyX%2b%2fymw%2bb4fdht2P3%2f7t683mx0TutILrvamNe2fr50DERGT0dBM8xmucss7w%2fVLh6D0fzIoi8i3AB6YQLbOkFoabqLH2PJgqEnfafkqd%2buS2ZI1wCGvyVkjFWvfNWJK4nna%2bz89my7bG5HNCRezNmKuPN5jiGbQiqvfe1LABLpVzUNsu%2bYaTbvoNfPfP%2buWE4XHe2AyfHf9EEP%2bvSUYExT7VWpOBMiIVE1FYluxYbon2A8OLI4ZpQcXJcsv87ERnUKxtdxgLQynYJdlKYP0ClrDIjSoUpyrUP3Aw4yarPMvjEP16yj6zRKUqK%2bJbSyAtW%2f0o%2bWf9CPgz4dBd2fcHKgTm41Toze5hR7EA3GUSxusVrdlfUKoRReymuhotH%2bgV1ZVkMfF9rpjo6ww%2fjPvLfIv2lTBpGEImoNG1JHWO%2b6l471QnuCnv3aRsiWcrh8u2AtfacTwKkvrFuYjZHEpCtAWocg9%2b1KslEka0zIbw%2bK0D6Wc%3d

Example Code

const int keySize = 256;
const int blockSize = 128;
const CipherMode cipherMode = CipherMode.CBC;
const PaddingMode paddingMode = PaddingMode.PKCS7;
const int ivLength = 16;
static Random random = new Random();

static void Main(string[] args) 
{
  var nonce = GenerateNonce();
  var utcNow = DateTime.UtcNow.ToString("o");

  var input = $@"{{""firstName"":""John"",""lastName"":""Doe"",""dateOfBirth"":""1970-01-01T00:00:00Z"",""emailAddress"":""[email protected]"",""phoneNumber"":""07000123456"",""address1"":""1 Anchor Road"",""address2"":""Kingsclere"",""town"":""Newbury"",""postalCode"":""RG20 5NE"",""partnerReference"":""01F0B37A-E056-4415-93D9-03F75506EE17"",""nonce"":""{nonce}"",""timestamp"":""{utcNow}""}}";
  
  var encryptionKey = Convert.FromBase64String("DbU0fQQc5IbF4v9a/KUvMrzF2EWGhnp7Ussb4GvWk9M=");
  var authenticationKey = Convert.FromBase64String("HPqKD+TDmsZPuKSZHSmpZT+Y2nGsZ7uijy1NesJWbyU=");
  
  string encryptedText = Encrypt(input, encryptionKey, authenticationKey);
  string decryptedText = Decrypt(encryptedText, encryptionKey, authenticationKey);
}

public static string Encrypt(string input, byte[] encryptionKey, byte[] authenticationKey) 
{
  var inputBytes = Encoding.UTF8.GetBytes(input);
  
  byte[] cipher;
  
  using (var aes = new AesManaged())
  {
    aes.KeySize = keySize;
    aes.BlockSize = blockSize;
    aes.Mode = cipherMode;
    aes.Padding = paddingMode;
    aes.Key = encryptionKey;
    aes.GenerateIV();
    
    using (MemoryStream ms = new MemoryStream())
    {
      ms.Write(aes.IV, 0, aes.IV.Length);
      using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
      {
        cs.Write(inputBytes, 0, inputBytes.Length);
        cs.FlushFinalBlock();
      }
      
      cipher = ms.ToArray();
    }
  }
  
  string base64SignedPayload;
  using (HMACSHA256 hmac = new HMACSHA256(authenticationKey))
  {
    using (MemoryStream ms = new MemoryStream())
    {
      ms.Write(cipher, 0, cipher.Length);
      var hash = hmac.ComputeHash(cipher);
      ms.Write(hash, 0, hash.Length);
      
      base64SignedPayload = Convert.ToBase64String(ms.ToArray());
    }
  }
  
  return HttpUtility.UrlEncode(base64SignedPayload);
}

public static string Decrypt(string encryptedText, byte[] encryptionKey, byte[] authenticationKey)
{
  var base64SignedPayload = HttpUtility.UrlDecode(encryptedText);
  var signedPayload = Convert.FromBase64String(base64SignedPayload);
  
  byte[] cipher;
  
  using (HMACSHA256 hmac = new HMACSHA256(authenticationKey))
  {
    var storedHash = new byte[hmac.HashSize / 8];
    var cipherLength = signedPayload.Length - storedHash.Length;
    
    cipher = new byte[cipherLength];
    
    using (MemoryStream ms = new MemoryStream(signedPayload))
    {
      ms.Read(cipher, 0, cipherLength);
      ms.Read(storedHash, 0, storedHash.Length);
      
      var computedHash = hmac.ComputeHash(cipher);
      
      if (!computedHash.SequenceEqual(storedHash))
      {
        return null;
      }
    }
  }
  
  using (AesManaged aes = new AesManaged())
  {
    aes.KeySize = keySize;
    aes.BlockSize = blockSize;
    aes.Mode = cipherMode;
    aes.Padding = paddingMode;
    aes.Key = encryptionKey;
    
    var iv = new byte[ivLength];
    var encrypted = new byte[cipher.Length - ivLength];
    
    using (MemoryStream ms = new MemoryStream(cipher))
    {
      ms.Read(iv, 0, ivLength);
      aes.IV = iv;
      ms.Read(encrypted, 0, encrypted.Length);
      
      using (MemoryStream decrypted = new MemoryStream())
      {
        using (CryptoStream cs = new CryptoStream(decrypted, aes.CreateDecryptor(), CryptoStreamMode.Write))
        {
          cs.Write(encrypted, 0, encrypted.Length);
        }
        
        return Encoding.UTF8.GetString(decrypted.ToArray());
      }
    }
  }
  
  private static string GenerateNonce()
  {
    var nonceLength = 32;
    var validChars =  "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._~";
    
    var nonceString = new StringBuilder();
    
    using (var rnd = new RNGCryptoServiceProvider())
    {
      while (nonceString.Length < nonceLength)
      {
        var bytes = new byte[1];
        rnd.GetBytes(bytes);
        var character = (char)bytes[0];
        if (validChars.Contains(character))
        {
          nonceString.Append(character);
        }
      }
    }
    return nonceString.ToString();
  }
}
var CryptoJS = require('crypto-js');
var _ = require('lodash');

var encryptionKey = CryptoJS.enc.Base64.parse('DbU0fQQc5IbF4v9a/KUvMrzF2EWGhnp7Ussb4GvWk9M=');
var authenticationKey = CryptoJS.enc.Base64.parse('HPqKD+TDmsZPuKSZHSmpZT+Y2nGsZ7uijy1NesJWbyU=');
var ivLength = 128 / 8;
var nonce = generateNonce(32);
var utcNow = new Date().toISOString();

var json = {
  "firstName": "John",
  "lastName": "Doe",
  "dateOfBirth": "1970-01-01T00:00:00Z",
  "emailAddress": "[email protected]",
  "phoneNumber": "07000123456",
  "address1": "1 Anchor Road",
  "address2": "Kingsclere",
  "town": "Newbury",
  "postalCode": "RG20 5NE",
  "partnerReference": "01F0B37A-E056-4415-93D9-03F75506EE17",
  "nonce": nonce,
  "timestamp": utcNow
}

var payload = JSON.stringify(json);

var encryptedText = encrypt(payload, encryptionKey, authenticationKey);

var decryptedText = decrypt(encryptedText, encryptionKey, authenticationKey);

function encrypt(input, encryptionKey, authenticationKey) {
  var iv = CryptoJS.lib.WordArray.random(ivLength);
  var encrypted = CryptoJS.AES.encrypt(payload, encryptionKey, {
    iv: iv,
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC
  });
  
  var hashData = iv.concat(encrypted.ciphertext);
  
  var hash = CryptoJS.HmacSHA256(hashData, authenticationKey);
  
  var signedPayload = hashData.concat(hash);
  
  var base64SignedPayload = CryptoJS.enc.Base64.stringify(signedPayload);
  
  return encodeURIComponent(base64SignedPayload);
}

function decrypt(encryptedText, encryptionKey, authenticationKey) {
  var base64SignedPayload = decodeURIComponent(encryptedText);
  var signedPayload = CryptoJS.enc.Base64.parse(base64SignedPayload);
  
  //CryptoJS WordArray is 4 bytes in a single value
  var wordArray = 4;
  var hashLength = 256 / 8; // bytes
  
  var cipherlength = signedPayload.words.length - (hashLength / wordArray);
  
  // Split the signedPayload into two arrays - cipher & hash
  var cipher = signedPayload.clone();
  cipher.sigBytes = cipherlength * wordArray;
  cipher.clamp();
  
  var hash = signedPayload.clone();
  hash.words.splice(0, cipherlength);
  hash.sigBytes -= cipherlength * wordArray;
  
  // compute the hash and compare to what was sent
  var computedHash = CryptoJS.HmacSHA256(cipher, authenticationKey);
  var out = _.difference(computedHash.words, hash.words);
  
  if (out.length != 0) {
    console.log("incorrect hash");
    return;
  }
  
  // split the cipher into the two arrays - iv & the encrypted message
  var iv = cipher.clone();
  iv.sigBytes = (ivLength);
  iv.clamp();
  
  var encrypted = cipher.clone();
  encrypted.words.splice(0, wordArray);
  encrypted.sigBytes -= ivLength;
  
  var decrypted = CryptoJS.AES.decrypt({
    ciphertext: encrypted
  }, encryptionKey, {
    iv: iv,
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC
  });
  return decrypted.toString(CryptoJS.enc.Utf8);
}

function generateNonce(length) {
  var bytes = new Uint8Array(length);
  var random = window.crypto.getRandomValues(bytes);
  var result = [];
  var charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._~';
  random.forEach(function (c) {
    result.push(charset[c % charset.length]);
  });
  return result.join('');
}