Installing GnuPG 2.4 on Ubuntu 22.04

Ubuntu 22.04 (Jammy Jellyfish) ships with GnuPG 2.2.27. This version of GnuPG notably has trouble sharing the use of smart cards with other applications (which typically use the standard pcsc-lite daemon for smart card access, rather than GnuPG’s homegrown scdaemon). A newer version of GnuPG, 2.4.0, fixes this.

To install this newer version of GnuPG on Ubuntu 22.04, you have to build it from source. This won’t overwrite the distribution’s original GnuPG binaries and shared libraries, which are installed to the /usr/bin/ and /lib/*-linux-gnu/ directories — but it will add new binaries and libraries to your /usr/local/bin/ and /usr/local/lib/ directories. You can then update your system to use these new binaries and libraries in place of the old ones (and you can reverse the process if you want to revert back to the distribution’s original version of GnuPG).

This article will walk you through the necessary steps:

Tip

If you install GnuPG from source, consider subscribing to the very low-volume gnupg-announce mailing list — or at least checking the list archives once in a while — to keep abreast of any major GnuPG releases or fixes to security vulnerabilities.

Install Prerequisites

To build GnuPG, you will need the core build tools from Ubuntu’s build-essential package, the bzip2 utilities, and the header files to several other shared libraries provided by the GnuPG project. You can install them with the following command:

$ sudo apt install build-essential bzip2 libassuan-dev libgcrypt20-dev libgpg-error-dev libksba-dev libnpth0-dev
Reading package lists...
...
No services need to be restarted.
No containers need to be restarted.
No user sessions are running outdated binaries.
No VM guests are running outdated hypervisor (qemu) binaries on this host.

The header files in the above *-dev packages match the distro’s version of GnuPG — some of these will continue to work for the GnuPG 2.4.x branch, but some will not. When you run GnuPG’s configure build step in the Try Configure section, GnuPG will let you know which libraries you need to update; we will build and install those libraries manually in the Install More Prerequisites section.

Download Release

Next, download the latest stable release of GnuPG, as listed on the GnuPG download page. Currently, this is version 2.4.0:

$ wget https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.4.0.tar.bz2
Resolving www.gnupg.org (www.gnupg.org)...
...
... ‘gnupg-2.4.0.tar.bz2’ saved [7666935/7666935]

If you haven’t already imported the GnuPG signature keys into your keyring, do so now:

$ wget https://www.gnupg.org/signature_key.asc -O - | gpg --import -
Resolving www.gnupg.org (www.gnupg.org)...
...
... written to stdout [5365/5365]

gpg: key BCEF7E294B092E28: 1 signature not checked due to a missing key
gpg: key BCEF7E294B092E28: public key "Andre Heinecke (Release Signing Key)" imported
gpg: key 528897B826403ADA: 4 signatures not checked due to missing keys
gpg: key 528897B826403ADA: public key "Werner Koch (dist signing 2020)" imported
gpg: key E98E9B2D19C6C8BD: 2 signatures not checked due to missing keys
gpg: key E98E9B2D19C6C8BD: public key "Niibe Yutaka (GnuPG Release Key)" imported
gpg: key 549E695E905BA208: 1 signature not checked due to a missing key
gpg: key 549E695E905BA208: public key "GnuPG.com (Release Signing Key 2021)" imported
gpg: Total number processed: 4
gpg:               imported: 4
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2025-02-19

Then verify the signature of the tarball you just downloaded:

$ wget https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.4.0.tar.bz2.sig -O - | gpg --verify - gnupg-2.4.0.tar.bz2
Resolving www.gnupg.org (www.gnupg.org)...
...
... written to stdout [238/238]

gpg: Signature made Fri Dec 16 17:24:40 2022 UTC
gpg:                using EDDSA key 6DAA6E64A76D2840571B4902528897B826403ADA
gpg: Good signature from "Werner Koch (dist signing 2020)" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 6DAA 6E64 A76D 2840 571B  4902 5288 97B8 2640 3ADA
gpg: Signature made Wed Dec 21 06:02:59 2022 UTC
gpg:                using EDDSA key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
gpg: Good signature from "Niibe Yutaka (GnuPG Release Key)" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: AC8E 115B F73E 2D8D 47FA  9908 E98E 9B2D 19C6 C8BD

Make sure the output of the verify command includes the phrase Good signature, along with the key fingerprint from at least one of the GnuPG signers listed on the GnuPG signature keys page.

Try Configure

Next, untar the release and change into the release directory:

$ tar xf gnupg-2.4.0.tar.bz2
$ cd gnupg-2.4.0

Before you run the configure command, make sure your user account has access to read the /usr/local/lib/pkgconfig/ directory — the configure command will try to access this directory to check if you’ve locally built and installed any of the shared directories on which GnuPG depends:

$ ls -ld /usr/local/lib/pkgconfig/
drwxrwx--- 2 root root 4096 Feb 16 01:12 /usr/local/lib/pkgconfig/

If not, change its permissions:

$ sudo chmod o+rx /usr/local/lib/pkgconfig/
$ ls -ld /usr/local/lib/pkgconfig/
drwxrwxr-x 2 root root 4096 Feb 24 01:12 /usr/local/lib/pkgconfig/

Then run the configure command:

$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
...
checking whether tests should be run... yes
configure:
***
*** You need libgpg-error to build this program.
**  This library is for example available at
***   https://gnupg.org/ftp/gcrypt/gpgrt
*** (at least version 1.46 is required.)
***
configure:
***
*** You need libksba to build this program.
*** This library is for example available at
***   https://gnupg.org/ftp/gcrypt/libksba/
*** (at least version 1.6.3 using API 1 is required).
***
configure: error:
***
*** Required libraries not found. Please consult the above messages
*** and install them before running configure again.
***

If this release of GnuPG requires some newer shared libraries than you have installed, the output of the configure command will tell you. The above output tells us we need to build and install the latest version of the libgpg-error and libksba libraries.

Install More Prerequisites

To install the latest version of these libraries, you need to download their source code, build, and install them. Change out of the GnuPG release directory (or open up a new terminal, so you can continue to reference GnuPG’s configure command result), and download and build each in turn.

Download and build the libgpg-error library with the following commands:

$ cd ..
$ wget https://www.gnupg.org/ftp/gcrypt/libgpg-error/libgpg-error-1.46.tar.bz2
Resolving www.gnupg.org (www.gnupg.org)...
...
... ‘libgpg-error-1.46.tar.bz2’ saved [1014291/1014291]
$ wget https://www.gnupg.org/ftp/gcrypt/libgpg-error/libgpg-error-1.46.tar.bz2.sig -O - | gpg --verify - libgpg-error-1.46.tar.bz2
Resolving www.gnupg.org (www.gnupg.org)...
...
... written to stdout [238/238]

gpg: Signature made Fri Oct  7 09:34:00 2022 UTC
gpg:                using EDDSA key 6DAA6E64A76D2840571B4902528897B826403ADA
gpg: Good signature from "Werner Koch (dist signing 2020)" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 6DAA 6E64 A76D 2840 571B  4902 5288 97B8 2640 3ADA
gpg: Signature made Tue Oct 11 05:02:58 2022 UTC
gpg:                using EDDSA key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
gpg: Good signature from "Niibe Yutaka (GnuPG Release Key)" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: AC8E 115B F73E 2D8D 47FA  9908 E98E 9B2D 19C6 C8BD
$ tar xf libgpg-error-1.46.tar.bz2
$ cd libgpg-error-1.46
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
...
config.status: creating po/Makefile

        libgpg-error v1.46 has been configured as follows:

        Revision: ea03187  (59907)
        Platform: aarch64-unknown-linux-gnu

$ make
make  all-recursive
make[1]: Entering directory '/home/justin/libgpg-error-1.46'
...
make[1]: Leaving directory '/home/justin/libgpg-error-1.46'
$ sudo make install
Making install in m4
make[1]: Entering directory '/home/justin/libgpg-error-1.46/m4'
...
make[1]: Leaving directory '/home/justin/libgpg-error-1.46'
$ sudo ldconfig

Download and build the libksba library with the following commands:

$ cd ..
$ wget https://www.gnupg.org/ftp/gcrypt/libksba/libksba-1.6.3.tar.bz2
Resolving www.gnupg.org (www.gnupg.org)...
...
... ‘libksba-1.6.3.tar.bz2’ saved [668287/668287]
$ wget https://www.gnupg.org/ftp/gcrypt/libksba/libksba-1.6.3.tar.bz2.sig -O - | gpg --verify - libksba-1.6.3.tar.bz2
Resolving www.gnupg.org (www.gnupg.org)...
...
... written to stdout [238/238]

gpg: Signature made Tue Dec  6 13:34:27 2022 UTC
gpg:                using EDDSA key 6DAA6E64A76D2840571B4902528897B826403ADA
gpg: Good signature from "Werner Koch (dist signing 2020)" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 6DAA 6E64 A76D 2840 571B  4902 5288 97B8 2640 3ADA
gpg: Signature made Wed Dec 21 23:44:57 2022 UTC
gpg:                using EDDSA key AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD
gpg: Good signature from "Niibe Yutaka (GnuPG Release Key)" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: AC8E 115B F73E 2D8D 47FA  9908 E98E 9B2D 19C6 C8BD
$ tar xf libksba-1.6.3.tar.bz2
$ cd libksba-1.6.3
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
...
config.status: executing libtool commands
configure:
***
*** Note: The installed yacc version is not GNU Bison.  You need
*** to install Bison if you want to change any grammar (.y) file.
***

        Libksba v1.6.3 has been configured as follows:

        Revision:  bffa9b3  (49146)
        Platform:  aarch64-unknown-linux-gnu

$ make
make  all-recursive
make[1]: Entering directory '/home/justin/libksba-1.6.3'
...
make[1]: Leaving directory '/home/justin/libksba-1.6.3'
$ sudo make install
Making install in m4
make[1]: Entering directory '/home/justin/libksba-1.6.3/m4'
...
make[1]: Leaving directory '/home/justin/libksba-1.6.3'
$ sudo ldconfig

Finish Configure

After installing all the libraries as directed by the original GnuPG configure command result, change back to the GnuPG release directory, and run the configure command again:

$ cd ..
$ cd gnupg-2.4.0
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
...
config.status: creating po/Makefile

        GnuPG v2.4.0 has been configured as follows:

        Revision:  c0556edb8  (49237)
        Platform:  GNU/Linux (aarch64-unknown-linux-gnu)

        OpenPGP:   yes
        S/MIME:    yes
        Agent:     yes
        Smartcard: yes (without internal CCID driver)
        TPM:       no
        G13:       no
        Dirmngr:   no
        Keyboxd:   no
        Gpgtar:    yes
        WKS tools: yes

        Protect tool:       (default)
        LDAP wrapper:       (default)
        Default agent:      (default)
        Default pinentry:   (default)
        Default scdaemon:   (default)
        Default keyboxd:    (default)
        Default tpm2daemon: (default)
        Default dirmngr:    (default)

        Dirmngr auto start:  yes
        Readline support:    no
        LDAP support:        n/a
        TLS support:         no
        TOFU support:        no
        Tor support:         only .onion

You should see a successful result when the GnuPG configure command completes, with its core components listed out as above.

Make and Install

Now you can build the GnuPG binaries:

$ make
make  all-recursive
make[1]: Entering directory '/home/justin/gnupg-2.4.0'
...
make[1]: Leaving directory '/home/justin/gnupg-2.4.0'

And install the new GnuPG binaries into your /usr/local/bin/ directory:

$ sudo make install
Making install in m4
make[1]: Entering directory '/home/justin/gnupg-2.4.0/m4'
...
make[1]: Leaving directory '/home/justin/gnupg-2.4.0'

As long as you have the /usr/local/bin/ directory on your path ahead of the /usr/bin/ directory, you will now be using the latest version of GnuPG by default:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
$ which gpg
/usr/local/bin/gpg
$ gpg --version
gpg (GnuPG) 2.4.0
libgcrypt 1.9.4
Copyright (C) 2021 Free Software Foundation, Inc.
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /home/justin/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed

GnuPG also will have installed some supplemental binaries into your /usr/local/libexec/ directory, so make sure your regular user account has permission to access this directory:

$ ls -ld /usr/local/libexec/
drwxrwx---  2 root root 4096 Feb 24 01:15 libexec

If not, change its permissions:

$ sudo chmod o+rx /usr/local/libexec/
$ ls -ld /usr/local/libexec/
drwxrwxr-x  2 root root 4096 Feb 24 01:15 libexec

If you have any scripts that use a hardcoded path to the GnuPG binaries, make sure you update them to use the new GnuPG binaries in /usr/local/bin/.

Update Gpg-Agent Service

One script that has a hardcoded path to the old GnuPG binaries is the systemd service for the gpg-agent. You can override it by running the following command:

$ systemctl --user edit gpg-agent

This will bring up your $EDITOR with the buffer containing a template to override the unit definition. Save it with the following content:

### Editing /home/justin/.config/systemd/user/gpg-agent.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file

[Service]
ExecStart=
ExecStart=/usr/local/bin/gpg-agent --supervised
ExecReload=
ExecReload=/usr/local/bin/gpgconf --reload gpg-agent

### Lines below this comment will be discarded

### /usr/lib/systemd/user/gpg-agent.service
# [Unit]
# Description=GnuPG cryptographic agent and passphrase cache
# Documentation=man:gpg-agent(1)
# Requires=gpg-agent.socket
#
# [Service]
# ExecStart=/usr/bin/gpg-agent --supervised
# ExecReload=/usr/bin/gpgconf --reload gpg-agent

Then restart the service by running the following command:

$ systemctl --user restart gpg-agent

The new version of the agent should now be running, as shown below:

$ systemctl --user status gpg-agent
● gpg-agent.service - GnuPG cryptographic agent and passphrase cache
     Loaded: loaded (/usr/lib/systemd/user/gpg-agent.service; static)
    Drop-In: /home/justin/.config/systemd/user/gpg-agent.service.d
             └─override.conf
     Active: active (running) since Fri 2023-02-24 01:31:18 UTC; 2s ago
TriggeredBy: ● gpg-agent-extra.socket
             ● gpg-agent.socket
             ● gpg-agent-ssh.socket
             ● gpg-agent-browser.socket
       Docs: man:gpg-agent(1)
   Main PID: 463117 (gpg-agent)
      Tasks: 1 (limit: 458)
     Memory: 328.0K
        CPU: 3ms
     CGroup: /user.slice/user-1001.slice/user@1001.service/app.slice/gpg-agent.service
             └─463117 /usr/local/bin/gpg-agent --supervised

Feb 24 01:31:18 colossus systemd[4178]: Started GnuPG cryptographic agent and passphrase cache.
Feb 24 01:31:18 colossus gpg-agent[463117]: gpg-agent[463117]: WARNING: "--supervised" is a deprecated option
Feb 24 01:31:18 colossus gpg-agent[463117]: gpg-agent (GnuPG) 2.4.0 starting in supervised mode.
Feb 24 01:31:18 colossus gpg-agent[463117]: using fd 3 for extra socket (/run/user/1001/gnupg/S.gpg-agent.extra)
Feb 24 01:31:18 colossus gpg-agent[463117]: using fd 4 for std socket (/run/user/1001/gnupg/S.gpg-agent)
Feb 24 01:31:18 colossus gpg-agent[463117]: using fd 5 for ssh socket (/run/user/1001/gnupg/S.gpg-agent.ssh)
Feb 24 01:31:18 colossus gpg-agent[463117]: using fd 6 for browser socket (/run/user/1001/gnupg/S.gpg-agent.browser)
Feb 24 01:31:18 colossus gpg-agent[463117]: listening on: std=4 extra=3 browser=6 ssh=5

One more thing: the new agent is going to expect to have a pinentry binary next to it in the /usr/local/bin/ directory, so link your existing /usr/bin/pinentry program there:

$ sudo ln -s /usr/bin/pinentry /usr/local/bin/pinentry

Test It Out

You should now be able to sign and decrypt content with the aid of the latest version of the gpg-agent:

$ echo test | gpg -se --default-recipient-self | gpg -d
gpg: encrypted with cv25519 key, ID 0x1234567890ABCDEF, created 2023-01-01
      "Justin (key1) <justin@example.com>"
test
gpg: Signature made Fri Feb 24 01:45:13 2023 UTC
gpg:                using EDDSA key 1234567890ABCDEF1234567890ABCDEF12345678
gpg:                issuer "justin@example.com"
gpg: Good signature from "Justin (key1) <justin@example.com>" [ultimate]

And in particular, accessing a smart card with a tool that uses the pcsc-lite daemon (like the YubiKey Manager CLI) should not stop the gpg-agent from being able being to access the card:

$ ykman info
Device type: YubiKey 5 NFC
...
$ echo test | gpg -se --default-recipient-self | gpg -d
gpg: encrypted with cv25519 key, ID 0x1234567890ABCDEF, created 2023-01-01
      "Justin (key1) <justin@example.com>"
test
gpg: Signature made Fri Feb 24 01:46:02 2023 UTC
gpg:                using EDDSA key 1234567890ABCDEF1234567890ABCDEF12345678
gpg:                issuer "justin@example.com"
gpg: Good signature from "Justin (key1) <justin@example.com>" [ultimate]

If you’re still having problems, make sure you have the disable-ccid and pcsc-shared flags set in your ~/.gnupg/scdaemon.conf config file:

$ cat ~/.gnupg/scdaemon.conf
disable-ccid
pcsc-shared

If not, add them, and run gpgconf --kill gpg-agent to kill the old agent process (a new one will be started on demand as necessary).

Uninstall

If you want to uninstall GnuPG 2.4 and revert back to the distribution’s original version of GnuPG, reverse the install process:

Revert Gpg-Agent Service

Revert your changes to the systemd service for the gpg-agent by running the following command:

$ systemctl --user revert gpg-agent
Removed /home/justin/.config/systemd/user/gpg-agent.service.d/override.conf.
Removed /home/justin/.config/systemd/user/gpg-agent.service.d.

Then restart the service with the following command:

$ systemctl --user restart gpg-agent

The old version of the agent should now be running:

$ systemctl --user status gpg-agent
● gpg-agent.service - GnuPG cryptographic agent and passphrase cache
     Loaded: loaded (/usr/lib/systemd/user/gpg-agent.service; static)
     Active: active (running) since Fri 2023-02-24 02:11:18 UTC; 5s ago
TriggeredBy: ● gpg-agent-extra.socket
             ● gpg-agent.socket
             ● gpg-agent-ssh.socket
             ● gpg-agent-browser.socket
       Docs: man:gpg-agent(1)
   Main PID: 506068 (gpg-agent)
      Tasks: 1 (limit: 458)
     Memory: 668.0K
        CPU: 3ms
     CGroup: /user.slice/user-1001.slice/user@1001.service/app.slice/gpg-agent.service
             └─506068 /usr/bin/gpg-agent --supervised

Feb 24 02:11:18 colossus systemd[4178]: Started GnuPG cryptographic agent and passphrase cache.
Feb 24 02:11:18 colossus gpg-agent[506068]: gpg-agent (GnuPG) 2.2.27 starting in supervised mode.
Feb 24 02:11:18 colossus gpg-agent[506068]: using fd 3 for extra socket (/run/user/1001/gnupg/S.gpg-agent.extra)
Feb 24 02:11:18 colossus gpg-agent[506068]: using fd 4 for std socket (/run/user/1001/gnupg/S.gpg-agent)
Feb 24 02:11:18 colossus gpg-agent[506068]: using fd 5 for ssh socket (/run/user/1001/gnupg/S.gpg-agent.ssh)
Feb 24 02:11:18 colossus gpg-agent[506068]: using fd 6 for browser socket (/run/user/1001/gnupg/S.gpg-agent.browser)
Feb 24 02:11:18 colossus gpg-agent[506068]: listening on: std=4 extra=3 browser=6 ssh=5

Uninstall GnuPG

Make sure you also update any other scripts that you had manually edited to use the GnuPG binaries in the /usr/local/bin/ directory, reverting them back to use the GnuPG binaries in the /usr/bin/ directory.

Then navigate back to the GnuPG 2.4 release directory, and run its make uninstall command:

$ cd gnupg-2.4.0
$ sudo make uninstall
Making uninstall in m4
make[1]: Entering directory '/home/justin/gnupg-2.4.0/m4'
...
make[1]: Leaving directory '/home/justin/gnupg-2.4.0'

This should revert you back to using the old GnuPG binaries:

$ which gpg
/usr/bin/gpg
$ gpg --version
gpg (GnuPG) 2.2.27
libgcrypt 1.9.4
Copyright (C) 2021 Free Software Foundation, Inc.
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /home/justin/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

Uninstall GnuPG Libraries

Finally, uninstall any of the shared libraries that you installed during the Install More Prerequisites step.

Uninstall the libksba library by returning to its release directory, and running the following commands:

$ cd ..
$ cd libksba-1.6.3
$ sudo make uninstall
Making uninstall in m4
make[1]: Entering directory '/home/justin/libksba-1.6.3/m4'
...
make[1]: Leaving directory '/home/justin/libksba-1.6.3'
$ sudo ldconfig

Uninstall the libgpg-error library by returning to its release directory, and running the following commands:

$ cd ..
$ cd libgpg-error-1.46
$ sudo make uninstall
Making uninstall in m4
make[1]: Entering directory '/home/justin/libgpg-error-1.46/m4'
...
make[1]: Leaving directory '/home/justin/libgpg-error-1.46'
$ sudo ldconfig