Updated : Dec 19, 2019 in Uncategorized

Decrypting Jotform encrypted CVS Files

When the encryption is been activated for the JotForms, exports of the forms in CSV format are saved with almost all fields in RSA encrypted format. This script below allows to decrypt such CSV files.

Since the private key is only saved at the JotForm user’s side, the JotForm server has no way of decrypting the forms content before they are exported in CSV format. Since the Key file is only usable by the user’s Browser, so far there has been no method found to let the user decrypt his saved CSV format files using his private key file.

Each field of the database of the original data is encrypted separately using openSSL and RSA asymmetrical key encryption. In order to decrypt the CSV file, each field’s encrypted string need to be decrypted separately as follows: (automatized in the script)

echo "<encrypted string>"| openssl enc -d -a \
| openssl rsautl -decrypt -inkey <keyfile>

Some fields data have been fragmented and each fragment separately encrypted. That means that some fields contain multiple separately encrypted parts separated by either ‘spaces‘ or ‘(…)’ or ‘‘ or ‘#Jot#‘. The script replaces each of those ‘separators’ by a space and then processes the decryption of these individual encrypted strings separately, filling the results in its single appropriate field in the output file.

Some encrypted fields are impossible to be decrypted because the encrypted string has been corrupted and don’t contain the normal ‘==’ needed characters at the end of the string. The script decrypts only the fields where such character combination are found. The result is that these corrupted fields will be transported to the output file ‘as is’ and will need to be cleaned up manually later. I tried to add “==” at the end of some of these strings, but the decryption still failed. Whether the corruption happened during the encryption or just in the creation of the CSV file is still unknown.

This script is not perfect. If any of you feels fit to improve it, I would be glad to get your version as a comment of this post. Thank you.

Script: jotcvsdecrypt.sh

# Name: 	jotcvsdecrypt.sh
# Purpose:  	decrypt an encrypted CVS exported JotForm file using the private key file
# Depends:      JotForm encryption private key file. Default: jotform.key in the current running directory
# Syntax:       jotcvsdecrypt.sh -i {input_encrypted_filename} -o {output_decrypted_filename} -k {private_key_filename}
# Description:  this script will:
#               - Find the number of fields in the input CVS file from the first line
#               - Copy the fields description line(first line of the input file) to the output file
#               - Scan each subsequent line of the input file: decrypt the appropriate fields and fragments, and write the results into the output file
# Console output: Record numbers and fields decryption progress will be displayed.
#      Legend:   'd' - Decrypted field
#                '-' - Passthrough(Not decrypted) field
# Author:       Michel Bisson (michel-at-itmatrix.eu)
# Changes:      19.12.2019	First implementation
# Constants:
# Set default RSA Private key filename

# Functions
usage() { echo "Usage: $0 [-i inputCVSfile] [-o outputCVSfile] [-k jotformKEYfile]" 1>&2; exit 1; }

# Loading the arguments
while getopts ":i:o:k:" o; do
    case "${o}" in
shift $((OPTIND-1))

# Set the KYFILE to Default if not given
if [ -z $KEYFILE ]; then
    echo "KEYFILE argument was not given. Setting to default KEYFILE: $defKEYFILE"

# Exiting if no keyfile can be used
if ! [ -e $KEYFILE ]; then
    echo "KEYFILE $KEYFILE does not exist"

# Make sure the input and output CVS files were given
if [ -z "${INFILE}" ] || [ -z "${OUTFILE}" ]; then

# Make sure the input file does exist
if ! [ -e $INFILE ]; then
    echo "KEYFILE $KEYFILE does not exist"

# Things look good so far, lets start 
# Find out the number of fields from the first line of the input SVS file
for field in $(head -1 $INFILE); do let nFIELDS++ ;done
unset IFS

# Good for debugging
echo "INFILE = ${INFILE}"
echo "Number of fields = $nFIELDS"
nRecords=$(wc -l $INFILE | awk '{print $1}')
echo "Number of records = $nRecords"

# Create a new output file and copy the fields description line (first line) of the input file 'as is' to the output file
head -1 $INFILE > $OUTFILE

#---------- Start the decryption ------------------
# Decrypt one line at the time
# Skip the first fields descriptions line and start decrypting
sed 1d $INFILE | while read line ; do
    let record++
    echo -n "Record No. $(printf "%0${length}d" $record) : "
    # Scan all fields of CVS line and decrypt only the encrypted fields otherwise copy them 'as is' to output file
    for nFIELD in $(seq 1 $nFIELDS); do
	data=$(echo $line | cut -d, -f${nFIELD})
	if (echo $data | grep -q '=='); then
	    # Cleaning up some disturbing characters and replace encrypted string delimiters with a space char.
	    clnDATA=$(echo $data | sed -e "s/\'//g" -e 's/\"//g' -e 's/(//g' -e 's/)//g' -e 's/\-/ /g' -e 's/#Jot#/ /g')
	    # ************** DECRYPTION *****************
	    if $(echo $clnDATA | grep -q " "); then
		# Fields with fragmented data (containing spaces as delimiters). Each fragment is decrypted here separately
		for sDATA in $clnDATA ; do
		    allDATA="$allDATA $(echo $sDATA | openssl enc -d -a | openssl rsautl -decrypt -inkey $KEYFILE)"
		# Here the field has single encrypted data string without spaces
	        allDATA="$(echo $clnDATA | openssl enc -d -a | openssl rsautl -decrypt -inkey $KEYFILE)"
	    echo -n "\"$allDATA\"," >> $OUTFILE
	    echo -n 'd'
	    echo -n "$data," >> $OUTFILE
	    echo -n '-'
    echo >> $OUTFILE

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: