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- ipand- 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.