Using GPG and SSH keys (GnuPG 2.1) with a Smartcard (Yubikey 4)

February 11, 2017

Background

The primary intention of this post is to document the research and the steps I’ve taken for my current ‘GPG & SSH keys on a smartcard’ setup, so that I can look it up for future reference and possible improvements. There are plenty of good articles around the same subject, but most of them are partly dated either regarding GnuPG versions, subkey separation or an older Yubikey.

The majority of this document is heavily based on the following articles. However, there are some subtleties that I’ve spent more time trying to figure out, hence documenting it here.

Additional references/sources will be provided wherever possible as we go along.

Goal

The goal of this documentation is to assist in the using GnuPG 2.1 to create GPG and SSH keys that can be stored and used via a Yubikey 4 smartcard. Further along, one should be able to use their public keys on traditional keyservers (such as MIT PGP Keyserver) as well as services such as Keybase.io.

If followed along, the following things should be in place towards the end.

In the upcoming sections, we will go through the above checklist. We will be omitting discussions around the “whys” and instead focus on “hows” in this document. However, here are some links for the curious readers to follow up on.

Requirements

For the security-minded folks out there, you might want to continue the guide from a separate air-gapped machine using a secure live operating system (such as Tails). This guide will skip the instructions for such a setup, assuming you have the expertise to do so.

Hardware

Smartcard

We will need a smartcard to store our GPG subkeys.

NOTE: This document was prepared using a Yubikey 4. If you decide to use another smartcard please read up on the relevant GPG documentation. One thing to note is that Yubikey 4 supports 4096 bit subkeys, which we will be working with. If your smartcard does not support 4096 bit keys, continue the guide with the largest supported keysize, most likely being 2048 bit keys.

Backup disks

This is where we will store the offline primary/master key, as well as the backup of subkeys.

It is a good idea to store copies of the keys in multiple encrypted physical disks. We will not go through the process of preparing an encrypted volume in this document, as that can easily be a separate article on it’s own. If unsure, using Veracrypt with exFAT is a solution that will get along well on various operating systems.

Software

After you’ve booted your operating system (optionally, on an air-gapped live OS using a USB stick), you will need to install the following packages from relevant sources.

NOTE: Based on your OS, your GPG client might be called gpg or gpg2. This document proceeds with gpg2, please adjust accordingly.

NOTE: If you plan to use your GPG/SSH keys via desktop/GUI environments, it’s recommend to use a GUI based pinentry. You should be able to find one for the desktop environment you are using.

Ubuntu Setup

On Ubuntu Xenial (16.04 LTS), the following should suffice.

sudo apt-get update
sudo apt-get install -y gnupg2 gnupg-agent pinentry-curses scdaemon

MacOS Setup

GnuPG 2.1 is not yet bundled with GPG Tools Suite, which otherwise is a well integrated bundle for MacOS. Instead, we can use homebrew to install the required components. By the time you are reading this, GnuPG 2.1 might have been bundled with the suite.

brew install gpg2 pinentry

Verification

Before we proceed, let’s make sure that we have the correct things installed and working. On a fresh terminal, run the following checks.

GnuPG version

Verify GnuPG version to be 2.1

$ gpg2 --version
gpg (GnuPG) 2.1.17
libgcrypt 1.7.6
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /Users/test/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

If this didn’t output 2.1, then you might have to double check your installed GnuPG version and/or the $PATH settings.

Smartcard status

Verify that you can read the smartcard. If using older Yubikeys, you might have to enable the CCID mode. This is not required for Yubikey 4.

$ gpg2 --card-status

Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: XXXXXXXX
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

If this doesn’t work, you might have to check if you have installed scdaemon properly. Run gpgconf to see what binary is being used.

Working Pinentry

Verify that you can use pinentry. In the following test, a pinentry popup should appear when you’re asked to enter the Smartcard Admin PIN. (default: 12345678 for Yubikey)

$ gpg2 --card-edit

Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: XXXXXXXX
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

gpg/card> admin
Admin commands are allowed

gpg/card> url
URL to retrieve public key: https://example.com/gpg.key

gpg/card> list

Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: XXXXXXXX
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : https://example.com/gpg.key
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

gpg/card> quit

If this doesn’t work, you might have to check if you have installed pinentry properly. Run gpgconf to see what binary is being used.

GPG Key generation

Now that we have verified that we have a working system, we can go about generating GPG keypairs. There’s plenty of articles that mention “why” we use separate subkeys and explain the chosen scheme better. In this document, we will only focus on the “how”. Here are some more links to follow up for better motivation of the key scheme we will be using.

There are discussions on various forums suggesting that one doesn’t gain much having larger keys, esp. above 3072 bits and ECC is the way to go forward. We will resort to using 4096 bit keys in this document since Yubikey 4 supports it, but please feel free to adjust accordingly.

Furthermore, there are several discussions around the expiry time for GPG keys and the expiry strategy for various subkeys. To keep things simple, we will arbritarily use an expiry time period of 3 years for all keys. Needless to mention, please customize things accordingly.

After you eventually copy your subkeys to the smartcard, you won’t need to remember your passphrase, but this is no excuse to not write down your passphrase somewhere safe. You’ve been warned.

There are several discussions on

Working directory

Create a temporary working directory and use it for GnuPG operations below.

$ export GNUPGHOME=$(mktemp -d) ; mkdir -p $GNUPGHOME/backups; echo $GNUPGHOME
/var/folders/3y/s20pdw8566ld5qqnq7_l55cw0000gn/T/tmp.FbNQHpqU

Run the following to create a recommended GnuPG configuration.

$ cat << EOF > $GNUPGHOME/gpg.conf
no-emit-version
no-comments
keyid-format 0xlong
with-fingerprint
list-options show-uid-validity
verify-options show-uid-validity
use-agent
fixed-list-mode
charset utf-8
personal-cipher-preferences AES256 AES192 AES CAST5
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
EOF

Create master key

Generate a new key, selecting RSA (sign only) and the appropriate keysize, optionally specifying an expiry.

$ gpg2 --full-generate-key
gpg (GnuPG) 2.1.17; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: keybox '/var/folders/3y/s20pdw8566ld5qqnq7_l55cw0000gn/T/tmp.FbNQHpqU/pubring.kbx' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 3y
Key expires at Tue Feb 11 17:58:02 2020 CET
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Test User
Email address: [email protected]
Comment:
You selected this USER-ID:
    "Test User <[email protected]>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /var/folders/3y/s20pdw8566ld5qqnq7_l55cw0000gn/T/tmp.FbNQHpqU/trustdb.gpg: trustdb created
gpg: key 0xA9A72FB118D8BE6C marked as ultimately trusted
gpg: directory '/var/folders/3y/s20pdw8566ld5qqnq7_l55cw0000gn/T/tmp.FbNQHpqU/openpgp-revocs.d' created
gpg: revocation certificate stored as '/var/folders/3y/s20pdw8566ld5qqnq7_l55cw0000gn/T/tmp.FbNQHpqU/openpgp-revocs.d/19B5D94493D0B2851E4E95CFA9A72FB118D8BE6C.rev'
public and secret key created and signed.

Note that this key cannot be used for encryption.  You may want to use
the command "--edit-key" to generate a subkey for this purpose.
pub   rsa4096/0xA9A72FB118D8BE6C 2017-02-11 [SC] [expires: 2020-02-11]
      Key fingerprint = 19B5 D944 93D0 B285 1E4E  95CF A9A7 2FB1 18D8 BE6C
uid                              Test User <[email protected]>

Export the key id obtained above, (next to the pub rsa4096/....key-id-here....)so that it can be used further along.

export KEYID=0xA9A72FB118D8BE6C

NOTE: The above key id will be your unique key identifier associated with this set of GPG keypair.

Revocation certificate creation

Create a revocation certificate to revoke your keys in case of loss or compromise.

$ gpg2 --gen-revoke $KEYID > $GNUPGHOME/backups/revoke.txt

sec  rsa4096/0xA9A72FB118D8BE6C 2017-02-11 Test User <[email protected]>

Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
(Probably you want to select 1 here)
Your decision? 1
Enter an optional description; end it with an empty line:
>
Reason for revocation: Key has been compromised
(No description given)
Is this okay? (y/N) y
ASCII armored output forced.
Revocation certificate created.

Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable.  But have some caution:  The print system of
your machine might store the data and make it available to others!

Export Master key

Create an ASCII armored copy of the private key block.

$ gpg2 --armor --export-secret-keys $KEYID > $GNUPGHOME/backups/master.key

Additional UIDs

Edit the key and add additional UIDs as needed. This is where you would add your additional [email protected] UID, so that it’s a part of the key. If you intend to use this GPG keypair on Github, Keybase etc., make sure you add more relevant uids as shown below.

$ gpg2 --edit-key $KEYID
gpg (GnuPG) 2.1.17; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2020-02-11
sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Test User <[email protected]>

gpg> adduid
Real name: Test User
Email address: [email protected]
Comment:
You selected this USER-ID:
    "Test User <[email protected]>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1)  Test User <[email protected]>
[ unknown] (2). Test User <[email protected]>

gpg>

Similarly, add multiple UIDs as you prefer. After you’re done, select the preferred uid(current selection marked with a *) and make it your primary UID(current primary marked with a .).

gpg> uid 1

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1)* Test User <[email protected]>
[ unknown] (2). Test User <[email protected]>

gpg> primary

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1)* Test User <[email protected]>
[ unknown] (2)  Test User <[email protected]>

gpg> uid 1

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Test User <[email protected]>
[ unknown] (2)  Test User <[email protected]>

gpg> save

Optional Photo

Optionally, add a photo to the key. Try to keep this tiny (max. 240x288), since this will significantly increase the size of the public key.

$ gpg2 --edit-key $KEYID
gpg (GnuPG) 2.1.17; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>

gpg> addphoto

Pick an image to use for your photo ID.  The image must be a JPEG file.
Remember that the image is stored within your public key.  If you use a
very large picture, your key will become very large as well!
Keeping the image close to 240x288 is a good size to use.

Enter JPEG filename for photo ID: ~/Downloads/gpg.jpg
Is this photo correct (y/N/q)? y

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ unknown] (3)  [jpeg image of size 640]

gpg> save

Create subkeys

Now to proceed to add subkeys that we’ll be using most of the time.

$ gpg2 --expert --edit-key $KEYID
gpg (GnuPG) 2.1.17; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2020-02-11
sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

Follow below without quitting the gpg key-edit session.

Create Signing Key

First, create a signing key, selecting RSA (sign only):

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 3y
Key expires at Tue Feb 11 18:07:24 2020 CET
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

Create Encryption Key

Next, create an encryption key, selecting RSA (encrypt only):

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 3y
Key expires at Tue Feb 11 18:08:36 2020 CET
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

Create Authentication Key

Finally, create the authentication key, selecting RSA (set your own capabilities):

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 8

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? S

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Encrypt

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? E

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions:

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? A

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? Q
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 3y
Key expires at Tue Feb 11 18:09:44 2020 CET
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

Save and exit the expert GPG key edit mode.

gpg> save

Check all the keys

List all your secret keys and see that you have all the 4 keys(1 master + 3 sub keys) in place.

$  gpg2 --list-secret-keys
/var/folders/3y/s20pdw8566ld5qqnq7_l55cw0000gn/T/tmp.FbNQHpqU/pubring.kbx
-------------------------------------------------------------------------
sec   rsa4096/0xA9A72FB118D8BE6C 2017-02-11 [SC] [expires: 2020-02-11]
      Key fingerprint = 19B5 D944 93D0 B285 1E4E  95CF A9A7 2FB1 18D8 BE6C
uid                   [ultimate] Test User <[email protected]>
uid                   [ultimate] Test User <[email protected]>
uid                   [ultimate] [jpeg image of size 640]
ssb   rsa4096/0x2A6589AD05FFF591 2017-02-11 [S] [expires: 2020-02-11]
ssb   rsa4096/0xAE50BFB8ACCA36D7 2017-02-11 [E] [expires: 2020-02-11]
ssb   rsa4096/0x745910FC4FB11754 2017-02-11 [A] [expires: 2020-02-11]

Export subkeys

Export a copy of your subkeys.

$ gpg2 --armor --export-secret-keys $KEYID > $GNUPGHOME/backups/master_with_sub.key

$ gpg2 --armor --export-secret-subkeys $KEYID > $GNUPGHOME/backups/sub.key

Export GPG and SSH public keys

Now, export both your public GPG and SSH keys. Since these are public keys, feel free to save them somewhere easily/publicly accesible.

$ gpg2 --armor --export $KEYID > $GNUPGHOME/backups/public_gpg.key

$ gpg2 --export-ssh-key $KEYID > $GNUPGHOME/backups/public_ssh.key

Backup all the keys

At this point, make multiple copies of the $GNUPGHOME folder in encrypted volumes. DO NOT PROCEED without doing this, as we will eventually remove the secret/sub keys on your machine.

Smartcard setup

Let’s personalise the smartcard and get these keys transferred to it. Insert your Yubikey, and check to ensure that your card in working with GnuPG.

$ gpg2 --card-status
Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: XXXXXXXX
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

Further description of the parameters above is provided here.

Personalise Yubikey

We will start with personalising the smartcard (for GPG) with user details.

Set attributes

We can set various parameters on the card. GnuPG compeletely ignores some of these, so please feel free to omit/add parameters as you think is necessary.

$ gpg2 --card-edit

Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: XXXXXXXX
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

gpg/card> admin
Admin commands are allowed

gpg/card> help
quit           quit this menu
admin          show admin commands
help           show this help
list           list all available data
name           change card holder's name
url            change URL to retrieve key
fetch          fetch the key specified in the card URL
login          change the login name
lang           change the language preferences
sex            change card holder's sex
cafpr          change a CA fingerprint
forcesig       toggle the signature force PIN flag
generate       generate new keys
passwd         menu to change or unblock the PIN
verify         verify the PIN and list all data
unblock        unblock the PIN using a Reset Code
factory-reset  destroy all keys and data

gpg/card> name
Cardholder's surname: User
Cardholder's given name: Test

gpg/card> lang
Language preferences: en

gpg/card> sex
Sex ((M)ale, (F)emale or space): F

gpg/card> url
URL to retrieve public key: https://keybase.io/username/pgp_keys.asc

gpg/card> login
Login data (account name): testuser

gpg/card> list

Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: XXXXXXXX
Name of cardholder: Test User
Language prefs ...: en
Sex ..............: female
URL of public key : https://keybase.io/username/pgp_keys.asc
Login data .......: testuser
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

Apart from the settings above, consider reading up on the Signature PIN behaviour (using the forcesig command)for your requirements.

Change the PINs

Next, we will change the default PINs. The default PIN codes for Yubikey 4 are: Admin PIN: 12345678 and PIN: 123456.

gpg2 --card-edit

Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: XXXXXXXX
Name of cardholder: Test User
Language prefs ...: en
Sex ..............: female
URL of public key : https://keybase.io/username/pgp_keys.asc
Login data .......: testuser
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

gpg/card> admin
Admin commands are allowed

gpg/card> passwd
gpg: OpenPGP card no. D2760001240102010006050313460000 detected

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 3
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 1
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? q

gpg/card> quit

Transfer keys

Now that the card is secured with new PINs(hope you wrote those down somewhere) and personlized to our needs, let’s finally transfer the GPG sub keys that we prepared with such care.

Also, transferring your keys is a one way operation. So, here’s your LAST WARNING about making multiple backups.

$ gpg2 --edit-key $KEYID
gpg (GnuPG) 2.1.17; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

gpg>

Continue below without quitting.

If using older GnuPG versions, you will need to run toggle before continuing. If you see pub and sub instead of sec and ssb as above, then you need to toggle. This will toggle the edit mode to private keys. This is not required in GnuPG 2.1.

As you select keys in each step below, you’ll see the corresponding ssb entry change to ssb*. This is how you know what keys are currently selected.

Transfer Signing key

gpg> key 1

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

gpg> keytocard
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

gpg> key 1

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

Transfer Encryption key

gpg> key 2

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb* rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb* rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

gpg> key 2

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

Transfer Authentication key

gpg> key 3

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb* rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

gpg> keytocard
Please select where to store the key:
   (3) Authentication key
Your selection? 3

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb* rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

gpg> key 3

sec  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
[ultimate] (1). Test User <[email protected]>
[ultimate] (2)  Test User <[email protected]>
[ultimate] (3)  [jpeg image of size 640]

Save and quit

gpg> save

Delete Master key

If you’re running off a live operating system or a temporary working directory, this step is not explictily required as the working directory is purged on a reboot.

At this point, all the secret sub keys (ssb) have been moved to the card, hence they do not exist on your system.

Next, you can explictly delete all the secret keys, being only the secret master key (sec) at this point.

$ gpg2 --delete-secret-keys $KEYID
gpg (GnuPG) 2.1.17; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


sec  rsa4096/0xA9A72FB118D8BE6C 2017-02-11 Test User <[email protected]>

Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y

Check smartcard keys

Remove and insert your Yubikey back. Then, verify the secret keys as seen by your GnuPG client.

A # next to sec means that the key doesn’t exist on this device, and a > next to ssb means that the key is a stubkey, located on a smartcard.

$ gpg2 --list-secret-keys
/var/folders/3y/s20pdw8566ld5qqnq7_l55cw0000gn/T/tmp.FbNQHpqU/pubring.kbx
-------------------------------------------------------------------------
sec#  rsa4096/0xA9A72FB118D8BE6C 2017-02-11 [SC] [expires: 2020-02-11]
      Key fingerprint = 19B5 D944 93D0 B285 1E4E  95CF A9A7 2FB1 18D8 BE6C
uid                   [ultimate] Test User <[email protected]>
uid                   [ultimate] Test User <[email protected]>
uid                   [ultimate] [jpeg image of size 640]
ssb>  rsa4096/0x2A6589AD05FFF591 2017-02-11 [S] [expires: 2020-02-11]
ssb>  rsa4096/0xAE50BFB8ACCA36D7 2017-02-11 [E] [expires: 2020-02-11]
ssb>  rsa4096/0x745910FC4FB11754 2017-02-11 [A] [expires: 2020-02-11]

Enable Touch only on Yubikey 4

Optionally, on a Yubikey 4 we can enable a touch-only mode, separately for signing, encryption and authentication key.

When this mode is enabled, the key will blink before running any of the enabled operations. One has to physically touch the key to confirm the operation. This is useful in scenarios where you don’t want any malicious program to perform automated/background GPG operations using your cached keys.

Since this feature is not a part of standard GnuPG client and is only present on the Yubikey 4, only references will be provided here so you can follow up on your own.

Clean up working directory

At this point, you can secure delete the $GNUPGHOME folder and reboot/shut down your key generation machine.

Using the GPG and SSH keys

Now that we have our secret sub keys on the smartcard, and the backups of private/public keys, we can start using it on our personal machines.

GnuPG configuration

Let’s begin with a recommended GnuPG configuration. If you want to use public keyservers, you’ll have to add additional configuration which I’ve omitted here.

On a new machine, list all the keys so that GnuPG client does some first-time setup.

$ gpg2 --list-keys
gpg: keybox '....../.gnupg/pubring.kbx' created
gpg: ....../.gnupg/trustdb.gpg: trustdb created

Create the gpg configuration to be used by the GnuPG client.

$ cat << EOF > ~/.gnupg/gpg.conf
no-emit-version
no-comments
keyid-format 0xlong
with-fingerprint
list-options show-uid-validity
verify-options show-uid-validity
use-agent
fixed-list-mode
charset utf-8
personal-cipher-preferences AES256 AES192 AES CAST5
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
EOF

GPG TTY configuration

This is only required if one is using a terminal based pinentry(also called pinentry-ncurses). If unsure, run the gpgconf command to see what pinentry binary is being used.

Add the following to your .bashrc or similar, so that the GnuPG client is able to figure out the TTY to use for pinentry operations.

# Configure the current TTY for GnuPG client.
export GPG_TTY=$(tty)

Import public GPG key

Import your public gpg key from file.(Get it first from wherever you’ve stored them before).

NOTE: You will not be able to list your secret keys (using --list-secrets-keys) on the smartcard before importing the related public key.

$ gpg2 --import < /mnt/public-gpg-key/public_gpg.key
gpg: key 0xA9A72FB118D8BE6C: public key "Test User <[email protected]>" imported
gpg: Total number processed: 1
gpg:               imported: 1

Now, list your keys.

$ gpg2 --list-keys
....../.gnupg/pubring.kbx
--------------------------------
pub   rsa4096/0xA9A72FB118D8BE6C 2017-02-11 [SC] [expires: 2020-02-11]
      Key fingerprint = 19B5 D944 93D0 B285 1E4E  95CF A9A7 2FB1 18D8 BE6C
uid                   [ unknown] Test User <[email protected]>
uid                   [ unknown] Test User <[email protected]>
uid                   [ unknown] [jpeg image of size 640]
sub   rsa4096/0x2A6589AD05FFF591 2017-02-11 [S] [expires: 2020-02-11]
sub   rsa4096/0xAE50BFB8ACCA36D7 2017-02-11 [E] [expires: 2020-02-11]
sub   rsa4096/0x745910FC4FB11754 2017-02-11 [A] [expires: 2020-02-11]

Verify Smartcard keys

Insert your smartcard, and list the secret keys.

$ gpg2 --list-secret-keys
....../.gnupg/pubring.kbx
--------------------------------
sec#  rsa4096/0xA9A72FB118D8BE6C 2017-02-11 [SC] [expires: 2020-02-11]
      Key fingerprint = 19B5 D944 93D0 B285 1E4E  95CF A9A7 2FB1 18D8 BE6C
uid                   [ unknown] Test User <[email protected]>
uid                   [ unknown] Test User <[email protected]>
uid                   [ unknown] [jpeg image of size 640]
ssb>  rsa4096/0x2A6589AD05FFF591 2017-02-11 [S] [expires: 2020-02-11]
ssb>  rsa4096/0xAE50BFB8ACCA36D7 2017-02-11 [E] [expires: 2020-02-11]
ssb>  rsa4096/0x745910FC4FB11754 2017-02-11 [A] [expires: 2020-02-11]

Use GPG keys

Now, let’s make use of the keys and see if it works. (Maybe we should have done this right after generating keys.)

Export your keyid for this session, so that it’s easy to use. In the long run, you might want to set your default key in the gpg configuration.

export KEYID=0xA9A72FB118D8BE6C

Trust master key

First things first, since this key belongs to us we would like to trust it with ultimate.

$ gpg2 --edit-key $KEYID
gpg (GnuPG) 2.1.17; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

pub  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: unknown       validity: unknown
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
     card-no: XXXX XXXXXXXX
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
     card-no: XXXX XXXXXXXX
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
     card-no: XXXX XXXXXXXX
[ unknown] (1). Test User <[email protected]>
[ unknown] (2)  Test User <[email protected]>
[ unknown] (3)  [jpeg image of size 640]

gpg> trust
pub  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: unknown       validity: unknown
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
     card-no: XXXX XXXXXXXX
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
     card-no: XXXX XXXXXXXX
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
     card-no: XXXX XXXXXXXX
[ unknown] (1). Test User <[email protected]>
[ unknown] (2)  Test User <[email protected]>
[ unknown] (3)  [jpeg image of size 640]

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

pub  rsa4096/0xA9A72FB118D8BE6C
     created: 2017-02-11  expires: 2020-02-11  usage: SC
     trust: ultimate      validity: unknown
ssb  rsa4096/0x2A6589AD05FFF591
     created: 2017-02-11  expires: 2020-02-11  usage: S
     card-no: 0006 05031346
ssb  rsa4096/0xAE50BFB8ACCA36D7
     created: 2017-02-11  expires: 2020-02-11  usage: E
     card-no: 0006 05031346
ssb  rsa4096/0x745910FC4FB11754
     created: 2017-02-11  expires: 2020-02-11  usage: A
     card-no: 0006 05031346
[ unknown] (1). Test User <[email protected]>
[ unknown] (2)  Test User <[email protected]>
[ unknown] (3)  [jpeg image of size 640]
Please note that the shown key validity is not necessarily correct
unless you restart the program.

gpg> quit

Encrypt and Decrypt

NOTE: If you’ve enabled touch-only mode, you’ll have to physically touch the key after it starts blinking.

The following command encrypts the output of a command to our public gpg key and then uses our private gpg key to decrypt it. The decryption will fail if you remove the key.

$ echo "$(uname -a)" | gpg2 --encrypt --armor --recipient $KEYID | gpg2 --decrypt --armor
gpg: encrypted with 4096-bit RSA key, ID 0xAE50BFB8ACCA36D7, created 2017-02-11
      "Test User <[email protected]>"
Darwin computer 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64

Sign and Verify

NOTE: If you’ve enabled touch-only mode, you’ll have to physically touch the key after it starts blinking.

Now, let’s sign a message and verify the signature. The signing process will fail if the key is removed.

$ echo "$(uname -a)" | gpg2 --clearsign --armor --default-key $KEYID | gpg2
gpg: using "0xA9A72FB118D8BE6C" as default secret key for signing
Darwin computer 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64
gpg: Signature made Sat Feb 11 22:45:42 2017 CET
gpg:                using RSA key C7A355855A7C1A19D0907F6D2A6589AD05FFF591
gpg: Good signature from "Test User <[email protected]>" [ultimate]
gpg:                 aka "Test User <[email protected]>" [ultimate]
gpg:                 aka "[jpeg image of size 640]" [ultimate]
Primary key fingerprint: 19B5 D944 93D0 B285 1E4E  95CF A9A7 2FB1 18D8 BE6C
     Subkey fingerprint: C7A3 5585 5A7C 1A19 D090  7F6D 2A65 89AD 05FF F591

Put it all together

NOTE: If you’ve enabled touch-only mode, you’ll have to physically touch the key after it starts blinking.

Putting it all together, let’s encrypt and sign a message for our public key, and then decrypt and verify the message. This should test all the keys at once.

echo "$(uname -a)" | gpg2 --encrypt --sign --armor --default-key $KEYID --recipient $KEYID  | gpg2 --decrypt --armor
gpg: using "0xA9A72FB118D8BE6C" as default secret key for signing
gpg: encrypted with 4096-bit RSA key, ID 0xAE50BFB8ACCA36D7, created 2017-02-11
      "Test User <[email protected]>"
Darwin computer 16.4.0 Darwin Kernel Version 16.4.0: Thu Dec 22 22:53:21 PST 2016; root:xnu-3789.41.3~3/RELEASE_X86_64 x86_64
gpg: Signature made Sat Feb 11 22:48:18 2017 CET
gpg:                using RSA key C7A355855A7C1A19D0907F6D2A6589AD05FFF591
gpg: Good signature from "Test User <[email protected]>" [ultimate]
gpg:                 aka "Test User <[email protected]>" [ultimate]
gpg:                 aka "[jpeg image of size 640]" [ultimate]
Primary key fingerprint: 19B5 D944 93D0 B285 1E4E  95CF A9A7 2FB1 18D8 BE6C
     Subkey fingerprint: C7A3 5585 5A7C 1A19 D090  7F6D 2A65 89AD 05FF F591

Use SSH key

Remember that Authentication sub key we created early on. Now, we can finally use it for SSH connections. One thing to remember is that you cannot extract the private SSH key, since there’s none.

Export the public SSH key if you haven’t already done so.

$ gpg2 --export-ssh-key $KEYID > /somewhere/public_ssh.key

GnuPG Agent configuration

The latest version of GnuPG lazily starts the gpg-agent as needed, hence it is not required to start a gpg-agent process manually. However, this doesn’t work if we enable ssh support via GnuPG. Sadly, we MUST start the gpg-agent manually if we want to enable the ssh support.

Depending on your setup, you need to stop using whatever SSH agent you are running, and delegate the ssh-agent responsibility to gpg-agent.

NOTE: macOS makes it extremely difficult to compeletly disable the built-in ssh-agent, for a good reason. You will have to boot in Recovery(disable SIP) to disable/modify the existing ssh-agent launchagent. Without it, you won’t be able to override the SSH_AUTH_SOCK outside of TTY/shell environments, and hence not be able to use the SSH key on GUI programs.

If you’re using a GUI based pientry or want the ttls to be different, update the configuration below accordingly.

$ cat << EOF > ~/.gnupg/gpg-agent.conf
enable-ssh-support
pinentry-program /usr/local/bin/pinentry
default-cache-ttl 600
max-cache-ttl 7200
default-cache-ttl-ssh 1800
max-cache-ttl-ssh 7200
EOF

NOTE: On older version of GnuPG, you had to explictily specify using use-standard-socket and write-env-file. These parameters are now deprecated.

Restart the gpg-agent to check the configuration.

$ pkill gpg-agent && gpg-agent --daemon
SSH_AUTH_SOCK=....../.gnupg/S.gpg-agent.ssh; export SSH_AUTH_SOCK;

Once you can confirm that the gpg-agent has started properly with the new configuration, we can add the following to .bashrc or similar.

# Launch gpg-agent
gpg-connect-agent /bye

# When using SSH support, use the current TTY for passphrase prompts
gpg-connect-agent updatestartuptty /bye > /dev/null

# Point the SSH_AUTH_SOCK to the one handled by gpg-agent
if [ -S $(gpgconf --list-dirs agent-ssh-socket) ]; then
  export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
else
  echo "$(gpgconf --list-dirs agent-ssh-socket) doesn't exist. Is gpg-agent running ?"
fi

Upload SSH public key

Upload your SSH public key to the servers/services you need access to. If you have a Github account, you can copy add your SSH key as follows and test the connection.

$ gpg2 --export-ssh-key $KEYID | xclip -sel clip

With all the settings above in your .bashrc or similar, open a fresh terminal, insert your key and run the following

$ ssh -T [email protected]
Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access.

Similarly, you should be able to upload your ssh public key to the servers you want SSH access to.

\o/////

This is (mostly) it. We’ve successfully generated GPG keypair with sub keys, stored in on our smartcard and then used it for gpg and ssh tasks.

Additional usage

There are some additional things you can use your smartcard for. Some of the immediate ones that comes to mind are included below.

Sign Git commits and tags

First, let Git know about the GPG key id and the GPG command you want to use. Make sure you’ve exported $KEYID as shown previously.

$ echo $KEYID
$ git config --global gpg.program gpg2
$ git config --global user.signingkey $KEYID

To sign your commits, add the -S flag to your commit command.

$ git commit -S -m "Some commit message"

To set all commits for a repository to be signed by default(in Git versions > 2.0.0)

$ git config commit.gpgsign true

This can also be enabled globally for all commits.

$ git config --global commit.gpgsign true

To sign your tags, add the -s flag to your tag command.

$ git tag -s some-tag

To verify the tag, add the -v flag.

$ git tag -v some-tag

Additionally, you can upload your public GPG key to Github, which will then show your verified commits and tags in their UI. Your GPG key must include a UID with the email associated with Github, else this will not work. Following links should provide more info.

Add GPG key to Keybase

While this could easily be a short post on it’s own, the following is recommended for your Keybase setup. Make sure to replace gpg with gpg2 in the snippets provided on the Keybase website.

Add GPG key to public keyservers

There are public keyservers around the world that you can use to send/receive gpg keys. This does not require any additional tooling as it’s already built into GnuPG for a long time now.

You will however need to configure your gpg.conf to use this. Just remember that while you can send your keys to these servers easily, you cannot just as easily remove them(which is how gpg works). You will have to upload your revocation certificate instead.

Wrap up

Without all the references mentioned throughout the document, this writing would not have been possible. Thanks to all the brave souls who have documented all this work making it easier for use to follow.

Getting all of this to work is a laborious process to follow. While I’ve written alongside following the process, there might have been some mistakes/misunderstanding on my part in the document. Please let me know in the comments section below.

As a final treat, I’ll leave you with these amazing follow up readings.


comments powered by Disqus