I’ve spent the day trying to get this setup working with GitHub and given the number of gotcha’s I encountered, it seemed like a good idea to document how I finally got this working with as few hacks as possible. There’s a lot of documentation out there (some of it old and misleading) and committing here for posterity will help me remember this when I inevitably need to do this again.
Rationale
Passwords are simply not enough these days. Regardless of the company, breaches (and the associated Personally Identifiable Information harvested) are a matter of not if, but when. There are a number of things you can do to protect yourself, but being on the tin-foil-hat side of paranoia, means there are a few Commandents that I adhere to (and recommend for other folks)[Insert link to Fight Club Rules for the Secure Internet].
That being said, if you use 2-factor authentication and have committed to using a hardware token such as the Yubikey, then you’re already ahead of the curve. The problem is that while this has been broadly adopted in enterprise, it still feels like the bleeding edge. It doesn’t help that the Yubikey documentation and tools are out-of-date and possibly not fully supported on MacOS. This doc is to address this gap and bring awareness to the somewhat-mainstream.
The Yubikey 5
The instructions below are for the Yubikey 5 (which is what I have). If you have an older version of the key, the instructions below may not work for you and you may want to consider upgrading to a newer version.
Relevant Features
- There are USB-C and USB-A versions of this key which make it compatible with most devices. CAVEAT: I don’t have an NFC version, so have not tested to see if these instructions work on these keys as well. If you have one and try these instructions (with or without success), post your experiences below in the comments.
- Supports FIDO / U2F / PIV-compatible Smart Card / OpenPGP. It’s these last two that we’ll focus on here as these are relevant to getting the key to provide ssh authentication credentials to a service like GitHub.
- As of writing, FIDO/U2F support for OpenSSH is from 8.2+. MacOS currently ships with
OpenSSH_8.1p1, LibreSSL 2.7.3
, so won’t be covering this.
- As of writing, FIDO/U2F support for OpenSSH is from 8.2+. MacOS currently ships with
- Support for the following type of cryptographic keys -
RSA 2048, RSA 4096 (PGP), ECC p256, ECC p384
.- Older versions of the key may not have full support for the strongest/newest ciphers. For example the neo 4 was limited to
RSA 2048
and was affected by this bug - YSA-2017-01 – Infineon weak RSA key generation
- Older versions of the key may not have full support for the strongest/newest ciphers. For example the neo 4 was limited to
- Out-of-the box support for a lot of services which can use one-time-passwords (OTP) generated by the key.
OpenSSH Keys vs. OpenPGP Keys
Given that both systems use keys, why can’t you use one with the other?
TL;DR - OpenSSH was designed to secure a stream of network communication vs. OpenPGP which was designed to encrypt the contents of a message.
Usually, when you first configure OpenSSH, you generate a keypair (a private key + a public key). This keypair is then used to access remote systems by transferring the public key to the remote machine and disabling PasswordAuthentication
in the /etc/ssh/sshd_config
file. Keys are usually stored in the local or remote machine’s ~/.ssh
directory. The ~/.ssh/authorized_keys
file on the remote machine contains the public key of the generated key pair which is used for authentication purposes.
OpenPGP on the other hand is designed as a general purpose decentralized public key infrastructure (PKI) for communicating encrypted messages which may be sent over a different network protocol (such as SMTP for mail, FTP, or even SSH). Instead of a single keypair, OpenPGP also allows for the generation of subkeys which can have specific capabilities enabled/disabled. This allows for the generation of purpose specific keys which are associated with a master key. We’ll use this technique to generate subkey pairs for authentication, encryption, and signing.
The trick for bridging this gap is to allow OpenPGP to act as an authentication secrets provider for OpenSSH.
OK. So where do I start?
We begin by creating an OpenPGP keypair. On of the key features of this keypair is the associated email address. In other words, every key MUST be associated with a valid email address*. I use Protonmail for this purpose as it provides encryption at for all of my email messages and it supports generating OpenPGP keys of various strengths (RSA2048, RSA4096, EdDSA256
). Previously, Protonmail only supported RSA2048
keys, but now includes newer/stronger ciphers which have various security/performance trade-offs.
I began generating an RSA4096 key using these instructions - Key management.
If you have a different email address you would like to create a keypair for, you can also generate a key using the gpg
command line (which you can install with Homebrew or alternatively, install GPGSuite).
NOTE: The generated key must be protected by a password. You’ll need this later for when you import the key into your OpenPGP keychain.
Why choose RSA4096
for the OpenPGP key cipher?
It seems that there are flaws within elliptic curve algorithms that suggest it’s better to go with the slower, but proven RSA cipher. For more background see - ECDSA: Handle with Care
Also, RSA is well supported by the Yubikey 5 as well as Github ;-)
Why don’t we generate an OpenPGP key on the Yubikey instead of creating one externally?
You can interact directly with the OpenPGP application on the Yubikey 5 to generate an OpenPGP keypair, but there are a number of implications when doing so:
- The private key remains solely on the device which means not being able to back up the private key (as only a stub is exported).
- In the event you lose of your physical key, you’re pretty much shit out of luck :( . If you’ve seen the size of the Yubikey 5c, you’ll know how real of a possibility it actually is…
Since I’m re-purposing my Protonmail OpenPGP key, I’ll be showing how to import this into the Yubikey.
The Yubikey
There are a number of articles in the dev/support sections of the Yubico site which would lead you to suspect that the tools they provide support configuration of OpenPGP on the Yubikey 5, but having put them to the test, I would say that the docs are woefully inadequate. To save you time (and future me from wasting more of it), I’ve documented the limitations here.
Yubikey’s default state of insecurity
Strangely, when I started down this rabbit hole, I expected that a company focused on security solutions would focus their user experience around, you know - “security”. Furthermore, the defaults for configuring the PIV functionality provide defaults which are easily found on the interwebs :(
For the record, the default PINs associated with the PIV functionality are:
- default PIN:
123456
- admin PIN:
12345678
(this should really be referred to as the Pin Unlock Key) - management key:
010203040506070801020304050607080102030405060708
What’s missing is a walk-through which should do the following:
- Provide installation instructions to download the latest version of the Yubikey Manager. Bonus points if previous versions are aware they are out-of-date and provide rolling updates.
- Provide a warning when you first insert your Yubikey and the Manager application recognizes it to provide new/secure PINs to override these known defaults.
- Provide a means to configuring the retries counts for the various PINs (preferrably without wiping any already configured PINs)
- Provide a means for verifying that the PIN changes have been persisted to the device.
WARNING* By default, the number of retries for PIN entry is 3. If you fail to provide the correct PIN, then that type of PIN is locked. You will need to use the admin PIN/PUK to unlock the device. In the PUK is entered incorrectly more than the number of configured retries, the device is locked and must be reset back to the factory defaults (resulting in the loss of all data/keys on the device)..
Here are some background resources provided by Yubico regarding these PINs/keys:
- PIN and Management Key
- Accessing administrative functions <- Note that this matrix is the ONLY place which documents that changing the retries count will RESET THE PINS TO THE DEFAULTS. Why dear God?
- PIV-enabled YubiKeys <- Mentions the PIVv default PINs/keys.
- pkirkovsky/yubikey-reset.sh <- User supplied way of resetting the Yubikey to the factory defaults using gpg. Not included in the official Yubico documentation.
NOTE: As we are not generating the OpenPGP keys on the device, but importing our external keys generated previously, we will NOT be using the PIV Attestation feature.
Yubikey Manager
I started out with an older version of this tool which did not have support for the Personal Identity Verification (PIV) functionality required to enable certain Smart Card functionality to import the various OpenPGP keys we need for our use case. Unfortunately, documentation using this tools is sparse and the terminology used for the various certificate management stores differs from how gpg
refers to it. YMMV.
NOTE: I was able to configure full functionality without using this application, but I noticed that although my key is configured with the certificates, this tool does NOT recognize it as such.
WARNING: When configuring another Yubikey, I thought to give this application a spin. I set the PINs and generated a new Management Key, but when attempting to use these settings with
ykman
the PINs were unrecognized. YMMV, but I would recommend setting them both ways to be sure. Also see the warning regarding Management Key generation below. Bad Yubico.
ykman
Searching the interwebs for how to enable certificates on the Yubikey brings up a lot of documents which refer to this CLI utility. As of writing, this tool was unstable, resulting in subsequent commands failing with the following error:
Usage: ykman [OPTIONS] COMMAND [ARGS]...
Try "ykman -h" for help.
Error: Failed connecting to YubiKey 5 [OTP+FIDO+CCID]. Make sure the application have the required permissions.
Not exactly informative… For example, attempting to use the openpgp functionality would repeatedly result in the above message, despite various info
commands returing successfully :(
NOTE: This failure may be due to locking of the
scdaemon
resposible for communicating with the Yubikey’s Smart Card functionality, but I didn’t try to get to the bottom of it either…
UPDATE 18.10.2020: The response from Yubico Support -
The overall issue I believe you are running into is one we've seen on macOS specifically with YubiKey functions that use its CCID (OpenPGP, PIV, and OATH). Essentially, intermittently on macOS, when you attempt to access these functions, they will not respond, and the YubiKey must be reinserted (sometimes multiple times) until things start working again. The intermittent nature has made it difficult for us to resolve, but from what we have uncovered, it doesn't seem to be an issue with our products.
Not sure I agree with the last statement though.
Here’s the version info reported for the tool (which was installed with Homebrew):
% ykman --version
YubiKey Manager (ykman) version: 3.1.1
Libraries:
libykpers 1.20.0
libusb 1.0.23
Resetting the PINs using ykman piv
Here’s the man page for this sub-command:
% ykman piv
Usage: ykman piv [OPTIONS] COMMAND [ARGS]...
Manage PIV Application.
Examples:
Generate an ECC P-256 private key and a self-signed certificate in
slot 9a:
$ ykman piv generate-key --algorithm ECCP256 9a pubkey.pem
$ ykman piv generate-certificate --subject "yubico" 9a pubkey.pem
Change the PIN from 123456 to 654321:
$ ykman piv change-pin --pin 123456 --new-pin 654321
Reset all PIV data and restore default settings:
$ ykman piv reset
Options:
-h, --help Show this message and exit.
Commands:
attest Generate a attestation certificate for a key.
change-management-key Change the management key.
change-pin Change the PIN code.
change-puk Change the PUK code.
delete-certificate Delete a certificate.
export-certificate Export a X.509 certificate.
generate-certificate Generate a self-signed X.509 certificate.
generate-csr Generate a Certificate Signing Request (CSR).
generate-key Generate an asymmetric key pair.
import-certificate Import a X.509 certificate.
import-key Import a private key.
info Display status of PIV application.
read-object Read arbitrary PIV object.
reset Reset all PIV data.
set-ccc Generate and set a CCC on the YubiKey.
set-chuid Generate and set a CHUID on the YubiKey.
set-pin-retries Set the number of PIN and PUK retries.
unblock-pin Unblock the PIN.
write-object Write an arbitrary PIV object.
NOTE: Notice that there are only two
change
commands - one for the PUK/admin PIN, and a “PIN”. This man page is unhelpful with regards to specifying which of the PINs can be changed. For the record,change-pin
command changes the Default PIN. See below.
Order is important. Set the number of PIN retries first!
For some reason, if you reset the number of retries, you will inadvertently reset the Default PIN and the PUK PIN (probably due to how these are stored on the device). You should do this first in order to prevent you from locking yourself out/disabling the device. By default you are allow 3 retries for either PIN (which doesn’t provide much of a margin of error). I set mine to 5 (NOTE: I use the Default PIN / Management Key from above):
% ykman piv set-pin-retries --help
Usage: ykman piv set-pin-retries [OPTIONS] PIN-RETRIES PUK-RETRIES
Set the number of PIN and PUK retries. NOTE: This will reset the PIN and PUK to their factory defaults.
Options:
-m, --management-key TEXT The management key.
-P, --pin TEXT PIN code.
-f, --force Confirm the action without prompting.
-h, --help Show this message and exit.
mel@zed protonmail % ykman piv set-pin-retries 5 5
Enter PIN:
Enter a management key [blank to use default key]:
WARNING: This will reset the PIN and PUK to the factory defaults!
Set PIN and PUK retry counters to: 5 5? [y/N]: y
Default PINs are set.
PIN: 123456
PUK: 12345678
Setting the Admin/PUK PIN
First order of business is to set a PUK so that you can unlock the device should should accidentally exceed the PIN retries. It’s unhelpful that the Yubico documentation refers to this PIN using two different terms (PUK/admin). The are one and the same :( . Also note the inconsistent use of the -p/-P
option for specifying the PIN argument for the commands.
(We use the default admin PIN here (12345678) for the device and NNNNNNNN as the new PUK PIN):
% ykman piv change-puk -p 12345678 -n NNNNNNNN
New PUK set.
Unfortunately, the tool provides no mechanism for verifying that the change has been applied :(
Setting the Default PIN
You can also change the Default PIN using ykman
. (replace NNNNNNNN a the new Default PIN):
% ykman piv change-pin -P 123456 -n NNNNNNNN
New PIN set.
PIN Constraints
It may not be clear that PINs can be comprised of the following characteristics:
- shoud not contain all or part of the user’s account name.
- can contain characters from three of the following four categories:
- English uppercase characters (A through Z)
- English lowercase characters (a through z)
- Base 10 digits (0 through 9)
- Nonalphanumeric characters (e.g., !, $, #, %)
There are additional Considerations when using a PIN for PIV management, most notably:
There are certain security and usability considerations which should be taken into account when using the PIN for PIV management, instead of a Management Key. The way this feature works, is that a Management Key is still used, but it is cryptographically derived from your PIN by the YubiKey PIV Manager, behind the scenes. One implication of this is that the Management Key changes whenever you change your PIN, and it is therefore crucial that you ONLY change your PIN using the YubiKey PIN Manager. Changing it using an external tool will render the YubiKey PIV Manager unable to derive the Management Key.
Setting the Management Key
You can change the Management Key using the following commands (where a new key is automatically generated and protected with the PIN. (replace XXXXXXXX with the Default PIN).
WARNING: As per the previous section, DO NOT attempt to specify your own Management Key as it is cryptographically derived from your PIN).
% ykman piv change-management-key -P XXXXXXXX -g -p
WARNING: This command for some reason does not entirely work and WILL NOT DISPLAY THE NEWLY GENERATED MANAGEMENT KEY. You may need to use the Yubikey Manager application to set this correctly.
Enabling touch for OpenPGP on the Yubikey
After fiddling with the ykman
tool, I was able to enable specific openpgp functionality by using the following commands. A few things to note:
- Interactive mode blocks on entry of the carriage return (it won’t accept it for some strange reason)
- The sub-command you need is
set-touch
NOTtouch
as some older documentation refers to.
% ykman openpgp set-touch --help
Usage: ykman openpgp set-touch [OPTIONS] KEY POLICY
Set touch policy for OpenPGP keys.
KEY Key slot to set (sig, enc, aut or att).
POLICY Touch policy to set (on, off, fixed, cached or cached-fixed).
The touch policy is used to require user interaction for all operations using the private key on the YubiKey. The touch policy is set indivdually for each key slot. To see the current touch policy, run
$ ykman openpgp info
Touch policies:
Off (default) No touch required
On Touch required
Fixed Touch required, can't be disabled without a full reset
Cached Touch required, cached for 15s after use
Cached-Fixed Touch required, cached for 15s after use, can't be disabled
without a full reset
Options:
-a, --admin-pin TEXT Admin PIN for OpenPGP.
-f, --force Confirm the action without prompting.
-h, --help Show this message and exit.
By default, the OpenPGP touch policies are disabled. You can see that by running the following command:
% ykman openpgp info
OpenPGP version: 2.1
Application version: 5.1.0
PIN tries remaining: 3
Reset code tries remaining: 3
Admin PIN tries remaining: 3
Touch policies
Signature key Off
Encryption key Off
Authentication key Off
WARNING: Be aware of the number of retries which can potentially lock the device if you fail to provide the correct PIN.
You can enable each of these functions non-interactively by using the following commands (replace XXXXXXXX with your admin/PUK PIN for the device):
% ykman openpgp set-touch enc on -a XXXXXXXX -f
% ykman openpgp set-touch aut on -a XXXXXXXX -f
% ykman openpgp set-touch sig on -a XXXXXXXX -f
You can verify that these touch settings have been enabled by running the ykman openpgp info
again:
% ykman openpgp info
OpenPGP version: 2.1
Application version: 5.1.0
PIN tries remaining: 3
Reset code tries remaining: 3
Admin PIN tries remaining: 3
Touch policies
Signature key On
Encryption key On
Authentication key On
yubico-priv-tool
I found additional references to this utility which looked like it would solve my initial configuration issues. Like ykman
above, permission errors are reported when attempting to use this tool.
Here’s the version information from the version of this tool I attempted to use:
% yubico-piv-tool --version
yubico-piv-tool 2.1.1
Verifying PIN retry changes
One useful application of yubico-priv-tool
was to verify the change in PIN retries.
% yubico-piv-tool -astatus
Version: 5.1.0
Serial Number: 086XXXXX
CHUID: XXXX
CCC: XXXX
PIN tries left: 5
The Secret Sauce - gpg
There are a number of articles which I used as a guide for determining what actually works, here are a few of them:
- OpenPGP SSH access with Yubikey and GnuPG
- Using Your YubiKey with OpenPGP
- PGP - Introduction
- Easy multifactor authentication for SSH using YubiKey NEO tokens
- Using GnuPG (2.1) for SSH authentication
- Setting up ssh public key authentication on macOS using a YubiKey 4
The rest of this document goes into the OpenPGP configuration and how to configure and load the keys onto the Yubikey.
Generating the individual subkeys
Recognizing the Yubikey as a Smart Card
We begin by looking at whether or not gpg
can see your Yubikey as a Smart Card:
% % gpg-connect-agent --hex "scd apdu 00 f1 00 00" /bye
D[0000] 05 01 00 90 00 .....
OK
The 05 01 00
in the response tells us that this Yubikey is using version 5.1.0
.
In addition, you can check the Yubikey’s Smart Card status using the following command. This is what mine looked like prior to configuration:
% gpg --card-status
Reader ...........: Yubico YubiKey OTP FIDO CCID
Application ID ...: D276XXXXXXXXXXXXXXXXXXXXXXXXXXXX
Application type .: OpenPGP
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: 086XXXXX
Name of cardholder: [not set]
Language prefs ...: [not set]
Salutation .......:
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa4096 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 0
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]
Generating an OpenPGP key (if you already don’t have one)
I’ll be using my Protonmail key for the purposes of this example, but for those not using Protonmail or who want to start with a fresh keypair, you can generate a key using the instructions here - Generate a Key
As mentioned previously, I use
RSA4096
as my cipher for various reasons, but feel free to modify this based on your own use case.
Assumptions
The following instructions assume that you’ve generated your OpenPGP keypair and that it has been imported into your keychain and granted Ultimate
Ownertrust.
Finding your OpenPGP Key ID
For example, in the following output:
% gpg --fingerprint
/Users/mel/.gnupg/pubring.kbx
-----------------------------
pub dsa2048 2010-08-19 [SC] [expires: 2024-05-11]
85E3 8F69 046B 44C1 EC9F B07B 76D7 8F05 00D0 26C4
uid [ unknown] GPGTools Team <team@gpgtools.org>
sub rsa4096 2014-04-08 [S] [expires: 2024-05-11]
sub rsa4096 2020-05-11 [E] [expires: 2024-05-11]
00D0 26C4
represents the Short Key ID76D7 8F05 00D0 26C4
represents the Long Key ID
WARNING: It is recommended that Long Key IDs are used as the collision space for Short Key IDs may result in duplicate identifiers.
Editing your OpenPGP key
We’ll be using gpg
’s interactive mode to add subkeys to our primary OpenPGP key. (**replace XXXXXXXX with the associated Key ID for your generated keypair):
% gpg --expert --edit-key XXXXXXXXXXXXXXXX
gpg (GnuPG/MacGPG2) 2.2.20; Copyright (C) 2020 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/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
[ultimate] (1). mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
You’ll note that there are two components to the key:
sec
- the secret partssb
- the subkey part. By default, we already have an encryption subkey (specified byusage: E
)
Create the Authentication Subkey
Since we’re using RSA4096
as our base cipher, we’ll be creating a similar Authenticataion subkey.
NOTE: Be default, subkeys have
Sign
andEncrypt
allowed actions enabled. You need to toggle their states to disable them. Current subkey capabilities are enumerated in theCurrent allowed actions:
field.
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 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) 0
Key does not expire at all
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/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
One the operation completes, you should see a new ssb
or subkey listed with your other keys.
[Optional] Create the Signing Subkey
Proceed as before in the previous section, but ensure that the subkey generated has on the sign capability
. Again, completing this operation should result in another ssb
/subkey entry for gpg
:
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/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: S
Save your configuration and export your modified private key for safe keeping
Quit out of the gpg
utility. This should prompt you to save your changes.
gpg> quit
Save changes? (y/N) y
% gpg --export-secret-key --armor XXXXXXXXXXXXXXXX > private_with_subkeys.key
WARNING: It’s important not to underestimate the value of your secret key. Anyone who can potentially copy/read it can impersonate you. It is recommended that you store it securely (and potentially offline).
Moving the subkeys to the Yubikey
Once the appropriate subkeys are generated, we’ll move them to be exclusively be accessible through the Yubikey Smart Card interface. This means that after this operation, removing the Yubikey from the system will also remove access to the associated OpenPGP key.
% gpg --expert --edit-key XXXXXXXXXXXXXXXX
gpg (GnuPG/MacGPG2) 2.2.20; Copyright (C) 2020 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/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: S
[ultimate] (1). mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
gpg> toggle
sec rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: S
[ultimate] (1). mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
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
After moving you primary key, you will need to select the individual subkeys for migration. Unfortunately, this is somewhat obtuse in the gpg
interactive mode. You can select keys by specifying their ssb
ordinal. The usage
determines where the key will be stored on the Yubikey/Smart Card.
NOTE: The selected subkey can be identified by the * appended to the
ssb
identifier.
WARNING: Key selection, like subkey generation, works with toggles. To disable a selected key, you MUST reissue the selection command AGAIN.
gpg> key 1
sec rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb* rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: S
[ultimate] (1). mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
gpg> keytocard
Please select where to store the key:
(2) Encryption key
Your selection? 2
gpg> key 1
sec rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: S
[ultimate] (1). mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
gpg> key 2
sec rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb* rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: S
[ultimate] (1). mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
gpg> keytocard
Please select where to store the key:
(3) Authentication key
Your selection? 3
sec rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: E
ssb* rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: A
ssb rsa4096/XXXXXXXXXXXXXXXX
created: 2020-10-14 expires: never usage: S
[ultimate] (1). mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
gpg> quit
Save changes? (y/N) y
There are only 3 available slots for certificates on the Yubikey/Smart Card. Note that we did not end up transferring our generated Signing subkey. This is OK given the that private key uploaded has the Signing capability enabled (usage: SC
).
Verify that the OpenPGP subkeys have been associated with the Yubikey
You can list -K
or --list-secret-key
argument for gpg
to verify that the OpenPGP keys have been migrated to the Yubikey. In particular, you should be able to the the associated secret (sec
), the three subkeys (ssb
) and a reference to the Yubikey Smart Card (Card serial no.
):
% gpg -K
/Users/mel/.gnupg/pubring.kbx
-----------------------------
... [REDACTED]
sec> rsa4096 2020-10-14 [SC]
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Card serial no. = 0006 086XXXXX
uid [ultimate] mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
ssb> rsa4096 2020-10-14 [E]
ssb> rsa4096 2020-10-14 [A]
ssb rsa4096 2020-10-14 [S]
... [REDACTED]
Alternatively, you can use gpg --card-status
.
Configure gpg-agent
to act as an authentication source for ssh-agent
This section assumes that you already have gpg-agent
running on your system (which should be true if you installed the GPG Suite mentioned earlier). The documentation I previously found described a bunch of hacks/workarounds. I used that material to describe a process that worked for me using the default functionality of gpg
and ssh
.
Enable ssh
support for the gpg-agent
Append the following line to your ~/.gnupg/gpg-agent.conf
file:
enable-ssh-support
Restart your gpg-agent
by sending the process the appropiate signal via kill
.
Identify the keygrip
associated with your OpenPGP key
This is required to identify which secrets can be passed through to the ssh-agent
.
WARNING: For the purposes of integration, we will be using ONLY the Authentication subkey.
% gpg2 --with-keygrip -k mel.llaguno@protonmail.com
... [REDACTED]
pub rsa4096 2020-10-14 [SC]
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Keygrip = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid [ultimate] mel.llaguno@protonmail.com <mel.llaguno@protonmail.com>
sub rsa4096 2020-10-14 [E]
Keygrip = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
sub rsa4096 2020-10-14 [A]
Keygrip = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX <<<< We will need this one.
sub rsa4096 2020-10-14 [S]
Keygrip = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
... [REDACTED]
Configure the Authentication Subkey Keygrip
Add the keygrip to the following file (which may not exist) - ~/.gnupg/sshcontrol
.
Confgure your shell to export the SSH_AUTH_SOCKET
environment variable
Add the following line to the appropriate start up file for your shell:
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
Verfiy that this environment variable is present:
% env | grep SSH
SSH_AUTH_SOCK=/Users/mel/.gnupg/S.gpg-agent.ssh
If noting is listed, then the environment variable has not been properly exported.
Test the visibility of your Encryption OpenPGP subkey
If everything is working correctly, your Authentication subkey should now be visible to ssh
. You can verify using the following commands:
% ssh-add -l
4096 SHA256:aLsYjSwRYVXc9WU6hrW0NjVIi4axQlN17DOhw5c2R7w cardno:0006086XXXXX (RSA)
NOTE: The
cardno
should be the same as the one associated with your Yubikey Smart Card ingpg
.
To generate the public portion of this new Authentication ssh key, use the following command:
% ssh-add -L
ssh-rsa AAAA...[REDACTED]...spqw== cardno:0006086XXXXX
This is the public key that you can upload to remote servers for authentication purposes.
Last but not least - Configure GitHub with the public key of the OpenPGP Authentication Subkey.
Instructions
To complete this last step, you will need to be logged into your GitHub account.
- Navigate to your https://github.com/settings/keys
- Create an
New SSH Key
- Paste in the output of the
ssh-add -L
command above. Label the key and save. - Attempt to pull/checkout a repository with your github account. When you first attempt to use the Yubikey, you should be prompted for the default PIN. Enter it to continue (and be mindful of the number of retries you have before the Yubikey is locked).
- If you’ve provided your default PIN correctly, you should notice the LED on your Yubikey flashing. Simply touch it to proceed.
Voila! You’re finally done.
Addendum - Code Signing
Now that we have OpenPGP support for GitHub, we can use the generated Signing subkey with GitHub commits. The official instructions are here - Telling Git about your signing key .
Be sure to use the separate Signing subkey for this purpose.
NOTE: This only works if you’ve enabled public visibility to your primary email address associated with your GitHub account and that address is the same as the one associated with the certificate we used for this process.
TODO
- code signing
- revocation