Secure yourself, Part 1: Air-gapped computer, GPG and smartcards

This blog entry is part of a series covering:

  • Logging into your laptop and decrypting it automatically
  • Encrypting and decrypting any passwords or secure info you have
  • Sending and receiving encrypted mails
  • Providing 2-step authentication codes for your web logins
  • Doing all of the above on your mobile phone
  • Authenticating for sudo on your machine
  • Managing your ssh keys seamlessly
  • Signing intents, that can be used for opening a door, etc


Of course, this is not 100% secure and this guide does not cover all corners, nor is “3 letter agencies”-proof. Think of it as a hobby from which you can learn: in the end is all about compromise, and I choose to draw the line in an arbitrary place, much like anyone else. Feel free to contact me if you see a hole or want to make suggestions.

PART 1: Air-gapped computer, GPG and smartcards

In this part we will:

  1. Set up an air-gapped computer
  2. Create the GPG keys and subkeys
  3. Publish the public key
  4. Configure and load the keys into a Yubikey Neo
  5. Backup the keys
  6. Set up another computer for the Yubikey Neo
  7. Migrate from your old keys
  8. Revoke the subkeys in case of emergency

Before starting, let’s learn some concepts. There are 3 different types of information that can be used for authentication:

  • Things only you are or do (e.g: your fingerprint, biometrics, usernames)
  • Things only you know (e.g: the combination of a pad, passwords)
  • Things only you have (e.g: a metal key, your phone, 2-step authentication tokens)

The first class, “things you only are”, is a bit blurry, as it can be morfed into the second, “things you only have”: a fingerprint for example can be easily obtained from left-overs (a plastic bottle or a cup), can be copied easily with household items and cannot be changed at will if compromised. Or someone can remove your finger from your hand, and most scanners won’t notice. That’s why biometrics should be used only as “usernames”. In contrast, a strong private password can only be sniffed out when you use it, and you are not leaving it anywhere you go.

The most comprehensive solution would be to use the 3 types of information, called as you know a 2-step authentication scheme: you use the thing you only are and you only know (login and password) and then a token you only have in that moment (a one-use code, or a code generated by a device that needs your credit card or your phone). An attacker would need to guess the password an steal your device to succeed.

Our token that we only have is going to be a yubikey NEO, which is a little durable USB key with one capacitive button. You can put it in your keyring, or better, on a bracelet that you keep always with you. Is quite possible that this kind of device will be the norm in 10 years: NFC-capable rings that store your passwords and log you in every thing imaginable, automatically.

image of Yubikey Neo

The Yubikey Neo provides us with:

  • One Time Passwords (OTPs) by the USB.
  • Can also serve OTPs by NFC, and read them on an Android phone with the Yubico Auth app.
  • Has secure element and JavaCard capabilities so it can act as a smartcard and contain a set of GPG subkeys. All the encryption/decryption happens inside the Yubikey, and it’s not possible to extract the keys.
  • And can also serve 1 static password by USB too.

For setting up our security scheme and our Yubikey, we need some foundations: an air-gapped computer.

1. Air-gapped computer

An air-gapped computer is one that is neither connected to the internet nor connected to any other system that is connected to the internet. For this purpose, a computer on a board is quite useful, such as a Raspberry Pi, a Beaglebone Black, etc. All of these are FOSS hardware, cheap (~40€), portable (credit card sized), have no on-chip wireless network connections so when it is not networked you are sure of it (unlike, lets say, a smartphone and their broadband chips), their microSD cards can be easily changed to carry different air-gapped computers and can be stored easily or even made tamper-proof with epoxy.

I’m going to use an Olimex A20 Olinuxino Lime , as is what I currently have.

A20 Olinuxino Lime

This computer will be used to generate our master GPG keys and subkeys, signing with our master keys, building and signing a binary, etc. We will move files in and out of our offline computer by an usb stick, which shouldn’t be too much of a handicap as these operations are rarely done.


For that, we are going to install Debian Jessie with no blobs (privative software) and simple frame buffer support on the A20 Olinuxino Lime. That will let us have a console on the HDMI output. For that, we need U-Boot bootloader version 2015.01 or more, and Linux kernel 3.19 or more. We have two options, either you use the weekly/daily Debian ARM ISO images, or we make the installation ourselves.

If you choose to make the installation, follow the steps for making the SD card image as instructed in the Linux-sunxi wiki (shameless plug: I contributed to those pages of the Linux-sunxi wiki). For building U-Boot, use the A20-OLinuXino-Lime_config, and use Olimex A20-OLinuXino-LIME as the device name for flash-kernel.

After you have your system running, update and install the following packages (if in the future you want to install new packages, remember that you will need to download the .debs and install them by USB).

# Update the system:
$ sudo apt-get update && sudo apt-get dist-upgrade
# For console and keyboard configuration:
$ sudo apt-get install console-data keyboard-configuration
# For checking GPG keys:
$ sudo apt-get install hopenpgp-tools # as of now, not yet on armhf
# For backing up the keys:
$ sudo apt-get install paperkey qrencode
# For using and configuring the Yubikey:
$ sudo apt-get install python-yubico python-yubico-tools yubikey-personalization
# For smartcards:
$ sudo apt-get install pcscd scdaemon pcsc-tools gpgsm
# Install gnupg2:
$ sudo apt-get install gnupg2
# For encrypted filesystems:
$ sudo apt-get install cryptsetup libpam-mount
# And install the packages you want:
$ sudo apt-get install git zsh tmux vim w3m

In the future, this will install gnupg2 v2.1 and up, which will simplify things. At this point, with Debian Jessie, this gives us:

$ aptitude versions gnupg2 scdaemon gpgsm pcscd
Package gnupg2:
Package gpgsm:
Package pcscd:
Package scdaemon:

As stated in this openPGP best practices guide, Put Jacob Appelbaum’s hardened gpg.conf as yours and read and understand it:

$ wget ~/.gnupg/gpg.conf
# Enable ssh support:
$ echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf

Now we can disconnect from LAN and be off-line. Of course from now on, don’t interface to your Olinuxino by UART, ssh or else: connect it to an HDMI screen and its own cabled keyboard.


If in the future you need to use again the computer, you will surely need to set the watch again with:

$ sudo date -s "15 MAY 2015 11:14:00"

A better option would be to connect a Realtime Clock Module with a battery to it, such as this one, or this other one, as I2C has been in the Kernel since 3.13 and it should just work(™) . There are many other modules, and are quite cheap. In our case, this SoC board has a RTC included: you only need to connect a battery to the Olinuxino and it will mantain the time (the controller is not already in the Mainline Kernel, so you will need to wait a bit for it).

In case you need to configure the console or connect a keyboard with different locale than what it is currently configured you can run:

$ sudo dpkg-reconfigure console-data
$ sudo dpkg-reconfigure console-setup
$ sudo dpkg-reconfigure keyboard-configuration && service keyboard-setup restart

Now you can lay back and contemplate your air-gapped secure computer up and running!

image of Olinuxino A20 Lime in the case

2. Generating the GPG keys

We are going to generate a master GPG key pair, and a subkey pair. If you want to get a peek on how public key cryptography works, this video is quite good. For more information about Subkeys I recommend reading the Debian page about Subkeys and this StackOverflow post. And now that you are at it, take a peak at this OpenPGP best practices recopilation. The master key pair is going to be used for signing other people keys and issuing new subkeys. The subkey is linked to the master keys, is going to be stored inside our Yubikey Neo, and will be the one that we will use for encrypting and decrypting. You can make more subkey pairs if you want and because they are linked to the master pair your friends don’t need to re-sign them if you lose them or become compromised. When generating the subkey pair remember that the Yubikey Neo can only store and work with keys up to 2048 bits.

As the Yubikey doesn’t allow us to make a 4096 master keypair, and 3 subkeys to that keypair that we can backup, we are going to generate the keys inside the air-gapped computer.

The expire date will be 1 year, with manual extending every year (see here the reasons why).

Generate the master keys:

# Generate a new master key:
$ gpg2 --full-gen-key
Your selection? 4 (RSA sign only)
What keysize do you want? (2048) 4096
Key is valid for? (0) 1y

# Add more user ids as desired:
$ gpg2 --edit-key <key id here>
gpg> adduid
# Select your primary uid:
gpg> uid 1
gpg> primary
gpg> save

Make a revocation certificate:

$ gpg2 --output \<\>.gpg-revocation-certificate --gen-revoke your@email

Make a backup of the master keys:

$ gpg2 --export-secret-keys --armor > \<\>.private.gpg-key
$ gpg2 --export --armor > \<\>.public.gpg-key
$ mkdir backup-master-keys
$ mv \<* backup-master-keys/

Generate the subkeys:

$ gpg2 --expert --edit-key <key id here>
gpg> addkey
Your selection? 4 (RSA sign only)
What keysize do you want? (2048) 2048
Key is valid for? (0) 1y
gpg> addkey
Your selection? 6 (RSA encript only)
What keysize do you want? (2048) 2048
Key is valid for? (0) 1y
gpg> addkey
Your selection? 8 (set your own capabilities)
Toggle: a,s,e so it is for Authentication
What keysize do you want? (2048) 2048
Key is valid for? (0) 1y
gpg> save

Make backups of the subkeys:

$ gpg2 --export-secret-keys --armor > \<\>.master-subkeys.gpg-key
$ gpg2 --export-secret-subkeys --armor > \<\>.subkeys.gpg-key
$ mkdir backup-subkeys
$ mv \<* backup-subkeys/

3. Publish the public key

Put it in the keyservers and in your web page:

# On your air-gapped computer:
$ mkdir ~/usb
$ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw
$ cp publickey.txt ~/usb/
$ sudo umount /dev/sda1

# On your networked computer:
$ gpg2 --import < /media/8GB/publickey.txt
$ gpg2 --send-keys <key id here>

4. Setting up the Yubikey Neo

Connect the Yubikey to your air-gapped computer. Make sure the eject flag in the Yubikey is set to 82 for OTP and CCID compatibility:

$ ykpersonalize -m82 # take it out and plug it back to restart it

and that you have a recent firmware version, 3.1.8 or later; use lsusb -v to find out. Also, make sure that you are using a recent Yubikey Neo that is not vulnerable to CVE-2015-3298. If it is, know that you are entitled to a free replacement + free shipping (even if you can just patch it).

After that, configure your smartcard:

$ gpg2 --card-edit
gpg/card> admin
# Change PIN and Admin PIN: (default are 123456 and 12345678 respectively)
gpg/card> passwd
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 1
PIN changed.
Your selection? 3
PIN changed.
Your selection? q
# Change the card's info:
gpg/card> name
gpg/card> lang
gpg/card> sex
# Set the url to where your public key is (keyserver, your page, etc):
gpg/card> url
gpg/card> login
gpg/card> quit

And move the subkeys to the smartcard (this will really move the subkeys, not copy them, so make sure you have the backups if you want to use this same subkeys in another Yubikey for example):

$ gpg2 --edit-key <key id here>
gpg> toggle
gpg> key 1 # select 1
gpg> keytocard
Please select where to store the key:
(1) Signature key
(3) Authentication key
Your selection? 1
gpg> key 1 # deselect 1
gpg> key 2 # select 2
gpg> keytocard
Please select where to store the key:
(2) Encryption key
Your selection? 2
gpg> key 2 # deselect 2
gpg> key 3 # select 3
gpg> keytocard
Please select where to store the key:
(3) Authentication key
Your selection? 3
gpg> save

Make backups of the stubs:

mkdir backup-masterstubs
$ gpg2 --export-secret-keys --armor > \<\>.master-stubs.gpg-key
$ gpg2 --export-secret-subkeys --armor > \<\>.subkeys-stubs.gpg-key
$ gpg2 --export --armor > publickey.txt
$ mv \<* publickey.txt backup-masterstubs/

Take the ~/backup-masterstubs/publickey.txt and put it on the URL you specified on the Yubikey Neo.

And you have your Yubikey set up! Hook it to your keychain or as I have done, put it on a wristband:

image of the yubikey in a wristband

5. Backup

You should backup you primary keypair and revocation certificates inside of your air-gapped computer in an USB flash drive for example:

$ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw
$ cp backup-* ~/usb/
$ sudo umount /dev/sda1

You can also put the keys on paper using Paperkey, which displays just the “secret bits” of the secret key for you to copy by hand (≈ 149 bytes). Remember, if you use a printer, the key can be disclosed on the printer’s memory. You should also store your air-gapped computer in a safe place too, or at least its microSD.

6. Using the Yubikey Neo in other computer

Configuring the machine

Install the needed packages:

# For smartcards:
$ sudo apt-get install pcscd scdaemon pcsc-tools gpgsm
# Install gnupg2 v2, for Yubikey, and ssh auth compatibility:
$ sudo apt-get install gnupg2

If you are using Gnome 3 (or something based on it), we have a problem. The gpg-agent protocol implementation of gnome-keyring is very incomplete and hence breaks at least the smartcard functions of gpg and most functions of gpgsm. Instead of using it, we are going to deactivate gnome-keyring-daemon ang use gpg-agent instead. The best solution I have found is the one in the gnupg mail list:

mkdir ~/.config/autostart/
cp /etc/xdg/autostart/gnome-keyring-gpg.desktop ~/.config/autostart/
echo 'Hidden=true' >> ~/.config/autostart/gnome-keyring-gpg.desktop

If you want to have a look at how I have configured it, you can peek at my dotfiles, which include the gpg-agent workaround and my shareable configs.

Fetching the keys

Fetch the public key and make the stub keys:

$ gpg2 --card-edit
gpg/card> fetch
	gpg: solicitando clave 0x223F15CA5AAF0E78 de http servidor
	gpg: clave 0xA2591E231E251F36: clave pública "Víctor Cuadrado Juan <me@redacted>" importada
	gpg: Cantidad total procesada: 1
	gpg:               importadas: 1  (RSA: 1)
gpg/card> quit

Now you should see that you have stubs in your gpg private key (sec#, with # meaning stub, nude key). In my case:

$ gpg2 -K
sec#  4096R/0xA2591E231E251F36 2015-05-13 [caduca: 2016-05-12]
      Huella de clave = E3C5 114C 0C5B 4C49 BA03  0991 A259 1E23 1E25 1F36
uid                            Víctor Cuadrado Juan <me@redacted>
uid                            Víctor Cuadrado Juan <victorcuad@redacted>
ssb>  2048R/0x223F15CA5AAF0E78 2015-05-13
ssb>  2048R/0x7413065A8F779586 2015-05-13
ssb>  2048R/0x3B1500BD8C0124E3 2015-05-13

Mark the key as ultimately trusted:

$ gpg2 --edit-key <your key id here>
gpg> trust
your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
gpg> quit

7. Key migration

If you are moving from an old keyset to the new ones we have just created, you should sign the old key with the new one, write up a transition statement and revoke the old key. A sane thing to do is to also backup that old keypair, so you can decrypt old files in case you need it.

Signing the old key

To sign the old key, export it, move it to the air-gapped computer, sign it and share it:

# On your networked computer:
$ gpg2 --export --armor <old key id here> > oldkey.txt

# Copy oldkey.txt to a USB stick and connect the stick to your air-gapped computer

# On your air-gapped computer:
$ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw

# Import the key and sign it:
$ gpg2 --import < ~/usb/oldkey.txt
$ gpg2 --sign-key <old key id here>
Really sign all user IDs? (y/N) y
Really sign? (y/N) y

# Export the old key and copy it to the USB:
$ gpg2 --export <old key id here> > ~/USB/signed-oldkey.txt
$ sudo umount /dev/sda1

# On your networked computer:
$ gpg2 --import < /media/8GB/signed-oldkey.txt
$ gpg2 --send-keys <old key id here>

Make a transition statement

About transition statements, This is a good example of one. Clearsign it with both keys (old and new), and then publish it in a stable place. Don’t forget to update the references you have to your GPG key too (email signatures, about page, presentation cards, etc).

# On your networked computer:
$ gpg2 --export --armor <old key id here> > oldkey.txt
$ gpg2 --export-secret-keys --armor <your old key id here> > secret-oldkey.txt

# Copy oldkey.txt and your transition statement to a USB stick and
# connect the stick to your air-gapped computer

# On your air-gapped computer:
$ sudo mount /dev/sda1 ~/usb -o uid=1000,gid=1000,rw

# Import the key and sign it:
$ gpg2 --import < ~/usb/secret-oldkey.txt

# Clearsign the statement. Note that we used “<new master key>!" to make GnuPG
# use the master key for signing rather than a subkey, which it would normally do:
$ cat unsigned_GPG_transition_statement_2015-5-15.txt |
gpg --clearsign --personal-digest-preferences "SHA512" --local-user <old key here>
--local-user <new master key>! > GPG_transition_statement_2015-5-15.txt

# Export the old key and copy it to the USB:
$ cp GPG_transition_statement_2015-5-15.txt ~/usb/
$ sudo umount /dev/sda1

# Put the transition statement somewhere public

Revoke the old key

$ gpg2 --edit-key <old key id here>
gpg> revkey # select a reason
gpg> save

# share the revoked key with the key servers:
$ gpg2 --send-keys <old key id here>

8. Revoke the subkeys in case of emergency

If your subkeys become compromised, stolen or lost, we will need to revoke them. Boot up your air-gapped computer, where the master keypair is (and if not, import them with gpg --import) and then revoke the subkeys:

$ gpg2 --edit-key <your key id here>
# toggle the subkeys:
gpg> key 1
gpg> key 2
gpg> key 3
gpg> revkey # select a reason
gpg> save

And now you need to export that keys, and publish them on a key server.


Version of this post in Markdown

If you want a Markdown version of this blog post for further reading or backup you can download it from here.

Japanese translation

This blog post has been translated to Japanese by news website. You can find it here.

Víctor Cuadrado Juan

I'm Víctor Cuadrado Juan, a developer and FOSS enthusiast, in love with Linux. Currently living in Nürnberg, Germany. Feel free to waste your precious time around here, or to contact me.