Quick Quotes via Encrypted URLs (Homelyfe)

📘

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 Homelyfe to obtain these.

Quick Quotes are used to provide an indicative price of insurance by making certain assumptions about risk data in order to provide users with a price indication as quickly and as easily as possible.

Quick Quotes via Encrypted URLs enable integrators to share data in a secure way with the Homelyfe app by encrypting the confidential customer data. Customer data sent in the request payload is encrypted using 256-bit AES encryption then hashed using the [Encrypt-then-MAC][1] method.
[1]: https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-then-MAC_(EtM)

Updates

Date

Description

20 September 2019

Updated to include the new renters lineOfBusiness and coverType options.

URL & Parameter Reference

GET https://homelyfe-test.aventus.app/widgets?partnerId=537584-8453-13K9-H2L7-4EB21A35723D&widget=vri&policyType=2&lineOfBusiness=home&coverType=homebuildingsandcontents&payload=mXt%2B9dGttxAx442woVGe%2FaQXv5hm%2F143ebP75gussaXUEGOMiEqmVC3M1%2FFvxM%2F0wdJ1h2FeF5VYGF2a1ODKy%2Flcf0YtiyLgPWooC9f9jm6W7v4z4taVjgUr%2BDvh0CzA2IDhHf76YVD8%2F%2FFpFs4ME%2F1tjcyd5a0RwY0yoanaeCtQc5seKKHsXBSZXnL17DMCzeQOP9zItW9dXWgKuXMzBLVHR%2BgXEe52miO1sjFD15UYGl6oqs6knMAszyp67Gt6F04eINaFgtS%2FB0Ed1LcNPygznrn0mw3L0sQjwHnGak%2FZFm90DV0PCSeDJtZwAs7sdzsJuwWsB5X3UzWKT%2FBOgqzzuII4mqSLR8iZhvkUqbowiSAJ8J8wxmn%2BTxCJ3CVjpllqCR7Y5xsZAKbq3xWR3Q%3D%3D

Homelyfe Environments

Parameters

Name

DataType

Description

partnerId

UUID (guid)
Required

Your partner ID

widget

string
Required

The Homelyfe service you're accessing.

vri

lineOfBusiness

string
Required

The type of insurance you're requesting.

home
renters

coverType

string
Required

The specific insurance product.

home lineOfBusiness:

  • homebuildingsandcontents
  • homebuildings
  • homecontents

renters lineOfBusiness:

  • homecontents

ps

string
Optional

The payment schedule, whether annual or monthly.
Omit this parameter if you want to enable the option to toggle between both.

  • a (annual)
  • m (monthly)

payload

string
Required

Encrypted JSON data payload.

See below for instructions of how to generate the payload

Payload reference

Payload is encrypted using 256-bit AES encryption then hashed using the [Encrypt-then-MAC][1] method to ensure users data remains safe & secure.
[1]: https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-then-MAC_(EtM)

The following is a reference of the JSON fields that are included in the encrypted payload.

Name

DataType

Description

firstName

string
Required

First name of customer

lastName

string
Required

Last name of customer

dateOfBirth

date
Required

Date of birth of customer, formatted YYYY-MM-DD

emailAddress

string
Required

Customers email address

phoneNumber

string
Required

Phone number of customer as an unformatted string

address1

string
Required

First line of address, usually house or flat number

address2

string
Optional

Second address line

town

string
Optional

Town/Region

postalCode

string
Required

UK Postcode

partnerReference

string
Optional

Your own unique reference ID for the customer.

When a policy is purchased we’ll include the partnerReference in the Policy Purchased webhook allowing you to link the policy purchase to the appropriate customer in your system.

nonce

string
Required

A random, unique string per request. This ensures that we process requests once, and is used to prevent replay attacks.

timestamp

datetime
Required

The datetime in UTC when the payload was generated.

The timestamp needs to be formatted using ISO 8601 Date formatting (e.g. 2018-10-08T11:18:57Z)

Generating the Encrypted Payload

You'll be provided with two keys, an encryption key to encrypt the payload and an authentication key for the MAC part. The keys will be provided in a base64-encoded format, your application will need to base64-decode the keys before using them.

🚧

You'll receive different partner IDs and secrets for each environment, this is for data safety reasons. Be sure not to mix these, and to double check you've entered the correct details.

Here's a step-by-step guide to creating the encrypted payload.

1. Construct the JSON payload containing the user's details

{
    "firstName" : "",
    "lastName" : "",
    "dateOfBirth"  : "",
    "emailAddress" : "",
    "phoneNumber" : "",
    "address1" : "",
    "address2" : "",
    "town" : "",
    "postalCode" : "",
    "partnerReference" : "",
    "nonce" : "",
    "timestamp" : "" 
}

The following fields are mandatory:

  • firstName
  • lastName
  • dateOfBirth
  • emailAddress
  • phoneNumber
  • address1
  • postalCode
  • nonce
  • timestamp

2. Encrypt the JSON payload using AES


2.1 Use the following AES configuration:

  • Key Size: 256 bits
  • Block Size: 128 bits
  • Cipher Mode: CBC
  • Padding Mode: PKCS7

2.2 Generate a random Initialization Vector (IV) of size 16 bytes

2.3 Encrypt the JSON payload using your encryption key and the Initialization Vector to generate the cipher

3. Sign the cipher using HMAC-SHA256 and your authentication key

3.1 Concatenate the IV and the cipher (IV + cipher)

3.2 Compute a hash of the (IV + cipher)
hash = HMAC-SHA256(IV + cipher)

3.3 Concatenate the hash to the (IV + cipher)
signed-payload = (IV + cipher + hash)

4. Base64-encode the (IV + cipher + hash)

base64-payload = base64(signed-payload)

5. URL encode the base64 string

payload = urlencode(base64-payload)

Step-by-step example

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)

This example uses the following keys:

Encryption key

DbU0fQQc5IbF4v9a/KUvMrzF2EWGhnp7Ussb4GvWk9M=

Authentication key

HPqKD+TDmsZPuKSZHSmpZT+Y2nGsZ7uijy1NesJWbyU=

Random 16 bytes Initialisation Vector (IV)

6AimI+hLvyAuxLlTlGN0pg==

Step

Description

Outcome

1

Construct 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"}

2

Encrypt the JSON with encryption key + IV

FYOX4sfwwshBs9sxACOP8W2Fez9shCP4eYUB0oTJf7/KbD5vh92G3Y/f/u3rzebHRO60guu9qY17Z+vnQMREZPR0EzzGa5yyzvD9UuHoPR/MiiLyLcAHphAts6QWhpuosfY8mCoSd9p+Sp365LZkjXAIa/JWSMVa981Ykriedr7Pz2bLtsbkc0JF7M2Yq483mOIZtCKq997UsAEulXNQ2y75hpNu+g1898/65YThcd7YDJ8d/0QQ/69JRgTFPtVak4EyIhUTUViW7FhuifYDw4sjhmlBxclyy/zsRGdQrG13GAtDKdgl2Upg/QKWsMiNKhSnKtQ/cDDjJqs8y+MQ/XrKPrNEpSor4ltLIC1b/Sj5Z/0I+DPh0F3Z9wcqBObjVOjN7mFHsQDcZRLG6xWt2V9QqhFF7Ka6Gi0f6BXVlWQx8X2umOjrDD+M+8t8i/aVMGkYQiag0bUkdY77qXjvVCe4Ke/dpGyJZyuHy7YC19o=

3.1

Concatenate IV + cipher

6AimI+hLvyAuxLlTlGN0phWDl+LH8MLIQbPbMQAjj/FthXs/bIQj+HmFAdKEyX+/ymw+b4fdht2P3/7t683mx0TutILrvamNe2fr50DERGT0dBM8xmucss7w/VLh6D0fzIoi8i3AB6YQLbOkFoabqLH2PJgqEnfafkqd+uS2ZI1wCGvyVkjFWvfNWJK4nna+z89my7bG5HNCRezNmKuPN5jiGbQiqvfe1LABLpVzUNsu+YaTbvoNfPfP+uWE4XHe2AyfHf9EEP+vSUYExT7VWpOBMiIVE1FYluxYbon2A8OLI4ZpQcXJcsv87ERnUKxtdxgLQynYJdlKYP0ClrDIjSoUpyrUP3Aw4yarPMvjEP16yj6zRKUqK+JbSyAtW/0o+Wf9CPgz4dBd2fcHKgTm41Toze5hR7EA3GUSxusVrdlfUKoRReymuhotH+gV1ZVkMfF9rpjo6ww/jPvLfIv2lTBpGEImoNG1JHWO+6l471QnuCnv3aRsiWcrh8u2Atfa

3.2

Compute hash

cTwKkvrFuYjZHEpCtAWocg9+1KslEka0zIbw+K0D6Wc=

3.3

Concatenate IV + cipher + hash

6AimI+hLvyAuxLlTlGN0phWDl+LH8MLIQbPbMQAjj/FthXs/bIQj+HmFAdKEyX+/ymw+b4fdht2P3/7t683mx0TutILrvamNe2fr50DERGT0dBM8xmucss7w/VLh6D0fzIoi8i3AB6YQLbOkFoabqLH2PJgqEnfafkqd+uS2ZI1wCGvyVkjFWvfNWJK4nna+z89my7bG5HNCRezNmKuPN5jiGbQiqvfe1LABLpVzUNsu+YaTbvoNfPfP+uWE4XHe2AyfHf9EEP+vSUYExT7VWpOBMiIVE1FYluxYbon2A8OLI4ZpQcXJcsv87ERnUKxtdxgLQynYJdlKYP0ClrDIjSoUpyrUP3Aw4yarPMvjEP16yj6zRKUqK+JbSyAtW/0o+Wf9CPgz4dBd2fcHKgTm41Toze5hR7EA3GUSxusVrdlfUKoRReymuhotH+gV1ZVkMfF9rpjo6ww/jPvLfIv2lTBpGEImoNG1JHWO+6l471QnuCnv3aRsiWcrh8u2AtfacTwKkvrFuYjZHEpCtAWocg9+1KslEka0zIbw+K0D6Wc=

5

URL encode

6AimI%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 = [email protected]"{{""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('');
}