Show Source

This is gross but it works. I use this stunt to avoid storing cryptographic keys in customized OpenWRT firmware images.

Importing keys to card

Readying Card For Use

Follow instructions at https://developers.yubico.com/yubico-piv-tool/YubiKey_PIV_introduction.html to use yubico-piv-tool to personalize the PIV application (set admin keys, PUK, and PIN). That amounts to:

dd if=/dev/random bs=24 count=1 | hexdump -v -e '/1 "%02X"' > mgmt.key.hex
yubico-piv-tool -a set-mgm-key -n `cat mgmt.key.hex`
yubico-piv-tool -k `cat mgmt.key.hex` -a change-pin -P 123456 -N $NEW_PIN
yubico-piv-tool -k `cat mgmt.key.hex` -a change-puk -P 12345678 -N $NEW_PUK

Keep the management key only on the high side machine; the PIN will doubtless need to be on the low-side machine and the PUK may merit being on a midlingly-secure piece of paper.

Importing Keys

I used slots 9a and 9d. The others claim to have some special features associated with them, but may be viable. (In particular, with a modern Yubikey and yubico-piv-tool program, 82-95 may be of interest, though I have not been able to test this.)

Import the key and certificate to the slot of your choosing This is perhaps most easily done from a PKCS#12 file containing both key and certificate:

yubico-piv-tool -k `cat mgmt.key.hex` -s 9a -a import-key -a import-cert -K PKCS12 -i ${FILE}.p12

Note that the Yubikey has only 2005 bytes of storage for PKCS#11 material, which limits the number of keys that can exist on the device. I am not sure if the certificate has to be present for the key to be useful.

Check the Card

Use pkcs11-tool to verify that the card is configured as you expect:

  • Show objects on card: pkcs11-tool -O
  • Read object to stdout from card by name: pkcs11-tool -r -y cert -a 'Certificate for Key Management'

OpenVPN

Testing

Check that openvpn --show-pkcs11-ids /usr/lib/pkcs11/opensc-pkcs11.so sees your key(s).

Caveats

  • It is necessary to tell OpenVPN not to fork(), as somewhere in OpenSC / pcsc-lite cannot deal with its clients forking. Thankfully, OpenVPN only forks to run scripts and ip and route, which are not mandatory features for me. If they are for you, it is likely that things will have to be patched (ick; I’m sorry). In any case, use these directives in the config file

    ifconfig-noexec
    route-noexec
    

    and avoid the use of up, down, etc.

  • While you can (and I do) use two OpenVPN servers using two different keys on the same card, this is apparently far more fragile than it should be. Using --show-pkcs11-ids, for example, will work but will cause all running daemons to be unable to do any more signatures ever, requiring restarts of the daemons. Don’t do that, especially not remotely.

  • The use of OpenSC PKCS#11 is incompatible with GnuPG’s scdaemon, which locks the card exclusively. This is incredibly annoying, meaning that although the card has both PIV and OpenPGP support, only one is usable at a time. I suppose the pain is less than the cost of another card.

Configuration

Then, tell OpenVPN about OpenSC:

pkcs11-providers /usr/lib/pkcs11/opensc-pkcs11.so

And use the output from --show-pkcs11-ids above to derive the appropriate pkcs11-id line. For me, that looks like

pkcs11-id 'piv_II/PKCS\x2315\x20emulated/1e06775fbfccbb86/PIV_II\x20\x28PIV\x20Card\x20Holder\x20pin\x29/01'

You may wish to use --management-query-passwords, too, so that OpenVPN asks on its management interface for the card’s PIN. Alternatively, I have a (gross) patch which adds an option (--pkcs11-pinfile) for reading the PIN in from a file. Thanks to Ondra Medek in https://openvpn.net/archive/openvpn-devel/2005-12/msg00014.html for pointing out what needed to change.