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
Base Path | |
---|---|
Test Environment | https://homelyfe-test.aventus.app/widgets |
Live Environment | https://app.homelyfe.com/widgets |
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 = $@"{{""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('');
}
Updated about 5 years ago