The GPG manual can feel a little.. cryptic sometimes *ba-dum-tss*.

Here's a simple and effective guide to hit the ground running with GnuPG.

This guide is mostly based on this great article about using ed25519 keys with GnuPG. I generated my current GPG keys following this article a few years ago. There's however, some missing stuff about how to get gpg-agent to work with your [A] key for ssh and some other things I have discovered over the course of using gpg-agent that I'd like to share.

Table of Contents

Key Generation

The keys we will be generating will use ed25519 and cv25519 algorithms. These algorithms are both based on the Twisted Edwards Curve. This is a special family of elliptic curves which is resistant to the side-channel attacks that other elliptic curves are vulnerable to.

Basically, ed25519 is the signing algorithm and cv25519 is its encryption counterpart.

We want to generate four keys. Specifically, 1 primary key and 3 subkeys. These are:

  1. Certificate Key [C]: Your primary key which will be used for creating and revoking your other keys.
  2. Authentication Key [A]: The subkey which will be used for SSH.
  3. Encryption Key [E]: The subkey for encrypting and decrypting messages (such as PGP/MIME encrypted emails).
  4. Signature Key [S]: The subkey for digitally signing messages (such as PGP/MIME signed emails).

Let's begin by generating our Certificate Key.

  gpg --quick-generate-key \
    'Your Name <your.email@example.com>' \
    ed25519 cert never

When it prompts you to enter a password, make sure you select a strong, memorable password that you will remember. Do not store this password in a password manager. You need to remember this so that you can reliably recall the password when the attacker hits you with the wrench.

The command will give the output:

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/shriram/.gnupg/openpgp-revocs.d/47BDC6DBB9460CE2471AA2258E9C8FBD1272F630.rev'
public and secret key created and signed.

pub   ed25519 2025-09-27 [C]
      47BDC6DBB9460CE2471AA2258E9C8FBD1272F630
uid                      Your Name <your.email@example.com>

Next, copy the fingerprint of the generated key and save it to a variable

  export KEYFP=47BDC6DBB9460CE2471AA2258E9C8FBD1272F630 # the fingerprint from above

Now, we can use this main key to generate our 3 subkeys. This should prompt you for your main key's password thrice.

gpg --quick-add-key $KEYFP ed25519 sign 1y
gpg --quick-add-key $KEYFP cv25519 encr 1y
gpg --quick-add-key $KEYFP ed25519 auth 1y

That's it. We have generated the 4 keys that we need. The main key does not expire and the three subkeys have a validity of 1 year each. (This can be extended by editing the keys using your main key later).

At this point, your gpg keychain should look something like this:

  gpg -K
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/shriram/.gnupg/pubring.kbx
--------------------------------
sec   ed25519 2025-09-27 [C]
      47BDC6DBB9460CE2471AA2258E9C8FBD1272F630
uid           [ultimate] Your Name <your.email@example.com>
ssb   ed25519 2025-09-27 [S] [expires: 2026-09-27]
ssb   cv25519 2025-09-27 [E] [expires: 2026-09-27]
ssb   ed25519 2025-09-27 [A] [expires: 2026-09-27]

Configuring GPG for SSH

In order to use your GPG [A] key for SSH, you need to switch your system's ssh-agent with the gpg-agent that comes with GnuPG.

To do this, open your code editor and add the following lines to your ~/.bash_profile or the equivalent for your shell (e.g., .zprofile)

  export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
  gpgconf --launch gpg-agent

Next, we need to tell GPG which key to use for SSH. We can do that by grabbing the keygrip of our [A] key and putting it in GnuPG's sshcontrol file.

First, get the keygrip of your [A] key like so:

  gpg -K --with-keygrip

This should give you an output like:

/home/shriram/.gnupg/pubring.kbx
--------------------------------
sec   ed25519 2025-09-27 [C]
      47BDC6DBB9460CE2471AA2258E9C8FBD1272F630
      Keygrip = 0BECF2A25D3647CCE20BB8486A7426912C14399C
uid           [ultimate] Your Name <your.email@example.com>
ssb   ed25519 2025-09-27 [S] [expires: 2026-09-27]
      Keygrip = 809DB3A0F806CF443114651FC3D67E0C7EEC99C4
ssb   cv25519 2025-09-27 [E] [expires: 2026-09-27]
      Keygrip = EB55552C8982AA5E2D3477D6B582A15BC30549D2
ssb   ed25519 2025-09-27 [A] [expires: 2026-09-27]
      Keygrip = 7B761F0445CC62FFAA4D2F68416BE178ED066537

Copy the keygrip under the [A] key and put it in the sshcontrol file like so.

  echo "7B761F0445CC62FFAA4D2F68416BE178ED066537" >> ~/.gnupg/sshcontrol

Now that we have done the required setup, logout and log back in to your desktop for the changes to take effect.

NOTE if you are using a code editor for editing these files, please ensure that the file ends with a newline, otherwise GPG will not parse the file properly, especially the sshcontrol file.

Pinentry

gpg-agent needs to know which tty to use for launching the pinentry program (the program that asks you for your GPG key's password), even if it's a GUI program like pinentry-qt.

So we need to update our .ssh/config file which runs every time you try to use SSH, to tell gpg-agent about the current tty.

Edit .ssh/config and add this line at the top. (create the file if it doesn't exist)

  Match host * exec "gpg-connect-agent UPDATESTARTUPTTY /bye"

Export the [A] key in SSH format

Run the following command to export your [A] key in SSH format.

Use the email associated with your certificate key here.

  gpg --export-ssh-key your.email@example.com

This should give you your [A] key's public component in SSH format. for example:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrNLpEGa6DxRqF+fg13PY0p/pwlBvWWcuWv8L23YXUw openpgp:0xC7BC60CC

You can now use this as your ssh key.

For example, you can add this to your github at https://github.com/settings/ssh/new

Give it any name like "my gpg key". Once you have submitted that form after pasting that key (the entire output of the previous command), you can test that authentication works using the following command:

ssh -T git@github.com

This should give you something like the following output: (if it prompts to add the host to known_hosts, type yes and hit enter)

The authenticity of host 'github.com (20.207.73.82)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
Hi shriramters! You've successfully authenticated, but GitHub does not provide shell access.

Troubleshooting

If you get a permission denied (maybe the pinentry window didn't show up), make sure you logged out and logged in again as mentioned in one of the previous sections.

Ensure that your configuration files .ssh/config, .gnupg/sshcontrol etc are proper and have a newline character at the end of the file.

Pinentry issues are specific to your operating system and desktop environment, so you may need to read the manual for the pinentry program you are using and how to configure gpg to use it (maybe by editing .gnupg/gpg-agent.conf).

You can also get the ssh command to produce verbose output to clearly understand the error.

   ssh -vvT git@github.com

Exporting and Importing Subkeys

If you have multiple machines and say you want to use your gpg [A] key to access github, [E] key for encrypting emails and [S] key to sign your commits, but you don't need your [C] key there, you can export your full [A], [E] and [S] keys for use in another machine.

The simplest way is to use:

  gpg --armor --export-secret-subkeys your.email@example.com > ssb-keys.key

This will export dummy packets for the primary key, so effectively only all your subkeys are exported.

If you don't need all the subkeys, you can individually export only the required subkeys by their fingerprints.

Like if you only need [A] and [S] key on your other machine:

  gpg -K --with-subkey-fingerprint
/home/shriram/.gnupg/pubring.kbx
--------------------------------
sec   ed25519 2025-09-27 [C]
      47BDC6DBB9460CE2471AA2258E9C8FBD1272F630
uid           [ultimate] Your Name <your.email@example.com>
ssb   ed25519 2025-09-27 [S] [expires: 2027-09-27]
      055BE71F465B5D1762C3393A7D4D5B421D76D88E
ssb   cv25519 2025-09-27 [E] [expires: 2027-09-27]
      D019979E67F90A344947EECDBFB31F285CEF4FE8
ssb   ed25519 2025-09-27 [A] [expires: 2027-09-27]
      5CFE67C0E17E7909F6BB847D7B1ED4B2C7BC60CC

Now export only [A] and [S] subkeys:

  gpg --armor --export-secret-subkeys "<subkey-S-fingerprint>!" "<subkey-A-fingerprint>!" > a-and-s.key
gpg --armor --export-secret-subkeys "055BE71F465B5D1762C3393A7D4D5B421D76D88E!" "5CFE67C0E17E7909F6BB847D7B1ED4B2C7BC60CC!" > a-and-s.key

NOTE the bang (!) is necessary to prevent the primary key from being exported.

Now, copy this a-and-s.key file to the other machine and run the command:

   gpg --import /path/to/a-and-s.key

This will prompt you for pinentry and then:

gpg: key 8E9C8FBD1272F630: public key "Your Name <your.email@example.com>" imported
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key 8E9C8FBD1272F630: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

Renewing Keys

If your subkeys expired and you want to extend the validity rather than generate new keys, you can do that like so:

 gpg --edit-key your.email@example.com

This should take you to an interactive CLI:

gpg (GnuPG) 2.4.7; Copyright (C) 2024 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  ed25519/8E9C8FBD1272F630
     created: 2025-09-27  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  ed25519/7D4D5B421D76D88E
     created: 2025-09-27  expires: 2026-09-27  usage: S   
ssb  cv25519/BFB31F285CEF4FE8
     created: 2025-09-27  expires: 2026-09-27  usage: E   
ssb  ed25519/7B1ED4B2C7BC60CC
     created: 2025-09-27  expires: 2026-09-27  usage: A   
[ultimate] (1). Your Name <your.email@example.com>

gpg>

We want to select all three subkeys (1, 2 and 3) to extend the expiry date for.

At the prompt, type key 1 press enter, type key 2 press enter and then type key 3 and press enter.

This will select all three keys and you will see * next to each selected subkey.

sec  ed25519/8E9C8FBD1272F630
     created: 2025-09-27  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb* ed25519/7D4D5B421D76D88E
     created: 2025-09-27  expires: 2026-09-27  usage: S   
ssb* cv25519/BFB31F285CEF4FE8
     created: 2025-09-27  expires: 2026-09-27  usage: E   
ssb* ed25519/7B1ED4B2C7BC60CC
     created: 2025-09-27  expires: 2026-09-27  usage: A   
[ultimate] (1). Your Name <your.email@example.com>

gpg>

Now, enter expire to edit the expiry date and then enter how long you want the key to be valid, for example enter 2y if you want it to be valid for 2 years.

gpg> expire
Are you sure you want to change the expiration time for multiple subkeys? (y/N) y
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) 2y
Key expires at Monday 27 September 2027 09:31:50 AM IST
Is this correct? (y/N) y

sec  ed25519/8E9C8FBD1272F630
     created: 2025-09-27  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb* ed25519/7D4D5B421D76D88E
     created: 2025-09-27  expires: 2027-09-27  usage: S   
ssb* cv25519/BFB31F285CEF4FE8
     created: 2025-09-27  expires: 2027-09-27  usage: E   
ssb* ed25519/7B1ED4B2C7BC60CC
     created: 2025-09-27  expires: 2027-09-27  usage: A   
[ultimate] (1). Your Name <your.email@example.com>

Finally, enter save at the prompt to save and exit.

Closing Words

In this guide, we've seen how to generate an ed25519 certificate key and ed25519 and cv25519 keys for Encryption, Authentication, Signing.

We've also seen how we can configure GPG for use with SSH and also how to export, import and renew keys.

You should have a pretty decent configuration by now. Next, download an email client which supports PGP/MIME and maybe upload your public key to a keyserver so that others can download it for sending you PGP encrypted mail etc.

Thank you for reading!