Skip to content

Instantly share code, notes, and snippets.

@ageis
Last active November 6, 2024 14:28
Show Gist options
  • Save ageis/5b095b50b9ae6b0aa9bf to your computer and use it in GitHub Desktop.
Save ageis/5b095b50b9ae6b0aa9bf to your computer and use it in GitHub Desktop.
Quick GPG Smartcard Guide

⚠️ Content below I now consider obsolete. My latest guide is now located here: Technical guide for using YubiKey series 4 for GPG and SSH. Please consult that resource instead.


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
keep-tty
enable-ssh-support
default-cache-ttl 3600
max-cache-ttl 7200
sh
debug-level none

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: [email protected].

@ageis
Copy link
Author

ageis commented Aug 22, 2017

Newer, updated version (covering single-smartcard setup) posted here: Technical guide for using YubiKey series 4 for GPG and SSH. Feedback welcome, @iggs0, @Phoenyx33, @drduh, @flamsmark, etc.

@kelvinnguyen
Copy link

I used OpenPGP Applet and load it into my card.
I can generate key from command line of GPG. But, I can't generate key from Kleopatra Tool. Do I sign with Kleopatra to run this functions?

Thanks.

@Sebi2020
Copy link

If gpg places a stub in the keyring, how can I use the smartcard on another computer?

@ageis
Copy link
Author

ageis commented Dec 30, 2020

@Sebi2020 Within the guide, I had placed a link to a thread which address that: https://lists.gnupg.org/pipermail/gnupg-users/2013-September/047412.html

@Sebi2020
Copy link

Sebi2020 commented Dec 31, 2020

@Sebi2020 Within the guide, I had placed a link to a thread which address that: https://lists.gnupg.org/pipermail/gnupg-users/2013-September/047412.html

Short story:
It looks like it works if you use only one smartcard for all three keys and run gpg2 --card-status on the other computer, but fails if you're trying to split the keys upon multiple cards.

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

@GioF
Copy link

GioF commented Sep 8, 2022

Thank you.

I didn't know that it was necessary to toggle the key in order to choose which one to send into the card (and was struggling with this for a week straight).

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