[UPDATED 2022.10.27]
Although we now all are smart cards and OpenSC gurus, thanks to my invaluable wisdom:
Chances are high having some infatuated IT kiddy bring to our attention that smart cards are things of the past - as we are (!) - and fashionably up-to-date geeks nowaday shove their YubiKey up their USB slot.
Turns out all we’ve learned about OpenSC, PKCS#11, key slots and PIN codes isn’t just there to make us look like fossilized fools but actually rings quite a bell when starting to deal with YubiKeys.
But let’s start from the beginning.
First-thing-first: get yourself a YubiKey! I will actually base this rant on a YubiKey 5C which you can get from:
YubiKeys are great little crypto cuties that actually provide several interfaces/features through multiple different applets:
yubikey-val
)In this document, we’ll concentrate on that last interface/applet, which makes the YubiKey a close sibling to the smart cards we’ve grown to cherish.
Let’s first install Yubico’s PIV tool:
apt-get install yubico-piv-tool
NOTE: on Debian/Buster, you may have to pick the package from Sid (hoping the Bug 926551 might be “fixed” before Buster is officially released)
Then check our brand new YubiKey is working correctly:
yubico-piv-tool -a list-readers -a version -a status
# (output for a YubiKey 5C)
Yubico YubiKey OTP+FIDO+CCID 00 00
Application version 5.1.2 found.
CHUID: No data available
CCC: No data available
PIN tries left: 3
(… CCID!?! that also rings a bell!!!…)
Which you can also confirm with OpenSC:
opensc-tool -a
# (output for a YubiKey 5C)
Using reader with a card: Yubico YubiKey OTP+FIDO+CCID 00 00
<hexadecimal fingerprint>
Now, before going any further, one MUST change the Management Key, PIN and PUK codes from their default:
# Set the Card Holder Unique ID (CHUID) - REQUIRED for Microsoft Windows
yubico-piv-tool -a set-chuid
# Set the Cardholder Capability Container (CCC) - REQUIRED for Apple Mac OS
yubico-piv-tool -a set-ccc
# Change the PUK code
PUK=<8-digit>
yubico-piv-tool -a change-puk -P 12345678
# Change the PIN code
PIN=<6-digit>
yubico-piv-tool -a change-pin -P 123456
WARNING: Now is not the time to conscientiously store those secrets somewhere you’ll never find them again when dire need comes!
Before proceeding with creating our various certificates, one must understand the purpose and characteristics (PIN-wise) of the key slots provisioned by YubiKey’s PIV applet:
Shortly put:
slot 9A: (personal) authentication (SSH, web SSL client, etc.); PIN asked once
slot 9C: signature (e-mail, code signing); PIN always asked
slot 9D: encryption (e-mail); PIN asked once
slot 9E: card authentication (physical access control); no PIN asked
We will not be using self-signed certificates. Come on!!!
So, before tearing your hair off, please refer to the OpenSSL Easy primer, which shall in no time make a Certification Authority warlock out of you:
In order to thwart jealousy-spawned claims you’re not actually using a YubiKey to show off your PKI skills, you’ll need an Attestation certificate, signed by the YubiKey itself and bound to (private) keys you’re about to generate.
Before we do, we need to retrieve your YubiKey-specific Intermediate Certification Authority (ICA):
yubico-piv-tool -s f9 -a read-certificate \
-o yubikey-piv-attestation-ica.pem
And its issuing Root Certificate Authority (CA):
wget -q https://developers.yubico.com/PIV/Introduction/piv-attestation-ca.pem \
-O yubikey-piv-root-ica.pem
The authentication (private) key shall be generated by the YubiKey itself, meaning it can never be recovered by anyone:
yubico-piv-tool -s 9a -a generate \
-A RSA2048 -H SHA256 \
-o yubikey-s9a-pub.pem
Which we’ll straight-away attest as being YubiKey-bound:
yubico-piv-tool -s 9a -a attest \
-o yubikey-s9a-attestation-cert.pem
Then, let’s generate the corresponding Certificate Signing Request (CSR):
USERNAME='<username>'
yubico-piv-tool -s 9a -a verify-pin -a request-certificate \
-S "/C=Country/ST=State/L=City/O=Example Org./OU=example.org/CN=${USERNAME}/"
-i yubikey-s9a-pub.pem -o yubikey-s9a-req.pem
NOTE: Make sure to specify a subject/DN (-S
) that matches your Certicate
Authority requirements (Country, STate, Locality, Organization name, etc.).
Once you receive the signed certificate - yubikey-s9a-cert.pem
- back from
your Certificate Authority, import it into the YubiKey:
yubico-piv-tool -s 9a -a import-certificate \
-i yubikey-s9a-cert.pem
And test everything works as expected:
pkcs11-tool \
--module /usr/lib/x86_64-linux-gnu/onepin-opensc-pkcs11.so \
--test --login --token-label "${USERNAME}"
As for the corresponding SSH authorized key:
ssh-keygen -D /usr/lib/x86_64-linux-gnu/onepin-opensc-pkcs11.so -e
There you go!
Now, should jealous morons dare pretend you don’t actually know how to use a YubiKey, just throw your Yubico-signed attestation in their face, let them verify it:
openssl verify \
-CAfile yubikey-piv-root-ica.pem \
-untrusted yubikey-piv-attestation-ica.pem \
yubikey-s9a-attestation-cert.pem
and check it matches your request or certificate (modulus):
openssl x509 -noout -modulus \
-in yubikey-s9a-attestation-cert.pem
openssl req -noout -modulus \
-in yubikey-s9a-req.pem
openssl x509 -noout -modulus \
-in yubikey-s9a-cert.pem
(prout!)
Before we carry on, a word about what a qualified certificate is and what it means when dealing with smart cards/tokens.
Generally speaking, a qualified certificate is “a certificate whose primary purpose is to identify a person with a high level of assurance”, as stated in RFC 3739:
In Switzerland, a qualified digital signature - based on a qualified certificate - is recognized as a legally valid signature - like the hand- written signature - in regards with the law (Loi sur la signature électronique, SCSE, RS 943.03) and its dependencies:
In addition, Trust Service Providers (TSP) issuing qualified certificates shall comply with ETSI technical requirements 319 411 (1+2) and 319 412 (2):
In the end, and shortly put, it means the technical constraints on qualified certificates are:
it MUST be delivered to living individuals only
the subject (DN
) of the certificate MUST uniquely identify the individual
as registered by authorities (e.g. as in your ID card or passport)
the certificate MUST NOT be used for any other purpose than digital signature (iow. it may not be used for encryption)
the private key SHALL be under the sole control of the subject; iow. the private key may not be in possession of the TSP/CA
private key access MUST be blocked after (max) 4 unsuccessful attempts
a blocked private key MUST NOT be unblocked without the TSP/CA verifying the identity of the holder of the certificate (iow. it can not be unblocked by a PUK)
All of it leading to a better understanding of why the YubiKey’s PIV applet might differentiate slot 9C (signature, PIN always asked) from slot 9D (encryption, PIN aksed once).
Given we nerds are no TSPs - would you trust me? I certainly don’t trust you! - and our next purpose is to issue a personal certificate for (both) e-mail signature and encryption, we’ll skip slot 9C (reserve it for its intended purpose, although we could re-purpose it) and use slot 9D for encryption and (non-qualified) signature.
Post Scriptum: actually, a YubiKey may never be used to store a qualified certificate since the TSP/CA has no mean to enforce point 6. above (unless it sends you the YubiKey without its Management Key and PUK code). On the other hand, it may be OK to store a regulated certificate (which requirements are lesser than for qualified ones). [TODO: check/document this]
Since we already covered how to have the YubiKey generate (and saveguard) the private key, let’s now use an externally generated key pair and certificate. (but that’s up to you; personally, I’d rather stick with the former approach)
We’ll just assume that your Certificate Authority’s policies are to use your
name and e-mail as address as component’s of your certificate’s subject (DN
):
commonName=<firstname> <surname>
emailAddress=<firstname>.<surname>@example.org
First, let’s import the private key:
yubico-piv-tool -s 9d -a import-key \
-i yubikey-s9d-key.pem
And then the certificate:
yubico-piv-tool -s 9d -a import-certificate \
-i yubikey-s9d-cert.pem
Or, if you have a PKCS#12 (private key + certificate) package handy:
yubico-piv-tool -s 9d -a import-key -a import-certificate \
-K PKCS12 -i yubikey-s9d-key+cert.p12
And to test everything works as expected:
TOKEN_LABEL='...' # as reported by pkcs11-tool -L
pkcs11-tool \
--module /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so \
--test --login --token-label "${TOKEN_LABEL}"
# Watch for both 'PIV AUTH' and 'KEY MAN' keys test
# (for keys reported by pkcs11-tool -O)
And that’s it!
The YubiKey provides 20 additional slots, numbered 82 to 95 and normally intended for Retired Key Management but which can be used similarly as the standard ones (9A to 9E).
In order for those slots to appear as PKCS#11 objects, the Key History Object must be initialized with the proper value, as per NIST 800-73-4 specifications (§3.3.3 and Appendix A, Table 19):
echo -n C10114C20100FE00 | yubico-piv-tool -a write-object --id 0x5FC10C -i -
Those slots can then be used for any operation, including signing (which is a slight deviation from the NIST requirements), which can come handy if one needs several slots to securely store the various Issuing CAs (ICAs) one may have as part of one’s own Public Key Infrastructure (PKI).
Most actions taken above - especially those modifying its content - can be administratively restricted by setting the YubiKey Management Key:
MKEY=$(dd if=/dev/urandom 2>/dev/null | tr -d '[:lower:]' | tr -cd '[:xdigit:]' | fold -w48 | head -1)
echo ${MKEY}
yubico-piv-tool -a set-mgm-key
Once this is done, one will need to use the -k
option to have the tool
prompt for the Management Key:
yubico-piv-tool -k ...
This is something that is not stricly required but which some IT department might do to ensure users don’t mess with their YubiKey.
As for how to use your brand new gadget, just refer to our previous smart card (PKCS#11) how-to:
Should you want to use your YubiKey as challenge-response source for KeePassXC, reprogram its slot 2 for HMAC-SHA1 challenge-response:
# Generate 20-bytes HMAC key
dd status=none if=/dev/urandom bs=20 count=1 | xxd -p -c 40
# Program the YubiKey
ykpersonalize -2 \
-a \
-ochal-resp -ochal-hmac -ohmac-lt64 \
-oserial-api-visible \
-oallow-update
Once reprogrammed, KeePassXC will automatically detect the YubiKey as source for challenge-response master key (along optional password and key file).
CRITICAL: make sure to keep a copy of the configured HMAC key (-a) somewhere safe!
NOTE: this does not affect any of the PIV-configuration covered above.
Beware of TLS 1.3
TLS 1.3 introduces new ciphers - incl. the Probabilistic Signature Scheme
(PSS) - that makes SSL client authentication fail miserably along OpenSC
PKCS#11 library (e.g. in Chromium: ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED
).
Until OpenSC fixes its PSS support, the only workaround is to disable TLS 1.3
in your favorite browser (e.g. for Chromium: --ssl-version-max=tls1.2
)
PKCS#11 URI
Should you need to troubleshoot a given YubiKey slot with OpenSSL, use
id=%<ID>
- mark the percentage (%) sign - in the required PKCS#11 URI:
openssl ... \
-engine pkcs11 \
-keyform engine \
-key "pkcs11:type=private;token=${USERNAME};id=%${ID}"
Where the ID is the one given by pkcs11-tool -O
(or with p11tool --list-...
from the gnutls-bin
package)`
Have you noticed what an excellent documentation Yubico provides to us geeks? Thanks guys!