Set up a YubiKey
Set up a YubiKey for GPG and SSH (for Git)
This guide describes recommended steps to set up a YubiKey for use with GPG (for commit signing) and SSH (for Git authentication with GitHub).
⚠️ Warning
Once you have followed this guide, your YubiKey is effectively now your only way to get into AWS or to Sign or push commits up to GitHub.
You should treat your YubiKey with care - do not lose it and do not leave it unattended unless you are prepared to get locked out of your systems.
Pre-Requisites
This guide assumes you are using a GDS-issued “Developer Build” MacBook Pro. We will predominantly use the CLI/terminal to interact with the YubiKey as this gives us the greatest flexibility with configuration.
You will need at least one YubiKey. We recommend having two: keep one in daily use and store a second as an offline backup in case the primary key is lost or damaged.
Recommended Devices
Recommended: YubiKey 5C NFC.
Alternative form factors: YubiKey 5C and YubiKey 5C Nano (if you prefer a smaller device).
⚠️ Note
Yubico sells another product called YubiKey Security Key, which is a FIDO-only device that does not support the features required to perform commit signing or SSH authentication. Make sure you are requesting or purchasing the correct device.
If you need to obtain a YubiKey, speak to your Delivery Manager or Tech Lead about how to get one - alternatively, you may be able to expense them with approval from your Line Manager.
Software Dependencies
You will first need to make sure the required software dependencies are installed and on the latest versions:
brew update
brew install gnupg
brew install libfido2
brew install openssh
brew install openssl
brew install ykman
Yubikey Initial Checks
Insert your YubiKey and open your favourite terminal. Run ykman list - you should see a result like this:
YubiKey 5C NFC (5.7.1) [OTP+FIDO+CCID] Serial: 31234567
The output might change slightly depending on the model of Yubikey and the Firmware version - for security reasons, Firmwares are permanently fixed and cannot be flashed or updated, as Yubikey operates on a “black box” security model.
You can also confirm which applications are supported and enabled or disabled on your YubiKey by running ykman info - you should get output like this:
Device type: YubiKey 5C NFC
Serial number: 31234567
Firmware version: 5.7.1
Form factor: Keychain (USB-C)
Enabled USB interfaces: FIDO, CCID
NFC transport is enabled
Applications USB NFC
Yubico OTP Enabled Enabled
FIDO U2F Enabled Enabled
FIDO2 Enabled Enabled
OATH Enabled Enabled
PIV Enabled Enabled
OpenPGP Enabled Enabled
YubiHSM Auth Enabled Enabled
If you see an output that resembles something like this…
Device type: YubiKey Security Key
Serial number: 31234567
Firmware version: 5.7.1
Form factor: Keychain (USB-C)
Enabled USB interfaces: FIDO
NFC transport is enabled
Applications USB NFC
Yubico OTP Not Supported Not Supported
FIDO U2F Enabled Enabled
FIDO2 Enabled Enabled
OATH Not Supported Not Supported
PIV Not Supported Not Supported
OpenPGP Not Supported Not Supported
YubiHSM Auth Not Supported Not Supported
…then you have a YubiKey Security Key and you’ll need to stop here and get a “proper” YubiKey 5 Series device before continuing.
Next, test that the GPG CLI can communicate with your YubiKey:
gpg --card-status
Reader ...........: Yubico YubiKey FIDO CCID
Application ID ...: D2760001240100000006322522270000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: 31234567
[truncated]
If you have reached this section, then ykman and GPG communication are working correctly and you can continue to configuration.
Initial Configuration
This will now take you through the recommended initial configuration of the YubiKey before you start generating credentials. If some of these options are not set correctly, you may need to destroy and re-create your credentials.
Default PINs
Some of the commands and options listed below will require you to confirm a User or Administrator PIN. If you have a new (or recently reset) YubiKey, the defaults will be:
- User: 123456
- Administrator: 12345678
Disable YubiKey OTP
YubiKey has a proprietary OTP protocol that we currently do not use. It is generally recommended to turn this off to prevent accidental plaintext spam caused by touching the buttons on your YubiKey. You may be familiar with this if you have seen seemingly “random” messages from people on Slack such as eerhtdgukfvhcetedhdejj.
To turn this off, run the following commands:
ykman config usb --disable otp
ykman config nfc --disable otp
Enable KDF (Recommended)
KDF is “Key Derived Format” - it means your PINs are transmitted and stored in a hashed format rather than in plaintext. This is optional but may harden your security posture.
The kdf-setup command may ask you for an Administrator PIN (the default is listed above). We will change this pin after enabling KDF, as this will reset the PINs anyway.
gpg --edit-card
gpg/card> admin
gpg/card> kdf-setup
Set Maximum GPG Pin Entries
The YubiKey’s GPG Application has a built-in mechanism to prevent brute-force attempts of the PIN. By default, this is set to 3 0 3 which represents:
- 3 Attempts of User PIN
- 0 Attempts of Reset PIN (Disabled by default)
- 3 Attempts of Administrator PIN
If the User PIN is entered incorrectly too many times, it must be unlocked using the Administrator PIN. If the Administrator PIN is entered too many times, the OpenPGP application on the YubiKey will be permanently locked and the key may need to be replaced or require a factory reset (which will remove stored keys).
We recommend setting these values to 10 10 10. Increasing retry counts reduces the likelihood of accidentally locking yourself out while still making online brute-force attacks impractical for an attacker.
To set this, use the following command:
ykman openpgp access set-retries 10 10 10
Set GPG User and Administrator PINs
Now you can set your User and Administrator PINs. If you enabled KDF earlier, this will have reset your PINs and you will need to set new ones here.
You will need to first confirm the “old” PINs - the Defaults are:
- User: 123456
- Administrator: 12345678
Note that whenever you are entering or setting a PIN in the terminal, it will be fully masked - you will not see any characters being output to the terminal when typing.
First, set your Admin PIN:
ykman openpgp access change-admin-pin
Enter Admin Pin: [12345678]
Enter New Admin Pin: [your new PIN here]
Confirm New Admin Pin: [confirm new PIN]
…and then set your User PIN:
ykman openpgp access change-pin
Enter User Pin: [123456]
Enter New Pin: [your new PIN here]
Confirm New Pin: [confirm new PIN]
Configuring GPG
Once your PINs are set, you can start to configure GPG (and then generate a key-pair)
gpg --edit-card
gpg/card> admin
gpg/card> help # Shows you the supported options
gpg/card> name # Set to your name
gpg/card> login # Set to your email address
Before generating the GPG key, set the key-attribute:
gpg/card> key-attr
key-attr
Changing card key attribute for: Signature key
Please select what kind of key you want:
(1) RSA
(2) ECC
Your selection? ECC
We recommend ECC as it is more modern and secure than RSA and is now supported by the vast majority of applications.
Generate Key Pair
Now you have configured the basic settings for GPG, it is time to generate a GPG key:
gpg/card> generate
You may be asked to make a backup - say “no”:
Make off-card backup of encryption key? (Y/n)
We strongly discourage backups as this increases security risk, as you would be allowing your private key to be exportable. We mitigate against this by suggesting you create a secondary key on a second YubiKey.
You will be asked to set a validity for the key - we recommend setting it to “does not expire”:
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
Next you will be asked to confirm your personal details - use the prompts to confirm:
Real name: Firstname Lastnamerson
Email address: firstname.lastnamerson@digital.cabinet-office.gov.uk
Comment: GDS
You selected this USER-ID:
"Firstname Lastnamerson (GDS) <firstname.lastnamerson@digital.cabinet-office.gov.uk>"
When you are happy, Enter “O” and continue:
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
Once you have confirmed with “Okay”, you should be asked to set a passphrase. Set this to something only you know and are happy to recite on a regular basis when prompted to use the key.
Configure FIDO PIN
The FIDO pin will be used by the SSH process. We will need to set this before continuing - for simplicity, you may wish to set this as the same as your GPG User PIN:
ykman fido access change-pin
…then follow the instructions on screen.
Configuring SSH to use the Yubikey
Once you have generated your key pair and set your FIDO PIN, set up SSH to use your GPG key. While this step will use ssh-keygen, all it is doing is creating a “pointer” to the Security Key (hence the -sk designation).
ssh-keygen -t ed25519-sk -O resident -O verify-required -C "Firstname Lastnamerson (GDS) <firstname.lastnamerson@digital.cabinet-office.gov.uk>"
If you encounter an error such as "No FIDO SecurityKeyProvider specified", then head to the Troubleshooting part of this documentation for a solution.
Take note of the options above…
-O residentinstructs that the actual key is “resident” to the Security Key and is what should be used for signing.-O verify-requiredinstructs that the key always requires touch “verification” each time it is to be used. If this option is omitted, the SSH client will not be forced to prompt the user (you) for a touch confirmation.
Optionally, if you want to store multiple SSH keys on a single YubiKey for some reason, you can differentiate between them by adding an extra option:
-O application=ssh:custom_nameoverrides the FIDO2 application name so you can store more than one SSH key on the device. The default is justssh:
During the creation process, you will be prompted to set a passphrase for the key and the filename to store the key - these are both optional. Normally we would recommend setting a passphrase, however, as this private key is a pointer to the credentials on the Security Key, the passphrase would be redundant.
Once the “pointer” file for the Security Key has been generated, the ssh-keygen command will output a path and filename of the new key. If you didn’t change the defaults, the key files will be:
- Private Key (pointer):
id_ed25519_sk - Public Key:
id_ed25519_sk.pub
Copy the path and filename and update your SSH config (located at ~/.ssh/config) to add a block like this:
Host *
IdentityFile /Users/firstname.lastnamerson/.ssh/id_ed25519_sk
IdentitiesOnly yes
IdentityAgent none
AddKeysToAgent yes
This section will configure your SSH agent to use the Security Key for every connection you make, including for GitHub.
Make sure the IdentityFile property matches the name and path of your private key pointer. The default filename will be id_ed25519_sk. This instructs your SSH agent and Git to find and use the correct file.
Configuring Git to use the SSH and Signing Keys
This is where we actually get to use the keys you have just generated. First we will configure authentication (via SSH) and then we will configure the Git client to sign all commits by default.
Configure Git to use your SSH Key
Once your SSH key is created and attached to the SSH agent, you will want to add the Public Key to your GitHub Profile.
- Go to your GitHub Settings Page -> Access -> SSH and GPG keys.
- Click “New SSH key”
- Set the “Title” to something you can remember, e.g. “YubiKey SSH Key”
- Select “Key type” as “Authentication Key”
- Paste the contents of
~/.ssh/id_ed25519_sk.pubinto the “Key” field. Make sure this is the file ending in.puband not the private key file.
Now this is done, you should be able to use your new key to pull and push to/from GitHub.
Configure Git to use your GPG Key
The next step will configure Git to sign your commits using your GPG key. This is to allow Git (and other users) to cryptographically verify that commits that were signed by your key came from you (and not an impersonator who has spoofed your email address).
First, you will need to find your key ID “fingerprint” from your YubiKey - run gpg --card-status to get something like this:
gpg --card-status
Reader ...........: Yubico YubiKey FIDO CCID
Application ID ...: D2760001240100000006322522270000
[truncated]
General key info..: pub ed25519/A3F1E9C0B827D54E 2025-09-23 Firstname Lastnamerson (GDS/DSIT) <firstname.lastnamerson@digital.cabinet-office.gov.uk>
sec> ed25519/A3F1E9C0B827D54E created: 2025-09-23 expires: never
card-no: 0006 31234567
ssb> ed25519/4B7E0C1F8A9D6325 created: 2025-09-23 expires: never
card-no: 0006 31234567
ssb> cv25519/9E5D2A6C8F0B4173 created: 2025-09-23 expires: never
card-no: 0006 31234567
Note the key ID from the “sec” row, e.g. A3F1E9C0B827D54E and add it to your Git config in ~/.gitconfig - make sure you have a config that looks like this:
[commit]
gpgsign = true
[user]
email = firstname.lastnamerson@digital.cabinet-office.gov.uk
name = Firstname Lastnamerson
signingkey = A3F1E9C0B827D54E
Now run this command from your terminal:
gpg --armor --export A3F1E9C0B827D54E
Take the output from this command and head back to GitHub Settings Page -> Access -> SSH and GPG keys, scroll down to “GPG keys”, then “New GPG key”.
Again, set the “Title” to something you might find helpful and paste the Public Key Block (that was printed from the gpg --armor --export command earlier) into the “Key” field. GitHub will now be able to verify any commits you make as coming from your GPG key (and therefore probably from you).
Setting Touch Preferences
You will want to set your touch preferences for when using your OpenPGP keys. The options are listed:
Off (default) no touch required
On touch required
Fixed touch required, can't be disabled without deleting the private key
Cached touch required, cached for 15s after use
Cached-Fixed touch required, cached for 15s after use, can't be disabled
without deleting the private key
We recommend the “Cached” option because it balances security and usability: it requires a touch when the key is first used and then gives 15 seconds of “grace” where the key will not prompt for another touch. If you would prefer higher assurance, you can use one of the other options.
Assuming you want to use the “Cached” option, this is how you set it for each of the PGP actions:
ykman openpgp keys set-touch sig cached
ykman openpgp keys set-touch dec cached
ykman openpgp keys set-touch aut cached
ykman openpgp keys set-touch att cached
Testing your credentials and finishing-up
Now you have a hardware-backed key for authenticating with Git, SSH and GPG, you can test that your new key is working by disabling your old keys. The easiest way to do this is to set the file permissions of your old keys to 000. For example, from your terminal:
cd ~/.ssh
ls -al
total 80
drwx------@ 11 firstname.lastnamerson staff 352 22 Sep 14:40 .
drwxr-x---+ 64 firstname.lastnamerson staff 2048 13 Oct 18:01 ..
-rw-------@ 1 firstname.lastnamerson staff 129 23 Sep 11:23 config
-rw-------@ 1 firstname.lastnamerson staff 2602 12 Jul 2023 google_compute_engine
-rw-------@ 1 firstname.lastnamerson staff 573 12 Jul 2023 google_compute_engine.pub
-rw-------@ 1 firstname.lastnamerson staff 436 1 Nov 2023 google_compute_known_hosts
-rw-------@ 1 firstname.lastnamerson staff 517 22 Sep 14:07 id_ed25519_sk
-rw-r--r--@ 1 firstname.lastnamerson staff 193 22 Sep 14:07 id_ed25519_sk.pub
-rw-------@ 1 firstname.lastnamerson staff 854 12 Jan 11:03 id_rsa
-rw-r--r--@ 1 firstname.lastnamerson staff 287 12 Jan 11:03 id_rsa.pub
-rw-------@ 1 firstname.lastnamerson staff 5720 19 Feb 2025 known_hosts
chmod 000 id_rsa
Now try to make an SSH connection to GitHub:
ssh -T git@github.com
Confirm user presence for key ED25519-SK SHA256:lrBwbswEEkWqyXAbd6WEPv9I++fSJdvkln1vlDkoFzs
Enter PIN for ED25519-SK key /Users/firstname.lastnamerson/.ssh/id_ed25519_sk:
Confirm user presence for key ED25519-SK SHA256:lrBwbswEEkWqyXAbd6WEPv9I++fSJdvkln1vlDkoFzs
User presence confirmed
Hi firstname-lastnamerson! You've successfully authenticated, but GitHub does not provide shell access.
If this worked, you have successfully set up a YubiKey for authentication with GitHub and commit signing. Now you should go and create a backup set of credentials with a second YubiKey which you can store in a safe place.
You may also want to go back and delete or revoke any old credentials that you do not plan to use any longer.
Other useful commands
ykman fido credentials list- List Resident Credentials (Keys)
Set up a YubiKey as an MFA device for AWS
- Sign into the
gds-usersAWS account. - Select your email address in the top-right corner of the page.
- Choose Security credentials from the drop-down menu.
- Scroll down to the Multi-factor authentication (MFA) section and select Assign MFA device.
- Specify your email address as the MFA device name.
- Select Authenticator app, not Passkey or security key.
- Click to reveal the TOTP Token.
- Use the command
ykman oath accounts add -t gds-usersand paste the TOTP Token in. The-toption forces touch verification. - Use the command
ykman oath accounts code gds-usersto generate two consecutive codes and then Save. Configure gds-cli to use the YubiKey:
gds config yubikey trueGo back to the Security credentials page and add the YubiKey again as a second MFA device, but choose Passkey or security key this time.
You have now:
- added your YubiKey as a U2F/FIDO2 Security Key for logging into the AWS web console more securely and conveniently
- added your YubiKey as a legacy OATH MFA device for compatibility with gds-cli/aws-vault on the command line
⚠️ Now that you have an unphishable Security Key as an MFA device, you should never type or copy/paste the 6-digit OATH one-time codes. They’re only for use via the gds-cli/aws-vault.
Always use the Security Key option and not the legacy Authenticator app option when signing into the AWS web console, to reduce the risk of phishing attacks.
Troubleshooting
“MFA device already exists”
If you get this error message when trying to set up your MFA token on AWS:
MFA device already exists
This is because gds-users has an IAM policy requiring your Authenticator app device name to match your gds-users username. You should make sure that:
- Your main/primary YubiKey is the one configured for the “Authenticator app” setting.
- You create Passkeys on each YubiKey or Security Key you want to use, so you can log into gds-users at any time and swap or replace the “Authenticator app” if needed.
“Not authorized to perform iam:CreateVirtualMFADevice”
If you get this error message when trying to set up your MFA token on AWS:
User: arn:aws:iam::123456789012:user/firstname.lastnamerson@digital.cabinet-office.gov.uk is not authorized to perform: iam:CreateVirtualMFADevice on resource: arn:aws:iam::123456789012:mfa/abc123 because no identity-based policy allows the iam:CreateVirtualMFADevice action
You need to make sure that your Authenticator app name is set to your email address (the same as your gds-users username), .e.g.
firstname.lastnamerson@digital.cabinet-office.gov.uk
“No FIDO SecurityKeyProvider specified”
If you get this error message when trying to generate your SSH Key:
No FIDO SecurityKeyProvider specified
Key enrollment failed: invalid format
This is because the default OpenSSH client bundled with macOS does not support Hardware Security Keys. You will need to override this so the version installed by Homebrew takes precedence. Follow this GitHub Gist for a solution.