Part 5 of Global Directory

This is a highly technical application, namely storing your OpenPGP public key in an LDAP server under your own domain. This enables others to establish your identity, to verify signed email and other messages, and to send you encrypted information. If your interest lies with storage of phonebook-style information, you should probably defer reading this until you have that working and are ready for more. Powerful technology ahead!

Distributed OpenPGP Directory

The classical model of OpenPGP has always been that "central" nodes host a list of most (and hopefully all) keys in use with OpenPGP. This is a weakness that is not in itself a requirement of the system, it is merely a practicality that makes it easier to find the public keys used by others. A problem that remains is that the origin of these keys cannot be established when retrieving them this way.

There are a few alternatives for storing OpenPGP information in a more distributed fashion -- notably, under the domain name that also hosts the email address mentioned in the public key certificates. This is possible in a number of ways:

  • One could publish the key on the website, and inherit the reliability of the website -- which is limited for anything but a TLS-protected website. You would still need a way to announce where on your website the OpenPGP key is located. Without that, automated tools could not simplify the use of your key material for your remote communication peers.
  • One could publish the key in DNS, using the special CERT record. Alternatively, a reference to a website could be made, possibly mentioning the key fingerprint. Assuming that DNSSEC is used, this could be a secure reference. The problem with mentioning user information in DNS is that everything is public, making it just as beneficial to abuse as it is to proper use.
  • As with other forms of contact information, LDAP is the better option. Standard formats have been defined for the use of OpenPGP with LDAP, including a standard reference to it in DNS. The global directory for OpenPGP is a reality, ready to be plucked.

Most importantly in all this is tool support. The most-used tool is GnuPG, and it has facilities to automatically download OpenPGP key information from LDAP, when provided with nothing more than an email address. There is no reason why OpenPGP and other contact information could not be stored in the same directory, but it is not required because the DNS reference to your domain's OpenPGP key service is independent from the normal LDAP setup.

The source of all wisdom in this case is a rare posting on the use of OpenLDAP under GnuPG:

OpenPGP Online Key Management

Note the different structures of X.509 certificates and PGP key packages; where X.509 presents a closed structure with one appointed parent node, the PGP structure is much more flexible and could implement such a schema among others.

PGP key packets are based on a public key and independently attached certificates, which are digitally signed to bind the public key to an identity assertion. A PGP key packet can be taken apart into the following components:

  • one public key
    • one or more signatures directly on the public key
  • one or more identity assertion, usually "Some Name <>"
    • one or more signatures confirming that the public key is related to the identity assertion
  • one or more encryption subkeys
    • one or more signatures confirming that the public key is related to the encryption subkey

Interestingly, this format supports a few very useful applications of OpenPGP in modern online conduct:

  • Keys can be split into any composition containing one public key and at least one identity assertion with at least a self-signature, followed by zero of more encryption subkeys with at least a self-signature. This can be achieved without cryptographic work, so it is easy to use one key in many places without revealing the other identities. Now imagine creating pseudonyms on the fly; all that is required to include those in the secure infrastructure is to self-sign the pseudonym and the potential encryption subkey with the private key.
  • Keys can be looked up in LDAP based on their value, making only mild assumptions about the interpretation of the OpenPGP user identity field; since the retrieved location must still confirm the key, there is no real security danger.
  • Keys can have their fingerprint stored in DNS, and protected through DNSSEC. The minimal format would be named as a key identity or fingerprint, valued with a CERT record that holds at least a fingerprint. Note that the name of the entry serves as a hint, whereas the status of the CERT record contents are formal. Also note that the security level that may be derived from DNSSEC is not absolute, but that it is far better than no security at all.
  • Note the lookup of key material in LDAP is better than doing the same in DNS; first, because LDAP permits access control which DNS does not, thus better protecting the usernames under a domain from harvesting; second, because LDAP is founded on TCP which eliminates the possibility of the remote party to falsify an IP address, which is not always possible with UDP-based DNS.
  • The use of OpenPGP keys in TLS exchanges is defined in RFC 6091. This is particularly interesting because OpenPGP is highly suited for peer-to-peer exchanges, especially when combined with pseudonyms as indicated in the previous points. There is a possibility of serving OpenPGP and/or X.509, dependent on client preference.

Server Installation

Install the keyserver schema, then create a key server descriptive node and mention it in DNS. The keyserver schema is available at

Install the schema

Install the keyserver schema /etc/ldap/schema/.

Now we need to construct an LDIF to insert it dynamically into OpenLDAP 2.4.

Construct a virtual configfile virtinclude.conf including all the above:

include /etc/ldap/schema/pgp-keyserver.schema

On this, run slapcat to convert it to an LDIF file:

mkdir -p schema-ldif
rm -rf schema-ldif/*
slapcat -f virtinclude.conf -F schema-ldif -n0 -s 'cn=pgp-keyserver,cn=schema,cn=config'

Now fetch the generated file and remove the rest:

cp schema-ldif/cn\=config/cn\=schema/cn\=\{0\}pgp-keyserver.ldif pgp-keyserver-schema.ldif
rm -rf schema-ldif

Now edit pgp-keyserver-schema.ldif and make the following modifications:

  • In the first line, remove the {0} and let OpenLDAP automtically assign the next index
  • Attach to the DN (if needed) the RDNs cn=schema,cn=config
  • Remove the {0} in the line cn={0}pgp-keyserver further down
  • Remove the following attributes:
    • structuralObjectClass
    • entryUUID
    • creatorsName
    • entryCSN
    • modifiersName
    • modifyTimestamp

Finally, add the schema to OpenLDAP with:

bash# ldapadd -f pgp-keyserver-schema.ldif
adding new entry "cn=pgp-keyserver,cn=schema,cn=config"

Create a keyserver description object

In the root of the keyserver, add a descriptive object, in this case under dc=orvelte,dc=nep:

dn: cn=pgpServerInfo,dc=orvelte,dc=nep
cn: pgpServerInfo
objectClass: pgpServerInfo
pgpBaseKeySpaceDN: dc=orvelte,dc=nep

Mention the keyserver in DNS

Create a specially named SRV record referring to the LDAP server:

_pgpkey-ldap._tcp.orvelte.nep.  3600    IN      SRV     10 10 389 ldap.orvelte.nep.

The server pointed at should of course exist, and be forwarded by NAT routers and have a hole opened in IPv6 firewalls.

Note that the SRV record name differs from the ldap.tcp name for normal LDAP service; as a result PGP key service may be located on another LDAP server than further contact information, if desired, but there is no technical need to do this.

Fetching information

Assuming that no keyserver information has been setup, the following query will retrieve a key from a LDAP server:

gpg --keyserver ldap://ldap.orvelte.nep/ --keyserver-options basedn=\"dc=orvelte,dc=nep\" --keyserver-options verbose --recv-keys 12345678

Finally, it is possible to retrieve the key information automatically, by expanding:

gpg -r bakker@orvelte.nep -e test

to retrieve from LDAP and not from local store:

gpg --auto-key-locate nodefault,ldap -r bakker@orvelte.nep -e test

This will perform the following actions:

  • due to nodefault skip local key lookup -- perhaps useful for mail servers -- but known to not work in older GnuPG versions
  • lookup the recipient domain name's SRV record for the key server
  • on the keyserver, lookup the server info and notably the base DN to query under the base DN look for (&(pgpUserId=*bakker@orvelte.nep*)(pgpDisabled=0))
  • import it into the local key base

I've also seen people setup a temporary key storage space in /tmp/keystore.$$, just to ensure that a fresh key is downloaded.

Improving Privacy of User Identities

Normally, PGP key information is considered public. This may however pose a problem, as the keys usually contain a user's identity in the form of an email address. Being able to iterate over all PGP keys in a repository implies being able to iterate over the email addresses of its users. This can be avoided if information that identifies the key or its user are only supplied when they are actively requested.

Some preprocessing in an RWM overlay can help to trim down overzealously inquisitive queries (which are commonly used) to a form that only works when the email address sought for is exactly matched in the search filter:

rwm-rewriteContext searchFilter
rwm-rewriteRule "(.*)\\\\(pgpUserId=\\\\*([^)=*]+\\\\*?\\\\)(.*)" "$1(|(pgpUserId=$2)(pgpUserId=`<$2>`))$3" ":"

This will change searches for (partial) email addresses into searches for complete email addresses. TODO: avoid abuse by matching everything once, and rewrite it to an exact mail address: [code]: ([^`<]*<)?[*]?(emailaddress)[*]?[>``]?.* TODO: look at other directory attributes. TODO: match all such attributes, but don't nest -- perhaps prefix a``< if not present, and separately postfix a >` when not present, and let it iterate as much as desired.

In addition to the above, the "razor" overlay can be used to require that at least one identifying attribute is matched by search filters:

razorObjectFilter: pgpCertID pgpKey pgpKeyID pgpSubKeyID pgpUserID*

Note that the pgpCertID is usually employed as the first/lowest RDN for storing the key material, so this statement requiring that some identifying information must be supplied before the pgpCertID can be released is very useful.

These are merely descriptive attributes anyway; since most are MAY attributes (only pgpCertID and pgpKey are declared as MUST keys) one could break up the object and strip off (identifying) attributes that have not been requested:

# commented-out attributes are not identifying for this key
# razorAttributeFilter: pgpDisabled
razorAttributeFilter: pgpKeyID
# razorAttributeFilter: pgpKeyType
razorAttributeFilter: pgpUserID
# razorAttributeFilter: pgpKeyCreateTime
razorAttributeFilter: pgpSignerID
razorAttributeFilter: pgpRevoked
razorAttributeFilter: pgpSubKeyID
razorAttributeFilter: pgpKeySize
# razorAttributeFilter: pgpKeyExpireTime

Note that pgpCertID is not included in this list, because it is common in the RDN. Had it been included in this list, then the entire object would be dropped if the pgpCertID would not be present in the search query, which it usually isn't. Moreover, it is a MUST attribute and should not be dropped individually.

Central Access Management for the Secure Shell </managedssh>