Sometimes the best way to learn scripting is to build something you would actually use.
For this project, I wanted a beginner-friendly Bash script that does something practical: encrypt and decrypt files from the terminal. Nothing too fancy, nothing enterprise-grade, just a clean little file locker that teaches Bash, OpenSSL, user input, basic error handling, and a few cybersecurity concepts along the way.
The idea is simple:
You choose a file, enter a password, and the script creates an encrypted version of that file. After successful encryption, the original unencrypted file is deleted. Later, when you decrypt the file, the script restores the original file and deletes the encrypted copy.
That sounds simple, but there is a lot of good learning packed into this small project.
First, What Is OpenSSL?
Before getting into the script, it helps to understand what OpenSSL is.
OpenSSL is a widely used open-source toolkit for cryptography and secure communication. Most people probably connect it with SSL/TLS, which is the technology behind secure HTTPS connections, but OpenSSL can do more than just support secure websites. It can generate keys, create certificate signing requests, inspect certificates, calculate hashes, test TLS connections, and encrypt or decrypt data from the command line (OpenSSL Project, n.d.).
For this project, I am using the OpenSSL command-line tool. That means I can call OpenSSL directly from a Bash script and let it handle the actual encryption and decryption work.
That is an important point: this script does not create its own encryption algorithm.
And that is a good thing.
In cybersecurity, creating your own encryption is usually a bad idea unless you are a cryptography expert. A better approach is to use established tools and understand how to use them correctly. In this project, Bash is the automation layer. OpenSSL is the cryptographic engine.
OpenSSL gives us the encryption capability, and Bash gives us the workflow around it: asking for the file, asking for the password, checking if files exist, preventing accidental overwrites, showing a spinner while the job is running, and cleaning up files only after a successful operation.
For a beginner, this is a great way to connect Linux, Bash scripting, and cybersecurity in one small project.
Installing the Dependencies on Ubuntu Linux
For this project, I am focusing on Ubuntu Linux.
That keeps the setup simple and realistic, especially for beginner Bash scripting and sysadmin practice. Ubuntu already includes Bash and most of the basic shell tools this script uses, such as read, printf, rm, command, and case.
The main dependency we need to install is OpenSSL.
Open a terminal and run:
sudo apt update
sudo apt install openssl
The first command updates the package list:
sudo apt update
The second command installs OpenSSL:
sudo apt install openssl
After installation, confirm that OpenSSL is available:
openssl version
You should see output showing the installed OpenSSL version.
You can also check where Ubuntu finds the OpenSSL command:
command -v openssl

On a typical Ubuntu system, this may return something like:
/usr/bin/openssl
That means the system can find and run OpenSSL from the terminal.
The script also includes its own dependency check:
check_openssl() {
if ! command -v openssl > /dev/null 2>&1; then
echo "Error: OpenSSL is not installed."
echo "Install it first using:"
echo "sudo apt install openssl"
exit 1
fi
}
This function checks whether OpenSSL is installed before the script continues.
The line:
command -v openssl
checks if Ubuntu can find the openssl command.
The part:
> /dev/null 2>&1
hides the output because the script only needs to know whether the command exists. It does not need to print the result.
If OpenSSL is missing, the script stops and tells the user how to install it:
sudo apt install openssl
That is a good habit in Bash scripting. Before your script depends on a tool, check that the tool is actually available.
For this project, the Ubuntu setup is simple:
sudo apt update
sudo apt install openssl
openssl version
Once that works, the system is ready to run the Bash File Locker.
What This Script Does
The script gives the user a simple menu:
echo "=========================="
echo " Bash File Locker"
echo "=========================="
echo "1. Encrypt a file"
echo "2. Decrypt a file"
echo "3. Exit"
This makes the script easier to use. Instead of remembering a long OpenSSL command, the user just picks an option.
The script then asks for the user’s choice:
read -r -p "Choose an option: " choice
Then it uses a case statement to decide what to do:
case "$choice" in
1)
encrypt_file
;;
2)
decrypt_file
;;
3)
echo "Goodbye."
exit 0
;;
*)
echo "Invalid option."
exit 1
;;
esac
This is one of those Bash patterns that every beginner should learn. It is clean, readable, and perfect for small menu-driven scripts.
Bash Is the Wrapper, OpenSSL Is the Engine
One important thing to understand is that Bash is not doing the encryption by itself.
Bash handles the menu, user input, file checks, password prompts, animation, and cleanup. The actual encryption and decryption work is done by OpenSSL.
That is also how a lot of system administration work happens in real life. You take a trusted command-line tool and wrap it in a repeatable process.
The practical cybersecurity lesson here is simple: do not reinvent the encryption. Use a trusted tool, understand what options you are using, and build a safer workflow around it.
Asking for the File
When encrypting, the script asks for the file name:
read -r -p "Enter the file to encrypt: " input_file
Then it checks if that file actually exists:
if [ ! -f "$input_file" ]; then
echo "Error: File does not exist."
return 1
fi
The -f test checks whether the input is a regular file.
This small check prevents the script from trying to encrypt something that is not there. It is a simple example of defensive scripting.
Also notice the quotes around the variable:
"$input_file"
That matters because file names can have spaces. Quoting variables is one of those small habits that saves you from annoying bugs later.
Creating the Encrypted File
The encrypted version of the file is created by adding .enc to the original file name:
output_file="$input_file.enc"
So this:
secret.txt
becomes this:
secret.txt.enc
Before writing the encrypted file, the script checks if that output file already exists:
if [ -f "$output_file" ]; then
echo "Error: Encrypted file already exists: $output_file"
return 1
fi
This protects the user from accidentally overwriting an existing encrypted file.
That may not look exciting, but it matters. A good script should not be careless with user data.
Password Input
The script asks the user for a password:
read -r -s -p "Enter password: " password
echo
The -s option hides the password while it is being typed.
Then the script asks the user to confirm the password:
read -r -s -p "Confirm password: " confirm_password
echo
After that, it checks if both passwords match:
if [ "$password" != "$confirm_password" ]; then
echo "Error: Passwords do not match."
return 1
fi
This is an important safety feature. If you mistype the password during encryption, you may lock yourself out of the file. In password-based encryption, the password is used to derive the encryption key, so it has to be correct (Turan et al., 2010).
The Encryption Method
The main encryption command looks like this:
printf '%s\n' "$password" | openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
-in "$input_file" \
-out "$output_file" \
-pass stdin
This one command does most of the heavy lifting.
The openssl enc part tells OpenSSL to use its encryption and decryption feature for files.
The encryption method used here is:
-aes-256-cbc
That means the script uses AES with a 256-bit key in CBC mode. AES stands for Advanced Encryption Standard. It is a symmetric encryption algorithm, which means the same secret is used to encrypt and decrypt the file. NIST identifies AES-128, AES-192, and AES-256 as members of the AES family, with the number referring to the key length (NIST, 2023).
In plain English, OpenSSL takes the readable file and turns it into ciphertext. Ciphertext looks like scrambled, unreadable data. Later, when the correct password is provided, OpenSSL reverses the process and turns the ciphertext back into the original file.
Why Salt Matters
The script uses this option:
-salt
Salt adds random data to the password-based encryption process.
That matters because without salt, using the same password could lead to repeated patterns in how encryption keys are derived. Salt helps make each encryption operation more unique, even if the same password is used again.
Salt does not replace a strong password, but it makes password-based encryption safer.
Why PBKDF2 Is Used
The script also uses:
-pbkdf2
PBKDF2 stands for Password-Based Key Derivation Function 2.
A password by itself is not directly used as the encryption key. Instead, PBKDF2 takes the password and derives stronger key material from it. NIST SP 800-132 discusses password-based key derivation for protecting stored data and includes concepts such as salts and iteration counts (Turan et al., 2010).
That sounds technical, but the basic idea is simple:
A human password is turned into cryptographic key material that OpenSSL can use.
Why the Iteration Count Matters
The script uses:
-iter 100000
This tells OpenSSL to run the key derivation process 100,000 times.
The purpose is to slow down password guessing. A real user entering the correct password only waits a short time. But an attacker trying thousands or millions of guesses has to pay that cost over and over again.
This does not make a weak password magically strong, but it helps make brute-force guessing harder.
Input, Output, and Password Handling
The input and output files are handled here:
-in "$input_file"
-out "$output_file"
The -in option tells OpenSSL which file to read. The -out option tells OpenSSL where to save the encrypted result.
The password is passed in through standard input:
-pass stdin
That works with this part:
printf '%s\n' "$password" |
The script asks the user for the password once, stores it in a variable, and then passes it into OpenSSL. That way, OpenSSL can use the password without asking the user to type it again.
After the operation, the script clears the password variables:
unset password
unset confirm_password
This does not magically erase every possible trace from memory, but it is still a better habit than leaving sensitive variables around longer than needed.
The Decryption Method
Decryption uses almost the same command, but with one major difference:
printf '%s\n' "$password" | openssl enc -d -aes-256-cbc -pbkdf2 -iter 100000 \
-in "$input_file" \
-out "$output_file" \
-pass stdin
The important option is:
-d
That tells OpenSSL to decrypt instead of encrypt.
So the encryption command locks the file:
openssl enc -aes-256-cbc
And the decryption command unlocks it:
openssl enc -d -aes-256-cbc
The password, PBKDF2 setting, iteration count, and cipher method need to match. If the wrong password is entered, OpenSSL cannot correctly recreate the key needed to decrypt the file.
That is why password handling matters. The script can protect the file, but it cannot recover a forgotten password.
Why I Added a Spinner
Encryption and decryption can take a few seconds, especially with larger files. Without any feedback, the script can look frozen.
So I added a simple spinner animation:
spinner() {
local pid="$1"
local message="$2"
local spin='|/-\'
while kill -0 "$pid" 2>/dev/null; do
for ((i=0; i<${#spin}; i++)); do
printf "\r[%c] %s" "${spin:$i:1}" "$message"
sleep 0.1
done
done
wait "$pid"
local status=$?
printf "\r"
return "$status"
}
This is not a real progress bar. It does not know if the encryption is 10%, 50%, or 90% complete.
It simply tells the user, “The script is still working.”
The spinner works because the OpenSSL command runs in the background:
{
printf '%s\n' "$password" | openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
-in "$input_file" \
-out "$output_file" \
-pass stdin
} &
The & runs the command in the background.
Then this captures the process ID:
pid=$!
The spinner watches that process ID. As long as the process is running, the animation continues.
This is a nice beginner lesson in background processes, process IDs, loops, and terminal output.
Deleting the Original File After Encryption
After encryption finishes, the script checks if OpenSSL completed successfully:
if [ $? -eq 0 ]; then
rm -f -- "$input_file"
echo "[OK] File encrypted successfully."
echo "[OK] Original unencrypted file deleted."
echo "Encrypted file: $output_file"
else
echo "[ERROR] Encryption failed."
rm -f -- "$output_file"
fi
This is one of the most important parts of the script.
The original unencrypted file is deleted only after encryption succeeds.
That means if encryption fails, the original file stays where it is.
That is a good example of safe failure. In system administration and cybersecurity, automation should not make a bad situation worse.
The delete command is:
rm -f -- "$input_file"
The -- tells rm that anything after it is a file name, not another command option. This helps avoid issues with unusual file names.
One important note: rm -f is not secure wiping. It removes the file from the directory, but depending on the file system, SSD behavior, backups, snapshots, or cloud sync, the data may still be recoverable. For a beginner project, this is fine, but it is not the same as secure destruction.
Restoring the File During Decryption
The decryption function works in the opposite direction.
The script asks for the encrypted file:
read -r -p "Enter the file to decrypt: " input_file
If the file ends in .enc, the script removes that extension:
if [[ "$input_file" == *.enc ]]; then
output_file="${input_file%.enc}"
else
output_file="$input_file.decrypted"
fi
So this:
secret.txt.enc
becomes this again:
secret.txt
After successful decryption, the encrypted file is deleted:
if [ $? -eq 0 ]; then
rm -f -- "$input_file"
echo "[OK] File decrypted successfully."
echo "[OK] Encrypted file deleted."
echo "Decrypted file: $output_file"
else
echo "[ERROR] Decryption failed. Wrong password or corrupted file."
rm -f -- "$output_file"
fi
Again, the script only deletes the encrypted file if decryption succeeds. If the password is wrong, the encrypted file remains in place.
That is exactly what we want.
Why This Matters in Cybersecurity
This project is small, but it connects to real cybersecurity ideas.
First, it demonstrates confidentiality. Encryption protects information by making it unreadable without the correct key or password. NIST describes AES as a cryptographic algorithm that can be used to protect electronic data (NIST, 2023).
Second, it introduces symmetric encryption. The same password-derived secret is used to encrypt and decrypt the file.
Third, it shows that passwords need help. A password by itself is not the same thing as an encryption key. That is why tools use key derivation functions like PBKDF2 to turn a password into cryptographic key material (Turan et al., 2010).
Fourth, it shows why salts and iterations matter. Salt helps make password-based encryption safer, and iterations slow down password guessing attempts.
Fifth, it teaches careful automation. The script checks files, avoids overwriting existing output, hides password input, checks command success, and deletes files only after successful operations.
That kind of thinking matters in cybersecurity. Security is not only about using strong tools. It is also about using them carefully.
Practical Uses
This script can be useful for basic personal file protection.
For example, you could use it to encrypt:
personal notes
small backup files
exported configuration files
lab documents
temporary project files
files stored on a USB drive
files before uploading them to cloud storage
For a system administrator, the practical value is also in the scripting patterns.
This project uses:
Bash functions
menus
file checks
quoted variables
password input
OpenSSL
background processes
exit-code checking
basic cleanup
Those same patterns can be reused in other scripts, such as backup tools, log collection scripts, system health checks, update scripts, or network troubleshooting utilities.
That is what makes this a good beginner project. You are not just learning encryption. You are learning how to build a workflow around a command-line tool.
Important Limitations
This is a learning project, not a full enterprise security solution.
The biggest limitation is that rm -f does not securely erase files. Deleted files may still exist in backups, snapshots, temporary files, or storage blocks.
The second limitation is password strength. A weak password can still be guessed. A long passphrase is better than a short password.
The third limitation is the encryption mode. AES-256-CBC provides encryption, but this OpenSSL enc workflow does not provide modern authenticated encryption. OpenSSL’s documentation notes that the enc command does not support authenticated encryption modes such as CCM and GCM (OpenSSL Project, n.d.).
The fourth limitation is recovery. If the password is forgotten, the file is effectively lost.
So this script is a good learning tool and a useful personal utility, but it should not be treated as the final answer for protecting highly sensitive business, legal, or regulated records.
Closing Thoughts
This was a fun beginner Bash project because it is small, practical, and security-related.
It starts with a simple question: “Can I lock a file from the terminal?”
Then it turns into a useful lesson on Bash scripting, OpenSSL, password handling, encryption, safe failure, and basic cybersecurity thinking.
That is the kind of project I like. Simple enough to build, but useful enough to teach something real.
References
NIST. (2023). FIPS 197: Advanced Encryption Standard (AES). National Institute of Standards and Technology.
OpenSSL Project. (n.d.). openssl-enc manual page. OpenSSL Documentation.
OpenSSL Project. (n.d.). OpenSSL Guide: An introduction to OpenSSL. OpenSSL Documentation.
OWASP. (n.d.). Cryptographic Storage Cheat Sheet. OWASP Cheat Sheet Series.
OWASP. (n.d.). Key Management Cheat Sheet. OWASP Cheat Sheet Series.
Turan, M. S., Barker, E., Burr, W., & Chen, L. (2010). NIST Special Publication 800-132: Recommendation for Password-Based Key Derivation, Part 1: Storage Applications. National Institute of Standards and Technology.