Starting with GPG and YubiKey

What is public-key cryptography?

There are many guides available online which describe the basics of public-key cryptography. This post will not focus on the basics but rather a specific implementation of using GPG with a YubiKey. With this approach you’ll have your GPG secrets in a portable and secure device which you can then use on any machine.

In short, public-key cryptography is aimed at allowing two parties to securely generate a secret, a secure key, through an insecure communication channel. In essence, two parties will share some public information, completely in the clear for anyone to see/record, yet they will still arrive at the same secret key! With this secret key, both parties can then encrypt data and securely pass it between themselves.

One key assumption with public-key cryptography is that the private portions of this key actually remains secret. Due to this, the Yubikey provides an excellent solution. The Yubikey hardware token provides a secure hardware solution to store OpenPGP keys, and act as a second factor device for many web services. With the Yubikey, and GPG, you can securely use your private GPG keys with the knowledge that it is techinically infeasible to remove them from the device.


In order to follow this you’ll want the following:

  1. A YubiKey 4 or better which supports 4096 bit RSA keys. Ideally buy two of them to have a backup.
  2. Two USB drives to create a secure OS (Tails) and backup your GPG keys. This is important because without the backup you will have no way to recover your GPG secret if you lose or damge your Yubikeys.
  3. Some time and patience. You’ll probably run into problems and will have to debug.

This will basically follow the guide by drduh with some additional details where I ran into issues.

Creating your GPG keys securely

One of the main benefits of using a Yubikey is that you can be certain that your private keys are actually secure. If you generate all of these “secret” keys on a public or compromised system then none of this stuff really matters. For this reason, it makes the most sense to do the following on a secure system, such as Tails OS.

Follow the instructions and create a secure Tails OS. You’ll essentially download Tails, create a live USB, then boot to this version and create a really secure version of Tails on your second USB drive. It’s important that the system you use to create your GPG keys is actually secure or it’ll defeat the whole point of your Yubikey if someone can just read your private key directly. If you’re really paranoid you could even use Tails on an airgapped machine.

Once you have Tails setup, boot into it and install the following software

sudo apt-get update
sudo apt-get install -y \
    curl gnupg2 gnupg-agent \
    cryptsetup scdaemon pcscd \
    yubikey-personalization \
    dirmngr \
    secure-delete \

Creating new GPG keys

Here we’ll create brand new keys. If you want you could transfer already created keys onto your Tails distribution. However, by securely creating brand new keys on Tails you can be more sure that the only copy of your private key will exist securely on your Yubikey and your backup.

First we’ll create a temporary directory for GPG

export GNUPGHOME=$(mktemp -d); echo $GNUPGHOME

Now you can use the hardended GPG settings from here or copy the following in $GNUPGHOME/gpg.conf

personal-cipher-preferences AES256 AES192 AES
personal-digest-preferences SHA512 SHA384 SHA256
personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed
default-preference-list SHA512 SHA384 SHA256 AES256 AES192 AES ZLIB BZIP2 ZIP Uncompressed
cert-digest-algo SHA512
s2k-digest-algo SHA512
s2k-cipher-algo AES256
charset utf-8
keyid-format 0xlong
list-options show-uid-validity
verify-options show-uid-validity

We’ll start by creating a Master Key. This Master key should be kept offline at all times and is only used to certify your other keys, namely encryption, signing, and authentication subkeys that will be stored on the Yubikey and used for all other purposes. In addition, the Master Key will be used to revoke or create new subkeys.

When creating your key you’ll be prompted to enter a password. Enter a good password and make sure you write it down and store it securely. If you forget you’ll have to start over.

Creating the master key

We’ll use GPG to create a master key with Certify only authority

gpg --expert --full-gen-key
  1. Select 8: RSA (set your own capabilities)

  2. Select E to toggle off the Encrypt capability, which will leave you with only Sign + Certify.

  3. Select S to toggle off the Sign capability, which leaves only Certify

  4. Set a 4096 bit key size, if you have some specific need you can chose 2048 bit for older hardware

  5. No need to set an expiration date for this key

  6. Enter your name, email and a comment to setup the UID.

  7. Setup a passphrase.

Once the key is generated you should note the KEYID

export KEYID=0x20D0685093466FC7

which you’ll use later

Create your subkeys

Here we will edit the Master Key to add a number of subkeys

gpg --expert --edit-key $KEYID

These subkeys will be loaded onto your Yubikey and used for all the daily purposes.

Signing subkey

Within gpg

gpg> addkey
  1. Choose (4) RSA (sign only)

  2. 4096 bits

  3. Set key expiration for 1 year. This can be extended later with your master key when required. By setting an expiration date you can limit any damage from a compromised subkey.

Encryption subkey

Now add another key following a similar process

gpg> addkey
  1. Choose (6) RSA (encrypt only)

  2. Choose 4096 bits

  3. Set the key expiration to 1 year again

Authentication subkey

Finally we create an authentication only subkey. GPG doesn’t provide a default key for this so we create our own

gpg> addkey
  1. Choose (8) RSA (set your own capabilities) and toggle the options until only Authenticate is allowed

  2. Choose 4096 bits again

  3. Set a 1 year expiration

Verify the keys

You can verify your keys

gpg --list-secret-keys
sec#  rsa4096/0x20D0685093466FC7 2018-10-20 [SC] [expires: 2019-10-21]
      Key fingerprint = 5DC0 E5C9 AD73 DC63 D61D  7445 20D0 6850 9346 6FC7
uid                   [ unknown] Shankar Kulumani (YubiKey) <>
ssb>  rsa4096/0x58AA35F282289879 2018-10-20 [S] [expires: 2019-10-21]
ssb>  rsa4096/0x65C3E441CAA89126 2018-10-20 [E] [expires: 2019-10-21]
ssb>  rsa4096/0xABCD933DE083D57D 2018-10-20 [A] [expires: 2019-10-21]

If you’d like you can add additional emails to your key using the adduid command.

Generate a revocation certificate

It’s a good idea to generate a revocation certificate in case GPG didn’t do it by default

gpg --output revocation.asc --gen-revoke $KEYID

Backing up your keys

This step is very IMPORTANT because once your keys are on the Yubikey it will be impossible to remove. Without backing these up if you lose your Yubikey you will lose access to anything secured by your keys!!

First we’ll export the GPG keys to an ASCII armored format

gpg --armor --output private_key.gpg --export-secret-key $KEYID
gpg --armor --output sub_keys.gpg --export-secret-subkeys $KEYID
gpg --armor --output public_key.gpg --export $KEYID

Take all of these files (private, sub, public, and revocation) and backup them up on a seperate USB drive. It’s important these files are safe and stored offline. Keep them safe with your important documents, such as your passport and birth certificate.

The public key will need to be loaded onto any of your systems and shared with the world. Move that to an easily accessible location. One option is to upload your public key to Keybase. Also consider creating a paper backup

Personalizing your Yubikey

The first step is to use GPG to enable the YubiKey to be used as a smartcard. In addition, we’ll change the default and admin PINs on the Yubikey. The default PIN is used to utilize your keys while the admin PIN is used to unlock the card and perform more critical operations.

Plug in your YubiKey:

gpg --card-edit
  1. Once in GPG type admin
  2. Enter passwd
  3. Follow the prompts to modify the Admin PIN (selection 3)
  4. You can also update name, lang, login and finally type quit when finished

Transfer your keys to the YubiKey

The keytocard operation is destructive and will remove the key from your sytem and place it securely onto the YubiKey. Ensure you have made backups before completing this step.

  1. Enter gpg --edit-key $KEYID to display your master (certify), signing, encryption, and authentication keys
  2. Enter key 1 to select the Signing key (indicated by a *) followed by keytocard and select 1 to transfer to the YubiKey
  3. Deselect the key using key 1 and select the next key with key 2 and then transfer using keytocard and pick the appropriate key type
  4. Repeat for the authentication key
  5. Once all keys are saved enter save

Finally verify the keys on the card using

gpg --list-secret-keys

The keys should be indicated by ssb> indicating that only a stub exists on the system and the keys are located on the card

sec#  rsa4096/0x20D0685093466FC7 2018-10-20 [SC] [expires: 2019-10-21]
      Key fingerprint = 5DC0 E5C9 AD73 DC63 D61D  7445 20D0 6850 9346 6FC7
uid                   [ unknown] Shankar Kulumani (YubiKey) <>
ssb>  rsa4096/0x58AA35F282289879 2018-10-20 [S] [expires: 2019-10-21]
ssb>  rsa4096/0x65C3E441CAA89126 2018-10-20 [E] [expires: 2019-10-21]
ssb>  rsa4096/0xABCD933DE083D57D 2018-10-20 [A] [expires: 2019-10-21]

Final steps

You should have already backed up your keys previously. If not, now is the time to export your public key to share with the world. You will need this public key on every system you want to use your YubiKey.

gpg --export --armor $KEYID > /tmp/public_key.gpg

Make sure you have the following before leaving your Live Image

Using your keys

You can reboot into the Live Image, or onto another system to test your keys

sudo apt-get update
sudo apt-get install gnupg2 gnupg-agent pcscd curl

Copy the hardended gpg.conf file to your system

mkdir ~/.gnupg ; curl -o ~/.gnupg/gpg.conf
chmod 600 ~/.gnupg/gpg.conf

Import the public key from your backup

gpg --import /tmp/public_key.gpg

Trust the master key (since it’s your own key) gpg --edit-key $KEYID followed by trust and ultimate trust.

Exit using save

Insert your Yubikey and enter gpg --card-status to see something similar to the following

Sex ..............: male
URL of public key :
Login data .......:
Signature PIN ....: not forced
Key attributes ...: rsa4096 rsa4096 rsa4096
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 29
Signature key ....: 1B79 243F 477B 0569 EA44  56FC 58AA 35F2 8228 9879
      created ....: 2018-10-20 19:13:49
Encryption key....: F5F1 51D1 1BDA 4FC6 7CF6  5FA9 65C3 E441 CAA8 9126
      created ....: 2018-10-20 19:15:17
Authentication key: 7973 F74B 9489 FFAA EE75  804C ABCD 933D E083 D57D
      created ....: 2018-10-20 19:17:38
General key info..: sub  rsa4096/0x58AA35F282289879 2018-10-20 Shankar Kulumani (YubiKey) <>
sec#  rsa4096/0x20D0685093466FC7  created: 2018-10-20  expires: 2019-10-21
ssb>  rsa4096/0x58AA35F282289879  created: 2018-10-20  expires: 2019-10-21
                                  card-no: 0006 07761351
ssb>  rsa4096/0x65C3E441CAA89126  created: 2018-10-20  expires: 2019-10-21
                                  card-no: 0006 07761351
ssb>  rsa4096/0xABCD933DE083D57D  created: 2018-10-20  expires: 2019-10-21
                                  card-no: 0006 07761351

The sec# means that the master key is not available (since it’s stored offline) If you see something about none key info, this means you need to import your public key.

Basic GPG usage

Here are some examples on how to use your YubiKey

  1. Encrypt to yourself
echo "message" | gpg --encrypt --armor --recipient $KEYID
  1. Encrypt to multiple recipients, just use multiple --recipient flags
echo "message" | gpg --encrypt --armor --recipient $KEYID --reciepient $KEYID_1

If you don’t use your own key, you won’t be able to later decrypt

  1. Decrypt
cat encrypted_message.txt | gpg --decrypt --armor
  1. Signing a message
echo "message signed" | gpg --armor --clearsign --default-key $KEYID
  1. Verify a signature
cat signed_message.txt | gpg --verify

You should a Good signature and if it complains you can set the trust level of your own key.

Setting up Git to use your GPG keys for SSH access and signing

We will use gpg-agent to handle keys and use it instead of ssh-agent

First download a hardened configuration for gpg-agent

curl -o ~/.gnupg/gpg-agent.conf

or copy the following to ~/.gnupg/gpg-agent.conf

pinentry-program /usr/local/bin/pinentry-curses
default-cache-ttl 60
max-cache-ttl 120

Add the following to your shell rc files to utilize gpg-agent

# set gpg stuff
export GPG_TTY=$(tty)
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

To use your Yubikey for SSH, copy the following to your servers authorized_keys file

$ ssh-add -L
ssh-rsa AAAAB3NzaC1yc2EAAAA[bunch of random stuff] cardno:000607761351

It also makes sense to explicitly use this identity for servers you’ve setup by modifying your ~/.ssh/config file

First save this identity

ssh-add -L | grep "cardno:" > ~/.ssh/

The modify ~/.ssh/config to use this identity

    IdentitiesOnly yes
    IdentityFile ~/.ssh/

Test your connection:

ssh -vvv

Git signing with your YubiKey

Tell Git to use your key

git config --global user.signingkey $KEYID

Now sign your commits/tags with the -S option which will automatically poll the YubiKey. Upload the public key to your Github account to get that sweet verified image:


Additional information