Index Blog Publications Contact

Another idiot’s guide to encryption with GnuPG


If you’re an idiot like me, you have probably asked yourself questions like:

What the hell is encryption, what the hell is GnuPG? Why the hell would I even use GnuPG? I want to use it, but how do I implement it with as little friction as possible?

Worry no more, this is another idiot’s guide to encryption with GnuPG. A tiny little star within the galaxy of “programming enthusiasts that think it is necessary to blog about something written in a manual”. I’ll try to give you as much background information as possible, without being too mind-numbing.

What the hell is encryption and what the hell is GnuPG?

Understanding the process of encryption is independent of how to use GnuPG, or at least to some extent. If you want dive right into the realms of the gpg-agent, then go ahead. The following paragraphs are only here for completeness, to give you some background to the commands.

At its very heart, encryption is the process of making something readable to something unredable, mind you (un)readable in this context refers to a computer, not to a human. You can switch between these two states with a keypair, it is called a pair because there is a public key and a private key which are a very (very) long combination of numbers and letters. You share your public key with other people, for verification purposes while you should hide your private key in a secret place.

With your keypair it is very easy to go from unreadable to readable, but without the private key, it becomes an (almost) infinitely difficult problem to do the reverse,. Furthermore if you know the public key of a person, you can send something unreadable to said person and only that person alone (private key of the owner) can make it readable. Let’s leave it at that, books could be written about encryption and I’ll keep that for another blog post.

Let me tell you something about the GNU Privacy Guard, GnuPG or simply GPG. It is the complete and free implementation of the OpenpGP standard (which is defined by RFC4880), the headline feature being encryption and signing of your data and communications. It is a command line tool that allows for easy integration in other applications, which is why there are a literal ton of frontends. It also provides support for S/MIME and Secure Shell. It helps you protect your privacy and the privacy of the people you communicate with.

Configuring and running GnuPG

A good place to start is the GnuPG Manual, it documents the administration, architecture and usage of GPG. You could also have a look at:

To get started you only need to know a handful of commands. Let me start with the ones that are related to properly integrating it into your system, no keypair generation yet. If you want to use the Secure Shell Agent, you should connect it to the gpg-agent. Make sure that SSH is able to use the gpg-agent for authentication by invoking:

gpg-connect-agent /bye

In a similar fashion you can make sure that the ssh-agent uses the gpg-agent on the current display, use:

gpg-connect-agent updatestartuptty /bye

The above two commands can mainly be used in scripts, where you install, configure and use the gpg-agent. Safely terminate the currently running gpg-agent with (it will restart once you invoke it):

gpgconf --kill gpg-agent

To properly set up GPG for the user, you should make sure to have the following lines in your .bashrc or similar:

export GPG_TTY

Similarly if you want proper ssh-agent support, then you have to tell ssh about it by appending the following:

if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
  export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"

Note: Make sure you have selected the correct pinentry-program in your gpg-agent.conf. Say you want to use pinentry-qt then you can find the correct path with which pinentry-qt.

A gpg-agent.conf could look like this:

# extend the password cache duration to 700 days
max-cache-ttl 60480000
default-cache-ttl 60480000
pinentry-program /usr/bin/pinentry-gnome3

Creating and listing keys

With gpg you have three options at your disposal to initialise a new keypair, each for a different scenario:

gpg --quick-generate-key

Note that --full-generate-key is not inherently better than --quick-generate-key as you can always edit the key with . To get the <keyid> or fingerprint of your primary keys or any of their subkeys, use:

gpg --list-keys # or -k
    --list-keys --keyid-format LONG # for subkeys
    --list-keys --fingerprint

# To get the private <keyid> replace --list-keys with:
gpg --list-secret-keys # or -K

Note: If you want to select a specific subkey, you should always append “!” to <keyid>. This will force the use of the selected key, no excuses. Suppose the <keyid> of a subkey was 0123456789ABCDEF, then and will give you different results since the former defaults to the primary key.

Use subkeys

Ideally you set up a primary key pair with subkeys for encryption and signing. These subkeys are independent of the primary keypair, they can be revoked individually and stored seperately. The benefit is that in the case any of your subkeys are compromised, your online reputation cannot be damaged, since they are rleated to your primary key. Backup your existing .gnupg in case of a disaster, create and backup your private key with:

umask 077; tar -cf $HOME/gnupg-backup.tar -C $HOME .gnupg

# (Optional) Create a new key pair e.g. with:
gpg --quick-generate-key

# Find your <keyid> and edit it.
gpg -k
gpg --edit-key <keyid>

# Add a subkey, choose "RSA (sign only)" and pick a key size between
# 2048 and 4096 bit.
gpg> addkey
gpg> save

cp -r $HOME/.gnupg /media/something

You should remove the private part of <keyid> from your machine and keep it somewhere safe, note that this step is necessary. Find the <keygrip>, remove the private key from your machine and update the password with:

gpg --with-keygrip -k <keyid>
shred -z $HOME/.gnupg/private-keys-v1.d/<keygrip>.key
shred -z $HOME/.gnupg/ # if exists

# Verify that your private primary key shows "sec#" when running:
gpg -K

# Note the presence of a dummy OpenPGP packet when running:
gpg --export-secret-keys <keyid> | gpg --list-packets

# Finally change the passphrase protecting the subkeys
gpg --edit-key <keyid> passwd

In case you need the private primary key, access it with:

export GNUPGHOME=/media/something
gpg -K # you should see "sec" now

# or
gpg --homedir=/media/something -K

Following all steps you should be set with highest security standard humanly possible /s. Add your public key to a keyserver to start building up a reputation and in case you should ever lose your bulic key you can recover it from any keyserver (since they all sync automatically):

gpg --keyserver --send-keys <keyid>

Back up your GnuPG directory on an encrypted USB drive

Backing up .gnupg on an encrypted, external USB drive is probably one of your best options. I suggest you have a look at sources like Arch Wiki, Gentoo Wiki or cryptsetup, the latter being by far the most extensive if you are interested in the details about disk encryption. Completely overwrite the USB drive with zeros:

cryptsetup open --type plain -d /dev/urandom /dev/sdx to_be_wiped

dd if=/dev/zero of=/dev/mapper/to_be_wiped bs=1M status=progress

cryptsetup close to_be_wiped

Note: SSDs are rather sensitive to being completely overwritten, for more information see this Gentoo Wiki article and this cryptsetup FAQ section.

After this you will most likely have to reconfigure your USB drive. Create a new partition table together with a partition, encrypt the partition and format it with:

# E.g:
fdisk /dev/sdx

# The following is inside the fdisk utility:
# Command (m for help): g
# Command (m for help): n # followed by some RET's
# Command (m for help): w

# Encrypt the partition with default options:
cryptsetup luksFormat /dev/sdxN

# Personal optiona:
cryptsetup luksFormat -c aes-xts-plain -s 512 -h sha512 --iter-time 3000 /dev/sdxN

cryptsetup luksOpen /dev/sdxN cryptusb

mkfs.btrfs /dev/mapper/cryptusb # btrfs for example

Now you can mount the freshly encrypted USB drive and back up your .gnupg:

mount /dev/mapper/cryptusb /media/something

cp -r $HOME/.gnupg /media/something

umount -R /media/something

cryptsetup close cryptusb

Back up your private keys on paper

9/10 mortals think it is enough to backup private keys on encrypted USB drives. You shouldn’t rely on magnetic or optical backup techniques but rather use paper - that’s right! There are some tools at your disposal, with which you can print your private keys to paper, either in a human readable format or encoded as a QR code. Check out the paperkey project page for a nice write up of using paper as a backup medium.

There are many ways to go about this, one is with paperkey for which there is a detailed guide on the Arch Wiki. The private keys are also directly printable with gpg, just note that paperkey reduces the exported key size by removing the public key from the private key. Both approaches can also be used to encode the secret key as a QR code (usually split up into multiple images, since your keys are very long). The procedure to backup and restore your secret keys with both methods goes along the lines of:

# Back up your secret key to human readable text, you can replace
# --export-secret-key with --export if you want to back up the public
# key aswell:
gpg --export-secret-key --armor --output key.asc <keyid>
gpg --export-secret-key <keyid> | paperkey --output secret.asc

# Restore paperkey backup directly or save it to a file:
paperkey --pubring public.asc --secrets secret.asc | gpg --import
paperkey --pubring public.asc --secrets secret.asc --output key.asc

# To restore key.asc use:
gpg --import key.asc

# If you get the error "Error: unable to parse OpenPGP packets (is this
# armored data?)" you should dearmor your public key first.
gpg --dearmor public.asc

# Back up your secret key as QR code by piping the raw output to
# qrencode. Note that you can additionally set the highest possible
# error correction level with "--level H" and split the output into
# multiple QR codes with "--structured --symversion=40". Again you can
# directly use GPG or reduce the output size with paperkey.
gpg --export-secret-key --armor key-id | \
qrencode --level=H --structured --symversion=40 \
         --8bit --output secret-key.qr.pngy

gpg --export-secret-key key-id | \
paperkey --output-type=raw | \
qrencode --level=H --structured --symversion=40 \
         --8bit --output secret-key.qr.png

# Restore the secret key from the QR codes:
zbarcam -1 --raw -Sbinary | \
gpg --import

zbarcam -1 --raw -Sbinary | \
paperkey --pubring public-key.asc | \
gpg --import

zbarimg -1 --raw -q -Sbinary secret-key.qr.png | \
gpg --import

zbarimg -1 --raw -q -Sbinary secret-key.qr.png | \
paperkey --pubring public-key.asc | gpg --import

# If you are using a scanned image you may have to blur it:
convert secret-key.qr.png -blur 0 secret-key-blurred.qr.png