GPG

GnuPG Configuration and Usage Guide

GnuPG Configuration and Usage Guide

Create a primary key

The primary key is the corner stone in the Web of Trust for proving your identity. Once compromised, you need to start all over from scratch again. Therefore, it must be kept safe. One possible policy is to keep the primary key offline in an encrypted removable drive, and only distribute per-device subkeys for daily signing.

Suppose we are Alice. On the removable drive, create GnuPG home directory:

mkdir .gnupg

Give the directory 700 permissions:

chmod 700 .gnupg

Change the working directory of GnuPG for this terminal session:

export GNUPGHOME=/path/to/.gnupg
💡 Tip

This needs to be set for the terminal session every time when working on the removable drive.

Just to be sure that the above took effect:

gpg --list-keys --keyid-format LONG

Expected output:

gpg: keybox '/path/to/.gnupg/pubring.kbx' created
gpg: /path/to/.gnupg/trustdb.gpg: trustdb created

Generate a primary key:

gpg --gen-key

Check the generated key with --list-keys, there should now be a primary key for signing and certification (SC) and a subkey for encryption (E). The output will be similar to:

pub   ed25519/9EE5F19B076C5F70 2026-04-29 [SC] [expires: 2029-04-28]
      FA33B59C88C72811E7FC2DB29EE5F19B076C5F70
uid                 [ultimate] Alice <alice@example.com>
sub   cv25519/94FFC07581986626 2026-04-29 [E] [expires: 2029-04-28]

Here, FA33B59C88C72811E7FC2DB29EE5F19B076C5F70 is the primary key ID, and 94FFC07581986626 is the subkey ID of the encryption subkey.

Edit the key:

gpg --edit-key FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Change the expiry of the key to no expiry:

gpg> expire
Changing expiration time for the primary key.
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

sec  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb  cv25519/94FFC07581986626
     created: 2026-04-29  expires: 2029-04-28  usage: E   
[ultimate] (1). Alice <alice@example.com>

Delete the encryption key. We will handle encryption later.

gpg> key 94FFC07581986626

sec  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb* cv25519/94FFC07581986626
     created: 2026-04-29  expires: 2029-04-28  usage: E   
[ultimate] (1). Alice <alice@example.com>

gpg> delkey
Do you really want to delete this key? (y/N) y

sec  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
[ultimate] (1). Alice <alice@example.com>

Save the changes:

gpg> save

Sharing the public key

We want to share out public key with our friend Bob, so that he can verify files signed by us, and send us encrypted files that we can decrypt.

On the removable drive, export the public key:

gpg --armor --output pub.asc --export FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Then, securely send the public key to Bob.

As Bob, import the public key:

gpg --import pub.asc

Optionally, fully trust the key by editing it:

gpg --edit-key FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

At the gpg> prompt:

gpg> trust
pub  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: unknown       validity: unknown
[ unknown] (1). Alice <alice@example.com>

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 4

pub  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: full          validity: unknown 
[ unknown] (1). Alice <alice@example.com>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

gpg> save

However, there’s a catch: we haven’t created keys for signing and encryption yet! Basically, the key isn’t very useful at this moment (although it can already be certified). Do come back and update Bob with the latest public key after creating the appropriate keys in later sections!

Getting the public key validated

If Bob trust that we are whom our key claims to be, he can sign our key, hence endorse the validity of our certificate. Here we suppose this is true, as Bob is our friend in real life.

With our public key shared to Bob, he can validate our public key with his primary key DE4E084E4060B0E3E511681C17D373C240C9B69E with:

gpg --local-user DE4E084E4060B0E3E511681C17D373C240C9B69E --edit-key FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

At the gpg> prompt:

gpg> sign

pub  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: full          validity: unknown
 Primary key fingerprint: FA33 B59C 88C7 2811 E7FC  2DB2 9EE5 F19B 076C 5F70

     Alice <alice@example.com>

Are you sure that you want to sign this key with your
key "Bob <bob@example.com>" (17D373C240C9B69E)

Really sign? (y/N) y

gpg> save

Export the signed public key:

gpg --armor --output signed_pub.asc --export FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Then, securely send the public key back to us.

For us, on the removable drive, import the now signed public key:

gpg --import signed_pub.asc

To verify:

gpg --list-sigs --keyid-format LONG

Expected output:

pub   ed25519/9EE5F19B076C5F70 2026-04-29 [SC]
      FA33B59C88C72811E7FC2DB29EE5F19B076C5F70
uid                 [ultimate] Alice <alice@example.com>
sig 3        9EE5F19B076C5F70 2026-04-29  [self-signature]
sig          17D373C240C9B69E 2026-04-29  Bob <bob@example.com>

Create and distribute per-device signing subkeys

On the removable drive, edit the key:

gpg --edit-key FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Create an ed25519 sign only key valid for 1 year:

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
  (10) ECC (sign only)
  (12) ECC (encrypt only)
  (14) Existing key from card
Your selection? 10
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection? 1
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) 1y
Key expires at Thursday, April 29, 2027 PM08:43:43 UTC
Is this correct? (y/N) y
Really create? (y/N) y
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.

sec  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb  ed25519/DFA5BF3B65FD74F5
     created: 2026-04-29  expires: 2027-04-29  usage: S   
[ultimate] (1). Test <example@example.com>

Save the changes:

gpg> save

As seen above, the subkey ID of the newly created key is DFA5BF3B65FD74F5. Export this particular subkey to be later imported to the device that will use this key by running the following. Do not omit the exclamation mark ! at the end:

gpg --armor --output device_name.asc --export-secret-subkeys DFA5BF3B65FD74F5!

--armor: Create ASCII armored output

💡 Tip

It could be a good idea to keep track of the target device and creation date of each subkey.

Safely copy device_name.asc to the target device. Then, on the target device:

gpg --import device_name.asc

Verify that the subkey is imported, while the primary key is not:

gpg --list-secret-keys --keyid-format LONG

Expected output. Note that there is a # after sec:

sec#  ed25519/9EE5F19B076C5F70 2026-04-29 [SC]
      FA33B59C88C72811E7FC2DB29EE5F19B076C5F70
uid                 [ unknown] Alice <alice@example.com>
ssb   ed25519/DFA5BF3B65FD74F5 2026-04-29 [S] [expires: 2027-04-29]

Edit the key:

gpg --edit-key FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Ultimate trust the key if it hasn’t been trusted yet:

gpg> trust
pub  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: unknown       validity: unknown
ssb  ed25519/DFA5BF3B65FD74F5
     created: 2026-04-29  expires: 2027-04-29  usage: S   
[ unknown] (1). Alice <alice@example.com>

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

pub  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: ultimate      validity: unknown
ssb  ed25519/DFA5BF3B65FD74F5
     created: 2026-04-29  expires: 2027-04-29  usage: S   
[ unknown] (1). Alice <alice@example.com>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

Optionally, change the password with passwd.

Save the changes:

gpg> save

The subkey is now good to use on the device. Repeat for every device and every expired key.

Since the primary key is now updated, it’s a good time to share the updated public key.

Signing files

On our device, to sign foo.txt with the DFA5BF3B65FD74F5 subkey:

gpg --local-user DFA5BF3B65FD74F5 --sign foo.txt

--local-user: The key to use for the signature

The signed file is foo.txt.gpg.

Alternatively, use --detach-sign to produce a separate signature without including the file’s content:

gpg --local-user DFA5BF3B65FD74F5 --detach-sign foo.txt

The detached signature is foo.txt.sig.

For text files, use --clear-sign to produce an inline signature:

gpg --local-user DFA5BF3B65FD74F5 --clear-sign foo.txt

The file with clear signature is foo.txt.asc.

Verifying signatures

Suppose our friend Bob, who has previously obtained our public key, wants to verify the file foo.txt signed by us.

To verify foo.txt.gpg signed by --sign, writing the contents of the original file to foo.txt:

gpg --output foo.txt --verify foo.txt.gpg

To verify foo.txt.sig signed by --detach-sign, make sure foo.txt is in the same directory, then run:

gpg --verify foo.txt.sig

To verify foo.txt.asc signed by -clear-sign, writing the contents of the original file to foo.txt:

gpg --output foo.txt --verify foo.txt.asc

Create and distribute encryption keys

The process is similar to that of create and distribute signing keys. When generating the subkey using addkey, instead of choosing (10) ECC (sign only), choose (12) ECC (encrypt only). However, unlike the signing subkey, which is created on a per-device basis, the encryption subkey is shared across all devices.

📝 Note

Unfortunately, GnuPG is said to encrypt only for the most recent encryption subkey. Therefore, the encryption key has to be shared across all devices. (ref)

Repeat when the key expires. Since the primary key is now updated, it’s a good time to share the updated public key.

Encrypting files

Suppose our friend Bob, who has previously obtained our public key, wants to to send us an encrypted file bar.txt using the 3D486C4725CF62E2 subkey:

gpg --recipient 3D486C4725CF62E2 --encrypt bar.txt
💡 Tip

If Bob’s GnuPG complains about the following:

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.

He can try signing our public key first.

Then, send the encrypted bar.txt.gpg to us.

Decrypting files

On our device, to decrypt bar.txt.gpg encrypted using our public key sent to us, writing the contents of the original file to bar.txt:

gpg --output bar.txt --decrypt bar.txt.gpg

Uploading the public key to a keyserver

Keyservers are of less use these days and most of them are not anymore in service. It is better to attach the key to a mail or provide keys using a Web Key Directory.

GnuPG FAQ

On the removable drive, to send the public key to the Synchronizing Key Server:

gpg --keyserver pool.sks-keyservers.net --send-key 

Some other keyservers:

  • Ubuntu: keyserver.ubuntu.com

Once the public key is uploaded to one keyserver, it will propagate to the other keyservers.

The keys may obtain new signatures (verifications) from time to time. To download any new signatures and update the keys:

gpg --refresh-keys

Revoking a subkey

On the removable drive, edit the key:

gpg --edit-key FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Select the subkey to be revoked, for example, for the encryption key 3D486C4725CF62E2:

gpg> key 3D486C4725CF62E2

sec  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb  ed25519/DFA5BF3B65FD74F5
     created: 2026-04-29  expires: 2027-04-29  usage: S   
ssb* cv25519/3D486C4725CF62E2
     created: 2026-04-29  expires: 2027-04-29  usage: E   
[ultimate] (1). Alice <alice@example.com>

gpg> revkey
Do you really want to revoke this subkey? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
Your decision? 1
Enter an optional description; end it with an empty line:
> 
Reason for revocation: Key has been compromised
(No description given)
Is this okay? (y/N) y

sec  ed25519/9EE5F19B076C5F70
     created: 2026-04-29  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb  ed25519/DFA5BF3B65FD74F5
     created: 2026-04-29  expires: 2027-04-29  usage: S   
The following key was revoked on 2026-04-29 by ? key 9EE5F19B076C5F70 Alice <alice@example.com>
ssb  cv25519/3D486C4725CF62E2
     created: 2026-04-29  revoked: 2026-04-29  usage: E   
[ultimate] (1). Alice <alice@example.com>

gpg> save

Then, send the updated public key to a keyserver.

Additional useful GnuPG commands

Delete a key:

gpg --delete-secret-and-public-keys FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Export secret keys, useful for creating complete backups of the entire key:

gpg --armor --output sec.asc --export-secret-keys FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

Export all secret subkeys without the primary key, as opposed to export one specific subkey:

gpg --armor --output sec_sub.asc --export-secret-subkeys FA33B59C88C72811E7FC2DB29EE5F19B076C5F70

References

https://help.gitkraken.com/gitkraken-desktop/commit-signing-with-gpg/

https://superuser.com/questions/466396/how-to-manage-gpg-keys-across-multiple-systems

https://wiki.debian.org/Subkeys?action=show&redirect=subkeys

https://tjl73.altervista.org/secure_keygen/en/en.html

https://superuser.com/questions/16160/short-easy-to-understand-explanation-of-gpg-pgp-for-nontechnical-people

https://www.phildev.net/pgp/gpgwhy.html

https://www.gnupg.org/faq/gnupg-faq.html

https://help.ubuntu.com/community/GnuPrivacyGuardHowto

https://www.gnupg.org/documentation/manuals/gnupg/Operational-GPG-Commands.html

Code licensed under the MIT License; all other content under CC BY-SA 4.0.
Last updated on Apr 30, 2026 05:48 UTC
Built with Hugo
Theme Stack designed by Jimmy