Existing EFCX Voice Recording Encryption Configuration Guide
Purpose
This document explains how to encrypt all existing un-encrypted call recordings for VRS (Voice Recording Solution), using a Python-based AES encryption script. This step is necessary to ensure compatibility with the latest version of VRS, which expects all recordings to be encrypted. the Below step can be followed to do all the configuration
1. Python Version
Python 3.6 or above
Check version:
python3 --version
You should see something like this:
Python 3.8.10
If you see an error like:
command not found: python3
or if the version is lower than 3.6, proceed with the steps below.
Install or Upgrade Python
sudo apt-get update sudo apt-get install python3 python3-pip -y
After installing, recheck the version:
python3 --version
You should see something like this:
Python 3.8.10
Verify pip3
is Installed
Run:
pip3 --version
If not found, install it:
sudo apt-get install python3-pip -y
2.Python Dependencies
Install required Python libraries:
pip3 install cryptography
If pip3
is not installed:
sudo apt-get update sudo apt-get install python3-pip
Run the following commands for some more dependencies
sudo apt-get update
sudo apt-get install -y libpq-dev
pip3 install psycopg2-binary
pip3 install psycopg2-binary
sudo apt update
sudo apt install -y libpq-dev python3-dev build-essential
3. Directory Structure
Here are the directories structure to keep in mind
Item | Path |
---|---|
Encryption Script |
|
Bash Script |
|
EFCX Recordings |
|
Log File |
|
4. Python Encryption Script
Navigate to the following directory and create a folder/directory.
cd /usr/share/freeswitch/scripts/
create a file with the name encrypt_calls.py
nano encrypt_calls.py
Now paste the following Python script
import os
import sys
import logging
import psycopg2
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
# Config
LOG_FILE = "/var/log/encrypt.log"
KEY_HEX = "42066107bda481f0266fd709627faf98b422e29a29b01495daa3ef3640ee6fe6"
IV = b'1234567890123456'
# Database config
DB_CONFIG = {
"host": "192.168.1.107",
"port": "5432",
"dbname": "fusionpbx",
"user": "fusionpbx",
"password": "KHT0ExtHnbTPyK0DzTKn3n4TE"
}
# Setup logging
logging.basicConfig(filename=LOG_FILE, level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s")
def get_connection():
return psycopg2.connect(**DB_CONFIG)
def is_already_encrypted(file_path):
"""Check DB to see if file is already encrypted.
Returns True/False if retrieved successfully, None if DB fails.
"""
try:
record_name = os.path.basename(file_path)
conn = get_connection()
cur = conn.cursor()
cur.execute(
"SELECT is_encrypted FROM v_xml_cdr WHERE json->'variables'->>'record_name' = %s",
(record_name,)
)
row = cur.fetchone()
cur.close()
conn.close()
if row is None:
logging.error(f"No DB record found for {record_name}")
return None
if row[0] == 1:
return True
return False
except Exception as e:
logging.error(f"DB error checking encryption status for {file_path}: {str(e)}")
return None
def mark_as_encrypted(file_path):
"""Update DB to mark file as encrypted."""
try:
record_name = os.path.basename(file_path)
conn = get_connection()
cur = conn.cursor()
cur.execute(
"UPDATE v_xml_cdr SET is_encrypted = 1 WHERE json->'variables'->>'record_name' = %s",
(record_name,)
)
conn.commit()
cur.close()
conn.close()
logging.info(f"Database updated: {record_name} marked as encrypted")
except Exception as e:
logging.error(f"DB error updating encryption status for {file_path}: {str(e)}")
def encrypt_file(file_path, key):
"""Encrypts file only if DB connectivity is successful and file is not already encrypted."""
status = is_already_encrypted(file_path)
if status is None:
logging.error(f"Skipping encryption due to DB check failure: {file_path}")
return
if status is True:
logging.info(f"File already encrypted, skipping: {file_path}")
return
try:
if not os.path.isfile(file_path):
logging.error(f"Not a file: {file_path}")
return
# Read file data
with open(file_path, 'rb') as f:
data = f.read()
# Pad the data
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data) + padder.finalize()
# Encrypt
cipher = Cipher(algorithms.AES(key), modes.CFB(IV), backend=default_backend())
encryptor = cipher.encryptor()
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
# Write encrypted data with IV prefix
with open(file_path, 'wb') as f:
f.write(IV + encrypted_data)
logging.info(f"Successfully encrypted: {file_path}")
mark_as_encrypted(file_path)
except Exception as e:
logging.error(f"Error encrypting {file_path}: {str(e)}")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python3 encrypt_calls.py <file_path>")
sys.exit(1)
file_path = sys.argv[1]
key = bytes.fromhex(KEY_HEX)
encrypt_file(file_path, key)
Update the database credentials below the line 15 in python script.
Now give the permission
chmod 777 encrypt_calls.py
5.Add is_encrypted column
Run the following query in the database
ALTER TABLE public.v_xml_cdr ADD is_encrypted boolean NULL;
6.Bash Script to Run Bulk Encryption
Navigate to the following directory.
cd /usr/local/freeswitch/scripts/
Create a bash script file with the name encrypt_calls.sh
nano encrypt_calls.sh
Now paste the following bash script.
❗Note: If you want to keep the backup of the files before running the script, Check the point 7 Backup Option (Highly Recommended)
#!/bin/bash
ENCRYPT_SCRIPT="/usr/share/freeswitch/scripts/encrypt_calls.py"
BASE_DIR="/var/lib/freeswitch/recordings/192.168.1.17/archive/2025/Aug/01/"
echo "Running encryption for all .wav files under: $BASE_DIR"
# Loop through year/month/day
find "$BASE_DIR" -type f -name "*.wav" | while read -r file; do
echo "Encrypting: $file"
python3 "$ENCRYPT_SCRIPT" "$file"
#sleep 1
done
Make the script executable:
chmod +x encrypt_calls.sh
7.Running the Script
To start encryption:
./encrypt_calls.sh
If no errors You’ll see output like

All encryption activities are logged into a file:
/var/log/encrypt.log
7.Backup Option (Highly Recommended)
Before running the encryption script, it is strongly recommended to create a backup of all .wav
files. This ensures that if there are any issues (e.g., encrypted files not playing correctly in VRS), you can restore the original unencrypted files easily.
To enable automatic backup, uncomment the following lines in the encrypt_calls.sh
script, line 6, 7 and 13:
# BACKUP_DIR="/var/vrs/recordings/cucmRecording/backup"crouching
# mkdir -p "$BACKUP_DIR"
# cp "$file" "$BACKUP_DIR/"
These lines will:
Create a
backup
directory (if it doesn't exist)Copy each
.wav
file to the backup folder before encrypting it
8.Restoring from Backup (If Needed)
If any issue occurs after encryption (e.g., recordings are not playable in VRS), you can restore the original un-encrypted files from the backup folder:
cp /var/vrs/recordings/cucmRecording/backup/*.wav /var/vrs/recordings/cucmRecording/sessions/
This will overwrite the encrypted files with the original versions, allowing you to re-test or re-encrypt after correcting the issue.
9.Cleanup (After Validation)
Once all files are encrypted successfully and confirmed playable via the VRS front-end, you can delete the backup folder to free up disk space:
rm -rf /var/vrs/recordings/cucmRecording/backup
rm /usr/local/freeswitch/scripts/encrypt_calls.py
rm /usr/local/freeswitch/scripts/encrypt_calls.sh