How to Create a Bash File Encryption Tool

How to Create a Bash File Encryption Tool

How to Create a Bash File Encryption Tool

I still remember the first time I lost a laptop with unencrypted client files on it. Nothing sensitive was ever misused, as far as I know, but the scare was enough to make me build a habit around encrypting anything that matters before it leaves my machine. Over the years I’ve settled on a simple, dependable approach: a small Bash script wrapped around OpenSSL that encrypts and decrypts files on demand. In this article I’m going to walk you through building that tool from scratch, explain exactly how it works under the hood, and share the security lessons I picked up the hard way.

This isn’t a theoretical exercise. By the end, you’ll have a working command-line utility you can drop into your ~/bin folder and use every day.

Why Build Your Own Encryption Tool in Bash

There are plenty of GUI encryption apps out there, but I prefer a script for a few reasons:

Prerequisites

Before I dive into the code, here’s what you need:

If OpenSSL isn’t installed, on Debian/Ubuntu you can grab it with:

sudo apt update && sudo apt install openssl -y

On macOS, it usually comes preinstalled, or you can get a newer version via Homebrew:

brew install openssl

Understanding the Core Concept: Symmetric Encryption

The tool I’m building uses symmetric encryption, meaning the same passphrase is used to both encrypt and decrypt the file. OpenSSL’s enc command supports this through the AES-256-CBC cipher, which is what I’ll use here. It’s not the newest cipher mode available, but it’s widely supported and battle-tested, which matters more to me than chasing the latest algorithm.

The basic OpenSSL syntax looks like this:

openssl enc -aes-256-cbc -salt -pbkdf2 -in secret.txt -out secret.txt.enc

And to reverse it:

openssl enc -aes-256-cbc -d -pbkdf2 -in secret.txt.enc -out secret.txt

That’s the engine. Now let’s build a proper script around it.

Step 1: Writing the Basic Script

I always start with a skeleton before adding bells and whistles. Create a file called cryptool.sh:

#!/usr/bin/env bash

set -euo pipefail

usage() {
    echo "Usage: $0 [encrypt|decrypt] <file>"
    exit 1
}

if [ "$#" -ne 2 ]; then
    usage
fi

ACTION="$1"
FILE="$2"

if [ ! -f "$FILE" ]; then
    echo "Error: File '$FILE' not found."
    exit 1
fi

case "$ACTION" in
    encrypt)
        read -s -p "Enter passphrase: " PASSPHRASE
        echo
        openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
            -in "$FILE" -out "${FILE}.enc" -pass pass:"$PASSPHRASE"
        echo "Encrypted: ${FILE}.enc"
        ;;
    decrypt)
        read -s -p "Enter passphrase: " PASSPHRASE
        echo
        openssl enc -aes-256-cbc -d -pbkdf2 -iter 100000 \
            -in "$FILE" -out "${FILE%.enc}.dec" -pass pass:"$PASSPHRASE"
        echo "Decrypted: ${FILE%.enc}.dec"
        ;;
    *)
        usage
        ;;
esac

Make it executable:

chmod +x cryptool.sh

Now test it:

./cryptool.sh encrypt notes.txt
./cryptool.sh decrypt notes.txt.enc

You should see a prompt for a passphrase, followed by confirmation that the encrypted or decrypted file was created.

Step 2: Explaining Each Part of the Script

I like to slow down here because understanding why each line exists is what lets you adapt the script later.

Step 3: Adding File Integrity Checks

Encryption alone doesn’t tell you if a file was tampered with. I like adding a checksum step so I can verify integrity before and after transfer:

sha256sum "$FILE" > "${FILE}.sha256"

And to verify later:

sha256sum -c "${FILE}.sha256"

I usually generate this checksum right after encrypting, so I have a fingerprint of the encrypted file, not the original.

Step 4: Batch Encrypting a Directory

Once the single-file version worked reliably for me, I extended it to handle entire directories, since that’s closer to how I actually use it — encrypting a folder of invoices or backups in one go.

#!/usr/bin/env bash
set -euo pipefail

DIR="$1"
read -s -p "Enter passphrase: " PASSPHRASE
echo

find "$DIR" -type f ! -name "*.enc" | while read -r file; do
    openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
        -in "$file" -out "${file}.enc" -pass pass:"$PASSPHRASE"
    echo "Encrypted: $file"
done

The find ... | while read -r file pattern is one I use constantly. find lists every regular file that doesn’t already end in .enc, and the while loop processes them one at a time, which avoids the pitfalls of using for file in $(find ...) on filenames with spaces.

Real-World Use Cases

Here’s how I actually use this tool day to day:

Automating It with Cron

If you want the encryption tool to run automatically, you can combine it with a cron job. Here’s an example that encrypts a nightly backup folder at 2 AM, using a passphrase stored securely in an environment variable rather than typed interactively:

0 2 * * * PASSPHRASE="$(cat /root/.backup_pass)" /home/user/scripts/cryptool.sh encrypt /home/user/backups/db_dump.sql

For this to work non-interactively, you’d modify the script to accept the passphrase from an environment variable when running in cron mode, since read won’t work without a terminal attached.

Security Considerations

This is the section I care about most, because a poorly built encryption script gives you false confidence.

Optimization Tips

Troubleshooting Common Issues

“bad decrypt” error on decryption — This almost always means the passphrase was wrong, or the file was encrypted with a different OpenSSL version that used different defaults. Always encrypt and decrypt with the same flags.

Script hangs with no output — Check whether you’re running it inside a cron job or CI pipeline without a terminal; read -s -p requires an interactive shell.

Permission denied errors — Make sure the script is executable (chmod +x) and that you have write permissions in the target directory.

Decrypted file is empty or corrupted — Verify you used the exact same cipher, salt, and PBKDF2 settings on both ends; mismatched OpenSSL versions occasionally change default behaviors between major releases.

Common Mistakes to Avoid

Frequently Asked Questions

Is AES-256-CBC still considered secure in 2026? Yes, when combined with a proper salt and PBKDF2 key derivation, AES-256-CBC remains cryptographically sound for personal and small business use. For very high-security needs, consider authenticated modes like AES-256-GCM, which OpenSSL also supports.

Can I use this script on Windows? Yes, through WSL (Windows Subsystem for Linux) or Git Bash, as long as OpenSSL is available in that environment.

What happens if I forget my passphrase? There’s no recovery. Symmetric encryption has no backdoor by design, so losing the passphrase means losing the data permanently.

Should I use GPG instead of OpenSSL? GPG is a great alternative, especially for asymmetric encryption (public/private key pairs) and signing. I stick with OpenSSL here because it’s simpler for quick symmetric use cases and requires no key management.

Summary

Building a Bash file encryption tool isn’t about reinventing cryptography — it’s about wrapping trustworthy, well-tested primitives like OpenSSL’s AES-256-CBC cipher in a script that’s convenient, repeatable, and safe to use daily. I’ve shown you how to build a single-file tool, extend it for batch directory encryption, automate it with cron, and avoid the security pitfalls that catch people off guard, like leaking passphrases through process lists. Start with the basic script, test decryption immediately, and gradually layer in the security practices as your needs grow.

References

Exit mobile version