Skip to main content
Skip table of contents

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:

CODE
python3 --version 

You should see something like this:

CODE
Python 3.8.10 

If you see an error like:

CODE
command not found: python3 

or if the version is lower than 3.6, proceed with the steps below.

Install or Upgrade Python

CODE
sudo apt-get update sudo apt-get install python3 python3-pip -y

After installing, recheck the version:

CODE
python3 --version

You should see something like this:

CODE
Python 3.8.10 

Verify pip3 is Installed
Run:

CODE
pip3 --version 

If not found, install it:

CODE
sudo apt-get install python3-pip -y

2.Python Dependencies

Install required Python libraries:

CODE
pip3 install cryptography 

If pip3 is not installed:

CODE
sudo apt-get update sudo apt-get install python3-pip 

Run the following commands for some more dependencies

CODE
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

/usr/share/freeswitch/scripts/encrypt_calls.py

Bash Script

/usr/share/freeswitch/scripts/encrypt_calls.sh

EFCX Recordings

/var/lib/freeswitch/recordings/<HOST-IP>/archive/<Year>/<Month>/<Date>/

Log File

/var/log/encrypt.log


4. Python Encryption Script

Navigate to the following directory and create a folder/directory.

CODE
cd /usr/share/freeswitch/scripts/

create a file with the name encrypt_calls.py

CODE
nano encrypt_calls.py

Now paste the following Python script

CODE
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

CODE
chmod 777 encrypt_calls.py

5.Add is_encrypted column

Run the following query in the database

CODE
ALTER TABLE public.v_xml_cdr ADD is_encrypted boolean NULL;

6.Bash Script to Run Bulk Encryption

Navigate to the following directory.

CODE
cd /usr/local/freeswitch/scripts/

Create a bash script file with the name encrypt_calls.sh

CODE
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)

CODE
#!/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:

CODE
chmod +x encrypt_calls.sh 

7.Running the Script

To start encryption:

CODE
./encrypt_calls.sh

If no errors You’ll see output like

image-20250805-073116.png

All encryption activities are logged into a file:

CODE
/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:

CODE
# 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:

CODE
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:

CODE
rm -rf /var/vrs/recordings/cucmRecording/backup
rm /usr/local/freeswitch/scripts/encrypt_calls.py
rm /usr/local/freeswitch/scripts/encrypt_calls.sh
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.