Featured image of post Secure File Encryption with GnuPG: A Command Line Solution

Secure File Encryption with GnuPG: A Command Line Solution

Learn how to encrypt files using GnuPG on Mac/Linux. Protect your data from unauthorized access with this secure command line solution.


Suppose you need to transfer a file/folder to someone without the risk of a third party intercepting it, either from an unsecured network or, for example, via a corporate e-mail program. In that case, it may be worth encrypting your document.

Several utilities, notably GnuPG (https://gnupg.org/)and OpenSSL (https://www.openssl.org/) are available on Mac/Linux to do this.

While using OpenSSL to encrypt my documents, I encountered a recurring error when encrypting specific files (typically concerning files containing NUL bytes). I came across this topic, which explains why it’s better to use GnuPG to encrypt your data (I’m oversimplifying, I admit).

I present here two GnuPG-based encrypt and decrypt functions, running on Linux and macOS, that provide an easy-to-use command line solution for encrypting and decrypting files and folders, respectively.


❯  Installation

First and foremost, if the command has not been installed yet, we will start installing gnupg.


❯  On macOs

We will install gnupg using Homebrew. This package manager allows packages to be installed on macOS systems quickly and easily. More information about Homebrew is on its official website.

In case where brew is not installed yet on your machine, let’s install it via the following command:

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

This install can take a moment, in particular because it will probably install a few additional Xcode dependencies.

Once the installation finished, to install gpg:

1
brew install gnupg

❯  On Linux

gpg is generally already installed. If this is not the case, you can install it via the package manager used by your system (apt, dnf, yum, pacman, zypper, …). For apt, the most popular package manager used in particular by the Debian-based operating systems like Debian itself, Ubuntu and its variants, Linux-Mint, Raspbian, …

1
sudo apt install gnupg

If for some reason, you want to install it by yourself, without using a package manager, or if you want to update it to a newer version, you can visit this page.


❯  Testing the installation

To test it, run the following command:

1
gpg --version

If gpg is correctly installed, you should see its version, plus some extra information. For example, running this command on my laptop gave me thee following result:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
➜  ~  gpg --version
gpg (GnuPG) 2.4.5
libgcrypt 1.10.3
Copyright (C) 2024 g10 Code GmbH
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /Users/letrome/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

❯  Encryption alias


❯  In using gpg

Given a file named a_file in input, the command to encrypt this file using the algorithm AES256 is the following:

1
gpg --symmetric --cipher-algo AES256 a_file

This will display a screen, asking you to type a passphrase that will be necessary to decrypt this file

Screen displayed, asking you to enter the passphrase
Screen displayed, asking you to enter the passphrase

If the passphrase is not robust enough (a robust passphrase must contain at least 8 characters, with at least 1 digit or special character), the same screen is displayed until you enter a robust enough passphrase or, depending on the version of gpg installed, a second screen is displayed, asking you confirmation for using this too weak passphrase:

Screen displayed, asking you confirmation for the use of the week passphrase
Screen displayed, asking you confirmation for the use of the week passphrase

For more information on the command gpg, the available options, and the available algorithms for the encryption:

1
man gpg

And for an abbreviated version, listing the most useful commands:

1
gpg -h

❯  A function wrapping the logic

Based on this command, below is a function that you can add directly on your .bashrc, .bash_profile, or any other external document that you can include on one of them in doing a source of this file (source ~/.file_containing_this_method.sh):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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."
    return 1
  fi

  if [ -d "${to_encrypt}" ] ; then
    if ! command -v zip &> /dev/null ; then
      printf "Required command 'zip' not installed."
      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

To encrypt a simple element (file or folder, named element_to_encrypt):

1
encrypt

It will ask you to give the element to encrypt. You can also give this element in parameter of the command line:

1
encrypt file_to_encrypt

or as a value of the parameter --input:

1
encrypt --input=file_to_encrypt

If the input that you provide is a folder, the function creates a zip archive before encrypting it (the command zip is required ; if zip is not installed, the function displays a message asking to install it).

By default, the output is the name of the input suffixed by .gpg. You can override in providing explicitly the name of the output via the parameter --output:

1
encrypt --input=file_to_encrypt --output=encrypted_file

❯  Decryption alias


❯  In using gpg

Given an encrypted file named a_file in input, the command to decrypt this file to a file named output_file is the following:

1
gpg --output output_file --decrypt a_file

If this file has been encrypted locally, then the passphrase you give during its encryption is automatically used. Else, similar to the encryption command, a prompt asking for a passphrase is displayed.


❯  A function wrapping the logic

Based on this command, below is a function that you can add directly on your .bashrc, .bash_profile, or any other external document that you can include on one of them in doing a source of this file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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

  echo "l85 ${to_decrypt} - ${output_file} - ${other_input} / ${#other_input}"

  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 [ $(echo "${to_decrypt:0-4}") = ".gpg" ] ; then
      output_file="${to_decrypt::-4}"
    else
      printf "path to the decrypted file: "
      read -r output_file;
    fi
  fi

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

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

unzip -t "${output_file}" &> /dev/null
if [ $? -eq 0 ] ; then
  if [ $(echo "${output_file:0-4}") != ".zip" ] ; then
    mv "${output_file}" "${output_file}".zip
    output_file=${output_file}.zip
  fi
  unzip "${output_file}"
  rm "${output_file}"
fi
}

❯  Usage

To decrypt an element:

1
decrypt

This will display the path to the element to decrypt, and the path to the output element. You can also give the element in parameter of the command line:

1
decrypt element_to_decrypt

Alternatively, you can give this element in value of the parameter --input:

1
encrypt --input=element_to_decrypt

You can also give the path of the output element via the parameter --output:

1
encrypt --input=element_to_decrypt --output=decrypted_element

I’ve covered everything I wanted to say in this blog post. I hope you’ll find it useful!