1. Introduction

Encryption is critical in all but the most basic and rudimentary applications of computing. Even then, a security layer, albeit possibly external, often exists beyond the base implementation. In fact, the basis of electronic signatures and identities is also encryption.

In this tutorial, we explore the OpenPGP (Open Pretty Good Privacy) standard as one of the most universal ways to encrypt and GNU Privacy Guard (GnuPG, GPG) as one of its main implementations. First, we cover the OpenPGP standard. After that, we turn to GPG. Initially, we go over its main features. Next, we go through several steps for key creation and data encryption. Finally, we talk about a common problem during encryption as well as a potential workaround.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.

2. Pretty Good Privacy With OpenPGP

The OpenPGP standard is a descendent and partial successor of the PGP standard and toolset.

One of the original implementations of PGP, also called PGP, was fully owned by the PGP Corporation. The latter is now owned by Symantec, which in turn is a part of Broadcom.

However, OpenPGP is now an international standard as defined in the latest RFC 4880 – OpenPGP Message Format. As such, anyone can leverage it by integrating the concepts and definitions into their development:

  • hybrid symmetric and asymmetric encryption system
  • data is encrypted symmetrically via a session key
  • session keys are encrypted asymmetrically

Alternatively, end users make use of the standard via a number of software products that implement OpenPGP. Although the main intended use case of OpenPGP is emails, many implementations provide additional features as well due to the versatility of its functions:

  • key and identity generation and management
  • data integrity via signatures
  • message authentication via signatures
  • deny data access via encryption
  • provide access to specific users

As with any security solution, the principle behind software alone doesn’t ensure confidentiality, integrity, or availability. Users need to also employ best practices when securing data and communications and keep their private and secret keys extremely safe.

As an example, let’s take a look at one of the most famous implementations of OpenPGP.

3. GNU Privacy Guard (GnuPG)

The GnuPG (GNU Privacy Guard), more commonly just GPG, is a toolkit that fully implements and extends the OpenPGP standard.

On top of the standard and optional OpenPGP featureset, GPG provides several improvements:

Of course, these options depend on the version of GPG:

The main tool of GnuPG is gpg. It implements and provides all functions of OpenPGP and beyond.

So, let’s briefly go over the steps to encrypt data with a key.

3.1. Generate Key Pair

We start off by generating an identity or key pair:

$ gpg --full-generate-key
gpg (GnuPG) 2.2.27; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection?

While we can use the simpler –generate-key (–genkey) option for key generation, –full-generate-key (–full-gen-key) provides all key configuration settings:

  • type
  • size
  • expiration
  • name
  • email address
  • comment
  • password

Going through the wizard, we can customize and protect our new identity. Once done, we should have a new private and public key pair along with a so-called revocation certificate.

In short, we use a revocation certificate for publicly declaring when a key should no longer be considered valid.

3.2. List Keys

We can check our current available keys via the –list-public-keys (–list-keys, -k) option:

$ gpg --list-public-keys
gpg: checking the trustdb
gpg: marginals needed: 0  completes needed: 0  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
/home/baeldung/.gnupg/pubring.kbx
------------------------
pub   rsa4096 2023-11-11 [SC]
      10666006DEAD0A18B19BADBEEF006B0FC83BB041
uid           [ultimate] baeldung
sub   rsa4096 2023-11-11 [E]

In this case, we have our [pub]lic key for the given uid user identifier along with a public [sub]key.

On the other hand, private or secret keys can be seen with –list-secret-keys (-k):

$ gpg --list-secret-keys
/home/baeldung/.gnupg/pubring.kbx
------------------------
sec   rsa4096 2023-11-11 [SC]
      10666006DEAD0A18B19BADBEEF006B0FC83BB041
uid           [ultimate] baeldung
ssb   rsa4096 2023-11-11 [E]

As expected, the [sec]ret key and secret [sub]key are for the same UID.

Both options above can take an argument in the form of a key or user identifier. Subkeys are associated with a main key but can be revoked independently.

3.3. Key Storage

In general, keys are stored in a keyring file, usually at $HOME/.gnupg.

To export keys to separate files, we can use –export alone or by also adding the key or user identifiers:

$ gpg --export [<KEYUSER1_ID> ... <KEYUSERn_ID>]

While the default is an export to stdout, –output (-o) can specify a file to write the extracted information to. After that, we can transfer this file to another machine.

3.4. Encrypt File or Data

After generating the actual keys, we can employ them with a basic stream or file:

$ printf <DATA_TO_ENCRYPT< | gpg --encrypt
You did not specify a user ID. (you may use "-r")

Current recipients:

Enter the user ID.  End with an empty line:

Here, we pipe the output of printf to gpg for encryption with the –encrypt subcommand. Specifically, the input comes from stdin, while the output goes to stdout. In any case, a prompt requests the UID of the message recipient so the encryption can use the correct key.

Alternatively, we can include that in the command via the –recipient (-r) option:

$ printf <DATA_TO_ENCRYPT< | gpg --encrypt --recipient <UID> --output <OUT_FILE_PATH>

Here, we also specified an –output (–o) file path. Another way to store the data is to redirect the output.

Finally, we can also append a file path to encrypt instead of using pipes:

$ gpg --encrypt --recipient <UID> <IN_FILE_PATH>

As a result, we get an encrypted file with the .gpg extension.

Critically, we can use a tool like tar to package whole directories for encryption with gpg.

3.5. Password-protect File or Data

Alternatively, we drop the recipient and keys altogether by encrypting with a passphrase alone:

$ printf <DATA_TO_ENCRYPT< | gpg --symmetric

In this case, we again encrypt stdin and output to stdout. Also, we use the default algorithm.

To encrypt a file and choose the exact method, we supply a path on the command line along with the –cipheralgo option with a specific algorithm name such as aes256:

$ gpg --symmetric --cipher-algo aes256 <IN_FILE_PATH>

Naturally, we can also encrypt a TAR archive.

3.6. Decrypt

The reverse operation of encryption is decryption. With gpg, this is done via the –decrypt (–d) subcommand:

$ gpg --decrypt <IN_FILE_PATH>

Just like before, we can also use stdin:

$ printf <ENCRYPTED_DATA> | gpg --decrypt --output <OUT_FILE_PATH>

In this case, we also specify the output file. Importantly, gpg automatically detects the encryption algorithm as long as it’s supported.

3.7. gpg: problem with the agent: Inappropriate ioctl for device

One common error during encryption and decryption is gpg: problem with the agent: Inappropriate ioctl for device. Usually, this is caused by GPG being unable to decide on the correct input and output streams:

$ echo 'Data' | gpg --symmetric
gpg: problem with the agent: Inappropriate ioctl for device
gpg: error creating passphrase: Operation cancelled
gpg: symmetric encryption of '[stdin]' failed: Operation cancelled

To work around this issue, we just specify the input comes from a TTY by exporting the GPG_TTY control variable:

$ export GPG_TTY=$(tty)

Specifically, we set its value to the current TTY as returned by the tty command.

4. Summary

In this article, we talked about OpenPGP and GnuPG, their common ground and differences.

In conclusion, OpenPGP is an open standard, while GPG is a specific implementation built on top of that standard.