GPG Key sharing via WKD (Web Key Directory)

If you are using GPG from time to time, the key exchange might be not easy in all situations. To simplify the process, you can host your own Web Key Directory.

Here you can find the specification document:
https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service

This mini-guide is for new-ish GPG versions (>2.2.12); might not work for older ones.

  • First things first, you will need a key that you will want to share in WKD. Generate one if you don't have it
  • You will need a subdomain
openpgpkey.yourdomain.com
  • Make sure you can upload the files to this domain
  • Locate the WKD hash of your key
gpg --with-wkd-hash --fingerprint you@yourdomain.com
pub   rsa4096 2023-07-29 [SC]
      DEAD BEEF DEAD BEEF DEAD BEEF DEAD BEEF DEAD BEEF
uid           [ultimate] You Lastname <you@yourdomain.com>
              01234567890123456789012345678901@yourdomain.com
sub   rsa4096 2023-07-29 [E]

In this example, the hash is 01234567890123456789012345678901

  • Export your key to a file
gpg --no-armor --export you@yourdomain.com > 01234567890123456789012345678901
  • Upload your key to the server
openpgpkey.yourdomain.com/.well-known/openpgpkey/yourdomain.com/hu/01234567890123456789012345678901

Your URL to the key should match the specifications outlined in the document:

  1. The scheme https://,
  2. the string openpgpkey,
  3. the domain-part,
  4. the string /.well-known/openpgpkey/,
  5. the domain-part in lowercase,
  6. the string /hu/,
  7. the above-constructed 32-octet string
  • Ensure you can access this file over HTTPS in your browser as a test
  • Ensure this file is served with the proper content type
application/octet-stream
  • Test if your key is working, for this sq utility is very helpful
sq wkd get you@yourdomain.com
  • Share instructions with someone you want to send an encrypted message to you (some email clients do this automatically)
gpg  --locate-external-keys you@yourdomain.com
gpg: key DEADBEEFDEADBEEF: public key "You Lastname <you@yourdomain.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1
pub   rsa4096 2023-09-19 [SC]
      DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF
uid           [ unknown] You Lastname <you@yourdomain.com>
sub   rsa4096 2023-09-19 [E]

Here is an example with one of my keys:

gpg --locate-external-keys security@hexide.com
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   8  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 8u
gpg: next trustdb check due at 2025-04-11
gpg: key DCD447A914B69472: public key "Hexide Security <security@hexide.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1
pub   rsa4096 2023-09-19 [SC] [expires: 2027-01-01]
      D5E40EDBF60562C905842C19DCD447A914B69472
uid           [ unknown] Hexide Security <security@hexide.com>
sub   rsa4096 2023-09-19 [E] [expires: 2027-01-01]

Troubleshooting

If, for whatever reason sq utility does not print out your key, try these things:

  • Print out the expected URL of the key with sq utility, and make sure you can access it in a web browser
sq wkd url you@yourdomain.com
  • Make file is served with the correct content type
curl 'https://openpgpkey.yourdomain.com/.well-known/openpgpkey/yourdomain.com/hu/01234567890123456789012345678901?l=you'

Look for < content-type

...
> GET /.well-known/openpgpkey/yourdomain.com/hu/01234567890123456789012345678901?l=you HTTP/2
> Host: openpgpkey.yourdomain.com
> User-Agent: curl/8.2.1
> Accept: */*
> 
< HTTP/2 200 
< date: Tue, 19 Sep 2023 17:53:18 GMT
< content-type: application/octet-stream
< content-length: 2269
< accept-ranges: bytes
...
  • Make sure gpg utility is a higher version than 2.2.12
gpg --version