vil posts words

  1. Adding gnupg Web Key Directory support to my site

    Web Key Directory (WKD) is a distributed protocol for automatically retrieving PGP keys for a given email address.

    Why support this? Frankly, I don't use PGP for anything except as an option for logging into sites that use IndieLogin, via the markup on my homepage. One of the reasons for this is that PGP is largely incomprehensible. However, WKD seems to be a fairly useful step in the direction of making it more usable. Since the maintenance overhead of WKD for a single-user site is basically zero, I decided I might as well set it up.

    Setting it up

    WKD lives in the .well-known/ namespace, and can be implemented as a flat file structure. There is a more complex mechanism called Web Key Service which can be used for more dynamic implementations, but this is overkill for a personal site.

    Generating the WKD directory structure can be done with the gpg-wks-server command in recent versions of gnupg. The wiki page claims you should use gpg-wks-client, but my installation doesn't have this tool and the server takes the same options. In my case, generating my directory looked like this:

    >> mkdir openpgpkey
    >> gpg --list-options show-only-fpr-mbox -k vil@vil.lv | gpg-wks-server -v --install-key -C openpgpkey
    gpg-wks-server: gpg: Total number processed: 1
    gpg-wks-server: using key with user id 'vilhalmer <vil@vil.lv>'
    gpg-wks-server: gpg: Total number processed: 1
    gpg-wks-server: directory 'openpgpkey/vil.lv' created
    gpg-wks-server: directory 'openpgpkey/vil.lv/hu' created
    gpg-wks-server: policy file 'openpgpkey/vil.lv/policy' created
    gpg-wks-server: key 7CFAF4A3748190EB6EAC778005FA2E1C6481BDF3 published for 'vil@vil.lv'

    You'll notice that the directory structure includes the domain. In the case of a personal site, you actually need to remove this layer from the path. From the wiki:

    The hu directory has to be published on your server as https://openpgpkey.example.com/.well-known/openpgpkey/example.com/hu/ (or https://example.com/.well-known/openpgpkey/hu/ iff openpgpkey.example.com is not resolvable via DNS).
    In our case, we didn't add an openpgpkey subdomain, so we need to follow the parenthetical rule.
    >> cd openpgpkey
    >> mv vil.lv/* .
    >> rmdir vil.lv

    Now the openpgpkey directory needs to be published to .well-known/ at the root of your site. This is left as an exercise to the reader. :)

    I had to add the execute bit to the hu subdirectory in order for nginx to be willing to serve the file within. I have other rules in place to prevent directory listings as the wiki specifies you should. This recommendation probably doesn't matter so much on a single-user site anyway, but is instead designed to prevent user enumeration on a shared instance.

    Finally, I verified that my key was fetchable. To be explicit you can add --auto-key-locate wkd, but this has been the preferred mechanism for a while.

    >> gpg --locate-external-keys vil@vil.lv
    gpg: key 05FA2E1C6481BDF3: "vilhalmer <vil@vil.lv>" not changed
    gpg: Total number processed: 1
    gpg:              unchanged: 1
    pub   rsa4096 2016-04-05 [SC]
          7CFAF4A3748190EB6EAC778005FA2E1C6481BDF3
    uid           [ultimate] vilhalmer 
    sub   rsa2048 2016-04-05 [E] [expires: 2024-04-03]
    sub   rsa2048 2016-04-05 [SA] [expires: 2024-04-03]
    Tada!

    Troubleshooting

    If it doesn't work, you'll see something like this instead:

    >> gpg --locate-external-keys vil@vil.lv
    gpg: error retrieving 'vil@vil.lv' via WKD: No data
    gpg: error reading key: No data

    If things aren't in quite the right place on the first try and gpg is unable to find the .well-known/ path when you query for your key, it will cache the belief that the domain does not support WKD. This makes it difficult to test again after you adjust things. You can tell this is happening by running gpg verbosely and looking for the following line:

    gpg: Note: WKD uses a cached result
    Restarting your dirmngr process will clear this internal cache and allow you to query again.