Home

Awesome

YubiKey setup memo

EN | JA | ES | ZH

This document describes how to set up a YubiKey.

References

Buy a YubiKey

Note: In my opinion, you don't need to buy 2 YubiKeys if you back up your keys carefully. If you lose a YubiKey, you can restore your keys from the backup. Also, it might be better to revoke the old keys and generate new ones from scratch, as lost keys could be used for malicious purposes.

Install

brew install gnupg
brew install ykman

Reset

ykman openpgp reset
rm -rf ~/.gnupg      # if you already have gpg keys, backup them somewhere
PIN:         123456
Reset code:  NOT SET
Admin PIN:   12345678

Generate gpg keys

Generate a master key and subkeys

Note: Email address must be set to the same email address as your GitHub account. Otherwise, you cannot sign commits with GPG. In my case, 1632335+susumuota@users.noreply.github.com.

gpg --expert --full-gen-key

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? 9

Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (2) Curve 448
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1

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: Susumu OTA
Email address: 1632335+susumuota@users.noreply.github.com
Comment:
You selected this USER-ID:
    "Susumu OTA <1632335+susumuota@users.noreply.github.com>"

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

# input passphrase

pub   ed25519 2023-03-17 [SC]
      KEY_FINGERPRINT_HERE
uid                      Susumu OTA <1632335+susumuota@users.noreply.github.com>
sub   cv25519 2023-03-17 [E]
gpg --list-secret-keys --keyid-format=long
export GPG_KEY_ID="3AA5C34371567BD2"  # replace with your key ID

Add subkeys for A

gpg --expert --edit-key $GPG_KEY_ID

# add a subkey for A

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? 11

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign

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

Your selection? a

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Sign Authenticate

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

Your selection? s

Possible actions for this ECC key: Sign Authenticate
Current allowed actions: Authenticate

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

Your selection? q
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (2) Curve 448
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1
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
Really create? (y/N) y

# input passphrase

gpg> save
gpg -k
-----------------------------
pub   ed25519 2023-03-17 [SC]
      KEY_FINGERPRINT_HERE
uid           [ultimate] Susumu OTA <1632335+susumuota@users.noreply.github.com>
sub   cv25519 2023-03-17 [E]
sub   ed25519 2023-03-17 [A]
brew install hopenpgp-tools
rehash
gpg --export $GPG_KEY_ID | hokey lint

Change expiration time for the subkeys

gpg --expert --edit-key $GPG_KEY_ID

gpg> key 1  # select E key
gpg> expire
Changing expiration time for a subkey.
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) 1y
Key expires at Sun Mar 17 00:41:24 2024 JST
Is this correct? (y/N) y

# input passphrase

gpg> key 1  # unselect E key
gpg> key 2  # select A key
gpg> expire
Changing expiration time for a subkey.
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) 1y
Key expires at Sun Mar 17 00:41:45 2024 JST
Is this correct? (y/N) y

# input passphrase

gpg> save
gpg -k
-----------------------------
pub   ed25519 2023-03-17 [SC]
      KEY_FINGERPRINT_HERE
uid           [ultimate] Susumu OTA <1632335+susumuota@users.noreply.github.com>
sub   cv25519 2023-03-17 [E] [expires: 2024-03-16]
sub   ed25519 2023-03-17 [A] [expires: 2024-03-16]

Backup gpg keys

mkdir -p workspace
sudo mount_tmpfs workspace  # for macOS
# sudo mount -t tmpfs tmpfs workspace  # for Linux
cd workspace
gpg --armor --export-secret-keys $GPG_KEY_ID > gpg_secret_keys.asc
gpg --armor --export-secret-subkeys $GPG_KEY_ID > gpg_secret_subkeys.asc
gpg --armor --export $GPG_KEY_ID > gpg_public_keys.asc
gpg --import gpg_secret_keys.asc
gpg --import gpg_secret_subkeys.asc
gpg --import gpg_public_keys.asc
gpg -k
gpg --output gpg_revoke.asc --gen-revoke $GPG_KEY_ID

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
mkdir -p /Volumes/GPGBAK1/20230317-01
mkdir -p /Volumes/GPGBAK1/20230317-02
cp -p gpg_* /Volumes/GPGBAK1/20230317-01
cp -p gpg_* /Volumes/GPGBAK1/20230317-02
mkdir -p /Volumes/GPGBAK2/20230317-01
mkdir -p /Volumes/GPGBAK2/20230317-02
cp -p gpg_* /Volumes/GPGBAK2/20230317-01
cp -p gpg_* /Volumes/GPGBAK2/20230317-02

Configure YubiKey

gpg --card-edit
gpg/card> admin
gpg/card> kdf-setup
gpg/card> list       # KDF setting ......: on
gpg/card> passwd

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

# select 1, 3, 4 then q

gpg/card> quit  # needs to quit to save PIN

Set name, etc.

gpg --card-edit
gpg/card> admin
gpg/card> name
gpg/card> salutation
gpg/card> lang
gpg/card> login
# gpg/card> url   # setup this later
gpg/card> list
gpg/card> quit

Transfer keys to YubiKey

Note: Transferring keys to YubiKey using keytocard is a destructive, one-way operation only. Make sure you have a backup of your keys before proceeding.

gpg --edit-key $GPG_KEY_ID

gpg> keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1

# input passphrase and PIN

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

# input passphrase and PIN

gpg> key 1  # unselect E key
gpg> key 2  # select A key
gpg> keytocard
Please select where to store the key:
   (3) Authentication key
Your selection? 3

gpg> save
gpg --card-edit

# confirm these lines

Key attributes ...: ed25519 cv25519 ed25519

Signature key ....: xxxx xxxx xxxx xxxx xxxx  xxxx xxxx xxxx xxxx xxxx
      created ....: 2023-03-17 15:30:34
Encryption key....: xxxx xxxx xxxx xxxx xxxx  xxxx xxxx xxxx xxxx xxxx
      created ....: 2023-03-17 15:30:34
Authentication key: xxxx xxxx xxxx xxxx xxxx  xxxx xxxx xxxx xxxx xxxx
      created ....: 2023-03-17 15:35:09

gpg/card> quit

Test sign, encrypt, verify and decrypt

echo test > test.txt
gpg -sea --default-recipient-self test.txt  # encrypt and sign
gpg -d test.txt.asc                         # verify and decrypt
gpg -d test.txt.asc
gpg -d test.txt.asc
cd ..
sudo umount workspace

SSH public key authentication

brew install pinentry-mac  # for macOS
rehash
which pinentry-mac  # /usr/local/bin/pinentry-mac
cd ~/.gnupg
wget https://raw.githubusercontent.com/drduh/config/master/gpg-agent.conf
# edit gpg-agent.conf, comment out default pinentry-program and enable pinentry-mac
# pinentry-program /usr/local/bin/pinentry-mac
# https://github.com/drduh/config/blob/master/gpg-agent.conf
# https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html
enable-ssh-support
ttyname $GPG_TTY
default-cache-ttl 60
max-cache-ttl 120
#pinentry-program /usr/bin/pinentry-curses
#pinentry-program /usr/bin/pinentry-tty
#pinentry-program /usr/bin/pinentry-gtk-2
#pinentry-program /usr/bin/pinentry-x11
#pinentry-program /usr/bin/pinentry-qt
#pinentry-program /usr/local/bin/pinentry-curses
pinentry-program /usr/local/bin/pinentry-mac
export GPG_TTY="$(tty)"
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
ssh-add -L | grep "cardno" > ~/.ssh/id_ed25519_yubikey.pub
ssh -p 10022 ota@pi3.local
emacs ~/.ssh/authorized_keys
# add the content of id_ed25519_yubikey.pub
exit
cat << EOF >> ~/.ssh/config
Host pi3.local
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_ed25519_yubikey.pub
EOF
emacs ~/.ssh/known_hosts
# delete all of the [pi3.local] lines
ssh -p 10022 ota@pi3.local -vvv

GitHub signing commits

cp -p ~/.gitconfig ~/.gitconfig.bak
gpg --armor --export $GPG_KEY_ID
git config --global --unset gpg.format
cat ~/.gitconfig
gpg --list-secret-keys --keyid-format=long  # copy your key ID
export GPG_KEY_ID="3AA5C34371567BD2"        # replace with your key ID
git config --global user.signingkey $GPG_KEY_ID
git config --global commit.gpgsign true     # add this to omit -S option
cat ~/.gitconfig
diff -u ~/.gitconfig.bak ~/.gitconfig
git add README.md
git commit -m "test"  # or git commit -S -m "test" if you didn't set commit.gpgsign true
git push origin main

Two-factor authentication (2FA)

FIDO U2F (Universal 2nd Factor)

gpg -sea --default-recipient-self recovery.txt  # encrypt and sign
gpg -d recovery.txt.asc                         # verify and decrypt

Time-based one-time password (TOTP)

gpg -sea --default-recipient-self secret_key.txt  # encrypt and sign
gpg -d secret_key.txt.asc                         # verify and decrypt
ykman oath accounts list
# Amazon:foo@example.com
# Google:foo@example.com
# Twitter:foo
# Twitter:bar
ykman oath accounts code
# Amazon:foo@example.com  111111
# Google:foo@example.com  222222
# Twitter:foo             333333
# Twitter:bar             444444

That's all.

License

MIT

Author

S. Ota

https://github.com/susumuota/yubikey-setup