Applied Cryptography: GPG File Encryption (Practical Guide)

This article focuses on practical implementation with a brief introduction to the underlying principles.

Throughout this article, I'll first cover the basic principles, then walk through three main sections: how to create GPG keys, how to manage your keys (key management commands), and hands-on practice: using self-created GPG keys to decrypt transmitted encrypted files.

Principle Overview

The principle behind using GPG for encrypting and decrypting documents is straightforward, as described in the documentation:

The procedure for encrypting and decrypting documents is straightforward with this mental model. If you want to encrypt a message to Alice, you encrypt it using Alice's public key, and she decrypts it with her private key. If Alice wants to send you a message, she encrypts it using your public key, and you decrypt it with your key.

The entire process is: I encrypt the file I want to send you with my private key. Then you use my public key to decrypt the file I want to send you, which has been encrypted by me.

This is the basic process.

This article provides a quick overview of how GPG encrypts document content.

This article is divided into two parts, starting with creating a usable GPG key.

How to Create GPG Keys

shell
[Se] gpg --list-keys
gpg: checking the trustdb
gpg: no ultimately trusted keys found

First, let's list whether my machine currently has any GPG keys. As we can see, there are none. This mainly refers to whether there are GPG public keys. Let's also check for private keys.

shell
[Se] gpg --list-secret-keys
[Se]

Nothing there either.

One Command to Rule Them All

Let's start creating.

The detailed steps begin with gpg --full-generate-key, as follows:

shell
[Se] gpg --full-generate-key
gpg (GnuPG) 2.4.0; 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
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
(14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: Chenwei Jiang
Email address: cheverjonathan@gmail.com
Comment: Used for learning
You selected this USER-ID:
"Chenwei Jiang (Used for learning) <cheverjonathan@gmail.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: revocation certificate stored as '/home/cheverjohn/.gnupg/openpgp-revocs.d/D1DED33E8FE9CA4B315A488968CC99424BF9EF81.rev'
public and secret key created and signed.
pub rsa4096 2023-09-19 [SC]
D1DED33E8FE9CA4B315A488968CC99424BF9EF81
uid Chenwei Jiang (Used for learning) <cheverjonathan@gmail.com>
sub rsa4096 2023-09-19 [E]

Detailed Stage-by-Stage Command Explanation

Let me break down what happens at each stage.

Stage 1: Select Encryption and Signing Algorithm

In the first stage, you need to select the encryption algorithm. I chose option 1, which means both encryption and signing use the RSA algorithm.

shell
[Se] gpg --full-generate-key
gpg (GnuPG) 2.4.0; 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
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
(14) Existing key from card
Your selection? 1

Oh, and there's also a copyright notice.

Stage 2: Select Key Length

Longer keys are more secure. I chose 4096 here.

shell
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits

Stage 3: Set Key Validity Period

Set the validity period.

shell
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

For this demonstration, I configured Key does not expire at all.

Stage 4: Personal Information

This section will ultimately be used to generate your user ID.

shell
GnuPG needs to construct a user ID to identify your key.
Real name: Chenwei Jiang
Email address: cheverjonathan@gmail.com
Comment: Used for learning
You selected this USER-ID:
"Chenwei Jiang (Used for learning) <cheverjonathan@gmail.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

Here I set my Real name to "Chenwei Jiang", my Email address to "cheverjonathan@gmail.com", and added a comment. These are some basic information I commonly use, primarily for demonstration purposes.

The result is a USER-ID: "Chenwei Jiang (Used for learning) cheverjonathan@gmail.com".

How to Manage Your Keys (Key Management Commands)

This section covers how to manage multiple keys on a host machine.

Listing Keys

There are two types of key listings: public keys and private keys.

shell
[Se] gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
/home/cheverjohn/.gnupg/pubring.kbx
-----------------------------------
pub rsa4096 2023-09-19 [SC]
D1DED33E8FE9CA4B315A488968CC99424BF9EF81
uid [ultimate] Chenwei Jiang (Used for learning) <cheverjonathan@gmail.com>
sub rsa4096 2023-09-19 [E]

The above command shows the details.

The key section:

shell
/home/cheverjohn/.gnupg/pubring.kbx
-----------------------------------
pub rsa4096 2023-09-19 [SC]
D1DED33E8FE9CA4B315A488968CC99424BF9EF81
uid [ultimate] Chenwei Jiang (Used for learning) <cheverjonathan@gmail.com>
sub rsa4096 2023-09-19 [E]

The first line shows the public key filename.

Value Explanation

Exporting Keys

The public key file is located at ~/.gnupg/pubring.kbx and stored in binary format. Using the armor parameter converts it to ASCII display.

Using commands to export as public-key.txt and private-key.txt:

shell
[gpg] pwd
/home/cheverjohn/Se/gpg
[gpg] ls
[gpg] ls -la
total 0
drwxr-xr-x. 1 cheverjohn cheverjohn 0 Sep 19 23:36 .
drwxr-xr-x. 1 cheverjohn cheverjohn 34 Sep 19 23:36 ..
[gpg] gpg --armor --output public-key.txt --export 'Chenwei Jiang (used for learning) <cheverjonathan@gmail.com>'
[gpg] ls
public-key.txt
[gpg] cat public-key.txt
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGUJt+QBEADAzoLPAdK8GfJ/5Ouxh2rOrMsClMmoOznMm2GOBcqSaQsdmP4G
................................................................
oM3YFFujtMxK/cQ/KkbmwAtlMkWx5x8RT/dJ
=NMtR
-----END PGP PUBLIC KEY BLOCK-----
[gpg]

As shown above, this demonstrates the steps to export public-key.txt. The detailed command is:

shell
gpg --armor --output public-key.txt --export 'Chenwei Jiang (used for learning) <cheverjonathan@gmail.com>'

Below are the steps to export private-key.txt:

shell
[gpg] ls
public-key.txt
[gpg] gpg --armor --output private-key.txt --export-secret-keys
[gpg] ls
private-key.txt public-key.txt
[gpg]

If you previously set a password, this operation requires the password. The command requiring password is:

shell
gpg --armor --output private-key.txt --export-secret-keys

Uploading Public Keys

Public key servers are network servers specifically for storing user public keys. The --send-keys subcommand can achieve this.

Public key servers have no verification mechanism - anyone can upload public keys in your name, so there's no guarantee of reliability for public keys on servers.

Generally, we publish a public key fingerprint on our own website for others to verify that the downloaded public key is authentic. The --fingerprint subcommand can generate a public key fingerprint.

Hands-On Practice / Practice Makes Perfect

In this section, I'll demonstrate exporting public keys, encrypting with private keys, and decrypting with public keys on another device.

I'll show how to encrypt a file containing "hello world" and then decrypt it remotely.

The steps are divided into encryption and decryption.

Detailed steps:

  1. Create file with the following commands:

    shell
    [gpg] touch demo.txt
    echo "hello world" > demo.txt
    [gpg] ls
    demo.txt private-key.txt public-key.txt
    [gpg] cat demo.txt
    hello world
    [gpg]

    The file creation commands are:

    shell
    touch demo.txt
    echo "hello world" > demo.txt

    Now we have a demo.txt file containing "hello world".

  2. Encrypt the file:

    shell
    [gpg] ls
    demo.txt private-key.txt public-key.txt
    [gpg] gpg --recipient 'cheverjonathan@gmail.com' --output demo.gpg --encrypt demo.txt
    [gpg] ls
    demo.gpg demo.txt private-key.txt public-key.txt
    [gpg] cat demo.gpg
    ..............
    &5p�.�)I�槹iQ%
    [gpg]

    Encryption command:

    shell
    gpg --recipient 'cheverjonathan@gmail.com' --output demo.gpg --encrypt demo.txt

    You can see that demo.gpg is the encrypted file.

  3. Decrypt the file:

    shell
    [gpg] gpg --output demo --decrypt demo.gpg
    gpg: encrypted with rsa4096 key, ID 13C117D5FEC4F051, created 2023-09-19
    "Chenwei Jiang (Used for learning) <cheverjonathan@gmail.com>"
    [gpg] ls
    demo demo.gpg demo.txt private-key.txt public-key.txt
    [gpg] cat demo
    hello world
    [gpg]

    Decryption command:

    shell
    gpg --output demo --decrypt demo.gpg

    After entering this command, you need to input the password set earlier, then you can get the file.

    The above demonstrates the process of encrypting files using local keys and decrypting them locally.

    Next, let's begin...

    Obtaining Files on Other Computers, Encrypting Them, Then Having the Original Host Decrypt Using Private Keys

    First, we transfer public-key.txt to another host. You can use GitHub's functionality to upload/download files. For convenience, I'll upload the following file tree from the above process:

    Shell
    cheverjohn@Dell-G33579 git:(doc/test-gpg*)% tree ~/workspace/Opensource/github.com/Chever-John/cheverjohn.me/docs/wait-for-publish/gpg
    .
    ├── demo
    ├── demo.gpg
    ├── demo.txt
    ├── private-key.txt
    └── public-key.txt

    After removing private-key.txt, I'll upload the files to GitHub.

    You can see I've downloaded the public-key.txt file on another host, as shown in the file tree:

    shell
    cheverjohn:wait-for-publish/ git:(doc/test-gpg*)$ tree gpg [0:54:06]
    gpg
    ├── demo
    ├── demo.gpg
    ├── demo.txt
    └── public-key.txt
    1 directory, 4 files

    Next, I need to use this public-key.txt to encrypt a file, then transfer this file back to the host with the private key for decryption.

    First, we need to import this public-key.txt locally:

    shell
    gpg --import public-key.txt

    Command execution result:

    shell
    cheverjohn:gpg/ git:(doc/test-gpg*)$ gpg --import public-key.txt [1:20:11]
    gpg: key 3BE465D20064251C: public key "Chenwei Jiang (Learning second) <cheverjonathan@gmail.com>" imported
    gpg: Total number processed: 1
    gpg: imported: 1

    Now we need to use this public key to encrypt text.

    First, let's create a file hello-no-encrypt.txt:

    shell
    cheverjohn:gpg/ git:(doc/test-gpg*)$ touch hello-no-encrypt.txt [1:16:07]
    cheverjohn:gpg/ git:(doc/test-gpg*)$ nvim hello-no-encrypt.txt [1:16:15]
    cheverjohn:gpg/ git:(doc/test-gpg*)$ cat hello-no-encrypt.txt [1:16:24]
    Hello Sasa!
    cheverjohn:gpg/ git:(doc/test-gpg*)$ tree [1:16:28]
    .
    ├── demo
    ├── demo.gpg
    ├── demo.txt
    ├── hello-no-encrypt.txt
    └── public-key.txt
    1 directory, 5 files

    We need to encrypt this file and then send it to the host with the private key for decryption.

    The entire encryption process:

    shell
    cheverjohn:gpg/ git:(doc/test-gpg*)$ gpg --output hello-encrypted.gpg --encrypt --recipient 'Chenwei Jiang (Learning second) <cheverjonathan@gmail.com>' hello-no-encrypt.txt
    gpg: 617C5542800731C1: There is no assurance this key belongs to the named user
    sub rsa4096/617C5542800731C1 2023-09-21 Chenwei Jiang (Learning second) <cheverjonathan@gmail.com>
    Primary key fingerprint: 5588 D37D AF51 50FD 9186 47E7 3BE4 65D2 0064 251C
    Subkey fingerprint: A038 0DA1 D481 62C7 517C D6C6 617C 5542 8007 31C1
    It is NOT certain that the key belongs to the person named
    in the user ID. If you *really* know what you are doing,
    you may answer the next question with yes.
    Use this key anyway? (y/N) y

    Encryption command:

    shell
    gpg --output hello-encrypted.gpg --encrypt --recipient 'Chenwei Jiang (Learning second) <cheverjonathan@gmail.com>' hello-no-encrypt.txt

    Then we need to upload the encrypted file hello-encrypted.gpg to the machine with the private key.

    Again, I'll use GitHub as the file transfer tool.

    You can see I've obtained the file on the host machine (the machine with the keys) and started decryption:

    shell
    [gpg] tree git:(doc/test-gpg)
    .
    ├── demo
    ├── demo.gpg
    ├── demo.txt
    ├── hello-encrypted.gpg
    ├── hello-no-encrypt.txt
    └── public-key.txt
    1 directory, 6 files

    Next, we need to decrypt hello-encrypted.gpg:

    shell
    [gpg] gpg --output hello-decrypted.txt --decrypt hello-encrypted.gpg git:(doc/test-gpg)
    gpg: encrypted with rsa4096 key, ID 617C5542800731C1, created 2023-09-21
    "Chenwei Jiang (Learning second) <cheverjonathan@gmail.com>"
    [gpg] ls git:(doc/test-gpg*)
    demo demo.gpg demo.txt hello-decrypted.txt hello-encrypted.gpg hello-no-encrypt.txt public-key.txt
    [gpg] cat hello-decrypted.txt git:(doc/test-gpg*)
    Hello Sasa!

    Decryption command:

    shell
    gpg --output hello-decrypted.txt --decrypt hello-encrypted.gpg

Comments