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
- 256-bit AES key and 96-bit IV are generated by us
- Keys are securely shared with the client
- 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
