DNSSEC requires private keys for signing DNS zones, just as your SSH client needs a private key to connect to a host via SSH. These private keys can be stored on a file system (and often are, particularly in the case of your SSH keys), but they can also be stored on a cryptographically secured hardware device. In the case of SSH this is often a Smart Card whereas with DNSSEC this is typically, in large environments anyway, one or more HSM, each costing anywhere up to several tens of thousands of Euros. But, as we all know, a high price doesn’t necessarily mean “good quality”; one of my favorite examples (true story) is this diagnostic message each time a EUR 50k HSM is accessed:

NFLog_AddFileDriver Success failed

So, what do people who want to store private keys on a secure hardware device purchase? One possibility is a Smart Card.

One of the worst “shopping experiences” I’ve ever had was when I wanted to purchase a high-end HSM for a project a couple of years ago; it was almost impossible to obtain documentation. Only after several phone calls to the vendor was I given access. (It’s as though they keep their docs in the bloody HSMs.) Runner up to that experience is probably trying to obtain a Smart Card and getting decent documentation on that. With the notable exception of the Yubikey, which has excellent, publically available documents, it’s a disaster. I asked Jakob Schlyter whether he knew of anything which could work for me together with OpenDNSSEC, and he recommended either of a CardContact SmartCard-HSM (prices here) or a Yubico Yubikey Neo.

The CardContact SmartCard-HSM comes in several form-factors and the “documentation” consists of a zip file which a bunch of Windows executables and a PDF with some screenshots. That really didn’t look convincing, so I selected the Yubikey Neo. To cut a very long story short, the Yubikey, while being a tremendously versatile bit of very well-documented kit, doesn’t support creation of keys via the PKCS#11 interface, so that was the end of that. Back to square one, i.e. the CardContact SmartCard-HSM.

If I wrote down everything I know about smart cards and HSMs, widely spaced, in a large font, I would cover a small postage stamp. One important thing to know is that software often interfaces to a HSM with PKCS#11.

SmartCard-HSM

The SmartCard-HSM token is a USB thing, so I plugged it in. Obviously. As I think I’ve already mentioned, there is zero documentation about this beast other than a short incorrect README tucked away in a subdirectory of the downloadable zip file which contains an obscure shared object (compiled for i386 – also not documented).

$ dmesg
[680574.152470] usb 5-1: USB disconnect, device number 13
[680583.884245] usb 5-1: new full-speed USB device number 14 using uhci_hcd
[680584.072654] usb 5-1: New USB device found, idVendor=04e6, idProduct=5817
[680584.072666] usb 5-1: New USB device strings: Mfr=1, Product=2, SerialNumber=5
[680584.072675] usb 5-1: Product: SCT3522CC token
[680584.072682] usb 5-1: Manufacturer: Identive
[680584.072689] usb 5-1: SerialNumber: 21121350600105

I then studied the OpenSC page on the SmartCard-HSM, but everything I tried seemed to result in the message card not present. At the bottom of this post it says:

please make sure that you are compiling and installing OpenSC 0.14

and this is confirmed here, so I installed from that PPA. Nothing doing. I then installed all the bits and pieces from source; nothing doing, so I went to sleep. During the night it ocurred to me that the bit communicating with the card is pcscd, so the next morning I built that from source; nada. After scrounging around for ages, I stumbled over a blog post by smartcard-hsm called SmartCard-HSM USB-Stick with new USB Product ID, and bingo! Thanks a million to these people for hiding that information so well, and thanks a lot also to the German vendor of the card for responding to my query with

alle relevant information you will find at http://www.smartcard-hsm.com/

(I’m not usually the shame and blame type, but well, this just sucks.)

So, let’s do something.

$ opensc-tool --list-readers
# Detected readers (pcsc)
Nr.  Card  Features  Name
0    Yes             CardContact SmartCard-HSM [CCID Interface] (21121350600105) 00 00
1    No              O2 Micro Oz776 01 00

$ pkcs11-tool --module opensc-pkcs11.so -L
Available slots:
Slot 0 (0xffffffffffffffff): Virtual hotplug slot
  (empty)
Slot 1 (0x1): CardContact SmartCard-HSM [CCID Interface] (21121350600105) 00 0
  token label        : SmartCard-HSM (UserPIN)
  token manufacturer : www.CardContact.de
  token model        : PKCS#15 emulated
  token flags        : rng, login required, PIN initialized, token initialized
  hardware version   : 24.13
  firmware version   : 1.2
  serial num         : DECC0100509
Slot 2 (0x5): O2 Micro Oz776 01 00
  (empty)

That looks promising, so can I now initialize the card and set the SO-PIN and the PIN? Following the very good instructions on doing so:

$ sc-hsm-tool --initialize --so-pin 0123012301230123 --pin 123456
Using reader with a card: CardContact SmartCard-HSM [CCID Interface] (21121350600105) 00 00

Now I’ll try to generate a key in order to determine whether it’s worth continuing to experiment with OpenDNSSEC (recall I was disappointed by the Yubikey in this respect)

$ pkcs11-tool --module opensc-pkcs11.so -l --keypairgen --key-type rsa:2048 --id 17 --label "JP first RSA keypair"
Using slot 1 with a present token (0x1)
Logging in to "SmartCard-HSM (UserPIN)".
Please enter User PIN:
Key pair generated:
Private Key Object; RSA
  label:      JP first RSA keypair
  ID:         17
  Usage:      decrypt, sign, unwrap
Public Key Object; RSA 2048 bits
  label:      JP first RSA keypair
  ID:         17
  Usage:      encrypt, verify, wrap

Yay! And a little green light on the USB thing blinks at me. Let me do another, this is fun!

$ time pkcs11-tool --module opensc-pkcs11.so -l --keypairgen --key-type rsa:2048 --id 18 --label "JP second RSA keypair"
...
real    0m11.358s
user    0m0.015s
sys     0m0.012s

What is this card actually capable of, at least in theory?

$ pkcs11-tool --module opensc-pkcs11.so --list-mechanisms
Using slot 1 with a present token (0x1)
Supported mechanisms:
  SHA-1, digest
  SHA256, digest
  SHA384, digest
  SHA512, digest
  MD5, digest
  RIPEMD160, digest
  GOSTR3411, digest
  ECDSA, keySize={192,320}, hw, sign, other flags=0x1500000
  ECDSA-SHA1, keySize={192,320}, hw, sign, other flags=0x1500000
  ECDH1-COFACTOR-DERIVE, keySize={192,320}, hw, derive, other flags=0x1500000
  ECDH1-DERIVE, keySize={192,320}, hw, derive, other flags=0x1500000
  ECDSA-KEY-PAIR-GEN, keySize={192,320}, hw, generate_key_pair, other flags=0x1500000
  RSA-X-509, keySize={1024,2048}, hw, decrypt, sign, verify
  RSA-PKCS, keySize={1024,2048}, hw, decrypt, sign, verify
  SHA1-RSA-PKCS, keySize={1024,2048}, sign, verify
  SHA256-RSA-PKCS, keySize={1024,2048}, sign, verify
  MD5-RSA-PKCS, keySize={1024,2048}, sign, verify
  RIPEMD160-RSA-PKCS, keySize={1024,2048}, sign, verify
  RSA-PKCS-KEY-PAIR-GEN, keySize={1024,2048}, generate_key_pair

So, can I “see” what’s on the card? Yes:

$ pkcs11-tool --module opensc-pkcs11.so --list-objects
Public Key Object; RSA 2048 bits
  label:      JP first RSA keypair
  ID:         17
  Usage:      none
Public Key Object; RSA 2048 bits
  label:      JP second RSA keypair
  ID:         18
  Usage:      none

Now I wipe one of the key pairs, ensuring I log into the card:

$ pkcs11-tool -l --pin 123456 --module opensc-pkcs11.so --delete-object --type privkey --id 18

$ pkcs11-tool --module opensc-pkcs11.so --list-objects
Using slot 1 with a present token (0x1)
Public Key Object; RSA 2048 bits
  label:      JP first RSA keypair
  ID:         17
  Usage:      none

Thinking I was ready to use the card with OpenDNSSEC was a little premature. To cut a painful story short, I won’t tell you. (No more blame and shame today.) BIND: same story (update: see below to a follow-up article). To be fair this isn’t necessarily an issue of the DNS server software; it can well be due to differing interpretation of the PKCS#11 “standard” (how I detest that word). Be that as it may, if somebody says to you “supports PKCS#11” be scared. Very scared. Update: Matthijs convinced me to file a bug report at OpenDNSSEC which I’ve done.

I abandoned this project.

At just about this time, Aki Tuomi heard I was playing with PKCS#11 and asked me to test his implementation for the Authoritative PowerDNS server. I’ll be honest: considering my experiences with OpenDNSSEC and BIND with PKCS#11, I was very reluctant to waste more time with this. I was wrong: in just a few hours yesterday, Aki enhanced the implementation for OpenSC support, and he got this working painlessly for me.

PowerDNS has an experimental PKCS#11 module which relies on P11-kit. According to the p11-kit manual, I must create a module file which associates a p11-kit module to a particular HSM. In order to connect to the SmartCard-HSM via OpenSC, I create /etc/pkcs11/modules/opensc.module. That name is important to remember, because we’ll see it referenced later. (I could have called it “blabla”, but I chose a slightly more formal “opensc”.)

module: /usr/opensc/lib/opensc-pkcs11.so
managed: yes
log-calls: no

I then verify that p11-kit can “see” my card; I identify this by the manufacturer:

$ p11-kit -l
...
opensc: /usr/opensc/lib/opensc-pkcs11.so
    library-description: Smart card PKCS#11 API
    library-manufacturer: OpenSC (www.opensc-project.org)
    library-version: 0.0
    token: SmartCard-HSM (UserPIN)
        manufacturer: www.CardContact.de
        model: PKCS#15 emulated
        serial-number: DECC0100509
        hardware-version: 24.13
        firmware-version: 1.2
        flags:
               rng
               login-required
               user-pin-initialized
               token-initialized

I create a test zone called cmouse.aa (cmouse is Aki’s IRC handle :-) in PowerDNS which I’ll use for these experiments.

The first thing I do is create two keys on my miniature HSM, one to be used as the KSK (2028 bits), the second as ZSK (1024 bits). I’m also using the -a switch to set a label on the keys so that I can identify them on the HSM later:

$ pkcs11-tool --module opensc-pkcs11.so -l --pin 123456 -k --key-type RSA:2048 -a 'cmouse.aa-KSK'
Using slot 1 with a present token (0x1)
Key pair generated:
Private Key Object; RSA
  label:      cmouse.aa-KSK
  ID:         a55b2ad19ab156bb8ab63a2361b9abf76e30315c
  Usage:      decrypt, sign, unwrap
Public Key Object; RSA 2048 bits
  label:      cmouse.aa-KSK
  ID:         a55b2ad19ab156bb8ab63a2361b9abf76e30315c
  Usage:      encrypt, verify, wrap

$ pkcs11-tool --module opensc-pkcs11.so -l --pin 123456 -k --key-type RSA:1024 -a 'cmouse.aa-ZSK'
Using slot 1 with a present token (0x1)
Key pair generated:
Private Key Object; RSA
  label:      cmouse.aa-ZSK
  ID:         0f1e61fd8c3b85f1b653fbc3a9273434c5712853
  Usage:      decrypt, sign, unwrap
Public Key Object; RSA 1024 bits
  label:      cmouse.aa-ZSK
  ID:         0f1e61fd8c3b85f1b653fbc3a9273434c5712853
  Usage:      encrypt, verify, wrap

So far, we have two key pairs on the HSM, but PowerDNS cannot use these yet; we’ve yet to associate these keys with the zone we want to sign. This is done with the hsm subcommand of the pdnssec utility.

command

The parameters are:

  1. Name of the zone from domains table.
  2. Signing algorithm
  3. The type of key. Values can be "zsk" or "ksk"
  4. The name of the p11-kit module PowerDNS will use to find the HSM. Recall I called it opensc
  5. The slot on said HSM. I identified it, above, as slot #1
  6. The PIN for said HSM. Careful: we’ll see this in clear text in a moment! Anybody who can access the MySQL cryptokeys table will be able to read the HSM’s PIN.
  7. The label of the key on the HSM.
$ pdnssec hsm assign cmouse.aa rsasha256 ksk opensc 1 123456 'cmouse.aa-KSK'
Module opensc slot 1 assigned to cmouse.aa with key id 16

$ pdnssec hsm assign cmouse.aa rsasha256 zsk opensc 1 123456 'cmouse.aa-ZSK'
Module opensc slot 1 assigned to cmouse.aa with key id 17

The key id issued by pdnssec is actually the row identifier in PowerDNS’ cryptokeys table. Here we go:

mysql> SELECT * FROM cryptokeys WHERE domain_id = (SELECT id FROM domains WHERE name = 'cmouse.aa');
*************************** 1. row ***************************
       id: 16
domain_id: 4
    flags: 257
   active: 1
  content: Private-key-format: v1.2
Algorithm: 8
Engine: opensc
Slot: 1
PIN: 123456
Label: cmouse.aa-KSK

*************************** 2. row ***************************
       id: 17
domain_id: 4
    flags: 256
   active: 1
  content: Private-key-format: v1.2
Algorithm: 8
Engine: opensc
Slot: 1
PIN: 123456
Label: cmouse.aa-ZSK

I warned you about the HSM PIN being available in clear-text; there it is. This is necessary, because PowerDNS needs to login to the HSM to get it to sign data whenever it needs to use the key material. This also means that the HSM is a limiting factor with regards to performance: the slower the HSM, the slower PowerDNS will be able to produce DNSSEC signatures.

So, if everthing is set up correctly, and if everything works, we ought to be able to have PowerDNS show us a DNSKEY or two and the signed zone. Let’s try.

$ pdnssec show-zone cmouse.aa
Zone is not presigned
Zone has NSEC semantics
keys:
ID = 16 (KSK), tag = 17284, algo = 8, bits = 2048       Active: 1 ( RSASHA256 )
KSK DNSKEY = cmouse.aa IN DNSKEY 257 3 8 AwEAAZOw37iBBoPHfLDjwyqGhAI00PSyVc92TfdIoYfryNtVc2nzX6p9iZMIOOGR70oicN/nIpA/9Pls7kUkb3Tf+P8TRa52SaxLIkwR/NBzBHj06q2JlJ6OUJ+BufD3WlLh5jZPHQzof5FFcRTg5Y4HwD0v+MbzuQoHOxM3PmG0qDUv+W2WZ2rFAmtEVQ2tupGHtzgtgfL7a4RU46rBpYujazHLU2A9Q9zbWJNlCeP4zPsYtDe7CiXtsEhs4c9VLF5WjOdBdUIXJNblEi6SnAxXpZrVGR1cDI+L1OshX0odOtN9e2fXF+rCxKPe2xtTGencx4H+zMyzbwBNYx0vN3UGY00= ; ( RSASHA256 )
DS = cmouse.aa IN DS 17284 8 1 0cd36f92047350aa481eaffa74ddd78197c89007 ; ( SHA1 digest )
DS = cmouse.aa IN DS 17284 8 2 cfdc012168c994ac53a83eb265db0f66e69e7fdefe4f57164ba61a7e70109f59 ; ( SHA256 digest )

ID = 17 (ZSK), tag = 5461, algo = 8, bits = 1024        Active: 1 ( RSASHA256 )

That looks wonderful, and what’s even nicer is, as I ran that command, I saw blinkenlights on the SmartCard-HSM. ;-)

$ dig @localhost cmouse.aa dnskey +multiline
;; ANSWER SECTION:
cmouse.aa.  300 IN DNSKEY 257 3 8 (
                    AwEAAZOw37iBBoPHfLDjwyqGhAI00PSyVc92TfdIoYfr
                    yNtVc2nzX6p9iZMIOOGR70oicN/nIpA/9Pls7kUkb3Tf
                    +P8TRa52SaxLIkwR/NBzBHj06q2JlJ6OUJ+BufD3WlLh
                    5jZPHQzof5FFcRTg5Y4HwD0v+MbzuQoHOxM3PmG0qDUv
                    +W2WZ2rFAmtEVQ2tupGHtzgtgfL7a4RU46rBpYujazHL
                    U2A9Q9zbWJNlCeP4zPsYtDe7CiXtsEhs4c9VLF5WjOdB
                    dUIXJNblEi6SnAxXpZrVGR1cDI+L1OshX0odOtN9e2fX
                    F+rCxKPe2xtTGencx4H+zMyzbwBNYx0vN3UGY00=
                    ) ; KSK; alg = RSASHA256; key id = 17284
cmouse.aa.  300 IN DNSKEY 256 3 8 (
                    AwEAAbwcLxxSvtlfPQVO7vv9cOF8KIwLnj8wb6iWwX60
                    MRMQ9jChRpjmVmIbnW+Y0No61jOwbVB9oN/+n2hj1uBT
                    jFd/4JK6I+sAqjcSOK/J3UNCFRJ5Lg7rBfegL2XNOKzz
                    54DzGE4m6AzM98gM0bItYKoqD0uN06blxk4qJTk+7Rot
                    ) ; ZSK; alg = RSASHA256; key id = 5461

More blinkenlights, and two DNSKEY records! Nice.

I can disable DNSSEC for a zone with the disable-dnssec subcommand of pdnssec which simply removes the key association from the cryptokeys table, leaving the keys on the HSM. These keys can be deleted from the device as shown above, using pkcs11-tool, or I can re-use them for a different zone or even associate them with more than one zone (i.e. shared keys).

Doing this with PowerDNS was a nice experience in spite of the fact that PKCS#11 support in PowerDNS is considered “experimental”.

Was experimenting with a smart card for DNSSEC worth the effort? With the notable exception I just described in detail, it certainly was not.

Futher reading