Serving public certificates via DNS is quite an obscure endeavor. This is made quite evident by the total lack of documentation on how to serve CERT type records using BIND. The Direct Project requires that public certificates are discoverable via DNS and LDAP. BIND is by far the most widely used nameserver on the Internet and so this article describes how to get BIND to serve certificates for a Direct implementation.

Adding the certificate to BIND’s zone file is the tricky bit. You must do two things to the certificate so that BIND will serve it correctly. 1) Calculate the “key tag” and 2) format the public key correctly. It is not just the contents of a .pem file. The the certs’s zone file entry must be all on one line.

To make this task easier we have created a free open-source command line utility called “BIND Certificate Converter” or just “bcc” for short. bcc accepts two command line parameters; 1) a host name and 2) a certificate file (in .pem format). bcc outputs a suitable BIND zone file entry. You can redirect the output of this utility to append to your zone file. We will illustrate this with an example bewlow. In this example, we assume you are using BIND 9 on Ubuntu. The utility BIND Certificate Converter “bcc” also requires that dnspython and openssl are installed. Let start by making sure we have everything we need installed.

sudo apt-get install python-dnspython openssl bind9

Copy the file “bcc” into “/usr/bin” and set it to executable. The source code is at the bottom of this blog entry.

sudo cp bcc /usr/bin
sudo chmod 755 /usr/bin/bcc

We have now installed BIND, bcc, and the other prerequisites. You will now have the directory /etc/bind. Lets change “cd” into that directory. The file we want to change for localhost is “/etc/bind/db.local”. We are assuming you have a file called “example.com.pem” sitting in your $HOME directory (/home/ubunutu/example.com.pem).

Now we can give our utility bbc (source code below) a dry run before outputting anything.

bcc examplehost example.com.pem

Let’s make things easier and just switch to a root shell.


sudo su -

Now lets write the output of bcc to the end of “/etc/bind/db.local”


cd /etc/bind
bcc examplehost /home/ubuntu/example.com.pem >> /etc/bind/db.local

Now we need to restart BIND for our change to take effect.

sudo /etc/init.d/bind9 restart

Now we can test it with “nslookup”. Execute the following command:


nslookup -type=CERT myexamplehost.localhost localhost

The BIND server responds:

Server:		localhost
Address:	127.0.0.1#53

myexamplehost.localhost	cert = PKIX 12437 RSASHA1 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzXVb5YJHmV7a ljGA2KHXPgJ3KmGkMCF9AIHOGsnNN1CuuN4gCRdPVhySVsyEjWLj2xby 6i7yc8Zau2AutrFEFBibXw1YQZvbzabxpG0zZV3tG88t+03OH2VJsK2t 5adxY8wufuY353NwiCMhLtsnRMMym9BbLqQWt3v1P+s9zqq1bLQYQYJC ZexUVhBnjEEVL5oschErtoahpRlmhE1LxtmxKr75mv8RfZV17Pbn7JbP Jk36wpFKpT9SGJWC27eqUFtorOOkH6Kr+j/fGs1GWKgXjMZpeADC14Yh KrDeJtpUL3zzUtsLN9nP/MbcCzHnwdRd4Sb+5V0K1S3R/vtrDQIDAQAB

It works (hopefully)! Here is the source code to bcc.


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4
# Copyright 2013 Videntity
# Freely reuse under the terms of http://www.apache.org/licenses/LICENSE-2.0.html
# Last Updated: August 18 , 2013

# BIND Certificate Convert - Read in a host name and PEM certificate and write out the corresponding line of BIND's zone file.

import os, sys, hashlib
import subprocess
import dns.rdata
import dns.dnssec
import dns.rdataclass
import dns.rdatatype
import dns.rdtypes.ANY.DNSKEY


def find_between( s, first, last ):
    try:
        start = s.index( first ) + len( first )
        end = s.index( last, start )
        return s[start:end]
    except ValueError:
        return ""
    
def bind_cert_convert(hostname, pemcert):
    output,error = subprocess.Popen(["openssl", "x509", "-in",
                                      pemcert, "-pubkey","-noout",],
                             stdout=subprocess.PIPE,
                             stderr= subprocess.PIPE
                            ).communicate()
    
    
    clean_pk = find_between(output,
                            "-----BEGIN PUBLIC KEY-----",
                            "-----END PUBLIC KEY-----")
    
    clean_pk = clean_pk.replace("\n", "")
    decoded_clean_pk = clean_pk.decode('base64', strict)
    dnskey = dns.rdtypes.ANY.DNSKEY.DNSKEY(dns.rdataclass.IN,
                                           dns.rdatatype.DNSKEY, 0, 0,
                                           dns.dnssec.RSASHA256, 
                                           decoded_clean_pk)
    
    
    bind_entry = "%s\tIN\tCERT\tPKIX %s RSASHA1 %s" % (hostname,
                                                       dns.dnssec.key_id(dnskey),
                                                       clean_pk)
    
    print bind_entry
    
    
    
if __name__ == "__main__":
 
    if len(sys.argv)!=3:
        print "Usage: [HOST_NAME] [PEM_CERT_FILENAME]"
        sys.exit(1)
 
    bind_cert_convert(sys.argv[1], sys.argv[2])


So now there is documentation! I’d like to give a special thank you to Bob Halley, author of the “dnspython” library for helping me figure this out.