Airgapped GPG

Airgapping GPG

Airgapped Media


I’ll save you the explanation of what GPG is. First thing I needed was a USB stick that would be compatible with Arch and MacOS. This fight is always interesting and I would rank the better operating system as the one that can comprimise on the filesystem. Naturally, insert the USB stick in the MacOS and format it as a Journaled HFS+ partition because we can install the driver on Arch.

Disable Journaling

sudo diskUtil disableJournal /dev/disk0s1

Or whatever your disk is. Arch doesn’t like the journaling yet.


I tried all the variants of FAT, exFAT, NTFS, and a couple others. This is the only filesystem that preserves permissions and case-sensitivity, so I think it is the safest bet.


You want to have an /etc/fstab entry that will let you mount this disk easily.

/dev/sdb1 /mnt/stick hfsplus user,rw,nosuid,nodev,noexec,relatime,sync,umask=22,uid=0,gid=0,nls=utf8

Now you can:

mount /mnt/stick

When your drive is inserted.


Make a directory with sudo on this drive and own it to your user. The UID for the rest of the drive will likely be 99 because of the way MacOS deals with users, but you can make this folder be yours to write freely to it.


Arch BBS SuperUser

Making Keys


This article was so good, that I copied the article in plaintext to my storage media for the keys too.


Here’s a copy of my ~/.gnupg/gpg.conf.



You will be making the GPG keys with your local utility, backing them up to the media, then deleting the master key from your current OS installation. When you take the media to a new computer, you will import the secret subkeys only. That will leave you with full capablilites of GPG but also protection against private key discovery.

First make a key that can only Certify:

gpg --expert --full-gen-key

Select 8 and toggle till you see this:

Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Certify

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q

Make the keysize 4096 because we live in the future, and give it an expiration of 1y. Fill in the rest of the fields how you would like. For the identity fields, question what the use of this key will be? Deniable encryption or identity verification?

You should have something like this now:

> gpg -k
pub   rsa4096/ED152F106BE94245 2018-12-19 [C] [expires: 2020-12-18]
      Key fingerprint = 332B DF93 D4CA 7C43 4E87  F0F2 ED15 2F10 6BE9 4245
uid                 [ultimate] Real Name <[email protected]>


Now that you have your master key, make the sub keys. Get the ID of the key you just made and enter expert mode.

gpg --expert --edit-key ED152F106BE94245

The idea now is to addkey to make a subkey for each of the tasks: Authenticate (A), Sign (S), and Encrypt (E). So you will select 8 again and toggle until you only have one of those permissions on a key, complete the generation process, then repeat for the other permissions.

You should have this at the end:

sec  rsa4096/ED152F106BE94245
     created: 2018-12-19  expires: 2020-12-18  usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/6D7B797688AD10C7
     created: 2018-12-19  expires: 2020-12-18  usage: S   
ssb  rsa4096/815437E38E23B6EE
     created: 2018-12-19  expires: 2020-12-18  usage: E   
ssb  rsa4096/707FB77767CBEDDD
     created: 2018-12-19  expires: 2020-12-18  usage: A   
[ultimate] (1). Real Name <[email protected]>

save to exit GPG.


After you have made your keys, generate a revocation key for the master. We will use this in case we need to invalidate the whole shebang.

gpg --output ED152F106BE94245.rev --gen-revoke ED152F106BE94245

ED152F106BE94245.rev Contains the revocation certificate for the master key. I’ve kept it on the media, because that’s where the master private key will only ever be.


gpg --export --armor ED152F106BE94245 > Contains all the public keys.

gpg --export-secret-keys --armor ED152F106BE94245 > ED152F106BE94245.sec.asc

ED152F106BE94245.sec.asc Contains the master private key.

gpg --export-secret-subkeys --armor ED152F106BE94245 > ED152F106BE94245.sub.sec.asc

ED152F106BE94245.sub.sec.asc Contains only the private keys of the subkeys.

Deleting the Master

Now, you can delete the master key from your local keyring to complete the setup. First, delete the whole thing, then import only the private subkeys that we just exported.

gpg --delete-secret-key ED152F106BE94245
gpg --delete-key ED152F106BE94245

Now, import the the private subkeys:

gpg --import ED152F106BE94245.sub.sec.asc

You should have this now:

> gpg -k
pub   rsa4096/ED152F106BE94245 2018-12-19 [C] [expires: 2020-12-18]
      Key fingerprint = 332B DF93 D4CA 7C43 4E87  F0F2 ED15 2F10 6BE9 4245
uid                 [ unknown] Real Name <[email protected]>
sub   rsa4096/6D7B797688AD10C7 2018-12-19 [S] [expires: 2020-12-18]
sub   rsa4096/815437E38E23B6EE 2018-12-19 [E] [expires: 2020-12-18]
sub   rsa4096/707FB77767CBEDDD 2018-12-19 [A] [expires: 2020-12-18]

> gpg -K
sec#  rsa4096/ED152F106BE94245 2018-12-19 [C] [expires: 2020-12-18]
      Key fingerprint = 332B DF93 D4CA 7C43 4E87  F0F2 ED15 2F10 6BE9 4245
uid                 [ unknown] Real Name <[email protected]>
ssb   rsa4096/6D7B797688AD10C7 2018-12-19 [S] [expires: 2020-12-18]
ssb   rsa4096/815437E38E23B6EE 2018-12-19 [E] [expires: 2020-12-18]
ssb   rsa4096/707FB77767CBEDDD 2018-12-19 [A] [expires: 2020-12-18]

Note the # near sec for the list private keys operation. That means that the master key no longer is present, only a stub. This is perfect.


Now that you have a full set of airgapped keys, you can move them back over to the other computer and import the private subkeys again. Then you can give your public key to who you want.

We did create and Authentication key too…

Exporting an SSH Key

gpg --with-keygrip -k
> gpg --with-keygrip -k

pub   rsa4096/ED152F106BE94245 2018-12-19 [C] [expires: 2020-12-18]
      Key fingerprint = 332B DF93 D4CA 7C43 4E87  F0F2 ED15 2F10 6BE9 4245
      Keygrip = 9E3B3A733EE6843BFF34A8519B8E2718FAC37CE9
uid                 [ unknown] Real Name <[email protected]>
sub   rsa4096/6D7B797688AD10C7 2018-12-19 [S] [expires: 2020-12-18]
      Keygrip = FF269048B780DDCC03C848E1EDC026DDA58112A5
sub   rsa4096/815437E38E23B6EE 2018-12-19 [E] [expires: 2020-12-18]
      Keygrip = 7848285CC2CF03B10FE43EC4679B86775224FCF9
sub   rsa4096/707FB77767CBEDDD 2018-12-19 [A] [expires: 2020-12-18]
      Keygrip = 3033CB663B155310BF8B95A92226E3D7A50B8B6E

You can see here the keygrips you need to complete the ssh authentication portion. Add the keygrip of the [A] key to the end of ~/.gnupg/sshcontrol.

Then you can export a key:

gpg --export-ssh-key ED152F106BE94245

And copy that to your remote server authorized keys or give it to Gitlab.

GPG Agent

You can use gpg-agent instead of ssh-agent. The setup is different on Mac and Linux. TODO: Include the setup once it is working.

As always, The Arch Wiki is a good place to start for configuration.

Signing Commits

Now that you have the keys, you can sign your commits. Github and Gitlab get a copy of you public key first, then you configure your local git installation to sign with your key:

git config --global user.signingkey ED152F106BE94245
git config --global commit.gpgsign true