Skip to content

Instantly share code, notes, and snippets.

@kafene
Last active December 6, 2024 07:12
Show Gist options
  • Save kafene/0a6e259996862d35845784e6e5dbfc79 to your computer and use it in GitHub Desktop.
Save kafene/0a6e259996862d35845784e6e5dbfc79 to your computer and use it in GitHub Desktop.
Setting up WKD for self-hosted automatic key discovery

I just got this working so I figured I'd share what I found, since there's hardly any information about this anywhere online except an RFC, the GPG mailing list and one tutorial from the GnuPG blog.

You can use automatic key discovery with WKD (Web key directory) to make it easy for users to import your key, in GPG since version 2.1.12. Since this feature is fairly new, it isn't yet available in the current LTS release of Ubuntu (16.04; xenial), however it is available in Debian stable (stretch).

I couldn't add a DNS CERT or DANE / OPENPGPKEY record through my email service (which also hosts my nameservers). I tried making the PKA record - a foo._pka.example.com TXT record but GPG doesn't seem to recognize it and fails; I'm still investigating why.

So the last option for self-hosted auto-discovery was WKD.

First thing I had to do was add an email address to my key. My primary UID is just my name so the key represents my identity rather than any particular email address. This was easy enough:

$ gpg --edit-key 0xDEADBEEFCAFEBABE
gpg> adduid
# follow the prompts
gpg> save

I used this to configure a sub-identity using my domain name as the "real name" and an email address (foo@example.com). I suppose most here will already have an email address associated with their uid, or else be familiar with the process of editing keys.

Then I created a directory on my server:

$ ssh example.com
> mkdir -p /var/www/.well-known/openpgpkey/hu

The file you'll put inside this directory needs to be named the same as the WKD hash for your key, to get that run:

gpg --with-wkd-hash --fingerprint foo@example.com

Below each uid line with an email address, you should see 32 random looking characters @yourdomain.tld, for example:

pub   2048R/0xDEADBEEFCAFEBABE 2015-01-25 [C] [expires: 2020-01-25]
      Key fingerprint = ....
uid                   [ultimate] Your Name <foo@example.com>
                      sc8wrug2g3mz8m8jz4tjrlgweilkgcba@example.com

So the WKD hash in this example is sc8wrug2g3mz8m8jz4tjrlgweilkgcba. Let's create the file that we'll be uploading:

gpg --no-armor --export foo@example.com > sc8wrug2g3mz8m8jz4tjrlgweilkgcba

Copy that file into .well-known/openpgpkey/hu directory on your web server. If you have SSH access to your server configured you can use scp:

scp ./sc8wrug2g3mz8m8jz4tjrlgweilkgcba example.com:/var/www/.well-known/openpgpkey/hu/

I found that you do not need to enable directory listings for this well-known directory. To specify the correct content type with Apache, you can create a .htaccess file inside the .well-known/openpgpkey/hu directory with the following content:

<IfModule mod_mime.c>
    ForceType application/pgp-key
</IfModule>

That will force all files within the directory to be served as application/pgp-key.

That's all you need to do.

You can test that it's working correctly:

gpg --no-default-keyring --keyring /tmp/gpg-$$ --auto-key-locate clear,wkd --locate-keys foo@example.com

Example output:

gpg: key DEADBEEFCAFEBABE: public key "Your Name" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: automatically retrieved 'foo@example.com' via WKD
...

You can instruct users who wish to import your key to run the command:

gpg --auto-key-locate clear,wkd --locate-keys foo@example.com

Or, to configure GPG to locate keys using wkd by placing this line in their gpg.conf:

auto-key-locate keyserver,dane,cert,pka,wkd,ldap,hkp://keys.gnupg.net

Note - that's just an example, only the wkd option is relevant for this, but the other options are handy too.

@wiktor-k
Copy link

wiktor-k commented Jul 4, 2018

Hi @kafene, this is an excellent guide, minor points since the spec has changed:

  1. MIME type of keys is no longer necessary (actually spec suggests just application/octet-stream),
  2. --auto-key-locate by default uses local,wkd so you can drop that part,
  3. clear,wkd will still try local, it's a weird behavior of GPG if you want WKD first try clear,wkd,local or clear,nodefault,wkd
  4. the long list of auto-key-locate... I would personally just stick to the default (local,wkd) :)

I'm glad that with WKD and sensible defaults of gpg one can just ask users to run one short command gpg --locate-keys foo@example.com and it works! (Actually EnigMail, GpgOL do that automatically for new e-mail addresses).

@erAck
Copy link

erAck commented Apr 12, 2019

Nice guide indeed, just needs some refreshing. Please note that meanwhile a .well-known/openpgpkey/policy file must exist as well, which clients can use to detect WKD support. It can (and in this case for just publishing own keys should) be empty (0 bytes). See draft, Policy Flags (version 07).

And yes, the Content-Type in the .well-known/openpgpkey/hu/.htaccess file should be application/octet-stream

@sanmai
Copy link

sanmai commented Apr 29, 2019

JFYI they deprecated foo._pka.example.com in favor of a different record format. Use this command to see it:

gpg --print-pka-records -k <userid>

TBH this is far from being as useful as before as this requires you to run own BIND instance.

@fogti
Copy link

fogti commented Apr 9, 2020

@sanmai

gpg --print-pka-records -k <userid>

is deprecated and replaced with:

gpg --export-options export-pka --export <userid>

@markgraf
Copy link

Thanks for this gist!
If you need to export multiple keys, try this:

#!/usr/bin/env -S bash - 

key_pattern=yourdomain.here

for item in $(gpg --list-secret-keys --with-colons ${key_pattern:- } \
              | awk -F: '($1 == "sec" && $2 == "u") { print $5}') ; do
	wkd_hash=$(gpg --with-wkd-hash --fingerprint "$item" \
             | grep -A 1 '^uid' \
             | tail -n 1 \
             | tr -d '[[:space:]]')
	gpg --export --no-armor "$item" > "${wkd_hash%%@*}"
	printf "%s\n" "Exported key $item to file $( realpath "${wkd_hash%%@*}")"
done

@trinitronx
Copy link

trinitronx commented Oct 11, 2023

As others have noted the Content-Type should be application/octet-stream, it appears that gpg version 2.2.41 at least does not seem to care.

I tested:

  • application/pgp-key (actually this is wrong... the "official" type for .key files appears to be: application/pgp-keys)

    $ grep -rin 'application/pgp-key'  /usr/share/mime/
    /usr/share/mime/packages/freedesktop.org.xml
    1322:  <mime-type type="application/pgp-keys">
    
    /usr/share/mime/application/pgp-keys.xml
    2:<mime-type xmlns="http://www.freedesktop.org/standards/shared-mime-info" type="application/pgp-keys">
    
    $ grep application/pgp-key /etc/mime.types
    application/pgp-keys
    
    $ xdg-mime query filetype /tmp/test.key
    application/pgp-keys
    
  • application/pgp-keys

  • application/octet-stream: This is correct for WKD servers, according to the specification

GPG happily imported the key via WKD when each Content-Type setting was used in my tests.

Makes sense that the client is more lenient to accept them given:

Source: draft-koch-openpgp-webkey-service-16: Section 3.1-18

The server SHOULD use "application/octet-stream" as the Content-Type for the data but clients SHOULD also accept any other Content-Type.

@wiktor-k
Copy link

@trinitronx AFAIK application/pgp-keys is used for ASCII-armored keys only and WKD mandates binary encoding (but as of recently GnuPG will also accept armored files). I guess there's just no MIME format for OpenPGP certificates (which I think is a problem of the spec).

@titanism
Copy link

Thank you @kafene for sharing this gist. We've included this in our guide for @forwardemail at https://forwardemail.net/en/faq#do-you-support-openpgpmime-end-to-end-encryption-e2ee-and-web-key-directory-wkd for OpenPGP/WKD support.

@pravi
Copy link

pravi commented Jun 1, 2024

I'm trying to implement WKD support in mailvelope keyserver - this is the work in progress branch mailvelope/keyserver#146 The ascii armored keys are available in the expected url but gpg and thunderbird don't seem to accept the keys.
I wonder if https://github.com/mailvelope/keyserver/blob/master/src/route/hkp.js#L52 is the problem. May be this should not be an attachment?

@wiktor-k
Copy link

wiktor-k commented Jun 1, 2024

The ascii armored keys are available in the expected url but gpg and thunderbird don't seem to accept the keys.

The keys must be binary, not armored:

The HTTP GET method MUST return the binary representation of the OpenPGP key for the given mail address.

Source: https://www.ietf.org/archive/id/draft-koch-openpgp-webkey-service-17.html#section-3.1

@pravi
Copy link

pravi commented Jun 1, 2024

Thanks! Though even after dearmoring, mailvelope/keyserver#121 (comment) gpg still fails to find the key.

@pravi
Copy link

pravi commented Jun 2, 2024

This is now working fine in mailvelope keyserver mailvelope/keyserver#147 ascii armored keys are fine for both gpg and thunderbird, the problem was wkd url now includes a query parameter and caddy rewrite rule was not expecting that.


❯ gpg-wks-client --print-wkd-url praveen@puri.sm 
https://openpgpkey.puri.sm/.well-known/openpgpkey/puri.sm/hu/g4rf118d16sbj3k77d5txywpm879pszg?l=praveen

@jult
Copy link

jult commented Oct 18, 2024

Truely stunning how needlessly complex the crypto crowd has made this PGP key-sharing. Even the RFC isn't clear on why or what is required for WKD or not. I have tried for hours to get the WKD working on a server, it still doesn't work. These are PUBLIC keys for crying out loud. Who cares how they're retrieved? This is why people give up on using PGP for messaging/email. They all seem to be losing sight of what this is actually used for, WHY it is used. You'd achieve your targeted intention faster using a tool like syncthing and then exchange a QR code for it sharing a folder between whoever you need to. Its encryption is harder to break than openpgp's old 4096b RSA, even. And honestly, me and some remote colleagues had a fully secure linked syncthing dir running for mobile, laptop and PC within minutes. Just sayin'.

@trinitronx
Copy link

trinitronx commented Oct 20, 2024

Truely stunning how needlessly complex the crypto crowd has made this PGP key-sharing.

Indeed! I had similar thoughts when setting up WKD (and GPG + Yubikeys in the past). It's been an age-old problem though, where UI/UX for crypto tools ends up too complex such that mere mortals have difficulty using such tools. It takes some time to read through all the disparate tools' man pages and learn how everything works. There are some other newer tools in the ecosystem that have been trying to solve for this...

Keybase made some great strides towards UX & usability while still enabling power users, up until Zoom Inc. acquired them and pretty much stalled the project. 1

Then, there's Keyoxide which while a bit more technical and still complex, is trying to solve for the "decentralized proofs" piece of the problem that Keybase was solving for by Merkle Tree chains. A bit more complex to figure out, but at least they have good documentation about creating signed proofs & GPG key ownership claims and either using "Ariadne Signature Proof Exchange" (ASPE) server/clients to distribute/gather them, and/or adding them to GPG keys. Keyoxide also has some good docs about setting up WKD, which you may find helpful.

Footnotes

  1. It's still unclear what their plans are for Keybase, and regularly people ask if Keybase is dead. Although, it does appear that some development pulse has picked up again...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment