Skip to content

Instantly share code, notes, and snippets.

@ageis
Last active September 10, 2024 14:48
Show Gist options
  • Save ageis/14adc308087859e199912b4c79c4aaa4 to your computer and use it in GitHub Desktop.
Save ageis/14adc308087859e199912b4c79c4aaa4 to your computer and use it in GitHub Desktop.
Technical guide for using YubiKey series 4 for GPG and SSH
#!/bin/bash
# script generates a GPG master key with Certify+Sign capabilities, and two subkeys each possessing the Encrypt and Authenticate capabilities.
# intended to fill the 3 slots on a YubiKey <https://www.yubico.com/products/yubikey-hardware/> or OpenPGP card <https://g10code.com/p-card.html>
# allows selection of variables via a single dependency besides GnuPG 2.x: dialog
# kevin gallagher (@ageis) <kevingallagher@gmail.com>
function gen_smartcard_key() {
local GPG_KEY_ALGO="RSA"
local GPG_KEY_CREATION_DATE="$(date +%Y-%m-%d)"
if [[ -z "${GNUPGHOME}" ]]; then
exec 4>&1;
export GNUPGHOME=$(dialog --inputbox 'Confirm your GNUPGHOME:' 0 0 "${HOME}/.gnupg" 2>&1 1>&4)
exec 4>&-;
fi
echo -e "GNUPGHOME is ${GNUPGHOME}\n"
exec 5>&1;
local GPG_KEY_PASSWORD=$(dialog --passwordbox 'Password:' 0 0 2>&1 1>&5);
exec 5>&-;
exec 6>&1;
local GPG_KEY_NAME=$(dialog --inputbox 'Name:' 0 0 2>&1 1>&6);
exec 6>&-;
exec 7>&1;
local GPG_KEY_EMAIL=$(dialog --inputbox 'E-mail:' 0 0 2>&1 1>&7);
exec 7>&-;
exec 8>&1;
local GPG_KEY_EXPIRY=$(dialog --inputbox 'Expiry:' 0 0 '5y' 2>&1 1>&8);
exec 8>&-;
local DIALOG_RESULT=$(mktemp 2>/dev/null)
# trap "rm -f ${DIALOG_RESULT}" 0 1 2 5 15
local GPG_KEY_SIZE=$(whiptail --title "GPG key size" --radiolist "Choose:" 12 40 4 \
1 "1024" off \
2 "2048" off \
3 "3072" off \
4 "4096" on 3>&1 1>&2 2>&3)
case $(echo ${GPG_KEY_SIZE}) in
1) local GPG_KEY_LENGTH=1024;;
2) local GPG_KEY_LENGTH=2048;;
3) local GPG_KEY_LENGTH=3072;;
4) local GPG_KEY_LENGTH=4096;;
esac
echo -e "We'll generate a cert+sign primary key with the following parameters:\n"
echo -e "Key-Type: ${GPG_KEY_ALGO}\n"
echo -e "Key-Length: ${GPG_KEY_LENGTH}\n"
echo -e "Name-Real: ${GPG_KEY_NAME}\n"
echo -e "Name-Email: ${GPG_KEY_EMAIL}\n"
echo -e "Expire-Date: ${GPG_KEY_EXPIRY}\n"
echo -e "Passphrase: ${GPG_KEY_PASSWORD}\n"
echo -e "Creation-Date: ${GPG_KEY_CREATION_DATE}\n"
export GPG_PARAM_FILE="$(mktemp)"
export GPG_KEY_TEMP_ALIAS=$(basename "${GPG_PARAM_FILE}" | cut -d'.' -f2)
cat << EOF > "${GPG_PARAM_FILE}"
%echo Generating a GPG key...
Key-Type: ${GPG_KEY_ALGO}
Key-Length: ${GPG_KEY_LENGTH}
Key-Usage: cert,sign
Name-Real: ${GPG_KEY_NAME}
Name-Email: ${GPG_KEY_EMAIL}
Expire-Date: ${GPG_KEY_EXPIRY}
Creation-Date: ${GPG_KEY_CREATION_DATE}
Passphrase: ${GPG_KEY_PASSWORD}
%no-protection
%transient-key
%pubring ${GNUPGHOME}/${GPG_KEY_TEMP_ALIAS}.pub
%secring ${GNUPGHOME}/${GPG_KEY_TEMP_ALIAS}.sec
%commit
%echo Done
EOF
export GPG_FINGERPRINT=$(gpg --homedir "${GNUPGHOME}" --verbose --batch --status-fd=1 --generate-key "${GPG_PARAM_FILE}" | cut -d' ' -f4)
gpg --import "${GNUPGHOME}/${GPG_KEY_TEMP_ALIAS}.pub"
gpg --import "${GNUPGHOME}/.gnupg/private-keys-v1.d/${GPG_FINGERPRINT}.key"
for cap in encrypt auth; do
echo -e "Generating $cap key for ${GPG_FINGERPRINT}...\n"
gpg --homedir "${GNUPGHOME}" --verbose --batch --quick-add-key ${GPG_FINGERPRINT} ${GPG_KEY_ALGO}${GPG_KEY_LENGTH} $cap ${GPG_KEY_EXPIRY}
done
echo -e "Setting your new key to ultimately trusted...\n"
gpg --homedir "${GNUPGHOME}" --verbose --yes --list-keys --fingerprint --with-colons "${GPG_FINGERPRINT}" |
sed -E -n -e 's/^fpr:::::::::([0-9A-F]+):$/\1:6:/p' |
gpg --homedir "${GNUPGHOME}" --verbose --import-ownertrust --yes
}

YubiKey 4 series GPG and SSH setup guide

Written for fairly adept technical users, preferably of Debian GNU/Linux, not for absolute beginners.

You'll probably be working with a single smartcard, so you'll want only one primary key (1. Sign & Certify) and two associated subkeys (2. Encrypt, 3. Authenticate). I've published a Bash function which automates this slightly special key generation process.

By skipping straight to the "Load subkeys onto the smartcard" section one could feasibly transfer an already-existent, unexpired GPG key(s) to your new YubiKey... If you wish to attempt that just make sure you back up the secret keys first. Depending on your threat model and interest in seriously protecting the secrets, and your level of confidence in the theoretical impenetrability of your own machine, I suppose you have the option to ignore the Tails and air-gap stuff and do all of this on your regular work computer—but be aware this directly compromises security and kind of defeats the whole point of moving such keys to separate offline hardware.

The purpose of having a YubiKey should be clear: even if your computer gets completely owned, then your keys and any encrypted data are still safe. They offer other useful functions as well, like static password output (which is well-suited for PAM user login or mounting LUKS password-protected volumes) or HOTP, or Universal Two-Factor (U2F) a FIDO Alliance standard used by Google, Facebook, GitHub, Dropbox and more... But here I'm just covering GPG and SSH. To customize and configure the configuration slots, download & use NEO manager and/or the Personalization Tool.

The YubiKey 4 has a status light. You can tell when it's actively performing RSA operations and the like. In certain modes, your computer simply recognizes it as a classic US keyboard. The button has three ways to activate the two individual configuration slots to accomplish different things: a single press, double tap, and a long press. You'll quickly learn to avoid pressing it by accident when there's an important password encoded in there 😜

An OpenPGP smartcard always has three separate key slots. These are labeled Signature, Encryption and Authentication. Each subkey usually has its own key ID.

There are a few different approaches to getting started with a smartcard. Sometimes people leverage two smartcards and put their master key away at home on one, and use a second with the subkeys for signing and decryption for daily usage... Additionally, people may migrate previously generated keys to a new card. We won't be doing any of that.

In this guide, you will generate a master key with the Certify and Sign capabilities and two subkeys with each of the Encrypt and Authenticate capabilities respectively and move them to one YubiKey.

A basic grasp of the concept of capabilities of private keys is helpful, so here's a table for elucidation:

Certify

With this you may sign other keys, "certifying" them, indicating you verified their identity and trust that person. Ordinarily set on the primary/master key, this is required to add or revoke UIDs, alter key details, etc.

Encrypt

Pretty self-explanatory... A key possessing this capability can decrypt messages to it.

Sign

A key possessing this capability may create digital signatures of messages, showing incontrovertible proof that you wrote the message or possess the key. Often confused with certify because this capability doesn't sign other keys.

Authenticate

Least commonly used, this is used for authenticating your identity via a challenge-response protocol. Hence can be used as an SSH identity.

I recommend generating keys on a secure computer instead of on the card, because it allows more flexibility. Ideally this means a machine running Tails or Qubes OS, more generally one that is air-gapped and not connected to the internet. Refer to the Tails documentation for instruction on creating a bootable USB with encrypted persistent storage if you'd like to go that route.

It's also good to possess offline backups, since with on-card generation you're basically screwed when/if you physically lose your YubiKey. The supposed backup option for smartcard key generation is actually pretty weak and useless. In theory private keys can never leave the device once they're loaded.

By default, GPG generates a master key with the Certify and Sign capabilities and a subkey with the Encrypt capability. We will override this using expert mode.

First, make sure you're running GnuPG 2.x. This is important because you can't use 4096-bit RSA keys on most smartcards with GnuPG 1.x. If it's not already the default, then a Bash alias will suffice, e.g. alias gpg=gpg2.

If you use the Enigmail add-on for Thunderbird mail client, the GPG path should be set to /usr/bin/gpg2.

If you plan to use a YubiKey on Linux in general, you'll need to add udev rules in order to interact with the device. Create a file named 70-yubikey.rules in /etc/udev/rules.d with the following contents:

ACTION=="add|change", SUBSYSTEM=="usb", ATTR{idVendor}=="1050", ATTR{idProduct}=="0010|0110|0111|0114|0116|0401|0403|0405|0407|0410", OWNER="amnesia", TAG+="uaccess"

⚠️ Replace the OWNER attribute above with the name of your system's user that you ordinarily login as before saving. That's only a Tails convention.

Then run: sudo udevadm control --reload-rules. Now you may insert or re-plug the USB device and you should have normal user access, which may be confirmed with gpg --card-status

The default PIN: 123456

The default admin PIN: 12345678

Why are there two? One is for decrypting messages and making signatures, which happens frequently. The admin PIN is needed to modify certain card attributes, which is more rare once you're set up. Write down and memorize your PIN choices, since the card is programmed to become blocked after the maximum number of incorrect attempts (usually 10).

Preferably right away you should initialize your new smartcard, setting some of the variables if you so choose (stuff like name, url, login, lang, sex), but most importantly modify the default PIN and admin PIN. You can do this by running gpg --card-edit and typing admin and then help to list available commands. Use passwd to change your PINs. You can also toggle the forcesig flag to control whether you'd like to require a PIN to be entered every time you sign a message.

Remember to set up a Reset Code! The pinentry dialog looks nearly the same as that which prompts for a password to an SSH key and accepts non-numeric input... By default if you get this wrong 10 times, you'll have to factory reset and start over, but setting a reset code in advance will mean you'll be able to unlock your card.

ℹ️ For the sake of brevity, this guide assumes that you will always enter correct passphrases and PINs, and answer Yes by typing y when prompted. Now let's get started!

Generate the master key

gpg --expert --full-gen-key

Select 8: RSA (set your own capabilities)

Select E to toggle off the Encrypt capability, which will leave you with only Sign + Certify.

Set a 4096 bit key size.

Set the expiration date.

Setup a UID.

Setup a passphrase.

The primary key is generated. Note your new key ID, as you'll be needing it henceforth.

Add UIDs

gpg --expert --edit-key <longid>

Use gpg> adduid to add as many UIDs or e-mail addresses as you need. Once you're done, toggle to gpg> uid <#> and use the gpg> primary command to set the primary UID.

Now we will generate subkeys for each additional capability to be transferred to the main smartcard designated for daily use.

Create the Encrypt key

gpg> addkey

Select 6: RSA (encrypt only).

Set a 4096 bit key size.

Set the expiration date.

The first subkey is generated.

Create the Authenticate key

gpg> addkey

Select 8: RSA (set your own capabilities)

Select S and E to toggle off the Sign and Encrypt capabilities.

Select A to toggle on the Authenticate capability and press Q.

Set a 4096 bit key size.

Set the expiration date.

The second subkey is generated.

Set trust level

By the way, you should probably set the public key to the ultimate trust level.

gpg> trust

Select 5 = I trust ultimately.

gpg> save

Save the key(s) you've been creating.

Add signatures

If you want to sign your new master key with a previous key that you're transitioning from, then the time is now. How else will people know you're not an impostor?

gpg -u <your_old_keyid> --sign-key <longid>

Generate revocation certificate

It's a good idea to create a revocation certificate, in case the YubiKey is lost or your private key is somehow compromised.

gpg --output revoke.asc --gen-revoke <longid>

Backup everything

gpg --armor --output privkey.sec --export-secret-key <longid>
gpg --armor --output subkeys.sec --export-secret-subkeys <longid>
gpg --armor --output pubkey.asc --export <longid>

You can move these private keys plus the revocation certificate someplace safe, like an encrypted partition or offline storage media.

As mentioned earlier, you can use gpg --card-edit to setup your smartcard: set the PINs, and variables like language, sex, your first and last name, or a public URL for downloading your key.

So, after you've created keys, initialized the card and set new PINs, let's import the keys.

Loading keys onto the smartcard

gpg --expert --edit-key <longid>

gpg> toggle

gpg> keytocard

Answer 'y' to "Really move the primary key?"

Select 1: Signature key.

Enter your passphrase and admin PIN as required.

gpg> key 1

gpg> keytocard

Select 2: Encryption key

Enter your passphrase and admin PIN as required.

Un-toggle key one: gpg> key 1

Toggle key two: gpg> key 2

gpg> keytocard

Select 3: Authentication key.

Enter your passphrase and admin PIN as required.

Un-toggle key two: gpg> key 2

gpg> save


Your card is good and ready to go. 👍 Now what?

You shouldn't have to delete any secret keys, as they were moved to the smartcard. When you use either keytocard command or perform key generation on the card, GnuPG places a "stub" in your keyring so that it knows the actual secret key material is located on the smartcard. It appears as though you possess the secret key in your keyring but you actually don't, and you can't decrypt anything without the card. It's just a stub pointing to the smartcard — which is something you do want to keep if you'd like this to be usable.

So what if you deleted the secret keys anyway and lost the stubs? Just run gpg --card-status anywhere or open the 'Manage Smartcard' menu in Enigmail in order to instantly re-associate and populate your keyring with the information from your smartcard. However, always keep in mind that you need the corresponding public key in your keyring to work with the smartcard on whatever computer you're using.

After you purposely delete the secret key stubs from your keyring (otherwise it will say the keys are already associated with another card), you can even put these same keys on a different smartcard by repeating part of the process above.

However, you should probably backup or transfer these stubs to your regular computer first, (in the double-smartcard setup I mentioned, when the stubs point to separate smartcards for different subkeys, it's very difficult to re-create if you lose them)—but it can be done using a tool called gpgsplit. Without all of the correct stubs in your keyring, GnuPG won't prompt you to insert your other smartcard with a different serial number when you try to certify another key or alter attributes.

gpg --armor --output stubs.asc --export-secret-keys <longid>

Transfer this file to your regular, non-airgapped machine and run gpg --import stubs.asc. This doesn't contain any actual secret key material — that's been migrated to the smartcard(s). Also make sure you transfer and import a copy of your pubkey.asc for things to work properly.

😃 You now have a working OpenPGP smartcard for use with GPG, Enigmail and more! Now you can let people know about your new key, upload it to keyservers, publish a transition statement, and all of that fun stuff.

There are lots of keychain apps (Seahorse, GNU Privacy Assistant, and more), however I don't really recommend any of them. Also the kbxutil tool provided to convert GnuPG (1.x/2.0) keyrings to the new, keybox format in GnuPG 2.1 which has been advertised as faster, I've found to be unusable.

Two-factor authentication

If you want, your YubiKey could be required to login to your computer or unlock your screen or access your session keyring and such things.

This can be done using the highly extensible Yubico PAM module. If you're interested, then apt-get install libpam-yubico then refer to the relevant documentation or manual from Yubico:

If you're not already aware, similar functionality may be achieved by Google's libpam-google-authenticator.

Tips

Fortunately Enigmail now has an option to conceal PGP metadata, moving the Subject field into the encrypted message body. Just get into the preferences and set extensions.enigmail.protectHeaders to true. Never write a descriptive or accurate subject line, this actively works against the reason you're encrypting messages in the first place. Think of some nonsense instead.

Addressing that same problem, GPG senders have options --throw-keyids or --hidden-recipient which make it so that any eavesdropper or recipient cannot tell which key or person the message is intended for. However, this is very annoying for those on the other end, as it means recipients have to try ALL of their local secret keys attempting to decrypt the message until finding the right one (or not). So I don't recommend it; it's merely good to be aware of.

Always use PGP/MIME if you can, it's superior to in-line PGP with better support for rich text or HTML, etc.

If you block your card or lose your PIN, all is not lost. There's a factory-reset command, or you could just pipe this text file containing raw hex commands to the device: gpg-connect-agent < smartcard-reset.txt.

Troubleshooting

In a GNOME desktop environment, gnome-keyring-daemon (at least at the time of first authorship) once had a bad habit of hijacking the GnuPG agent, causing cards and readers to be unrecognized or to behave unpredictably. Many of these issues go away if you disable the SSH & GPG components of gnome-keyring-daemon and let gpg-agent handle them instead. Run gnome-keyring-daemon with only --components=pkcs11,secrets.

To do this you can rm /etc/xdg/autostart/gnome-keyring-ssh.desktop and gnome-keyring-gpg.desktop or just add Hidden=true plus X-GNOME-Autostart-enabled=false to those launchers. If you prefer, you can create a new launcher only for starting gpg-agent (ideally in ~/.config/autostart).

If you are experiencing "Card not available" or "Card error", then you might want to try killing and restarting gpg-agent.

⚠️ The udev rules are absolutely essential! If they don't work when you plug the device in, then as a workaround you can always run lsusb to figure out the device and bus ID and then manually chown the YubiKey device file to your user in /dev/bus/usb/<bus#>/<device#>

It can sometimes be a bit challenging to get your smartcard working reliably until you figure these things out.

Related configuration

To make sure your smartcard works in every terminal you open, it helps to add the following to ~/.profile:

GPG_TTY=$(tty)
export GPG_TTY
if [ -z "$GPG_AGENT_INFO" ]; then
    eval "$(gpg-agent --daemon --options ~/.gnupg/gpg-agent.conf)"
fi

If you intend to use gpg-agent as your SSH agent and authenticate to servers that way then include these lines as well:

if [ -z "$SSH_AUTH_SOCK" ]; then
    SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
    export SSH_AUTH_SOCK
fi

While we're at it, here's what my ~/.gnupg/gpg-agent.conf looks like lately:

pinentry-program /usr/bin/pinentry-qt4
enable-ssh-support
keep-display
keep-tty
default-cache-ttl 3600
max-cache-ttl 7200
scdaemon-program /usr/lib/gnupg/scdaemon
sh
debug-level basic
homedir ~/.gnupg

ℹ️ This particular configuration depends on two packages you must install via apt-get, pinentry-qt4 and scdaemon. If you're annoyed by debug messages then just set that value to none.

Back in the day most pinentry programs didn't allow pasting from clipboard, making them hard to use with a password manager. For this ability one once had to build from source with ./configure --enable-pinentry-qt4-clipboard=yes. This isn't the case anymore in Debian stretch/9.x!

Using the smartcard key for SSH authentication

It's all just RSA, right? OpenPGP, OpenSSL, and OpenSSH keys can work together and be friends. To be specific, you can use your 4096-bit Authenticate key on the smartcard with SSH. This has the advantage that you can't log in to any servers without possession of the device. It's easiest to do with the latest GnuPG 2.1.x, otherwise you may have to apt-get install monkeysphere and use the openpgp2ssh tool, which we're going to skip. Note: this won't work at all unless you've already set a non-default smartcard PIN of at least 6 digits.

On GnuPG 2.0.x, when you run gpg-agent with enable-ssh-support so that it may take over for ssh-agent, your smartcard's Authenticate subkey should automatically be recognized as a valid SSH key and become available for the SSH client to use as an identity. But you might have trouble seeing it or getting it to work, so here are some extra steps which can assist the process:

  1. Ensure enable-ssh-support is present in ~/.gnupg/gpg-agent.conf.

  2. Fetch the keygrip of your master public key via gpg2 --with-keygrip -K and add these 40 hex digits as a line to~/.gnupg/sshcontrol if they're absent.

  3. Make sure something similar to gpg-agent --daemon --options ~/.gnupg/gpg-agent.conf is running in the background via ps -u. If you then run ssh-add -l it should list an SSH identity corresponding to the RSA key on your smartcard.

  4. Check echo $SSH_AUTH_SOCK - it should be pointing to gpg-agent's socket instead of ssh-agent. If it's not you have to kill ssh-agent with fire or make sure it doesn't start.

  5. Get the 16-bit long ID of your Authenticate subkey and feed it into gpgkey2ssh:

gpg --export-ssh-key 00698E823F6DD692 > ssh_id.pub

You can add the contents of ssh_id.pub to ~/.ssh/authorized_keys on any system you like, or you can try ssh-copy-id.

Now your SSH agent should be communicating with gpg-agent and the RSA Authenticate key on your smartcard is a valid SSH identity. When you run SSH with your smartcard connected, it will automatically attempt to authenticate using it.


If you run into trouble getting your YubiKey to work with SSH, there is a method of last resort, but it's quite technically involved:

  1. GnuPG 2.0 or later doesn't allow exporting private keys which aren't protected with a password. This is the main problem.

  2. So, somehow downgrade to GnuPG v1.4.x or build it from source and import the keys into your secring.gpg (not the secring.kbx) and use gpg --expert --edit-key [primary_keyid_here], remove the passphrase entirely, and then save the key.

ℹ️ Setting an alternate $GNUPGHOME or --homedir when working between different GnuPG versions may be more efficient. Another tip, if you're on 2.1 with Debian stretch by default, then just make instead of make install while building 2.0.x or 1.4.x, and move binaries from ./g10 in the source tree to /usr/local/bin with names like 'gpgv14' or 'gpgv20'.

  1. Find the key ID for your Authenticate subkey, it'll of course be different from the Encrypt and Sign subkeys (see gpg --card-status)

  2. Export only the subkey if possible, with minimal options: gpg --export-options export-reset-subkey-passwd,export-minimal,no-export-attributes --export-secret-subkeys ![authenticate_subkey_id_here] > subkey.sec

  3. Note the prefixing of an exclamation point to the beginning of the key ID – this lets you target specific subkeys for export.

  4. If you still have no luck, then do a complete --export-secret-keys of the primary key to a file like key.sec, run gpg --list-packets key.sec to examine your key(s) and their capabilities. I recommend installing a program called pgpdump which can also come in handy for analyzing keys, messages and signatures.

  5. You then must run gpgsplit key.sec in an empty folder which will disassemble your key into its constituent packets and write them to the current working directory.

  6. Then, concatenate the secret subkey and its signature packet together, ignoring all other keys and packets. The main point is to get rid of the master/primary key, since the protection/passphrase requirements are problematic here. Ultimately isolating your SSH RSA Authenticate key might look something like this: cat 000005-002.sig 000004-007.secret_subkey > ssh_key.sec

  7. Finally the isolated ssh_key.sec file is something that can be fed via STDIN to openpgp2ssh (another tool provided by monkeysphere). Like in the example above, just do openpgp2ssh [authenticate_subkey_id_here] > ssh_key.sec

  8. If step 9 fails then return to step 6 and try with --export-secret-subkeys [primary_keyid_here] subkey.sec instead.

More resources:

Transport layer security in fetching from keyservers

A requirement of accessing HKPS keyservers, especially through apt-key operations, is to install gnupg-curl.

Here's the contents of my ~/.gnupg/dirmngr.conf:

log-file ~/.gnupg/dirmngr.log`
debug-level basic
verbose
disable-ipv6
keyserver hkps://hkps.pool.sks-keyservers.net
hkp-cacert ~/.gnupg/sks-keyservers.netCA.pem

There's a great option called use-tor, which would conceal your location and IP address while requesting a key, if you have a Tor daemon running and available. In my torrc I enable the SOCKSPort with no authentication for localhost and set CookieAuthentication to 1. You may also need to fiddle with the ControlPort or torsocks.conf.

Run touch ~/.gnupg/dirmngr_ldapservers.conf. Without a blank file present there, you'll receive a needless warning message.

Download the certificate here: https://sks-keyservers.net/sks-keyservers.netCA.pem (more info)

Finally, the relevant addition to ~/.profile:

if [ -z "$DIRMNGR_INFO" ]; then
    eval "$(dirmngr --daemon)"
fi

Maximally secure gpg.conf, algorithm, digest and cipher preferences

It's crucial to prefer 32-bit long key IDs instead of short ones, which are vulnerable to malicious collision. See Evil 32 for details. It's also not necessary to reveal your GnuPG version, better to retrieve keys via HKPS rather than plain/unencrypted HKP, as well as insisting upon the strongest cipher and digest algorithms available.

Here's what my ~/.gnupg/gpg.conf looks like:

keyserver hkps://hkps.pool.sks-keyservers.net 
keyserver-options ca-cert-file=~/.gnupg/sks-keyservers.netCA.pem
use-agent
charset utf8
keyid-format 0xlong
no-emit-version
#default-key 0x00000000 REPLACE THIS!!
no-greeting
cipher-algo AES256
digest-algo SHA256
personal-digest-preferences SHA512
personal-cipher-preferences AES256 AES
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB ZIP Uncompressed
s2k-cipher-algo AES256
s2k-digest-algo SHA512
s2k-mode 3
s2k-count 65536
keyserver-options no-honor-keyserver-url
utf8-strings
auto-key-locate keyserver cert pka

More resources:

For the nerds:

How to obtain the OpenPGP smartcards and USB readers

I now recommend the YubiKey series 4 instead of the OpenPGP smartcard from g10 code. It's modern hardware, much faster, and has many great features. These devices can be purchased from Amazon or directly via Yubico's website.

It's also available in USB-C which works on your Android smartphone with OpenKeychain or Android Privacy Guard (APG) and integrates with K-9 Mail.

If you have questions or suggestions concerning any of the information within this guide, feel free to contact me directly.

@jdormansteele
Copy link

jdormansteele commented Apr 15, 2018

Hello Ageis --

I have a question about the Yubikey reset code, which was the subject of a recent commit. I am not clear what the scenario is when you would need this code. The documentation (not yours, but in general) around the PIN/PUK/reset codes is sparse and inconsistent, as is apparent from these notes I made:

About the passphrases....

https://developers.yubico.com/PIV/ [Person Identity Verification]
https://developers.yubico.com/yubico-piv-tool/YubiKey_PIV_introduction.html
https://csrc.nist.gov/projects/piv/piv-standards-and-supporting-documentation
https://csrc.nist.gov/csrc/media/publications/sp/800-73/4/archive/2015-05-29/documents/sp800_73-4_pt2_revised_draft.pdf
https://developers.yubico.com/PIV/Introduction/Admin_access.html
https://developers.yubico.com/yubikey-piv-manager/PIN_and_Management_Key.html
http://g10code.com/docs/openpgp-card-2.0.pdf

NIST PIV specification:
* PIN = "Card Application PIN" (6-8 bytes, chars 0-9 only [0x30 – 0x39], required)
* PUK = "PIN Unblocking Key" (8 bytes, any 8-byte binary value [0x00 – 0xFF], required)
* ??? = "PIV Card Application Administration Key" (not clearly defined, optional)

OpenPGP card specification:
* PW1 = "user-password" (6 chars minimum, factory default "123456")
* PW3 = "admin-password" (8 chars minimum, factory default "12345678")

GnuPG terminology from the UI:
* PIN
* Admin PIN
* Reset Code

Yubico documentation
* PIN = used for "normal" operations (6-8 ASCII chars, factory default "123456", required)
* PUK = used to reset PIN if it is blocked (6-8 ASCII chars, factory default "12345678",
disabled if PIN is used for management [see below], if blocked requires complete token reset, optional)
* Management Key = used for PIV management functions (24 byte 3DES key,
factory default "010203040506070801020304050607080102030405060708",
management functions handled by PIN if absent, optional)

In practice, the only thing that's clear is that the PIN is needed to access the secrets on the Yubikey, so it is effectively equivalent to a GPG secret key passphrase, though it operates at the token level.

Would it be fair to say that I don't need a reset code if I am satisfied with the 3-try lockouts for PIN and PUK? That's a lot of recovery capacity if you are preserving an offline a copy of the master secret, or even a duplicate Yubikey.

Thanks for the clear and concise guide.

-- @jdormansteele

@ageis
Copy link
Author

ageis commented Aug 12, 2018

@jdormansteele That's right, it's not essential to have a reset code. Besides, if you have cold storage backups of your keys then you can just do the factory reset/wipe and re-load them. That's a lot of work though if you're going back and forth across an airgap, so I just suggested it to save from that hassle... My own card got locked once because I'd thought the PIN popup was asking me for my GPG passphrase.

@clee
Copy link

clee commented Nov 26, 2018

I ran into some problems (which I think I caused by accidentally using the wrong key ID with gpg2 --export-ssh-key), but I figured out a better alternative; instead of using gpg2 --export-ssh-key, use ssh-add -L to generate the list of public keys for SSH, and then drop that output into ~/.ssh/authorized_keys or wherever you like.

@wiktor-k
Copy link

Android Privacy Guard is unsupported, and the play link to it is broken.

gpg.conf example is not needed when using fresh versions of GnuPG that already includes reasonable defaults. Some of these advices are either obsolete (e.g. use-agent is a no-op as it's not possible to avoid agent now, no-honor-keyserver-url is the default), or lead to worse security (e.g. auto-key-locate list using insecure PKA). I like no-emit-version though :)

@ageis
Copy link
Author

ageis commented Feb 24, 2019

@wiktor-k duly noted, this guide is becoming dated and I am planning to update it soon

@vorburger
Copy link

FYI https://github.com/drduh/YubiKey-Guide#transfer-keys is a somewhat similar guide.

@ageis
Copy link
Author

ageis commented Aug 22, 2019

@vorburger, yup, I'm fully aware of that one and in fact I would probably redirect/refer people to it as being more up-to-date plus featuring community contributions. The first version of this was actually written before that came out or existed.

@alireza11048
Copy link

Hi,
I've injected the same RSA keys to two different smartcard using the pkcs11-tool, but when I import them in the gpg I got different keyids. With this behavior a critical problem arises in the below scenario:
Consider I've generated an RSA key pair to sign and verify patches of a product, and I backed up them in a safe environment and injected them in a smartcard#1. Then I imported the smart-card resident keys to gpg. In the product, I use the corresponding public key to verify the product patches.
If something goes wrong with smartcard#1, I expect to inject the backed-up keys to smartcard#2 and use it to sign patches of the product. However, if I import smartcard#2 keys in the gpg it leads to different key ids and as the result, the product couldn't verify the patch because the keyids are different.
Is there any workaround for this problem?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment