Smartcard commands

This is a guide to setup a PGP key with a Yubikey and to create a backup of the keys. The guide is written for MacOS but if you take security seriously, please consider using a dedicated offline Linux machine with Tails OS (GPG commands are mostly the same).

Preliminary steps

brew install gnupg pinentry-mac ykman
export EMAIL="exemple@exemple.com"
echo $EMAIL

Generate pgp keys and create a backup folder

Create backup folder:

mkdir ~/Documents/mykeys
cd ~/Documents/mykeys

Create the master password and the admin pin:

openssl rand -base64 32 > passphrase.txt
openssl rand -base64 32 > adminpin.txt 

Create configuration files:

gpg --list-keys
echo "default-cache-ttl 60\nmax-cache-ttl 120\npinentry-program /opt/homebrew/bin/pinentry-mac" > ~/.gnupg/gpg-agent.conf
gpg-connect-agent /bye
echo "reader-port Yubico Yubi" > ~/.gnupg/scdaemon.conf
gpgconf --reload scdaemon

Generate the master key:

gpg --expert --full-generate-key

gpg (GnuPG) 2.4.3; Copyright (C) 2023 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (13) Existing key
  (14) Existing key from card
Your selection? 8

Possible actions for this RSA key: Sign Certify Encrypt Authenticate 
Current allowed actions: Sign Certify Encrypt 

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

Your selection? s

Possible actions for this RSA key: Sign Certify Encrypt Authenticate 
Current allowed actions: Certify Encrypt 

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

Your selection? e

Possible actions for this RSA key: Sign Certify Encrypt Authenticate 
Current allowed actions: Certify 

   (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? (3072) 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) 0
Key does not expire at all
Is this correct? (y/N) y

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

Real name: simon
Email address: s.glatre@gmail.com
Comment: 
You selected this USER-ID:
    "simon <s.glatre@gmail.com>"

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: directory '/Users/simon/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/Users/simon/.gnupg/openpgp-revocs.d/B897195F0C977FD64539A7DCCC749C9B694007E4.rev'
public and secret key created and signed.

pub   rsa4096 2024-01-31 [C]
      B897195F0C977FD64539A7DCCC749C9B694007E4
uid                      simon <s.glatre@gmail.com>

Generate the subkeys:

gpg --expert --edit-key $EMAIL

gpg (GnuPG) 2.4.3; Copyright (C) 2023 g10 Code GmbH
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/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1). simon <s.glatre@gmail.com>

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
  (14) Existing key from card
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 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) 2y
Key expires at Ven 30 jan 11:03:28 2026 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/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
[ultimate] (1). simon <s.glatre@gmail.com>

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
  (14) Existing key from card
Your selection? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 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) 2y
Key expires at Ven 30 jan 11:03:54 2026 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/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
[ultimate] (1). simon <s.glatre@gmail.com>

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
  (14) Existing key from card
Your selection? 8

Possible actions for this 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? =a
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 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) 2y
Key expires at Ven 30 jan 11:04:34 2026 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/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

gpg> save

Check the result:

gpg --list-public-keys
gpg --list-secret-keys

Backup the keys and useful public information:

gpg --armor --export-secret-keys $EMAIL > master.asc
gpg --gen-revoke $EMAIL > revoke.asc
gpg --armor --export-secret-subkeys $EMAIL > sub.asc
gpg --fingerprint --fingerprint $EMAIL > fingerprints.txt
gpg --armor --export $EMAIL > pub.asc
cp -r ~/.gnupg ./gnupg.backup

Setup the Yubikey

Insert the Yubikey and check that it is recognized:

gpg --card-status

Transfer the subkeys to the Yubikey:

gpg --edit-key $EMAIL

gpg (GnuPG) 2.4.3; Copyright (C) 2023 g10 Code GmbH
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/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

gpg> key 1

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb* rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

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

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb* rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

Note: the local copy of the secret key will only be deleted with "save".
gpg> key 1

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

gpg> key 2

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb* rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

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

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb* rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

Note: the local copy of the secret key will only be deleted with "save".
gpg> key 2

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

gpg> key 3

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb* rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

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

sec  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
ssb* rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
[ultimate] (1). simon <s.glatre@gmail.com>

Note: the local copy of the secret key will only be deleted with "save".
gpg> save

Harden the Yubikey by changing the pin and the admin pin, remember that the admin pin is in adminpin.txt, use a memorable password for the pin:

gpg --change-pin

gpg: OpenPGP card no. D2760001240100000006177802720000 detected

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? 3
PIN changed.

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

Your selection? q

Continue the hardening by setting requiering physical touch on the Yubikey when signing, encrypting or authenticating:

ykman openpgp info
ykman openpgp keys set-touch sig on
ykman openpgp keys set-touch enc on
ykman openpgp keys set-touch aut on
ykman openpgp info

Recreate a fresh environment, with no private information

Reset gpg:

rm -r ~/.gnupg
gpg --list-public-keys
echo "default-cache-ttl 60\nmax-cache-ttl 120\npinentry-program /opt/homebrew/bin/pinentry-mac" > ~/.gnupg/gpg-agent.conf
gpg-connect-agent /bye
echo "reader-port Yubico Yubi" > ~/.gnupg/scdaemon.conf
gpgconf --reload scdaemon

Import the public key and create stubs for the private keys:

gpg --import pub.asc
gpg --list-public-keys
gpg --list-secret-keys
gpg --card-status
gpg --list-secret-keys

Trust your own key with ultimate trust using the “trust” command in gpg:

gpg --edit-key $EMAIL
gpg (GnuPG) 2.4.3; Copyright (C) 2023 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret subkeys are available.

pub  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: unknown       validity: unknown
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
     card-no: 0006 17780272
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
     card-no: 0006 17780272
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
     card-no: 0006 17780272
[ unknown] (1). simon <s.glatre@gmail.com>

gpg> trust
pub  rsa4096/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: unknown       validity: unknown
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
     card-no: 0006 17780272
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
     card-no: 0006 17780272
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
     card-no: 0006 17780272
[ unknown] (1). simon <s.glatre@gmail.com>

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/CC749C9B694007E4
     created: 2024-01-31  expires: never       usage: C   
     trust: ultimate      validity: unknown
ssb  rsa4096/8BAE7179A13643CB
     created: 2024-01-31  expires: 2026-01-30  usage: S   
     card-no: 0006 17780272
ssb  rsa4096/E4B84DE860069A6B
     created: 2024-01-31  expires: 2026-01-30  usage: E   
     card-no: 0006 17780272
ssb  rsa4096/E41FCDFD84F9D4F0
     created: 2024-01-31  expires: 2026-01-30  usage: A   
     card-no: 0006 17780272
[ unknown] (1). simon <s.glatre@gmail.com>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

gpg> save
Key not changed so no update needed.

Encrypt the backup folder

Keep a copy of pub.asc in your documents, you will need it later. Optionally, publish it on a keyserver for others to find it easily.

Use the veracrpyt GUI or LUKS to create the encrypted backup. The password to encrypt the backup must be strong and new.

Delete the mykeys folder when the backup is done.

Test the setup

cd ~/Documents
echo "Privacy is the power to selectively reveal oneself to the world. - Eric Hughes" > message.txt
gpg --encrypt --armor --recipient $EMAIL -o encrypted.asc message.txt
gpg --decrypt encrypted.asc
gpg --armor --clearsign -o signed.asc message.txt
gpg --verify signed.asc

Add SSH support

Add ssh support:

echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent

Replace ssh-agent with gpg-agent by adding to ~/.zshrc:

export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"