> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://infonite.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://infonite.dev/_mcp/server.

# Payload Encryption & Decryption

To ensure absolute confidentiality and data integrity, webhook events and session results delivered by INFONITE can be configured with payload encryption. This page details both the encryption and decryption processes used to protect communication.

We use **AES-256-GCM** (Galois/Counter Mode) encryption. All encrypted payloads are returned as a Base64-encoded string prefixed with `aes::`.

***

## The Encrypted Format

The encrypted payload is delivered as a string prefixed with `aes::` followed by the Base64-encoded binary data.

Prefix

aes::

(Discard first)

\+

Base64 Encoded Envelope

U2FsdGVkX19...

(Decode to raw binary)

IV / Nonce

First 12 bytes

Ciphertext

Middle bytes

Auth Tag

Last 16 bytes

The raw byte layout after decoding the Base64 portion follows a strict concatenation sequence:

| Component              | Size           | Description                                            |
| :--------------------- | :------------- | :----------------------------------------------------- |
| **IV (Nonce)**         | 12 bytes       | Cryptographically secure random initialization vector. |
| **Ciphertext**         | Variable bytes | The actual encrypted JSON string.                      |
| **Authentication Tag** | 16 bytes       | GMAC integrity and authenticity validation tag.        |

***

## Encryption & Decryption Workflows

Here is the technical walkthrough for implementing the cryptography pipelines.

### Encryption Walkthrough

#### Encryption Walkthrough

**Generate IV**: Create a cryptographically secure random 12-byte initialization vector (nonce).

**Encrypt**: Encrypt the plaintext payload using the AES-256-GCM algorithm with your allocated 32-byte key and the generated IV.

**Assemble Envelope**: Concatenate the `IV` (12 bytes), the resulting `ciphertext` (variable bytes), and the 16-byte `authentication tag` returned by the cipher.

**Encode and Prefix**: Base64-encode the concatenated byte block and prepended with the `aes::` prefix.

### Decryption Walkthrough

#### Decryption Walkthrough

**Remove Prefix**: Strip the `aes::` prefix from the string.

**Decode Base64**: Decode the remaining Base64-encoded string to extract the raw bytes.

**Extract Payload Parts**:

* **IV (Initialization Vector)**: Extract the first 12 bytes.
* **Tag (Authentication Tag)**: Extract the last 16 bytes.
* **Ciphertext**: Extract the middle bytes (everything between the first 12 bytes and the last 16 bytes).

**Decrypt**: Decrypt the Ciphertext using the AES-256-GCM algorithm with your allocated Session `aes_key` (configured during [Create Session](api:POST/es-public-administration/v1/manager/init)), the extracted IV, and the Tag.

***

## Interactive Cryptography Sandbox

Test your GCM encryption and decryption logic directly in this sandbox tool. Toggle the switcher below to change between the Encrypt and Decrypt pipelines.

AES-256-GCM Playground

<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />

<path d="M7 11V7a5 5 0 0 1 10 0v4" />

<button id="sandbox-toggle-encrypt">
  Encrypt Mode
</button>

<button id="sandbox-toggle-decrypt">
  Decrypt Mode
</button>

AES Key (32-byte Base64)

<input type="text" id="sandbox-key" placeholder="Enter Base64-encoded AES key..." />

<button id="sandbox-btn-generate-key">
  Generate Key
</button>

Plaintext JSON to Encrypt

<textarea id="sandbox-plaintext" rows={4} placeholder="{&#x22;test&#x22;: &#x22;You got it!!!&#x22;}" />

Encrypted Payload (prefixed with aes::)

<textarea id="sandbox-ciphertext" rows={4} placeholder="aes::..." />

<button id="sandbox-btn-encrypt">
  Encrypt Plaintext
</button>

<button id="sandbox-btn-decrypt">
  Decrypt Payload
</button>

Output Results

Output will appear here...

***

## Implementation Code Examples

Select your preferred programming language tab below to copy production-ready code examples matching this AES-256-GCM specification.

```javascript title="Node.js"
const crypto = require('node:crypto');

/**
 * Encrypt plaintext using AES-256-GCM and return the base64-encoded string prefixed with "aes::".
 * @param {Buffer} key - 32-byte AES key
 * @param {Buffer} plaintext - Data to encrypt
 * @returns {string} Encrypted payload prefixed with "aes::"
 */
function encryptGcm(key, plaintext) {
  if (key.length !== 32) throw new Error('Key must be exactly 32 bytes');
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
  const tag = cipher.getAuthTag();
  const envelope = Buffer.concat([iv, ciphertext, tag]);
  return `aes::${envelope.toString('base64')}`;
}

/**
 * Decrypt an aes:: prefixed Base64 string using AES-256-GCM.
 * @param {Buffer} key - 32-byte AES key
 * @param {string} payloadB64 - Encrypted payload prefixed with "aes::"
 * @returns {Buffer} Decrypted plaintext bytes
 */
function decryptGcm(key, payloadB64) {
  if (key.length !== 32) throw new Error('Key must be exactly 32 bytes');
  const blob = Buffer.from(payloadB64.replace(/^aes::/, ''), 'base64');
  const iv = blob.subarray(0, 12);
  const tag = blob.subarray(blob.length - 16);
  const ciphertext = blob.subarray(12, blob.length - 16);
  
  const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
  decipher.setAuthTag(tag);
  
  return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
}

// Example usage:
const key = Buffer.from('9127m7kfzkNxS14A2U71lEzcyaqv9K5xDLtKuFTg+84=', 'base64');

// Encrypt example
const secretData = Buffer.from(JSON.stringify({ test: 'You got it!!!' }), 'utf8');
const encrypted = encryptGcm(key, secretData);
console.log('Encrypted Payload:', encrypted);

// Decrypt example
const decrypted = decryptGcm(key, encrypted);
console.log('Decrypted Plaintext:', decrypted.toString('utf8'));
```

```python title="Python"
import os
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

def encrypt_gcm(key: bytes, plaintext: bytes) -> str:
    """
    Encrypts plaintext using AES-256-GCM and returns a base64 string prefixed with "aes::".
    """
    iv = os.urandom(12)
    cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    
    # Concatenate IV || Ciphertext || Tag
    blob = iv + ciphertext + encryptor.tag
    envelope = base64.b64encode(blob).decode("utf-8")
    return f"aes::{envelope}"

def decrypt_gcm(key: bytes, payload_b64: str) -> bytes:
    """
    Decrypts an aes:: prefixed Base64 GCM payload using a 32-byte key.
    """
    # Strip prefix and decode Base64
    clean_payload = payload_b64.replace("aes::", "")
    blob = base64.b64decode(clean_payload)
    
    # Extract components (IV, ciphertext, tag)
    iv, encrypted_data, tag = blob[:12], blob[12:-16], blob[-16:]
    
    # Execute GCM Decryption
    cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag), backend=default_backend())
    decryptor = cipher.decryptor()
    return decryptor.update(encrypted_data) + decryptor.finalize()

# Example usage:
key_bytes = base64.b64decode("9127m7kfzkNxS14A2U71lEzcyaqv9K5xDLtKuFTg+84=")

# Encrypt example
plaintext_bytes = b'{"test": "You got it!!!"}'
encrypted_payload = encrypt_gcm(key_bytes, plaintext_bytes)
print("Encrypted Payload:", encrypted_payload)

# Decrypt example
decrypted = decrypt_gcm(key_bytes, encrypted_payload)
print("Decrypted Plaintext:", decrypted.decode("utf-8"))
```

```java title="Java"
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Base64;

public class AesGcmCoder {

    private static final SecureRandom random = new SecureRandom();

    public static byte[] fromBase64(String b64) {
        if (b64.startsWith("aes::")) b64 = b64.substring(5);
        return Base64.getDecoder().decode(b64);
    }

    public static byte[] concat(byte[]... parts) {
        int total = 0;
        for (byte[] p : parts) total += p.length;
        ByteBuffer buf = ByteBuffer.allocate(total);
        for (byte[] p : parts) buf.put(p);
        return buf.array();
    }

    /**
     * Encrypts plaintext using AES-256-GCM. Returns envelope prefixed with "aes::".
     */
    public static String encryptGcm(byte[] key, byte[] plaintext) throws Exception {
        if (key.length != 32) throw new IllegalArgumentException("Key must be 32 bytes");
        byte[] iv = new byte[12];
        random.nextBytes(iv);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);

        byte[] cipherTextWithTag = cipher.doFinal(plaintext);
        byte[] ciphertext = new byte[cipherTextWithTag.length - 16];
        byte[] tag = new byte[16];
        System.arraycopy(cipherTextWithTag, 0, ciphertext, 0, ciphertext.length);
        System.arraycopy(cipherTextWithTag, ciphertext.length, tag, 0, 16);

        byte[] blob = concat(iv, ciphertext, tag);
        return "aes::" + Base64.getEncoder().encodeToString(blob);
    }

    /**
     * Decrypts GCM payload byte structure: IV(12) || CIPHERTEXT || TAG(16)
     */
    public static byte[] decryptGcm(byte[] key, String payloadB64) throws Exception {
        if (key.length != 32) throw new IllegalArgumentException("Key must be 32 bytes");
        byte[] blob = fromBase64(payloadB64);
        
        byte[] iv = new byte[12];
        byte[] tag = new byte[16];
        byte[] ciphertext = new byte[blob.length - 12 - 16];
        
        System.arraycopy(blob, 0, iv, 0, 12);
        System.arraycopy(blob, 12, ciphertext, 0, ciphertext.length);
        System.arraycopy(blob, blob.length - 16, tag, 0, 16);
        
        // Java JCE GCM decryptor expects Ciphertext concatenated with Tag
        byte[] ctWithTag = concat(ciphertext, tag);
        
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
        return cipher.doFinal(ctWithTag);
    }

    public static void main(String[] args) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode("9127m7kfzkNxS14A2U71lEzcyaqv9K5xDLtKuFTg+84=");
        byte[] plaintext = "{\"test\": \"You got it!!!\"}".getBytes("UTF-8");

        // Encrypt
        String encrypted = encryptGcm(keyBytes, plaintext);
        System.out.println("Encrypted Payload: " + encrypted);

        // Decrypt
        byte[] decrypted = decryptGcm(keyBytes, encrypted);
        System.out.println("Decrypted Plaintext: " + new String(decrypted, "UTF-8"));
    }
}
```