I have a new GnuPG Key

I have transitioned my GnuPG key from 1024-bit DSA to 4096-bit RSA. The old key is revoked and the new key is effective immediately.

My GnuPG Public Key:

  • ID 22D05A45
  • FP 44E4 4ABB 2410 742A 8476 BC9B 955B 200A 22D0 5A45
  • ID john@lane.uk.net

My key is also available at:

My Prior key 395DDE80 transition statement was a 1024-bit DSA key created on October 17th, 2004.

In the rest of this article, I explain how I use GnuPG and record various GnuPG titbits.

Key Generation

This is a 4096-bit RSA key. I used batch mode to create a new key using the following input file, john.keygen:

Key-Type:      RSA 
Key-Length:    4096
Key-Usage:     sign 
Subkey-Type:   RSA 
Subkey-Length: 4096
Subkey-Usage:  encrypt 
Preferences:   SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
Name-Real:     John Lane
Name-Email:    john@lane.uk.net


  • the UID comprises the name, optional comment and email fields. A UID cannot be changed because they are signed as a whole. You can add new UIDs and revoke existing ones, however.

Generate with

$ gpg --gen-key --verbose --batch john.keygen
gpg: key 22D05A45 marked as ultimately trusted

Start gpg interactively with the old (current) key as default and to edit the new key:

$ gpg --default-key 395DDE80 --edit-key 22D05A45

! using --default-key ensures that the old key is selected and will be used to s.gn the new key.

Confirm that SHA1 is avoided: SHA1 should be listed LAST in the list of offered digest algorithems (the OpenPGP standard requires it to be offered).

gpg> showpref
[ultimate] (1). John Lane <john@lane.uk.net>
    Cipher: AES256, AES192, AES, CAST5, 3DES
    Digest: SHA512, SHA384, SHA256, SHA224, SHA1
    Compression: ZLIB, BZIP2, ZIP, Uncompressed
    Features: MDC, Keyserver no-modify

Confirm ultimate trust:

gpg> trust
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y

Add photo ID. The recommended photo size is 240x288 in a file no bigger than 6,144 bytes. I used GIMP to crop and resize an image to those dimensions and save it as JPEG within the size constraint. (gpg outputs a warning if it's too large).

To add the photo:

gpg> addphoto
Enter JPEG filename for photo ID: /path/to/photo.jpg
Is this photo correct (y/N/q)? y

! the photo is exported with the public key but not with the secret key or secret subkeys.

Sign with old key (this signs both UIDs - the name/comment/email and the photo):

gpg> sign
Really sign all user IDs? (y/N) y
Are you sure that you want to sign this key with your key "John Lane <john@lane.uk.net>" (395DDE80)
Really sign? (y/N) y

Set a passphrase

gpg> passwd
This key is not protected.
Enter the new passphrase for this secret key.

Save and exit

gpg> save

Set the default key in ~/.gnupg/gpg.conf

default-key 22D05A45

Sign old key

To sign the old key with the new one:

$ gpg --default-key 22D05A45 --edit-key 395DDE80
gpg> sign
gpg> save
$ gpg --check-sigs 395DDE80
sig!         22D05A45 2015-02-11 ...

Signing Subkey

Create a second signing subkey for general-purpose use. The master signing key can then be removed from the keyring and stored elsewhere as a security precaution in case the keyring is stolen. In such an event the master signing key can be used to revoke the stolen key. Use interactive gpg:

$ gpg --edit-key 22D05A45
gpg> addkey
Please select what kind of key you want:
(4) RSA (sign only)
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
Key is valid for? (0) 
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
gpg> save

Revocation Certificate

Generate a revocation certificate that can be used to revoke the master key pair should this be necessary.

$ gpg --output 22D05A45.gpg.revcert --gen-revoke 22D05A45

(Revoking only subkeys should be done using them master key and not with this certificate.)

You can view its contents with

$ pgpdump 22D05A45.gpg.revcert

! pgpdump is a separate AUR package.

Export keys

Export the secret key (and subkey):

$ gpg --export-secret-keys --armor 22D05A45 > 22D05A45.gpg.key

Export only the secret subkey

$ gpg --export-secret-subkeys --armor 22D05A45 > 22D05A45.gpg.subkey

and the public key:

$ gpg --export --armor 22D05A45 > 22D05A45.gpg.pub

Export only the secret subkey

$ gpg --export-secret-subkeys --armor 22D05A45 > 22D05A45.gpg.subkey

Deleting keys

To remove a key from the key-ring. To remove the secret keys:

$ gpg --delete-secret-key 22D05A45

To remove the public keys:

$ gpg --delete-key 22D05A45

You are normally prevented from removing the public key if there is a secret key (you have to remove the secret key and then the public key). However, should you want to do this:

$ gpg --expert --delete-key 22D05A45

! removing a public key in this way is useful to un-revoke a key (providing it has not been sent to a key server). Do this:

$ gpg --expert --delete-key 395DDE80
$ gpg --import 395DDE80.gpg.pub

Using keys

To use a key, it must be in the keyring. To import only the public key or subkey, first delete the secret key from the keyring

$ gpg --delete-secret-key 22D05A45
Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y

and then import the subkey:

$ gpg --import 22D05A45.gpg.subkeys
gpg:   secret keys imported: 1

or the public key:

$ gpg --import 22D05A45.gpg.pub
gpg:               imported: 1  (RSA: 1)

Verify (e.g. with the subkey imported)

$ gpg -K 22D05A45 # secret keys
sec#  4096R/22D05A45 2015-02-10
uid                  John Lane...
uid                  [jpeg image of size 6144]
ssb   4096R/EA49BD51 2015-02-10
ssb   4096R/5A6F13C6 2015-02-10

$ gpg -k 22D05A45 # public key
pub   4096R/22D05A45 2015-02-10
uid       [ unknown] John Lane <john@lane.uk.net>
sub   4096R/EA49BD51 2015-02-10
sub   4096R/5A6F13C6 2015-02-10

! the # suffix on the secret key indicates that the signing key isn't in the keyring.

You can list the contents of a key file with any of these examples:

$ gpg 22D05A45.gpg.subkeys
$ gpg --list-packets 22D05A45.gpg.subkeys
$ gpg --with-fingerprint 22D05A45.gpg.pub
$ pgpdump 22D05A45.gpg.subkeys

Here is a more involved way that uses a temporary keyring but lets you access all the data (because it does import the key into a keyring):

$ mkdir temp_gpg
$ gpg --homedir=temp_gpg --import amajohn.co.uk/22D05A45.gpg.subkeys
$ gpg --homedir=temp_gpg --list-keys
$ gpg --homedir=temp_gpg --list-secret-keys
$ rm -r temp_gpg

Use of master signing key

The master signing key is required to sign other keys and it must be imported into the keyring so that it can be used. Assuming the above steps have been taken, the following steps do this:

$ gpg --delete-secret-key 22D05A45
$ gpg --import 22D05A45.gpg.key

and to remove it after use

$ gpg --delete-secret-key 22D05A45
$ gpg --import 22D05A45.gpg.subkeys

Or use a temporary keyring

$ mkdir temp_gp
$ gpg --homedir=temp_gpg --import ../newkey/22D05A45.gpg.key
$ gpg --homedir=temp_gpg --import F195C84A.gpg.key
$ gpg --homedir=temp_gpg --default-key 22D05A45 --edit-key F195C84A
gpg> sign
gpg> save
$ gpg --homedir=temp_gpg --export --armor F195C84A > F195C84A.gpg.pub
$ gpg --homedir=temp_gpg --export-secret-keys --armor F195C84A > F195C84A.gpg.key
$ gpg --homedir=temp_gpg --export-secret-subkeys --armor --export-options export-reset-subkey-passwd F195C84A > F195C84A.gpg.subkey
$ shred -u temp_gpg/* && rmdir temp_gpg

Use on Android

There is an application caled OpenKeychain which works with K9 Mail. There is a tutorial here.

Key Storage

I have safe copies of my secret key 22D05A45.gpg.key and revocation certificate 22D05A45.gpg.revcert in a number of places and formats.

Firstly, I use paperkey to create appropriate "paper" versions of my secret key:

$ gpg --dearmor < 22D05A45.gpg.key | paperkey > 22D05A45.gpg.key.paper

I store the paper keys, ahem, on paper.

Revoking old key

First, prepare a transition statement and then sign it with both keys:

$ gpg --clearsign -u 22D05A45 -u 395DDE80 gpg_transition_395DDE80_22D05A45

Now, to revoke the old key. Import the revocation certificate:

$ gpg --import /path/to/395DDE80.gpg.revcert

Check that its status is revoked

$ gpg --list-keys 395DDE80
uid       [ revoked] ...

Upload revoked key to key server

$ gpg --send-keys 395DDE80

Upload new key to key server

$ gpg --send-keys 22D05A45

! Retain old key in keyring so that it can still be used to decrypt.

Key Signing

To facilitate getting keys signed by others, the signing-party package contains some tools.

Using SSH

To use a GnuPG key with SSH, first configure the agent (~/.gnupg/gpg-agent.conf) - add a line with enable-ssh-support and restart the agent:

$ gpg-connect-agent KILLAGENT \bye
$ gpg-connect-agent /bye

Set environment variable for SSH:

$ export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

An alternative way to start the agent and set the variable is to do:

$ eval $(gpg-agent --daemon --disable-scdaemon --enable-ssh-support)

Create an authentication subkey

get its keygrip:

$ gpg --with-keygrip -k 9B36BA063A808C39
Keygrip = F6FB7CD341C847DB34ADBB77A46ABF65D12AB840

Put in GnuPG SSH config ~/.gnupg/sshcontrol

$ echo F6FB7CD341C847DB34ADBB77A46ABF65D12AB840 >> ~/.gnupg/sshcontrol

Check that the key is available:

$ ssh-add -l

Extract SSH format public key (for inclusion in remote authorized_keys files). Using SSH:

$ ssh-add -L > ssh_key.pub

or GnuPG:

$ gpg --export-ssh-key alice > ssh_key.pub

Extract the SSH format secret key. This cannot be done with GnuPG alone but the Monkeysphere has a tool called openpgp2ssh that can be used instead. It only works with unprotected keys (that don't have passphrases), so these must be removed first:

$ gpg --export-secret-key alice > alice.gpg
$ mkdir -m 700 .gnupg-temp
$ gpg --homedir .gnupg-temp --import alice.gpg
$ gpg --homedir .gnupg-temp --passwd alice
  (remove the passwords)
$ gpg --homedir .gnupg-temp --export-secret-key alice | openpgp2ssh DD53AC86 > alice.key
$ chmod 600 alice.key

This method can also be used to extract the public key:

$ gpg --export alice | openpgp2ssh DECAFBAD > alice.key.pub

GnuPG prior to 2.1 had an export option export-reset-subkey-passwd that could be used with --export secret-subkeys to export the key without a passphrase. This is no longer available due to the secret keys being in the gpg-agent.

Normal SSH keys may be loaded into the gpg-agent with ssh-add. This adds keygrip entries into the sshcontrol file and stores the key in the agent's private key directory (~/.gnupg/private-keys-v1.d). The agent uses its Pinentry to request a passphrase with which to encrypt its copy of the key.

It is not possible to use ssh-add to add a key that the GPG agent already knows about (because it's a secret key in the GnuPG keyring). It may appear to work:

$ ssh-add alice.key
Identity added: alice.key (alice.key)

but it doesn't:

$ ssh-add -l
The agent has no identities.

It does not matter if GnuPG knows the public key - ssh-add works when gpg -K does not return the private key whether or not gpg -k returns the public key.

Note: deleting keys (gpg --delete-secret-key) does not delete any related keygrips from the sshcontrol file.

A key already referenced in sshcontrol but absent from ~/.gnupg/private-keys-v1.d is added successfully.

A secret key present in the agent (e.g. by ssh-add) is not recognised by gpg -K unless the public gpg key is imported.


Private Key Passphrase Strength

NOTE the provisions in this section do not work at the moment; see the note at the end of the section.

GnuPG protects a private key with a passphrase by encrypting it. You can find out how well your private key is protected:

$ gpg --export-secret-key 22d05a45 | gpg --list-packets

Check sections for secret key packet and secret sub key packet which look like this:

version 4, algo 1, created 1423561917, expires 0
pkey[0]: [4096 bits]
pkey[1]: [17 bits]
iter+salt S2K, algo: 7, SHA1 protection, hash: 2, salt: F3BE7E2A5D67C291
protect count: 23068672 (230)
protect IV:  cc 2e d0 28 c0 1d 82 bd ab dc 03 d3 cb e5 c7 f2
skey[2]: [v4 protected]
keyid: 955B200A22D05A45

This reveals the symmetric-key (algo) and hashing (hash) algorithms in use, as defined in RFC4880. The above shows that algo: 7 is AES with 128-bit key and hash: 2 is SHA-1 which will be the default selections for the version of gpg that was used to create the key.

The iter+salt tag is the string-to-key mode that GnuPG uses to convert the passphrase (string) into a key. The default value iter+salt means that a salt is added to the passphrase and that string is hashed multiple times (a technique called key stretching which aims to increase difficulty of brute-forcing passphrases).

Whilst browsing the web to learn more about how GnuPG encrypts private keys, I read blog posts by Aaron Toponce and Chris Wellons who both suggest that, although these are good defaults, using AES-256 with SHA-512 would be an improvement.

Changing the algorithms on your key is a simple matter of specifying your choices and changing your passphrase (entering the same passphrase is ok - the intention is to change the algorithms):

$ gpg --passwd --s2k-cipher-algo AES256 --s2k-digest-algo SHA512 22d05a45

You can add, for example, --s2k-mode 3 --s2k-count 65011712 to change the number of key stretching iterations (man gpg states that this should lie in the range 1024-65011712). The four --s2k options are documented at man gpg.

NOTE The s2k options don't work with GnuPG 2.x (see s2k issue in the bugtracker and my gnupg-users thread)


I have registered as https://keybase.io/johnlane. I have not uploaded my private key. Some arguments for and against doing this are:

Signed Gist

I published my key as a signed GitHub Gist:

$ curl -i https://git.io -F "url=$(gpg --armour --export 22d05a45 | gpg --armour --clearsign | gist -s -f "PGP KEY 22D05A45" -d "$(gpg --with-colons --fingerprint 22D05A45 | awk -F: '$1 == "fpr" {print $10;}' | head -1)")"

Which also gives short and anonymous URLs. I also created a short raw link which can be verified like this:

$ curl -L https://git.io/vM5c1 | gpg --verify
Good signature from "John Lane ..."

! gist is Gist (gem github pkg)


To refresh a keyring with public key from a key server:

$ gpg --refresh-keys 395DDE80

or, to refresh all public keys:

$ gpg --refresh-keys

UID maintenance (e.g to add/remove email addresses). It isn't possible to delete information from keyservers. This is by design. You can, however, add new UID values and revoke old ones. See these gpg commands: uid and revuid.


Migrating to a new .gnupg is a way to remove old cruft. It is worth reading about What’s new in GnuPG 2.1 because it explains how migration from 1.x affetcs the [GPG- configuration] which helps identify files that are no longer required.

Start with a new empty directory:

$ gpgconf --kill gpg-agent    # stop the agent
$ mv .gnupg .gnupg.old
$ mkdir -m700 .gnupg    
$ ls -A .gnupg                # show that it is empty
total 0

Identify what a new install has:

$ gpg-connect-agent /bye      # start the agent
$ ls -A .gnupg
$ gpg -k
$ ls -A .gnupg
$ pubring.kbx  trustdb.gpg

Copy those things from the old directory

$ gpgconf --kill gpg-agent    # stop the agent
$ cp -a .gnupg.old/{private-keys-v1.d,pubring.kbx,trustdb.gpg} .gnupg
$ gpg -k                      # confirm public keys are there
$ gpg -K                      # confirm secret keys are there

Revocation certificates

$ cp -a ~/.gnupg.old/openpgp-revocs.d ~/.gnupg

The openpgp-revocs.d directory contains pre-generated revocation certificates for key-pairs generated with GnuPG version 2.x. They are named after the fingerprint of the key.

SSH Control file

The sshcontrol file is used by gpg-agent when support for the secure shell agent protocol has been enabled ( by enable-ssh-support in gpg-agent.conf).

$ cp -a ~/.gnupg.old/sshcontrol ~/.gnupg

Ephemeral files

The following files are automatically generated and don't need to be migrated:

  • random_seed is used to preserve the state of the internal random pool.
  • *.*~ are temporary backup copies of other files

The ~ (tilde) suffix is a standard Unix pattern for a backup of another file. GnuPG makes backups of some files when working on them.

Check what's left in the old directory:

$ diff <(ls -A ~/.gnupg) <(ls -A ~/.gnupg.old)
  • crls.d and dirmngr.conf are for dirmngr, a server for managing and downloading certificate revocation lists (CRLs) for X.509 certificates. I haven't used this feature.
  • .gpg-v21-migrated is an empty file that indicates that a migration to GnuPG 2.1 has been done.

Record what's in the new directory:

$ ls -A
gpg-agent.conf  openpgp-revocs.d   pubring.kbx   random_seed  tofu.db
gpg.conf        private-keys-v1.d  pubring.kbx~  sshcontrol   trustdb.gpg

Of these, GunPG created tofu.db (trust on first use). I don't use this trust model so I presume that this file can be ignored also. Therefore, I only backup the following:

gpg-agent.conf  openpgp-revocs.d/*.rev   pubring.kbx
gpg.conf        private-keys-v1.d/*.key  sshcontrol   trustdb.gpg

Of which the following are sensitive

openpgp-revocs.d/*.rev  private-keys-v1.d/*.key

Mailserver key

My mailserver can automatically encrypt or sign outbound messages. It will only do this if not already signed or encrypted. It will encrypt mail if the recipient's public key is in the mail server's keyring, otherwise it will sign the message using its own secret signing key.

I create a key for the server to use for this purpose following the process outlined above:

Key-Type:      RSA
Key-Length:    4096
Key-Usage:     sign 
Preferences:   SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
Name-Real:     amajohn.co.uk
Name-Email:    postmaster@amajohn.co.uk

$ gpg --gen-key --verbose --batch amajohn.co.uk.keygen
$ gpg --edit-key F195C84A
gpg> passwd
gpg> sign

I don't create en encryption key because it makes no sense to receive mail encrypted with it (such inbound mail should be encrypted with the recipient's key, not the server's).

The mailserver key is is set with a passphrase (extracting subkeys doesn't work if there is no passphrase). It is signed by my own master key and then the secret and public keys are exported:

$ gpg --export --armor F195C84A > F195C84A.gpg.pub
$ gpg --export-secret-keys --armor F195C84A > F195C84A.gpg.key
$ gpg --export-secret-subkeys --armor --export-options export-reset-subkey-passwd F195C84A > F195C84A.gpg.subkey

The subkey is exported without a passphrase so that the server can use it without user intervention.

The file F195C84A.gpg.subkey is uploaded to the mail server and imported:

$ gpg --import F195C84A.gpg.subkey

Export its revocation certificate:

$ gpg --output F195C84A.gpg.revcert --gen-revoke F195C84A

Outbound filer (Procmail)

The server's outbound filter is implemented with Procmail with the following recipes:

# Detect message signed/encrypted by client
# (the end-user, sender of the message)
:0 Bw
*? egrep -q -- '^-----BEGIN PGP SIGNATURE-----$'

:0 EBw
*? egrep -q -- '^-----BEGIN PGP MESSAGE-----$'

# Encrypt for recipient and sign with server's key
# The trust model 'always' is used to allow encryption
# to any public key in the key ring without explicitly
# setting a trust level for the key (using `gpg` trust).
:0 E
  ENCRYPT_AUDIT="server-encrypted for $RECIPIENT"

  :0 fhbw 
  | /usr/bin/mimegpg -s -e -- --recipient $RECIPIENT --trust-model always

# If unable to encrypt, just sign with server's key.  This
# occurs when a recipient's key is absent from the key ring.
:0 e

  :0 fhbw
  | /usr/bin/mimegpg -s

# If unable to sign then it's just plaintext!
:0 e

# Log audit message
:0 c
| $AUDIT outbound $ENCRYPT_AUDIT sending from $SENDER to $RECIPIENT

# Send mail
:0 w
| /usr/bin/sendmail -G -i -f $SENDER $RECIPIENT

(This is an excerpt. For further information refer to my Linux Voice article).

Messages already encrypted / signed are not modified.

Use of QR Codes

Using QR codes to reprersent keys has been widely discussed. I wanted to generate a QR code (pacman -s qrencode) but could not get my key to fit inside. Here are some stats for my public key:

raw key   10745 => 14517 as base64
armor key 14649 => 19789 as base64

This is the command-line to produce the QR code from a public key

$ gpg --dearmor < 22D05A45.gpg.pub | base64 | qrencode -lM -o 22D05A45.gpg.pub.png
Failed to encode the input data: Numerical result out of range

and from a secret key

$ gpg --dearmor < 22D05A45.gpg.key | paperkey --output-type raw | base64 | qrencode -lM -o 22D05A45.gpg.key.png

Some paranoia

  • Many printers keep a copy of what they print in memory, and some do so for a very long time (especially if it has an internal hard drive). So be sure to destroy your printer before getting rid of it if you print your GPG key with it.


Permission denied errors were not due to tty issue but permissions on and within the ~/.gnupg directory created by Amylum. Fixed by removing

To look at

  • pass uses gpg to store passwords.
  • gpgdir directory encryption.


The below might be useful for OSX