Skip to content

Instantly share code, notes, and snippets.

@btcdrak
Forked from ageis/openpgp-card-guide.md
Created September 1, 2016 21:31
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save btcdrak/73f7b54eafbed2a3f10f41375a04cb6d to your computer and use it in GitHub Desktop.
Save btcdrak/73f7b54eafbed2a3f10f41375a04cb6d to your computer and use it in GitHub Desktop.
Quick GPG Smartcard Guide

Quick GPG Smartcard Guide

We will generate a master key with only the Certify capability and three subkeys with each of the Sign, Encrypt and Authenticate capabilities. These latter three keys are meant for daily use and will be transferred to an OpenPGP smartcard, which has three corresponding slots. The master private key can then be moved to offline cold storage, or stored on a second smartcard.

We are generating keys on a secure computer instead of on the card, because it allows more flexibility. Ideally this means a machine running Tails or one that is air-gapped and not connected to the internet.

This guide assumes that if you want to sign other peoples keys, then you will require the aforementioned secondary smartcard with your master key stored in its Signature slot, or if you only have one smartcard, then you'll have to fetch the master key out of cold storage. 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. A Bash alias will suffice, i.e.alias gpg=gpg2.

If you use the Enigmail Thunderbird add-on, make sure the GPG path to /usr/bin/gpg2.

If you're using a YubiKey on Tails, you might 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"

Then run sudo udevadm control --reload-rules.

For the sake of brevity, this guide assumes that you will always enter passphrases and PINs, and answer Yes by typing y when prompted.

At some point either before or after 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 (123456) and admin PIN (12345678). 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.

Generate the master key

$ gpg --expert --gen-key

Select 8: RSA (set your own capabilities)

Select S, E, and Q so that you're left with only the Certify capability.

Set a 4096 bit key size.

Set the expiration date.

Setup a UID.

Setup a passphrase.

The key is generated.

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 add subkeys for each capability to be transferred to the main smartcard designated for daily use.

Create the Sign key

gpg> addkey

Select 4: RSA (sign only).

Set a 4096 bit key size.

Set the expiration date.

The key is generated.

Create the Encrypt key

gpg> addkey

Select 6: RSA (encrypt only).

Set a 4096 bit key size.

Set the expiration date.

The key 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 key 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

Add signatures

If you want to sign the new master key with your previous key that you're transitioning from, the time is now.

$ gpg --sign-key <longid>

Generate revocation certificate

While you still have access to the master key with the Certify capability, it's a good idea to create a revocation certificate.

$ 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.

Transfer your master key to a secondary smartcard

If you have two smartcards available, then you can store your master key in the Signature slot of a second smartcard, and use this one for stuff like signing other peoples keys, and making changes to your key, as in the scheme recommended by Tom Lowenthal's guide. After initializing the card and setting new PINs:

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

gpg> toggle

gpg> keytocard

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

Select 1: Signature key.

gpg> save

As mentioned, switching to this smartcard will be required whenever you want to sign somebody else's key or make modifications to your key. Now eject it and put it away somewhere. You may want to create a label so you can tell them apart. Insert the primary smartcard that you've selected for daily use.

Load subkeys onto the smartcard

You can use gpg --card-edit to initialize your smartcard: set the PINs, and variables like language, sex, your first and last name, or a URL for downloading your key. Now let's load the keys onto it.

$ gpg --expert --edit-key <longid>
    gpg> toggle
    gpg> key 1
    gpg> keytocard

Select 1: Signature key.

Un-toggle key one:gpg> key 1

Toggle key two:gpg> key 2

  gpg> keytocard

Select 2: Encryption key.

Un-toggle key two: gpg> key 2

Toggle key three: gpg> key 3

  gpg> keytocard

Select 3: Authentication key.

  gpg> save`

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 looks like you have the secret key on your computer 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 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, since they're pointing to separate smartcards for different subkeys, and 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, 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. 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.

Tips

gnome-keyring-daemon has 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 just for starting gpg-agent (more 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.

This resolved many issues and my smartcard now works reliably this way on Debian jessie with GnuPG 2.x.

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

GPG_TTY=$(tty)
export GPG_TTY
eval $(cat ~/.gnupg/gpg-agent-info)

This assumes the presence of write-env-file ~/.gnupg/gpg-agent-info in your gpg-agent.conf. Evaluating that file helps make sure you have the correct GPG_AGENT_INFO, SSH_AUTH_SOCK and SSH_AGENT_PID environment variables in each terminal session. While we're at it, here's what my ~/.gnupg/gpg-agent.conf looks like:

pinentry-program /usr/bin/pinentry-qt4
keep-display
display :0.0
enable-ssh-support
write-env-file ~/.gnupg/gpg-agent-info
default-cache-ttl 300
max-cache-ttl 900
scdaemon-program /usr/lib/gnupg2/scdaemon
sh
debug-level none
homedir ~/.gnupg

This configuration depends on two packages you must install via apt-get, pinentry-qt4 and scdaemon.

One annoying thing about most pinentry applications is that they don't allow pasting from clipboard, making them hard to use with a password manager. If you want to be able to do this, just grab the latest source of pinentry-qt4 (0.8.4 or greater) and then compile it with this option: ./configure --enable-pinentry-qt4-clipboard=yes.

Using the smartcard key for SSH authentication

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 install monkeysphere and use the openpgp2ssh tool, which we're going to skip. Note: this won't work unless you've 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 takes 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 getting it to work, so here are some extra steps which can assist the process:

Add enable-ssh-support and write-env-file to ~/.gnupg/gpg-agent.conf

Fetch the keygrip of your master public key with gpg2 --with-keygrip -k and add these 40 hex digits as a line to~/.gnupg/sshcontrol.

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

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 or make sure it doesn't start.

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

$ gpgkey2ssh 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.

More resources:

https://www.inuits.eu/blog/ssh-authentication-your-pgp-key http://www.bootc.net/archives/2013/06/09/my-perfect-gnupg-ssh-agent-setup/

How to obtain the OpenPGP smartcards and USB readers

I now recommend the YubiKey version 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.

If you have any questions about the information in this guide, you can reach me on Twitter @ageis, by e-mail to kevin [at] freedom [dot] press (PGP key), or XMPP/Jabber: ageis@jabber.calyxinstitute.org.

@skull-squadron
Copy link

skull-squadron commented Jun 4, 2022

Still relevant in 2022. 👍

Since GPG UX and conceptual clarity is somewhat bad, I'm interested if GPG private key material on the local machine was deleted (desired) or still remains in the keychain (undesired).

I moved a gpg (2.x) generated key to a Yubikey 5 NFC card with multiple subkeys and generated an encryption subkey on the card to fill the Yubikey encryption slot. Also, I use this Yubikey as one of several 2FA options for Bitwarden where all other service 2FAs are stored rather than in authenticator apps.

sec  ed25519/
     created: 2022-02-08  expires: 2025-02-07  usage: SC
     card-no: 
     trust: ultimate      validity: ultimate
ssb  cv25519/
     created: 2022-02-08  expires: 2025-02-07  usage: E
ssb  rsa4096/
     created: 2022-02-08  expires: 2025-02-07  usage: S
ssb  rsa4096/
     created: 2022-02-08  expires: 2025-02-07  usage: E <-- new subkey generated on the card
ssb  rsa4096/
     created: 2022-02-08  expires: never       usage: A

Edit: To answer my own question:

  1. I backed-up and shredded ~/.gnupg/private-keys-v1.d/*
  2. Ran gpg --card-status to repopulate ~/.gnupg/private-keys-v1.d/* with shadowed keys
  3. Investigated the contents of ~/.gnupg/private-keys-v1.d/*.

Looks good.

Some private keys weren't transferred to the card but there's a key for each function now that can be rolled-over in every use-case. (gpg-agent as ssh-agent needed keys rolled.)

Now:

  • To ssh to a system requires inserting the Yubikey and entering the Yubikey OpenGPG pin to retrieve the SSH-equivalent secret key in the keychain AND entering a TOTP (Google Authenticator-like) stored in BitWarden (OpenBSD: login_oath, Linux: libpam-google-authenticator, macOS: google-authenticator-libpam).
  • To use GPG requires the Yubikey, entering the Yubikey PIN and OpenGPG pin.
  • Logging into BitWarden requires the master password and a 2FA (Yubikey, email, or backup code).
  • Logging into or unlocking a macOS system requires the Yubikey and its PIN (OR) a complicated "backup" keyboard password.
  • Other features of Yubikey aren't used because BitWarden stores passwords and all TOTP 2FAs.

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