YubiKey -- Code signing with a smart card

November 25, 2016 | 5 min Read

Code signing and verification is the process of digitally signing executables or scripts to ensure that the software you are executing has not been altered since it was signed. I previously outlined how to perform code signing and verification with OpenSSL, using both the command line and OpenSSL API.

While the tutorial explained how to create an RSA KeyPair, it didn’t mention anything about protecting the Keys; in particular, protecting the private key. If the private key is ever compromised, someone could change your source code and re-sign it. This would make it appear as though you authored the changes. A great tool for securing private keys is PIV-Compliant Smart Card such as the YubiKey 4 or YubiKey Neo. The YubiKey is often used as a Universal Second Factor Authentication Device (U2F), but these devices provide much more functionality. The PIV slot on the Yubikey is a Write-Only slot, meaning you can store a private key on the device but you cannot read it back. You can however use the PIV applet on the device to produce a digital signature using the stored key.

In this extended tutorial we will look at how to create a digital signature with a YubiKey 4 and verify it with OpenSSL.

By Yubico - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=52063469

Introducing the YubiKey

YubiKey is a small hardware device primarily intended to provide second factor authentication. It supports a number of two factor authentication protocols, including One-Time Passwords, Challenge-Response and FIDO-U2F – a new standard being jointly developed by Yubico, Google and several other members of the Fido Alliance. In addition to Two-Factory Authentication, the YubiKey has several other slots. The card can be used to store PGP Private Keys, emit a static password and even act as a personal identity verification (PIV) card.

PIV cards have a number of certificate slots, which can be used for a variety of tasks such as signing, encryption, decryption and verification (among other things). It is these slots, and in particular the Certificate for Digital Signatures, that can be used for code signing.

Configuring your YubiKey

RSA keys can be generated on the YubiKey or generated elsewhere and imported. The former is much more secure since the private key never needs to be stored on a computer and since the YubiKey is write-only it can never be exported. This means that the only way someone could access your private key is if they stole your physical device. The YubiKey can also be protected with a PIN, so even if someone stole your key, they still couldn’t use it (after 3 false PIN attempts, the card will lock itself).

Before you begin, there are a number of useful tools to help you manage the PIV functionality of your YubiKey.

The first two downloads will help you manage the device itself (set the PIN, generate the keys, etc…). The first download is a command line tool while the second one provides the functionality via a graphical interface. The 3rd download (OpenSC) provides several tools for working with open smart card standards. These tools provide the signing / encryption functionality. In particular, these tools provide PCKS#11 and PCKS#15, a standard platform-independent API to cryptographic tokens.

You should begin by changing the default PIN and PUK. When you first launch the PIV-Manager it will prompt you to do this. Once configured, you can now generate a public / private key pair. We will generate the pair on slot 9c (Digital Signature).

You can now export the certificate and extract the public key or use the pkcs15-tool to directly dump the public key:

$ pkcs15-tool  --read-public-key 02
Using reader with a card: Yubico Yubikey 4 OTP+U2F+CCID
-----END PUBLIC KEY-----

Signing with the YubiKey

When signing documents, you typically sign a hash of the document. This is no different with a YubiKey. In fact, you must first produce the hash and then instruct the key to produce the signature. OpenSSL can be used to produce a hash a follows:

openssl dgst -sha256 -binary plaintext.txt  > plaintext.txt.sha256

This uses the SHA-256 hash function to produce a 256 bit value from the document. The YubiKey can now sign this value. Since the private key cannot be read from the card, an applet on the card performs the signing. The applet can be invoked with pkcs15-crypt. As an added layer of security, the card will prompt you for your PIN (If you enter the PIN incorrectly 3 times, the card will lock).

$ pkcs15-crypt -s -i plaintext.txt.sha256 -o signed.output -f openssl --sha-256 --pkcs1 -k 02
Using reader with a card: Yubico Yubikey 4 OTP+U2F+CCID
Enter PIN [PIV Card Holder pin]:

When the signing command finishes, the signature will be written to signed.output. The other arguments are used to specify the input (plaintext.txt.sha254), instruct the card to output the data in OpenSSL format, and that the SHA-256 hash function was used. Finally, -k 02 specifies slot 02 (also known as 9c, where the key was stored).

Verifying the Signature

To verify the signature, you only need the public key, signature and the original text, which we exported earlier. This is exactly the same as the previous tutorial. Since we used SHA-256 to produce the hash, we must specify that when verifying the signature.

$ openssl dgst -sha256 -verify mykey.pub  -signature signed.output plaintext.txt
Verified OK

Over the next few weeks I’ll be writing about other uses of the YubiKey, especially for cryptography. Follow me on Twitter for updates.

Ian Bull

Ian Bull

Ian is an Eclipse committer and EclipseSource Distinguished Engineer with a passion for developer productivity.

He leads the J2V8 project and has served on several …