Skip to main content

Overview

AES-256-GCM is our primary encryption mechanism for securing sensitive API communications.

Key Features

  • 256-bit encryption key for strong security
  • 96-bit initialization vector (IV) for GCM mode
  • 128-bit authentication tag for integrity verification
  • Base64URL encoding for safe transmission

Key Exchange Process

  1. 256-bit AES key and 96-bit IV are generated by us
  2. Keys are securely shared with the client
  3. Both the parties use the same key-IV pair for encryption/decryption

Implementation Examples

Encryption (Client-side)

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class PFMEncryption {

    public static String encryptPayload(String jsonPayload, String key, String iv) {
        try {
            // Decode the Base64 encoded key and IV
            byte[] encryptionKey = Base64.getDecoder().decode(key);
            byte[] encryptionIv = Base64.getDecoder().decode(iv);

            // Convert JSON to bytes
            byte[] plaintextBytes = jsonPayload.getBytes("UTF-8");

            // Setup AES-GCM encryption
            SecretKeySpec keySpec = new SecretKeySpec(encryptionKey, "AES");
            GCMParameterSpec gcmSpec = new GCMParameterSpec(128, encryptionIv);

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);

            // Encrypt and encode
            byte[] encryptedBytes = cipher.doFinal(plaintextBytes);
            return Base64.getUrlEncoder().withoutPadding().encodeToString(encryptedBytes);

        } catch (Exception e) {
            throw new RuntimeException("Encryption failed: " + e.getMessage(), e);
        }
    }
}
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64

def encrypt_payload(json_payload, key, iv):
    """
    Encrypt JSON payload using AES-256-GCM

    Args:
        json_payload (str): JSON string to encrypt
        key (str): Base64 encoded AES key
        iv (str): Base64 encoded IV

    Returns:
        str: Base64URL encoded ciphertext
    """
    try:
        # Decode key and IV
        encryption_key = base64.b64decode(key)
        encryption_iv = base64.b64decode(iv)

        # Convert JSON to bytes
        plaintext_bytes = json_payload.encode('utf-8')

        # Encrypt using AES-GCM
        aesgcm = AESGCM(encryption_key)
        encrypted_bytes = aesgcm.encrypt(encryption_iv, plaintext_bytes, None)

        # Return Base64URL encoded result
        return base64.urlsafe_b64encode(encrypted_bytes).decode('ascii').rstrip('=')

    except Exception as e:
        raise Exception(f"Encryption failed: {str(e)}")
const crypto = require('crypto');

function encryptPayload(jsonPayload, key, iv) {
    try {
        // Decode Base64 encoded key and IV
        const encryptionKey = Buffer.from(key, 'base64');
        const encryptionIv = Buffer.from(iv, 'base64');

        // Create cipher
        const cipher = crypto.createCipherGCM('aes-256-gcm');
        cipher.setIVLength(encryptionIv.length);

        // Encrypt
        let encrypted = cipher.update(jsonPayload, 'utf8');
        encrypted = Buffer.concat([encrypted, cipher.final()]);

        // Get authentication tag
        const authTag = cipher.getAuthTag();

        // Combine encrypted data and auth tag
        const result = Buffer.concat([encrypted, authTag]);

        // Return Base64URL encoded without padding
        return result.toString('base64url');

    } catch (error) {
        throw new Error(`Encryption failed: ${error.message}`);
    }
}

Decryption (Client-side)

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class PFMDecryption {

    public static String decryptResponse(String ciphertext, String key, String iv) {
        try {
            // Decode the Base64 encoded key and IV
            byte[] encryptionKey = Base64.getDecoder().decode(key);
            byte[] encryptionIv = Base64.getDecoder().decode(iv);

            // Decode the encrypted response
            byte[] encryptedBytes = Base64.getUrlDecoder().decode(ciphertext);

            // Setup AES-GCM decryption
            SecretKeySpec keySpec = new SecretKeySpec(encryptionKey, "AES");
            GCMParameterSpec gcmSpec = new GCMParameterSpec(128, encryptionIv);

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);

            // Decrypt and return
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            return new String(decryptedBytes, "UTF-8");

        } catch (javax.crypto.AEADBadTagException e) {
            throw new RuntimeException("Authentication tag verification failed! Wrong key, IV, or corrupted data.", e);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("Invalid Base64URL format in ciphertext.", e);
        } catch (Exception e) {
            throw new RuntimeException("Decryption failed: " + e.getMessage(), e);
        }
    }
}
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64

def decrypt_response(ciphertext, key, iv):
    """
    Decrypt response using AES-256-GCM

    Args:
        ciphertext (str): Base64URL encoded encrypted data
        key (str): Base64 encoded AES key
        iv (str): Base64 encoded IV

    Returns:
        str: Decrypted JSON string
    """
    try:
        # Decode key and IV
        encryption_key = base64.b64decode(key)
        encryption_iv = base64.b64decode(iv)

        # Add padding if needed and decode ciphertext
        padding = 4 - len(ciphertext) % 4
        if padding != 4:
            ciphertext += '=' * padding
        encrypted_bytes = base64.urlsafe_b64decode(ciphertext)

        # Decrypt using AES-GCM
        aesgcm = AESGCM(encryption_key)
        decrypted_bytes = aesgcm.decrypt(encryption_iv, encrypted_bytes, None)

        return decrypted_bytes.decode('utf-8')

    except Exception as e:
        if "InvalidTag" in str(e):
            raise Exception("Authentication tag verification failed! Wrong key, IV, or corrupted data.")
        raise Exception(f"Decryption failed: {str(e)}")
const crypto = require('crypto');

function decryptResponse(ciphertext, key, iv) {
    try {
        // Decode Base64 encoded key and IV
        const encryptionKey = Buffer.from(key, 'base64');
        const encryptionIv = Buffer.from(iv, 'base64');

        // Decode ciphertext
        const encryptedData = Buffer.from(ciphertext, 'base64url');

        // Split encrypted data and auth tag (last 16 bytes)
        const authTag = encryptedData.slice(-16);
        const encrypted = encryptedData.slice(0, -16);

        // Create decipher
        const decipher = crypto.createDecipherGCM('aes-256-gcm');
        decipher.setIVLength(encryptionIv.length);
        decipher.setAuthTag(authTag);

        // Decrypt
        let decrypted = decipher.update(encrypted, null, 'utf8');
        decrypted += decipher.final('utf8');

        return decrypted;

    } catch (error) {
        if (error.message.includes('bad decrypt')) {
            throw new Error('Authentication tag verification failed! Wrong key, IV, or corrupted data.');
        }
        throw new Error(`Decryption failed: ${error.message}`);
    }
}

Security Best Practices

Key Management

  • Keys are generated using cryptographically secure random number generators
  • Regular key rotation is recommended
  • Keys should be stored securely and never logged or transmitted in plain text
  • Client can place a request with us to generate new keys

Implementation Guidelines

  • Always validate the authentication tag during decryption
  • Use proper error handling to avoid information leakage
  • Implement secure key storage mechanisms