Request Field Encryption
Request Field Encryption
Certain Open API and WebHook callback fields—such as WhatsApp numbers and contact names—must be encrypted before transmission. This page describes the scope, algorithm rules, and reference implementation.
The platform supports both AES-ECB and AES-CBC. Which mode is enabled depends on tenant configuration. When obtaining your encryption key, confirm the active mode with customer success or your administrator. Encryption and decryption parameters must match the platform side exactly; otherwise decryption will fail.
Encryption Rules
Common Parameters
| Item | Description |
|---|---|
| Algorithm | AES |
| Padding | Zero Padding: append 0 bytes until the plaintext length is a multiple of 16; if already a multiple of 16, append one full block (16 bytes) of 0 |
| Encoding | Ciphertext is transmitted as a Base64 string |
| Key | Issued by the platform. Contact customer success or your administrator to obtain it |
| Key length | Supports 128 / 192 / 256 bits; shorter keys are zero-padded by byte; keys longer than 32 bytes are truncated to the first 32 bytes |
Mode Comparison
| Mode | Transformation | IV Required | Notes |
|---|---|---|---|
| ECB | AES/ECB/NoPadding | No | Common default; the same plaintext always produces the same ciphertext |
| CBC | AES/CBC/NoPadding | Yes | Requires an initialization vector (IV); more secure than ECB |
CBC mode notes:
- IV length is fixed at 16 bytes (128 bits). It is provided together with the key or generated per an agreed rule.
- Use the same Key, IV, and
AES/CBC/NoPaddingparameters for both encryption and decryption. The IV is not included in the Base64 output; it is only used as algorithm input. - If you are unsure how the IV is derived, do not guess. Confirm with customer success or your administrator.
Affected APIs and Encrypted Fields
For the APIs below, the listed fields must be sent as AES-encrypted values (Base64 strings) in requests or callback payloads.
| # | Type | Endpoint Path | Encrypted Fields |
|---|---|---|---|
| 1 | API | /wscrm-bus-api/customer/api/batchImportWhatsContact | friendName, whatsApp (within each element of the data array) |
| 2 | API | /wscrm-bus-api/open/customer/setPrincipalAsNull | data (each WhatsApp number in the array) |
| 3 | API | /wscrm-bus-api/open/action/batchAdd | friendWhatsId within each element of the data array |
| 4 | API | /wscrm-bus-api/open/customer/delFriendsContacts | info.friendWhatsId |
| 5 | WebHook | /aotu/whatsApp/sync | whatsId, friendWhatsId, currentWhatsId; name and whatsApp inside the userProfile object |
| 6 | WebHook | /aotu/whatsApp/onlineStatus/sync | whatsId |
Usage Notes
- API requests: Encrypt only the fields listed above. Other fields (such as
tenantId,agentAccount, etc.) remain plaintext and should be sent as documented in each API reference. - WebHook callbacks: The platform pushes the listed fields as ciphertext. Receivers must decrypt them before persisting or processing.
- Array fields: When
datais a list, encrypt each field that requires encryption individually. Do not encrypt the entire JSON array as a single value.
Encryption Code Reference (Java)
The sample below includes both ECB and CBC implementations. aesEncrypt / aesDecrypt use ECB by default; for CBC, call the methods that accept an iv parameter.
package com.debt.mexico.mexicocore.airudder.client;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;
/**
* API encryption/decryption utility supporting AES-ECB, AES-CBC, and SHA256 signing
*/
public class AesUtils {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION_ECB = "AES/ECB/NoPadding";
private static final String TRANSFORMATION_CBC = "AES/CBC/NoPadding";
public static String aesEncrypt(String text, String aesKey) throws Exception {
byte[] textBytes = text.getBytes("UTF-8");
// Zero padding to make the input a multiple of block size (16 bytes for AES)
int blockSize = 16; // AES block size is 16 bytes
int remainder = textBytes.length % blockSize;
int paddingLength = 0;
if (remainder != 0) {
paddingLength = blockSize - remainder;
} else {
// If the text length is already a multiple of block size, add a full block of padding
paddingLength = blockSize;
}
byte[] paddedText = new byte[textBytes.length + paddingLength];
System.arraycopy(textBytes, 0, paddedText, 0, textBytes.length);
SecretKeySpec secretKey = new SecretKeySpec(normalizeKey(aesKey), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(paddedText);
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/** AES-CBC encryption; iv is a 16-byte initialization vector */
public static String aesEncryptCbc(String text, String aesKey, byte[] iv) throws Exception {
byte[] paddedText = zeroPad(text.getBytes("UTF-8"));
SecretKeySpec secretKey = new SecretKeySpec(normalizeKey(aesKey), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(normalizeIv(iv)));
return Base64.getEncoder().encodeToString(cipher.doFinal(paddedText));
}
public static String aesDecrypt(String text, String aesKey) throws Exception {
byte[] decodedBytes = Base64.getDecoder().decode(text);
SecretKeySpec secretKey = new SecretKeySpec(normalizeKey(aesKey), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
int end = decryptedBytes.length;
while (end > 0 && decryptedBytes[end - 1] == 0) {
end--;
}
return new String(decryptedBytes, 0, end, "UTF-8");
}
/** AES-CBC decryption; iv is a 16-byte initialization vector */
public static String aesDecryptCbc(String text, String aesKey, byte[] iv) throws Exception {
byte[] decodedBytes = Base64.getDecoder().decode(text);
SecretKeySpec secretKey = new SecretKeySpec(normalizeKey(aesKey), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(normalizeIv(iv)));
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
int end = decryptedBytes.length;
while (end > 0 && decryptedBytes[end - 1] == 0) {
end--;
}
return new String(decryptedBytes, 0, end, "UTF-8");
}
private static byte[] zeroPad(byte[] textBytes) {
int blockSize = 16;
int remainder = textBytes.length % blockSize;
int paddingLength = remainder != 0 ? blockSize - remainder : blockSize;
byte[] paddedText = new byte[textBytes.length + paddingLength];
System.arraycopy(textBytes, 0, paddedText, 0, textBytes.length);
return paddedText;
}
private static byte[] normalizeIv(byte[] iv) {
byte[] normalized = new byte[16];
System.arraycopy(iv, 0, normalized, 0, Math.min(iv.length, 16));
return normalized;
}
private static byte[] normalizeKey(String key) {
byte[] keyBytes = key.getBytes();
// AES supports 128-bit (16 bytes), 192-bit (24 bytes), or 256-bit (32 bytes) keys
if (keyBytes.length <= 16) {
byte[] normalized = new byte[16];
System.arraycopy(keyBytes, 0, normalized, 0, Math.min(keyBytes.length, 16));
return normalized;
} else if (keyBytes.length <= 24) {
byte[] normalized = new byte[24];
System.arraycopy(keyBytes, 0, normalized, 0, Math.min(keyBytes.length, 24));
return normalized;
} else if (keyBytes.length <= 32) {
byte[] normalized = new byte[32];
System.arraycopy(keyBytes, 0, normalized, 0, Math.min(keyBytes.length, 32));
return normalized;
} else {
// If the key is too long, use the first 32 bytes
byte[] normalized = new byte[32];
System.arraycopy(keyBytes, 0, normalized, 0, 32);
return normalized;
}
}
public static String sha256Sign(String text) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes("UTF-8"));
return Hex.encodeHexString(hash);
} catch (Exception e) {
throw new RuntimeException("SHA256 signature error", e);
}
}
}Before integration testing, encrypt and decrypt a known plaintext locally using your key (and IV for CBC mode) to verify the output matches the platform before calling live APIs.
