Publishing A Public Key via WKD: Web Key Directory

This document describes how to setup GnuPG Web Key Directory for an OpenPGP key.

An OpenPGP Web Key Directory is a method for users to discover the public key of a new contact. The user requests the public key from the contacts organization maintains. This differs from a Key Server where a the user looks up a key on a 3rd party server, the server provides all keys that match requested address and the user must determine which key to use. This practice bears the problem that the key-servers are not able to give a positive confirmation that a key actually belongs to the mail addresses given in the key. Further, there are often several keys matching a mail address and thus one needs to pick a key on good luck.

GnuPG has a new key discovery scheme - Web Key Directory. Compared to previous schemes that relied on DNS, WKD can be easily deployed on any HTTPS server.

Notice! WKD lookup is implemented in GnuPG since v2.1.12. It is enabled by default since 2.1.23.

Building a Web Key Directory Service

Web Key Directory is simply a lookup scheme that relies on HTTPS and correctly placed files on a web server. No other software is required to run on the web server.

There are two methods of key discovery described in network working group specification section 3.1 (Key Discovery), the basic method and the advanced method.

These two methods are fundamentally the same.

The Basic method uses the domain address https://example.com while the Advanced method uses domain address like https://openpgpkey.example.com.

Method 1: Basic WKD Service

The basic method stories the public keys under the main domain name (ie. example.com), opposed to the advanced method that stores the keys under unique domain name (openpgpkey.example.com). All requests must be made via a TLS channel (https://).

Setting up the File System

Once complete the key/file must be accessible via a special URL constructed by appending https://, user domain, /.well-known/openpgpkey/hu/ and a hash value.

For the key I will be using in this how-to the full URL should be: https://mattrude.com/.well-known/openpgpkey/hu/d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht

So you must create the directory .well-known/openpgpkey/hu/ inside the root of your html website.

For example, if you use the default Ubuntu config, you may simply run the following command.

$ mkdir -p /var/www/html/.well-known/openpgpkey/hu

After you have created the WKD directory, you need to create a policy file. This file tells clients how your WKD service works. Since we are creating a default setup, the file should be empty, so you can may just run.

$ touch /var/www/html/.well-known/openpgpkey/policy

Setting up the Web Server

On Nginx

    location ^~ /.well-known/openpgpkey {
        default_type        "text/plain";
        add_header          'Access-Control-Allow-Origin' '*' always;
    }

On Apache

    <Directory "/.well-known/openpgpkey">
        $gt;IfModule mod_headers.c>
            Header set Access-Control-Allow-Origin "*"
        $gt;/IfModule>
    </Directory>

On Lighttpd

    setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )

Finding the local-part hash

After you have created the needed directories, you next need to find the hash of the UID you are going to use. The simplest way of doing that is via the --with-wkd option.

 $ gpg --list-keys --with-wkd 0x94c32ac158aea35c
pub   ed25519 2019-03-05 [SC] [expires: 2024-03-03]
      1B9910529DF4FE1FE3C6B03794C32AC158AEA35C
uid           [ultimate] Matt Rude <matt@mattrude.com>
              d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht@mattrude.com
sub   cv25519 2019-03-05 [E] [expires: 2024-03-03]

Creating the WKD file

Now that you have UID hash, you are ready to go.

All you need to do is export your public key binary (not ASCII armored) file and place it as a correctly named file on your webserver.

So assuming that the root of your webserver is at /var/www/html/, you will run the following command.

$ gpg --export 0x94c32ac158aea35c > /var/www/html/.well-known/openpgpkey/hu/d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht

For that key the full URL is: https://mattrude.com/.well-known/openpgpkey/hu/d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht

Method 2: Advanced WKD Service

The Advanced method is basically the same as the basic method, but has a different URL and URI structure.

Setting up the File System

Once complete the key/file must be accessible via a special URL constructed by appending https://openpgpkey.<example.com>/.well-known/openpgpkey/<example.com>/hu/ and a hash value.

For the key I will be using in this how-to the full URL should be: https://openpgpkey.mattrude.com/.well-known/openpgpkey/mattrude.com/hu/d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht

So you must create the directory .well-known/openpgpkey/mattrude.com/hu/ inside the root of your html website.

For example, if you use the default Ubuntu config, you may simply run the following command.

$ mkdir -p /var/www/openpgpkey.mattrude.com/.well-known/openpgpkey/mattrude.com/hu

After you have created the WKD directory, you need to create a policy file. This file tells clients how your WKD service works. Since we are creating a default setup, the file should be empty, so you can may just run.

$ touch /var/www/openpgpkey.mattrude.com/.well-known/openpgpkey/mattrude.com/policy

Setting up the Web Server

On Nginx

    location ^~ /.well-known/openpgpkey {
        default_type        "text/plain";
        add_header          'Access-Control-Allow-Origin' '*' always;
    }

On Apache

    <Directory "/.well-known/openpgpkey">
        <IfModule mod_headers.c>
            Header set Access-Control-Allow-Origin "*"
        </IfModule>
    </Directory>

On Lighttpd

    setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )

Finding the local-part hash

After you have created the needed directories, you next need to find the hash of the UID you are going to use. The simplest way of doing that is via the --with-wkd option.

 $ gpg --list-keys --with-wkd 0x94c32ac158aea35c
pub   ed25519 2019-03-05 [SC] [expires: 2024-03-03]
      1B9910529DF4FE1FE3C6B03794C32AC158AEA35C
uid           [ultimate] Matt Rude <matt@mattrude.com>
              d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht@mattrude.com
sub   cv25519 2019-03-05 [E] [expires: 2024-03-03]

Creating the WKD file

Now that you have UID hash, you are ready to go.

All you need to do is export your public key binary (not ASCII armored) file and place it as a correctly named file on your webserver.

So assuming that the root of your openpgpkey webserver is at /var/www/mattrude.com/, you will run the following command.

$ gpg --export 0x94c32ac158aea35c > /var/www/openpgpkey.mattrude.com/.well-known/openpgpkey/mattrude.com/hu/d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht

For that key the full URL is: https://openpgpkey.mattrude.com/.well-known/openpgpkey/mattrude.com/hu/d6tq6t4iirtg3qpyw1nyzsr5nsfcqrht

Method 3: Building a Group of Files

Using the generate-openpgpkey-hu script, you can build your WKD from a GnuPG keyring you already have populated with keys.

First you must download the generate-openpgpkey-hu script.

curl -Las https://hg.intevation.de/gnupg/wkd-tools/raw-file/default/generate-openpgpkey-hu -o generate-openpgpkey-hu
chmod 755 generate-openpgpkey-hu

Once the script is downloaded and the permissions are set correctly, you are ready to start.

Testing key discovery

GnuPG can be instructed to force discovery of the key via WKD even if it is locally present:

$ gpg -v --auto-key-locate clear,wkd,nodefault --locate-key matt@mattrude.com
gpg: using pgp trust model
gpg: pub  ed25519/94C32AC158AEA35C 2019-03-05  Matt Rude <matt@mattrude.com>
gpg: key 94C32AC158AEA35C: public key "Matt Rude <matt@mattrude.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: auto-key-locate found fingerprint 1B9910529DF4FE1FE3C6B03794C32AC158AEA35C
gpg: automatically retrieved 'matt@mattrude.com' via WKD
pub   ed25519 2019-03-05 [SC] [expires: 2024-03-03]
      1B9910529DF4FE1FE3C6B03794C32AC158AEA35C
uid           [ unknown] Matt Rude <matt@mattrude.com>
sub   cv25519 2019-03-05 [E] [expires: 2024-03-03]

If the key cannot be found via WKD or if it’s in a wrong format (e.g. ASCII armored instead of binary) an error will be produced:

$ gpg --auto-key-locate clear,wkd,nodefault --locate-key matt@mattrude.com
gpg: error retrieving 'matt@mattrude.com' via WKD: No data

Importing a key via WKD

You may run the following command to import your key into your key ring. Just change matt@mattrude.com to the email address you wish to import.

As wkd is the default key discovery method there is no need to use the --auto-key-locate option.

$ echo "Test message" | gpg -ear matt@mattrude.com
gpg: key DD23BF73: public key "Matt Rude <matt@mattrude.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)
gpg: DD23BF73: There is no assurance this key belongs to the named user

pub  4096R/DD23BF73 2014-06-21 Matt Rude <matt@mattrude.com>
 Primary key fingerprint: AE73 8427 2B91 AD63 5902  320B 2714 3AFF DD23 BF73

It is NOT certain that the key belongs to the person named
in the user ID.  If you *really* know what you are doing,
you may answer the next question with yes.

Use this key anyway? (y/N) y

Web Key Service (WKS)

The Web Key Service (WKS) is a method for end users to send their public key via email to the WKD server.

The WKS stores a file the named submission-address inside the WKD folder structure. A users email client then checks for this file, downloads it, and should find an email address. The email client then check the WKD site for the public key of the submission address. Assuming it finds a public key, it downloads the public key, then sends the users public key to the submission address via an encrypted email.

Once the WKS receives the message, it stores the public key in the pending folder and sends an encrypted email back to the users email asking for them to confirm the request.

Once the user confirms the request, an email is sent back to the WKS service that process the confirmation and moves the public key from the pending folder to the hu folder. Once the public key is in the hu folder, other users may start downloading it via WKD.

Other WKD Resources

Modified: 09-Aug-2019