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.
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.
The parameters are:
- Name of the zone from domains table.
- Signing algorithm
- The type of key. Values can be
"zsk"
or"ksk"
- The name of the p11-kit module PowerDNS will use to find the HSM. Recall I called it opensc
- The slot on said HSM. I identified it, above, as slot #1
- 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.
- 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.