Static OpenPGP Web Key Directory Setup

7 minute read

If you want to setup an OpenPGP Web Key Directory (short: WKD) to enable automatic lookup of your OpenPGP public keys based on your email address, you can choose between two options:

  1. using a static WKD on your own web server or hosted webpage, or
  2. use WKD as a service such as the one provided by keys.openpgp.org.

Here, I will describe my own setup, which uses the first option. For further information and pointers see also the WKD page at the GnuPG wiki.

But what can you do with a WKD? Using a Web Key Directory, you can make sure that someone who wants to send you an encrypted email via [email protected] can securely and automatically retrieve your public key without knowing your key fingerprint or key ID first. Various email client nowadays support that out of the box, for instance Thunderbird provides built-in support for WKD with Thunderbird 78 and later using the “Discover OpenPGP Key” feature.

As a running example, let us assume that you have a User ID User Name <[email protected]> attached to your OpenPGP key, such as:

% gpg -k --with-wkd-hash [email protected]
pub   ed25519/0xEDC14B80E1BCD026 2021-02-06 [SC]
      Key fingerprint = 055E C39D D320 4223 11EE  2261 EDC1 4B80 E1BC D026
uid   [ unknown] User Name <[email protected]>
      [email protected]

Above command uses option --with-wkd-hash to additionally show the WKD hash nmxk159crbcuk3imqiw13gkjmfwd8mqj associated with email address [email protected]. Such a hash string will be used to discover the key on your future static WKD deployed on your website.

For the rest of this article, make sure that you have the sq CLI for sequoia-pgp ready and installed. Check the requirements for prerequisites, if all dependencies are there, all you need to do is to run cargo install sequoia-sq.

Also have a look at the WKS client from gnupg. Although not required for creating the WKD as described here, but most functionality is also available through gpg-wks-client, which helps to generate WKD hashes or check a WKD in a programmatic way.

% export WKSCLIENT=$(gpgconf --list-dirs libexecdir)/gpg-wks-client
% ${WKSCLIENT} --print-wkd-hash [email protected]
nmxk159crbcuk3imqiw13gkjmfwd8mqj [email protected]

Note that gpg-wks-client is usually installed in a directory like /usr/lib/gnupg, hence you first need to retrieve the installation location using gpgconf --list-dirs libexecdir. On a Debian-based distribution, you get gpg-wks-client by installing the gpg-wks-client package. Fedora and friends would have gpg-wks-client already installed as part of the gnupg2 package.

Back to top ↑

Key Discovery using the Web Key Directory Protocol

Before going into details, let’s first check whether the public key for [email protected] can be found using a WKD lookup. You can utilize gpg-wks-client to just do a simple verification, which should fail when there is no WKD for example.org and thus noone can retrieve the public key material for [email protected] yet:

% ${WKSCLIENT} --verbose --check [email protected]
gpg-wks-client: public key for '[email protected]' NOT found via WKD

Alternatively, you may also use gpg to perform that query and verify that WKD is not yet functional:

% gpg --auto-key-locate clear,nodefault,wkd --locate-external-keys [email protected]
gpg: error retrieving '[email protected]' via WKD: No data
gpg: error reading key: No data

As a third alternative, you may als use sq from sequoia-pgp:

% sq wkd get [email protected]
Error: unexpected EOF

Behind the scene the WKD protocol uses HTTPS to perform key lookups, thus every WKD client would issue a special request, such as the following for [email protected].

% sq wkd url [email protected]
https://openpgpkey.example.org/.well-known/openpgpkey/example.org/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj?l=user

As you can see, the WKD lookup is expected to be available on host openpgpkey.example.org for serving the public key(s) for [email protected] using the path /.well-known/openpgpkey/example.org/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj and query l=user.

If the WKD for example.org were already set up, a WKD lookup like the gpg call below will succesfully retrieve the public key material and store them into the local gnupg keyring.

% gpg --auto-key-locate clear,nodefault,wkd --locate-external-keys [email protected]
gpg: key 0xEDC14B80E1BCD026: "User Name <[email protected]>" imported
gpg: Total number processed: 1
gpg:               imported: 1
pub   ed25519/0xEDC14B80E1BCD026 2021-02-06 [SC]
      Key fingerprint = 055E C39D D320 4223 11EE  2261 EDC1 4B80 E1BC D026
uid   [ unknown] User Name <[email protected]>

Similarly, gpg-wks-client would succeed as follows.

% ${WKSCLIENT} --verbose --check [email protected]
gpg-wks-client: public key for '[email protected]' found via WKD
gpg-wks-client: gpg: Total number processed: 1
gpg-wks-client: fingerprint: 055EC39DD320422311EE2261EDC14B80E1BCD026
gpg-wks-client:     user-id: User Name <[email protected]>
gpg-wks-client:     created: 2021-02-06T11:38:47 UTC
gpg-wks-client:   addr-spec: [email protected]

And sq would give us the ASCII-armored public keys on stdout.

% sq wkd get [email protected]
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Key #0
Comment: 055E C39D D320 4223 11EE  2261 EDC1 4B80 E1BC D026
Comment: User Name <[email protected]>

[...]
-----END PGP PUBLIC KEY BLOCK-----

In the next two sections, you will see how to deploy a static Web Key Directory on your website that you can use to serve the public keys for your email addresses using the WKD protocol.

Back to top ↑

Direct method

First let’s setup WKD without changing any DNS records. The direct method is a fallback method for discovering keys through WKD and uses a request for

https://example.org/.well-known/openpgpkey/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj?l=user

to fetch the public key for [email protected]. This way, you can just use your own website for example.org to serve the keys by deploying the keymaterial to /.well-known/openpgpkey/hu/. To make this work, you need to be able to upload some files to the web service hosting https://example.org for securely providing access to your public key(s).

Generating the directory structure and key file for [email protected] utilizing WKD’s direct method requires the following call:

gpg --export --yes --export-options export-clean --armor 0xEDC14B80E1BCD026 \
  | sq wkd generate -d . example.org

Now you can list the content of your WKD stored in your local working directory with

% sq keyring list .well-known/openpgpkey/hu/*
0. 055EC39DD320422311EE2261EDC14B80E1BCD026 [email protected]

Now everything is generated and you can upload the content of the directory ./.well-known/openpgpkey to the root directory of your website in order to serve the files https://example.org/.well-known/openpgpkey/policy (the so-called policy flags file, see Policy Flags for details) and https://example.org/.well-known/openpgpkey/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj.

You can use curl to check that everything is working as planned by retrieving the keys and print some metadata.

% curl -s -O \
    -w 'File: %{filename_effective}\nContent: %{content_type}\nSize: %{size_download}\nResponse: %{http_code}\n' \
    'https://example.org/.well-known/openpgpkey/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj?l=user'
File: nmxk159crbcuk3imqiw13gkjmfwd8mqj?l=user
Content: application/octet-stream
Size: 54068
Response: 200

Additionally, the methods for retrieving keys through WKD discovery now will start working as expected, see the discovery section above.

Back to top ↑

Advanced method

The advanced method is essentially the same as the direct method, except that you need to setup a webserver for serving https://openpgpkey.example.org, which usually requires you to create a DNS entry for your domain example.org.

I do not want to go into details for setting up your DNS, instead I will just show you the differences between the direct and the advanced method.

First, the advanced method is the preferred method for WKD clients, thus the first thing every WKD client will do is to lookup openpgpkey.example.org to try to reach the webserver for the advanced key discovery method. If this DNS request fails, they will lookup example.org and then use this host for the direct method. Thus you save one DNS request if you setup openpgpkey.example.org compared to using the direct method alone to serve our keys.

If you have a webserver running to serve https://openpgpkey.example.org, all you need to do next is to upload the static files for the Web Key Directory. In order to discover [email protected], a WKD client makes a request to

https://openpgpkey.example.org/.well-known/openpgpkey/example.org/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj?l=user

So another difference between the direct and the advanced method is the path in the request, which now contains the domain name example.org between openpgpkey and hu. Everything else remains constant.

Now, you can build the directory structure on your local machine by calling

gpg --export --yes --export-options export-clean --armor 0xEDC14B80E1BCD026 \
  | sq wkd generate . example.org

Note that sq wkd generate now is invoked without -d and thus defaults to building an advanced WKD structure.

Now everything is built and ready to deploy. Just upload the content of directory ./.well-known/openpgpkey to the root of your webserver for https://openpgpkey.example.org in order to serve the files https://openpgpkey.example.org/.well-known/openpgpkey/example.org/policy and https://openpgpkey.example.org/.well-known/openpgpkey/example.org/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj.

Again, you can use curl to verify that everything works as expected:

% curl -s -O \
    -w 'File: %{filename_effective}\nContent: %{content_type}\nSize: %{size_download}\nResponse: %{http_code}\n' \
    'https://openpgpkey.example.org/.well-known/openpgpkey/example.org/hu/nmxk159crbcuk3imqiw13gkjmfwd8mqj?l=user'
File: nmxk159crbcuk3imqiw13gkjmfwd8mqj?l=user
Content: application/octet-stream
Size: 54068
Response: 200

Back to top ↑

gpg-export-wkd.sh

I wrote a small shell script to make the static WKD setup process more comfortable. In order to build a direct WKD, all you need to do is to run

% gpg-export-wkd.sh -d -e [email protected] -o ~/web
Creating direct WKD for [email protected] in /home/user/web/.well-known/openpgpkey

Similarly, the files for the advanced method can be created by calling

% gpg-export-wkd.sh -e [email protected] -o ~/web
Creating advanced WKD for [email protected] in /home/user/web/.well-known/openpgpkey

You can download gpg-export-wkd.sh or just copy it here:

Back to top ↑

Categories:

Updated: