Publishing PGP Keys With WKD
By hernil
Why?
While having a sort of rough reputation amongst security die-hards PGP is still a good way of increasing security around your presence on the web. At least without tieing yourself to a given platform. Signal is perhaps the gold standard for encrypted communications today, but it is still centralized and can therefore more easily be blocked or restricted.
So yes, encrypting email with PGP is perhaps not the best you can do for secure communcations, but it is a heck of a lot better than sending plain text. And if you sign the message with an established key as well, the recipient can be pretty confident that the message was written by you and not someone else.
A big challenge with PGP keys has been getting your key out there for people to see, use and trust. This is the problem that the so called Web of Trust tries to solve, but it has seen challanges due to hassle and complexity of actually organizing and attenting things like signing parties. Meeting in person to exchange and sign keys is probably “as good as it gets” to establish trust, but it isn’t really helpful when I just want to ship a quick email to someone on the other side of the world.
Services like keys.openpgp.org are ok for publishing the key, but the discoverability is not obvious (which server is the key published to?). And, perhaps more importantly, you have to trust a third party to expose the correct key for your identity. Otherwise someone could pretend to be you. How much of a problem that is in practice is up for debate. It’s probably not too bad, but there is definitely room for something between meeting in person and having a third party service distribute your keys for you.
This is where WKD (Web Key Directory) fits nicely in. It’s a standard way to publish your keys in a discoverable way for anyone looking. It is pretty easy, pretty flexible and decentralized by being tied to your domain.
So yes, that is the caveat - you will need your own domain or be dependant on your (email) provider. Protonmail for example supports WKD for their accounts.
So for me, when sending email with Thunderbird to a Protonmail account I can more or less seamlessly encrypt the communication.
How
The simplest way to do WKD is just a “patch” on the discoverability problem of publishing the key to a key server. You create a CNAME DNS record that looks like this
openpgpkey.example.org. 300 IN CNAME wkd.keys.openpgp.org.
which basically tells anyone looking that the keys for this domain (example.com) are found on the keys.openpgp.org domain. The keyserver needs to support the WKD standard for discoverability to be seamless though. You publish the key(s) you want to that keyserver and everything is fine. With the caveat of having to trust the keyserver to supply your actual key to anyone asking of course.
Hosting your own key
Hosting your own key shouldn’t be much of a problem if you already have a webserver. WKD is basically a convention for how the keys should be placed and exposed in the .well-known directory.
There are however two methods. One requires an additional DNS entry at openpgpkey.example.com, while the other is at the root of your email domain. Serving these files over https is required by the spec. This is to avoid man-in-the-middle attacks.
WKD clients will be looking for keys at the following urls:
Advanced:
https://openpgpkey.example.org/.well-known/openpgpkey/example.org/policy
Direct:
https://example.org/.well-known/openpgpkey/policy
The steps to get up and running are the following:
Export the keys
We assume that you have already created a set of keys. If not have a look at how I manage my keys on Yubikeys for inspiration.
gpg --export devblog@example.com > devblog.asc
Name the keys
The naming convention of the keys is a bit obscure, but it is baked into gpg since GnuPG v2.1.12 so just do
➜ gpg --with-wkd-hash -k devblog@example.com
pub ed25519 2025-01-09 [SC]
F5295541836A684D09D7CBDA4EF3F96C97F4323B
uid [ unknown] Nils Herde <devblog@example.com>
95us44ueecpzcamyfa4cc6a19p1km9ty@example.com # <- this hash right here
sub cv25519 2025-01-09 [E]
sub ed25519 2025-01-09 [A] [expires: 2028-04-13]
sub ed25519 2025-01-09 [S]
mv devblog.asc 95us44ueecpzcamyfa4cc6a19p1km9ty
Organize the keys
The folder structure for exposing keys over WKD looks like this:
.well-known
└── openpgpkey
├── hu
│ └── 95us44ueecpzcamyfa4cc6a19p1km9tys
├── policy
└── example.com -> ../openpgpkey # <- This is part of the "advanced" spec.
Notice the file called “policy”. This is essential to signal that you are hosting keys over WKD but it can simply be an empty file.
Serve the keys
You need to expose the .well-known directory from a webserver. This is how it could look with nginx.
location /.well-known/ {
alias /var/www/.well-known/;
add_header Access-Control-Allow-Origin "*";
default_type application/octet-stream;
}
I run this in a tiny docker container with nginx, behind a Traefik reverse proxy. My docker-compose.yml looks like this. (note that the web network has been created previously and is how traefik talks with the container)
networks:
web:
external: true
services:
yvn:
image: nginxinc/nginx-unprivileged:alpine
container_name: example_com
expose:
- 8080
volumes:
- ./well-known:/var/www/.well-known:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
labels:
- traefik.http.routers.example-com.rule=(Host(`example.com`) || Host(`openpgpkey.example.com`))
- traefik.http.routers.example-com.tls=true
- traefik.http.routers.example-com.tls.certresolver=lets-encrypt
networks:
- web
Test your setup
Both webkeydirectory.com and wkd.dp42.dev/ seem like fine places to test that all is running as it should.
If you want you can send me an email and I’ll reply with an encrypted response if your keys are available to me!