Skip to main content
A futuristic digital illustration featuring a glowing neon green padlock hovering over a transparent command-line terminal interface. The terminal screen displays the text 'GnuPG ENCRYPTION ACTIVE', set against a dark background of blue and green circuit board patterns and falling binary code.

Simplifying File Encryption on Linux & macOS with GnuPG and Bash

2 min 336 words

Protect your data from unauthorized access with this robust command-line workflow for macOS and Linux.

Suppose you need to transfer a file or folder to someone without the risk of interception—whether over an unsecured network or via corporate email. In that case, encrypting your documents is essential.

While several utilities exist for macOS and Linux, notably GnuPG and OpenSSL, I recommend GnuPG. While attempting to use OpenSSL, I encountered recurring errors with specific files (typically those containing NUL bytes). A discussion on this topic highlights why GnuPG is generally the superior choice for this use case.

In this post, I present two GnuPG-based Bash functions compatible with Linux and macOS. These wrappers provide a streamlined command-line solution for encrypting and decrypting both files and folders.

Installation

First, we need to ensure gnupg is installed.

On macOS

We will use Homebrew, the popular package manager for macOS. If brew is not installed yet, run the following command:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Once Homebrew is ready, install gpg:

brew install gnupg

On Linux

gpg is usually pre-installed. If not, use your distribution’s package manager. For Debian-based systems (Ubuntu, Mint, etc.):

sudo apt install gnupg

Testing the installation

Verify the installation by checking the version:

gpg --version

You should see output similar to this:

gpg (GnuPG) 2.4.5
libgcrypt 1.10.3
Copyright (C) 2024 g10 Code GmbH
...
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256...

Encryption

Using the raw GPG command

To encrypt a single file named a_file using the AES256 algorithm:

gpg --symmetric --cipher-algo AES256 a_file

GPG will prompt you to enter a passphrase.

Note: Choose a robust passphrase (at least 8 characters, mixing digits and special characters). If the password is too weak, GPG may ask for confirmation or reject it depending on your configuration.

A wrapper function for easier usage

The raw command doesn’t handle folders natively (you must archive them first). The function below handles both files and folders automatically. Add this to your .bashrc or .bash_profile:

function encrypt(){
  to_encrypt=""
  original_to_encrypt=""
  output_file=""
  other_input=""
  is_folder=0

  for param in "$@" ; do
    key=$(echo "${param}" | cut -d"=" -f1)
    value=$(echo "${param}" | cut -d"=" -f2)
    if [ "${key}" = "--input" ] ; then
      to_encrypt=${value}
      original_to_encrypt=${value}
    elif [ "${key}" = "--output" ] ; then
      output_file=${value}
    else
      other_input=${param}
    fi
  done

  if [ ${#to_encrypt} -eq 0 ] ; then
    if [ ${#other_input} -gt 0 ] ; then
      to_encrypt=${other_input}
      original_to_encrypt=${other_input}
    else
      printf "Path to the file to encrypt: "
      read -r to_encrypt;
      original_to_encrypt=${to_encrypt}
    fi
  fi

  if ! command -v gpg &> /dev/null ; then
    printf "Required command 'gpg' not installed.\n"
    return 1
  fi

  if [ -d "${to_encrypt}" ] ; then
    if ! command -v zip &> /dev/null ; then
      printf "Required command 'zip' not installed.\n"
      return 1
    fi

    zip -r "${to_encrypt}.zip" "${to_encrypt}"
    to_encrypt="${to_encrypt}.zip"
    is_folder=1
  elif [ ! -f "${to_encrypt}" ] ; then
    echo "Encryption impossible: there is no file named ${to_encrypt}"
    return 1
  fi

  if [ ${#output_file} -eq 0 ] ; then
    output_file="${original_to_encrypt}.gpg"
  fi
  
  if gpg --output "${output_file}" --symmetric --cipher-algo AES256 "${to_encrypt}" ; then
    echo "${original_to_encrypt} successfully encrypted!"
  else
    echo "An error occurred during the encryption of ${original_to_encrypt}"
  fi

  if [ ${is_folder} -eq 1 ] ; then
    rm "$to_encrypt"
  fi
}

Usage

Simply run:

encrypt
# Or with arguments:
encrypt file_to_encrypt
encrypt --input=my_folder --output=secure_archive.gpg

If the input is a folder, the script automatically zips it before encryption.

Decryption

Using the raw GPG command

To decrypt a file:

gpg --output output_file --decrypt a_file.gpg

A wrapper function for easier usage

This function detects if the decrypted file was a zip archive (from our previous step) and extracts it automatically.

function decrypt(){
  to_decrypt=""
  output_file=""
  other_input=""

  for param in "$@" ; do
    key=$(echo "${param}" | cut -d"=" -f1)
    value=$(echo "${param}" | cut -d"=" -f2)
    if [ "${key}" = "--input" ] ; then
      to_decrypt=${value}
    elif [ "${key}" = "--output" ] ; then
      output_file=${value}
    else
      other_input=${param}
    fi
  done

  if [ ${#to_decrypt} -eq 0 ] ; then
    if [ ${#other_input} -gt 0 ] ; then
          to_decrypt=${other_input}
        else
          printf "Path to the file to decrypt: "
          read -r to_decrypt;
        fi
  fi

  if [ ${#output_file} -eq 0 ] ; then
    if [[ "${to_decrypt}" == *".gpg" ]] ; then
      output_file="${to_decrypt%.gpg}"
    else
      printf "Path for the decrypted file: "
      read -r output_file;
    fi
  fi

  if ! command -v gpg &> /dev/null ; then
    printf "Required command 'gpg' not installed.\n"
    return 1
  fi

  gpg --output "${output_file}" --decrypt "${to_decrypt}"
  if [ $? -ne 0 ] ; then
    printf "Error during decryption. Are the key and file valid?\n"
    return 1
  fi

  # Auto-unzip logic
  if unzip -t "${output_file}" &> /dev/null; then
      if [[ "${output_file}" != *".zip" ]] ; then
        mv "${output_file}" "${output_file}.zip"
        output_file="${output_file}.zip"
      fi
      unzip "${output_file}"
      rm "${output_file}"
  fi
}

Usage

decrypt encrypted_file.gpg
# Or
decrypt --input=encrypted_file.gpg --output=my_secret_file