Encrypt with GPG
Image source: GnuPG


GnuPG [1] (or GPG for short) is an open source library used for encrypting and signing of files. It stands for GNU Privacy Guard, and it supports both symmetric and asymmetric key encryption on the file level [2]. Their site has a great description:

GnuPG is a complete and free implementation of the OpenPGP standard as defined by RFC4880 (also known as PGP). GnuPG allows you to encrypt and sign your data and communications; it features a versatile key management system, along with access modules for all kinds of public key directories. GnuPG, also known as GPG, is a command line tool with features for easy integration with other applications. A wealth of frontend applications and libraries are available. GnuPG also provides support for S/MIME and Secure Shell (ssh). [1]

This is vastly different than the block level encryption that’s also widely used, such as Luks, File Vault, etc. [3]

There are many implementations of the PGP standards, and two big players are GnuPG and OpenPGP [8]

Why Use It?

GPG encrypts at the file level, not the block level [3]. What this means is that the key used to encrypt this bit of data doesn’t have to stay in memory as long as software-based block-level symmetric encryption options (Such as Bit Locker, Luks, but not File Vault). Even better is that if you’re using asymmetric encryption, the public key can be used to encrypt the file one-way. Meaning, the host/OS that you’re storing the file on doesn’t even know what’s in the file, and is closer to a zero trust setup.

It’s also worth noting that both ProtonMail [4], and SpiderOak [5] use this type of philosophy.

The above is a better case scenario than we’ll do below. The use case for this is if you have sensitive files present, and they need to be taken care of now. In this case, lets assume we have sensitive files, and more will come in. How do we deal with the fact these files exist now, then move to fix it better in the long term? Long term is discussed at the end of this post.

Steps and Usage

In this post, I’ll talk about what we did for a Windows environment. First, you’ll need Gpg4win from the GnuPG (https://gnupg.org/download/) website. Install that. Note you don’t need Kleopatra for anything we’re doing here, but it doesn’t hurt to have it for viewing this all from a GUI point of view.

Generating a Master Key

The first thing to do is generate a master key. This will implicitly include the ability to encrypt (which are flags and sub keys):

gpg --expert --full-generate-key

This will launch a walk-through asking various questions. All are fairly important, and are self explanatory except for the first few options. Select the following:

  • Kind of Key: (1) RSA and RSA (default)
  • What keysize do you want? (3072) 4096
  • What keysize do you want for the subkey? (3072) 4096
  • Key is valid for? (0)

What the above does is generate a 4096 bit master key (which is used to create sub-keys), and a 4096 bit subkey. If I was doing this for my Yubikey or more manual encryption, I’d set a validity period of a year, but since this will be largely automated, (0) is fine.

It’s worth noting that you’ll get a prompt for a password during this whole process. It’s a good idea to set one, and a strong one, at that. We’ll talk more about all that in the security section later in this post.

Viewing Details

You can use the gpg --list-keys or gpg --list-secret-keys to view details about a key. We have a few components of interest, shown below:

SQL Server Extended Events

Encrypting and Decrypting a File

The basic usage of this is incredibly simple. For Encryption, you need to have the public key, and for Decryption, you need to have the private key and the password when generating the master key in the first place.

# Encrypt
gpg -e -r 'email@domain.com' .\file_to_encrypt.xml

# Decrypt
gpg -d .\file_to_encrypt.xml.gpg | out-file your_destination_file.xml

In the first command, the -e says we want to encrypt, and the -r says who we want to encrypt it to. When you generated the master key earlier, you provided this information. The User Name, or the Hash, can also be used instead of Email Address here. gpg will create a file of <input_file>.gpg, after it’s done encrypting.

In the second command, the -d says we want to decrypt the file. The default is to spit the text back out into standard out, so you need to either use -o (which I had problems getting to work in windows), or out-file.

Automating the Process

To automate this process, looking on a drive/network-share/etc. and encrypting all .xml files in this fashion can be done in the following source example:

$sourceDir = "C:\Users\dthole\temp"

$files = Get-ChildItem -Path $sourceDir -Filter '*.xml'

cd -Path $sourceDir

Foreach ($file in $files) {
    Write-Host $file.Name
    $cmd = "gpg -e --yes -r 'email@domain' " + $File.Name
    Write-Host $cmd
    Invoke-Expression $cmd

    Remove-Item $file.Name

The elements that need to be changed are mainly the source directory, the filter, and the recipient. This can be scheduled in your tool of choice to watch for, and encrypt, any new incoming files. Ideally, this should be done better, and is covered in the closing remarks.

Notes on security

Backup and Protect Your Master Key

Your master key is unnecessary on the encryption box, and we’ll talk next on deleting that. But first, you need to backup these keys.

# Export Private Key
gpg --armor --export-secret-keys <HASH> | out-file private-master.gpg

# Export Public Key
gpg --armor --export <HASH> | out-file public-master.gpg

What the two commands do is export an ASCII-representation of what your key is. These can be printed, but better is to move them into encrypted cold storage. Either way, the public key can be provided to anyone, but that private key absolutely must be protected. This is despite the fact that we have a password on it.

I found that out-file doesn’t encode these files properly though. So what I found useful is to bring both the public and private keys into Visual Studio Code, and change the encoding. To do this:

  1. Open the files in Visual Studio Code
  2. On the lower right you may see UTF-16 and CRLF.
    • Click on CRLF, and select LF
    • Click on UTF-16, and select “Save with Encoding” from the drop-down and select UTF-8
  3. Save the file

Decouple Your Encryption from Your Decryption

The machine that does the encryption does not need to know about how to decrypt the files. Or, more accurately, it shouldn’t need to know about how to decrypt the file. To delete your master key from the machine doing the encryption, you can run the following:

gpg --delete-secret-keys <HASH>

Reimporting the Master Key

When you need to, or where you need to, decrypt these files, you should import the secret key again.

gpg --import private-master.gpg

You’ll need the password that you created when generating the key in the first place. The decryption commands earlier discussed will work again.

Removing the Password from the Master Key

If you need an automated way to decrypt files, you can remove the password. This has a bit of limited usefulness since you can set GPG to not prompt for a period of time, but in a more truly automated environment this may be necessary. To do this, reload your master key and type:

gpg --edit-key <HASH>

When in the passwd wizard, don’t enter a new password when prompted. This will throw up a lot of alerts about this being a bad idea (which, in most cases, it is).

Closing Comments

GnuPG is a really useful tool for encryption and signing at a file-based level. YubiKey supports it [6], as do other hardware tokens. It can be used with the TPM [7] as well.

It’s worth warning that there are performance differences between doing file-based and block-level encryption [3], but when dealing with small files it’s not as big of a deal.

A better, more resilient solution to the problem is to, immediately after processing the file, to either:

  1. Get rid of it
  2. Encrypt it at that point, then put it into an archive.

Getting rid of it is the best option, but not entirely feasible in all cases. Instead, if you have a process that dealing with the immediate needs of that file, to have the encryption done there. If you’re using Java (ugh..), you could use one of the many examples at https://didisoft.com/java-openpgp/examples/encrypt/. If you’re using Golang, you could use the example at https://gist.github.com/stuart-warren/93750a142d3de4e8fdd2 as a starting off point.

Either way, encrypt during process, and don’t let the unencrypted contents hit disk in the first place.


David Thole

David Thole
Senior Software Architect, Developer, Instructor. Reads/studies a lot and enjoys all things technology

Local Artificial Intelligence Tools

# IntroductionI was in a recent meeting when the presenter of the meeting spoke about running LLMs in the cloud, and how expensive it can...… Continue reading

Effective prompting with AI

Published on January 09, 2024

Creating Flashcards with Generative AI

Published on January 02, 2024