Distributing OpenPGP Keys

As can be seen with the --auto-key-locate configuration parameter of there are various ways to find and import a key.

Key-Servers

Note

In recent years, various problems with with key-servers and their federation model have been discovered. In terms of reliability, abuse-resistance, privacy, and usability, the use of key servers can no longer be recommended.

Notable keys servers include:

While public key servers are still the mostly widely used way to find OpenPGP keys, but other methods come with significant benefits over the old key servers.

DNS CERT

Publishing a key as OpenPGP packet using DNS CERT type 3 (PGP), as specified in RFC 4398.

PKA

Publishing a key as DNS CERT record type 6 (IPGP), as specified in RFC 4398. The record contains the fingerprint and/or the URL of an OpenPGP packet.

The gpg command provides a way to output the required DNS records for a key with the export-pka export option:

$ gpg --export-options export-pka \
    --export-filter keep-uid="uid=~@example.net" \
    --export $GPGKEY

This outputs records in generic format as TYPE37 containing the fingerprint without any URL.

Testing:

$ env GNUPGHOME=$( mktemp --tmpdir --directory ) \
    gpg --verbose --auto-key-locate clear,pka \
        --locate-keys john.doe@example.net

DANE

The RFC 7929 titled “DNS-Based Authentication of Named Entities (DANE) Bindings for OpenPGP” describes a mechanism to associate a user’s OpenPGP public key with their email address, using the OPENPGPKEY DNS RRtype. These records are published in the DNS zone of the user’s email address. If the user loses their private key, the OPENPGPKEY DNS record can simply be updated or removed from the zone.

As with other DANE records like TLSA, the OPENPGPKEY data is supposed to be secured by DNSSEC.

The gpg command provides a way to output the required DNS records for a key with the export-dane export option.

Since this will be published as DNS record, we want to export the smallest possible key. We therefore also add the export-minimal export-option.

Additionally, most users have multiple user-ids (email addresses) in their key. Probably not all of those domains provide write-access to their DNS records (i.e. gmail.com). With the keep-uid export-filter, only the records for the domain we actually are allowed to publish will be shown:

$ gpg --export-options \
            export-minimal,export-dane \
    --export-filter keep-uid="uid=~@example.net" \
    --export $GPGKEY

This outputs records in generic format as TYPE61.

If you want standard OPENPGPKEY format records:

$ export MY_USER=john MY_DOMAIN=example.net
$ echo -e "$( echo -n "$MY_USER" | sha256sum | head --bytes=56 )._openpgpkey.${MY_DOMAIN}. IN OPENPGPKEY (\n $( gpg --export-options export-minimal --export-filter keep-uid="uid=~@${MAIL_DOMAIN}" --export $GPGKEY | hexdump -e '"\t" /1 "%.2x"' -e '/32 "\n"' )\n )"

There is also the online DNS OPENPGPKEY Record Generator who generates standard OPENPGPKEY records.

Testing:

$ env GNUPGHOME=$( mktemp --tmpdir --directory ) \
    gpg --verbose --auto-key-locate clear,dane \
        --locate-keys john.doe@example.net

Web Key Directory (WKD)

OpenPGP Web Key Directory or WKD is an Internet draft standard that allows discovering OpenPGP keys from email addresses.

Whenever a modern OpenPGP application needs a public key which isn’t found in its keyring yet, it will automatically try to fetch it from well-known locations on web-servers in the domain of the users mail address.

Let’s say, you start to write an encrypted mail to John.Doe@example.net and don’t have his public key, as you never wrote to him before. Your mail client will try to download it from web-servers in the example.net domain.

It will first try:

If that fails, it will try:

While iy9q119eutrkn8s1mk4r39qejnbu3n5q is a hash of john.doe. It’s hashed to protect John’s mail address from mail address harvesters and spammers.

Here is how you can create the necessary directories and files for your website:

$ MY_DOMAIN=example.net
$ mkdir -p "/tmp/openpgpkey"
$ gpg --list-options show-only-fpr-mbox --list-keys "$GPGKEY" \
    | grep "$MY_DOMAIN" \
    | /usr/lib/gnupg/gpg-wks-client -C "/tmp/openpgpkey" --install-key

The /tmp/openpgpkey directory now contains a subdirectory named example.net. Inside it you find a policy file and the hu subdirectory. The hu directory (for “hashed user-id”) contains your OpenPGP public key in a file named after the hash of your mail-address:

/tmp/openpgpkey/
└── example.net
    ├── hu
    │   └── iy9q119eutrkn8s1mk4r39qejnbu3n5q
    └── policy

Upload the contents of /tmp/openpgpkey/example.net/ to your web-server, so that it will be reachable as https://example.net/.well-known/openpgpkey/

For example:

# Create necessary directories on the server, if they don't exist yet
ssh $WEB_SERVER mkdir -p /var/www/${MY_DOMAIN}/public_html/.well-known/openpgpkey/hu

# Copy subdirectory and files
scp -r "${_temp_dir}/${MY_DOMAIN}/*" \
    ${WEB_SERVER}:/var/www/${MY_DOMAIN}/public_html/.well-known/openpgpkey/

# Set ownership to make it available to website visitors
ssh $WEB_SERVER chown -R www-data:www-data \
    /var/www/${MY_DOMAIN}/public_html/.well-known

The exact commands and locations need to be adapted to your web-hosting environment.

Testing:

$ env GNUPGHOME=$( mktemp --tmpdir --directory ) \
    gpg --verbose --auto-key-locate clear,wkd \
        --locate-keys john.doe@example.net

There is also an online test page:

Keybase.io

TBD.

QR-Code

openpgp4fpr: is a IANA registered URI scheme used to identify OpenPGP version 4 public keys.

Supporting client applications who encounter an openpgp4fpr: URI, can process the contained information as OpenPGP fingerprint.

By creating and distributing a QR code who’s content starts with the text OPENPGP4FPR: followed by your fingerprint, you can tell other people which OpenPGP key you are using. Make sure all letters are in uppercase.

Print the QR-code on business-cards and letterheads, or add it online to your website and social network profiles.

Other people can then scan the QR-code with their smartphone or webcam, without the need of exchanging, verifying and typing-in long rows of numbers and letters.

This is especially useful, when printed on business-cards, which you hand out personally to people, without any third-parties or online-devices involved. Maybe in combination with other proof of identity like ID-card, drivers-licence or passport.

To create such a QR-code on the command-line:

$ qrencode -o "${HOME}/Pictures/${GPGKEY}.png" -i \
    "OPENPGP4FPR:$( gpg --with-colons --fingerprint "$GPGKEY" \
        | grep -m 1 "^fpr" \
        | egrep -o "[0-9A-F]{40}" \
    )"
$ xdg-open "${HOME}/Pictures/${GPGKEY}.png"

You can create QR-codes online with the QR Code Generator from the ZXing Project and many others.

Publish on Websites

RFC 3156 describes how ASCII-armored OpenPGP keys, alongside encrypted data and signatures are to presented to clients.

ASCI Armored

To publish an ASCII armored PGP public key on a website, first we export the key to a file named with a aexpk (PGP Armored EXtracted Public Key) file-extension:

$ gpg --armor --export "$GPGKEY" >"${GPGKEY}.aexpk"

Upload it to your website and create a link with the MIME-type application/pgp-keys on the page from where visitors can download it as follows:

<html>
...
    Download my (ASCII armored) PGP public key:
    <a href="0x0123456789ABCDEF.aexpk"
        title="Jon Doe's PGP Public Key"
        type="application/pgp-keys">
        0x0123456789ABCDEF
    </a>
...
</html>

Binary

To publish a binary public key file, export it without the --armor option and name it with a bexpk (PGP Binary EXtracted Public Key) file-extension:

$ gpg --export "$GPGKEY" >"${GPGKEY}.bexpk"

Links to binary files should have the application/octet-stream MIME-type:

<html>
...
    Download my (binary) PGP public key:
    <a href="0x0123456789ABCDEF.bexpk"
        title="Jon Doe's PGP Public Key"
        type="application/octet-stream">
        0x0123456789ABCDEF
    </a>
...
</html>

You may need to add these MIME types to your web-server. For Nginx insert the following lines in to the file /etc/nginx/mime.types:

# OpenPGP MIME types (RFC-3156)
application/octet-stream              bex bexpk;
application/octet-stream              pgp;
application/pgp-keys                  aex aexpk;
application/pgp-signature             asc sig;

Testing

$ env GNUPGHOME=$( mktemp --tmpdir --directory ) \
    gpg --verbose --fetch-key https://example.net/0x0123456789ABCDEF.bexpk