Skip to main content
Star us on GitHub Star

YubiKey by Yubico

Yubico is a manufacturer of HSM devices. A popular line of HSM produced by Yubico is the YubiKey. This quickstart guide will use specific device from Yubico - the YubiKey 5 nfc.


The YubiKey 5 nfc is a multi-purpose device with a few different security-minded uses. One of the applications on the device is an application called PIV or "Personal Identity Verification". PIV is a standard published by NIST and describes the kinds of credentials which make up the standard. PIV credentials have certificates and key pairs, pin numbers, biometrics like fingerprints and pictures, and other unique identifiers. When put together into a PIV credential, it provides the capability to implement multi-factor authentication for networks, applications and buildings.

In this quickstart, you will see the commands and tools needed to use a YubiKey 5 nfc with a network. This document is intended to serve as a quickstart. That means limited context will be provided for each step. When appropriate, there will be a small amount of context or a comment included to aid in the understanding of what is happening. Most, if not all, of these commands are easily found in the search engine of choice.


  • YubiKey 5 nfc - You'll need one to use this quickstart!
  • OpenSC is installed and pkcs11-tool is either on the PATH or at a known location. Not required however this quickstart uses the pkcs11-tool to illustrate that the device is PKCS#11 compliant. HSM manufacturers will generally provide a similar tool and often expand it's usage. See more below. yubico-piv-tool - YubiKey provides a similar tool to the pkcs11-tool. This tool is needed to be installed because it contains the pkcs#11 module (driver) for the HSM. As this is a tool specific to Yubico we've chosen to not use this in the following commands.
  • Ensure the YubiKey is factory reset. To avoid any complications with existing information in the YubiKey ensure the device is factory reset using the YubiKey Manager.
  • To successfully use the YubiKey, the libraries provided by the yubico-piv-tool must either be on the path or in a location that is known to the OS. On Linux, this is likely done by the YubiKey software installation, but on Windows, you'll need to take any additional actions highlighted in the Windows-specific sections.
  • Linux Only: If you're using Linux, you'll need to follow the build instructions provided by Yubico. Before you can do anything with the Yubikey you'll need to make sure the exists on your system. When creating this quickstart the library was built to ./yubico-piv-tool-2.0.0/ykcs11/.libs/
  • Ubuntu was used to test this guide. An attempt was also made with linux mint however the attempt failed when trying to compile the Yubikey software and was aborted. It would likely have worked if enough effort was put into discovering why that linux variant had issues pulling and compiling the necessary software. If you see strange errors when following this guide and are not using Ubuntu it may be related.
  • ziti and ziti-tunnel are both downloaded and on the path.

Let's Use the YubiKey!

Here's the list of steps we'll accomplish in this quickstart:

  • Establish a bunch of environment variables to make it easy to copy/paste the other commands. You'll want to look at these environment variables. If you have problems with this guide, it is almost certainly because you have an environment variable set up incorrectly.
  • Make a directory and generate a configuration file for Ziti
  • Use the Ziti CLI to:
    • create two identities - one demonstrating an RSA key, one EC
    • enroll the identities
    • create a test service
    • create test router/service policies
  • Use the pkcs11-tool provided by OpenSC to interact with the YubiKey to:
    • initialize the PIV app
    • create a key
  • Use ziti-tunnel to enroll the identities using the YubiKey
  • Use ziti-tunnel in proxy mode to verify things are working and traffic is flowing over the network

Do NOT use id 2 or 02 for any keys you add. Id 02 corresponds to the YubiKey's "Management Key". You will not be able to write a key into this location. The error you will see will indicate: CKR_USER_NOT_LOGGED_IN.

Establish Environment Variables

Open a command line and establish the following environment variables. Note that for the YubiKey we do not use id 02 as it appears to be reserved by the YubiKey. The default SOPIN and PIN are used as well. When using the YubiKey Manager software the "SO PIN" corresponds to the "Management Key" while "pin" is the same both here and in the YubiKey Manager.

# the name of the ziti controller you're logging into
export ZITI_CTRL=local-edge-controller
# the location of the certificate(s) to use to validate the controller
export ZITI_CTRL_CERT=/path/to/controller.cert

export ZITI_USER=myUserName
export ZITI_PWD=myPassword

# a name for the configuration
export HSM_NAME=yubikey_demo

# path to the yubikey pkcs11 libraries
export HSM_ROOT=/path/to/yubico-piv-tool-2.0.0
export PKCS11_MODULE=${HSM_ROOT}/ykcs11/.libs/

# the id of the key - you probably want to leave these alone unless you know better
export HSM_ID1=01
export HSM_ID2=03

# the pins used when accessing the pkcs11 api
export HSM_SOPIN=010203040506070801020304050607080102030405060708
export HSM_PIN=123456
export RSA_ID=${HSM_NAME}${HSM_ID1}_rsa
export EC_ID=${HSM_NAME}${HSM_ID2}_ec

# location for the config files to be placed
export HSM_LABEL=${HSM_NAME}-label

# make an alias for ease
alias p='pkcs11-tool --module $PKCS11_MODULE'

Make Directories for Config Files

cd ${HSM_ROOT}

rm -rf ${HSM_NAME}
mkdir -p ${HSM_NAME}

cd ${HSM_NAME}

Use the Ziti CLI

ziti edge login $ZITI_CTRL:1280 -u $ZITI_USER -p $ZITI_PWD

# create a new identity and output the jwt to a known location
ziti edge create identity device "${RSA_ID}" -o "${HSM_DEST}/${RSA_ID}.jwt"

# create a second new identity and output the jwt to a known location
ziti edge create identity device "${EC_ID}" -o "${HSM_DEST}/${EC_ID}.jwt"

Use pkcs11-tool to Setup the YubiKey

p --init-token --label "ziti-test-token" --so-pin $HSM_SOPIN

# create a couple of keys - one rsa and one ec
p -k --key-type rsa:2048 --usage-sign --usage-decrypt --login --id $HSM_ID1 --login-type so --so-pin $HSM_SOPIN --label defaultkey
p -k --key-type EC:prime256v1 --usage-sign --usage-decrypt --login --id $HSM_ID2 --login-type so --so-pin $HSM_SOPIN --label defaultkey

Use ziti-tunnel to Enroll the Identities

ziti-tunnel enroll -j "${HSM_DEST}/${RSA_ID}.jwt" -k "pkcs11://${PKCS11_MODULE}?id=${HSM_ID1}&pin=${HSM_PIN}" -v
ziti-tunnel enroll -j "${HSM_DEST}/${EC_ID}.jwt" -k "pkcs11://${PKCS11_MODULE}?id=${HSM_ID2}&pin=${HSM_PIN}" -v

Use ziti-tunnel to Verify Things Work

# if you only have a single edge router this command will work without the need for copy/paste
EDGE_ROUTER_ID=$(ziti edge list edge-routers | cut -d " " -f2)

# IF the above command doesn't work - run this command and get the id from the first edge-router.
# ziti edge list edge-routers

# then use the id returned from the above command and put it into a variable for use in a moment
# EDGE_ROUTER_ID={insert the 'id' from above - example: 64d4967b-5474-4f06-8548-5700ed7bfa80}

# remove/recreate the config - here we'll be instructing the tunneler to listen on localhost and port 9000
ziti edge delete config wttrconfig
ziti edge create config wttrconfig ziti-tunneler-client.v1 "{ \"hostname\" : \"localhost\", \"port\" : 9000 }"

# recreate the service with the EDGE_ROUTER_ID from above. Here we are adding a ziti service that will
# send a request to to retrieve a weather forecast
ziti edge delete service wttr.ziti
ziti edge create service wttr.ziti "${EDGE_ROUTER_ID}" tcp:// --configs wttrconfig

# start one or both proxies
ziti-tunnel proxy -i "${HSM_DEST}/${RSA_ID}.json" wttr.ziti:8000 -v &
ziti-tunnel proxy -i "${HSM_DEST}/${EC_ID}.json" wttr.ziti:9000 -v &

# use a browser - or curl to verify the ziti tunneler is listening locally and the traffic has flowed over the ziti network
curl -H "Host:" http://localhost:8000
curl -H "Host:" http://localhost:9000

Putting It All Together

Above we've only shown the commands that need to run and not what the output of those commands would look like. Here we'll see all the commands put together along with all the output from the commands. This section is long - you are warned! Also note that this content is subject to change. If the output you see is not identical it's because the software has changed since this information was captured. File an issue if you'd like to see it updated.

The tabs to the right contain example output from running all the commands in sequence. If you want to see what the output would likely look like click one of the tabs to the right. Reminder - it's a lot of commands and a lot of output! :)