Static OpenPGP Web Key Directory Setup
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:
- using a static WKD on your own web server or hosted webpage, or
- 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.
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.
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.
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
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: