Skip to main content

Using encrypted payloads

  • This document covers all the logic related to encrypting and decrypting payloads.
  • We support encrypted payloads in select APIs.

Getting started

Important
  • Encrypted payloads are not enabled by default so please reach out to our team if you want this functionality
  • We will be happy to assist wherever possible, please reach out to help@nimbbl.biz

Generating your Encryption/Decryption key

  1. You will be using the access_secret shared with you on your dashboard for the encryption and decryption process.
  2. Remove the string part up to and including the text access_secret.
    • For example, if your access_secret is access_secret_a1x7BxYkRpB4p5H, you should remove access_secret_ to get a1x7BxYkRpB4p5H.
  3. Generate a SHA256 digest of the remaining key obtained from step 2. This digest will be used as the encryption/decryption key.

Steps for encryption

  1. AES Encrypt the unencrypted payload with AES Mode: AES_GCM.
  2. Encrypt the data and create a digest.
  3. Create a bytes text in following format.
    • First 16 bytes as nonce.
    • After that the encrypted payload.
    • Last 16 bytes as authentication tag (This is either automatically generated or has to be manually passed depending on the programming language).
  4. Convert the bytes text (created in step 3) to hex.
  5. Send the generated hex in the form of string in the encrypted_payload key.

Sample Code

  • Given below is the code snippet for encryption.
  • If you need help please reach out to help@nimbbl.biz.

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;

public class Main {

private static final int GCM_TAG_LENGTH = 16; // in bytes
private static final int GCM_NONCE_LENGTH = 16; // in bytes (explicit padding applied for <=16 bytes)

// Generate a key using SHA-256
public static byte[] generateKey(String secret) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest(secret.getBytes(StandardCharsets.UTF_8));
}

// Convert byte array to Hex string
public static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
}

// PKCS7 Padding for nonce
public static byte[] padNonce(byte[] nonce) {
if (nonce.length >= GCM_NONCE_LENGTH) return nonce;
byte[] paddedNonce = new byte[GCM_NONCE_LENGTH];
System.arraycopy(nonce, 0, paddedNonce, 0, nonce.length);
for (int i = nonce.length; i < GCM_NONCE_LENGTH; i++) {
paddedNonce[i] = (byte) (GCM_NONCE_LENGTH - nonce.length);
}
return paddedNonce;
}

// Encrypt data using AES-GCM
public static String encrypt(String message, String secret) throws Exception {
byte[] key = generateKey(secret);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

// Generate random nonce
byte[] nonce = new byte[GCM_NONCE_LENGTH];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(nonce);
nonce = padNonce(nonce);

GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);

byte[] encryptedPayload = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));

// Extract authentication tag from the encrypted data
int tagStartIndex = encryptedPayload.length - GCM_TAG_LENGTH;
byte[] payloadWithoutTag = new byte[tagStartIndex];
byte[] tag = new byte[GCM_TAG_LENGTH];
System.arraycopy(encryptedPayload, 0, payloadWithoutTag, 0, tagStartIndex);
System.arraycopy(encryptedPayload, tagStartIndex, tag, 0, GCM_TAG_LENGTH);

// Concatenate nonce, payload, and tag
byte[] result = new byte[nonce.length + payloadWithoutTag.length + tag.length];
System.arraycopy(nonce, 0, result, 0, nonce.length);
System.arraycopy(payloadWithoutTag, 0, result, nonce.length, payloadWithoutTag.length);
System.arraycopy(tag, 0, result, nonce.length + payloadWithoutTag.length, tag.length);

// Convert to hex
return bytesToHex(result);
}
}

Steps for decryption

  1. Convert the hex string (received in the encrypted_response key) back to bytes.
  2. Extract the first 16 bytes as the nonce.
  3. Extract the last 16 bytes as the authentication tag.
  4. The remaining bytes in the middle are the encrypted payload.
  5. Initialize the AES cipher in GCM mode with the extracted nonce and the encryption key.
  6. Decrypt the encrypted payload and verify it using the authentication tag.

Sample Code

  • Given below is the code snippet for decryption.
  • If you need help please reach out to help@nimbbl.biz.

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

public class Main {

private static final int GCM_TAG_LENGTH = 16; // in bytes
private static final int GCM_NONCE_LENGTH = 16; // in bytes

// Generate a key using SHA-256
public static byte[] generateKey(String secret) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest(secret.getBytes(StandardCharsets.UTF_8));
}

// Convert Hex string to byte array
public static byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] byteArray = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
int value = Integer.parseInt(hexString.substring(i, i + 2), 16);
byteArray[i / 2] = (byte) value;
}
return byteArray;
}

// Decrypt data using AES-GCM
public static String decrypt(String encryptedMessage, String secret) throws Exception {
byte[] encryptedData = hexStringToByteArray(encryptedMessage);
byte[] key = generateKey(secret);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");

// Extract nonce, payload, and tag
byte[] nonce = new byte[GCM_NONCE_LENGTH];
System.arraycopy(encryptedData, 0, nonce, 0, nonce.length);

byte[] tag = new byte[GCM_TAG_LENGTH];
System.arraycopy(encryptedData, encryptedData.length - GCM_TAG_LENGTH, tag, 0, GCM_TAG_LENGTH);

byte[] payload = new byte[encryptedData.length - GCM_NONCE_LENGTH - GCM_TAG_LENGTH];
System.arraycopy(encryptedData, GCM_NONCE_LENGTH, payload, 0, payload.length);

GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);

// Concatenate payload and tag for decryption
byte[] payloadWithTag = new byte[payload.length + tag.length];
System.arraycopy(payload, 0, payloadWithTag, 0, payload.length);
System.arraycopy(tag, 0, payloadWithTag, payload.length, tag.length);

byte[] plaintext = cipher.doFinal(payloadWithTag);
return new String(plaintext, StandardCharsets.UTF_8);
}
}