Transitioning to a new GPG keypair  gpg

Since my old GPG keypair had a few UIDs I don’t employ any more and did not adhere to current best practices, I decided to transition to a new one. As usual, you can download it from this Web site, or from the SKS keyservers pool (preferred, since you will also fetch all certification signatures I will get in the future); make sure to check its fingerprint before relying on it. You can also get the transition statement (more on that later) to verify that, in fact, this new key was generated by me.

GPG best practices

The de facto reference guides to create secure keypairs are Creating the perfect GPG keypair by Alex Cabal and OpenPGP Best Practices by the Riseup collective; the latter, however, as of now (January 2017) is outdated, and there is no single document that details how to rollover keys, write a transition statement and (possibly) a certification practice statement (more on these last points later).

After some careful reading, I decided to document here the procedure I followed in an easy-to-read format.

Starting with a fresh configuration

To generate my new keypair, I decided to start from a completely clean configuration.

First, export all public keys in your keyring to a single file in order not to have to fetch them again later:

$ gpg --export -a > ~/allpublickeys.asc

Kill gpg-agent if it is running:

$ gpgconf --kill gpg-agent

Now that the agent is stopped, move the old .gnupg directory to a safe place, as we will need the old keypair later:

$ cd ~
$ mv .gnupg .gnupg_old

Create a new .gnupg directory and set its permissions to 700, otherwise GnuPG will (rightly) complain about it:

$ mkdir .gnupg
$ chmod 700 .gnupg

It is now time to configure GnuPG. Starting from the skeleton configuration template provided with GnuPG, we will first set up the directory manager (dirmngr) so that it will use the SKS keyservers secure pool to fetch the public keys of the people we will be corresponding with over TLS, thus keeping our interactions with the servers private and also preventing any man-in-the-middle attacks. Since October 2015, the custom CA used by the pool is bundled with GnuPG, so there is no need to download it separately; if you do not trust the package maintainers for your distribution sufficiently, you might still want to verify the CA certificate fingerprint according to the official instructions.

$ cat > ~/.gnupg/dirmngr.conf <<EOF
# dirmngr.conf - Options for Dirmngr
# Written in 2015 by The GnuPG Project <https://gnupg.org>
#
# To the extent possible under law, the authors have dedicated all
# copyright and related and neighboring rights to this file to the
# public domain worldwide.  This file is distributed without any
# warranty.  You should have received a copy of the CC0 Public Domain
# Dedication along with this file. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
#
# Unless you specify which option file to use (with the command line
# option "--options filename"), the file ~/.gnupg/dirmngr.conf is used
# by dirmngr.  The file can contain any long options which are valid
# for Dirmngr.  If the first non white space character of a line is a
# '#', the line is ignored.  Empty lines are also ignored.  See the
# dirmngr man page or the manual for a list of options.
#

# --keyserver URI
#
# GPG can send and receive keys to and from a keyserver.  These
# servers can be HKP, Email, or LDAP (if GnuPG is built with LDAP
# support).
#
# Example HKP keyservers:
#      hkp://keys.gnupg.net
#
# Example HKP keyserver using a Tor OnionBalance service
#      hkp://jirk5u4osbsr34t5.onion
#
# Example HKPS keyservers (see --hkp-cacert below):
#       hkps://hkps.pool.sks-keyservers.net
#
# Example LDAP keyservers:
#      ldap://pgp.surfnet.nl:11370
#
# Regular URL syntax applies, and you can set an alternate port
# through the usual method:
#      hkp://keyserver.example.net:22742
#
# Most users just set the name and type of their preferred keyserver.
# Note that most servers (with the notable exception of
# ldap://keyserver.pgp.com) synchronize changes with each other.  Note
# also that a single server name may actually point to multiple
# servers via DNS round-robin.  hkp://keys.gnupg.net is an example of
# such a "server", which spreads the load over a number of physical
# servers.
#
# If exactly two keyservers are configured and only one is a Tor hidden
# service, Dirmngr selects the keyserver to use depending on whether
# Tor is locally running or not (on a per session base).

#keyserver hkp://jirk5u4osbsr34t5.onion
#keyserver hkp://keys.gnupg.net
keyserver hkps://hkps.pool.sks-keyservers.net

# --hkp-cacert FILENAME
#
# For the "hkps" scheme (keyserver access over TLS), Dirmngr needs to
# know the root certificates for verification of the TLS certificates
# used for the connection.  Enter the full name of a file with the
# root certificates here.  If that file is in PEM format a ".pem"
# suffix is expected.  This option may be given multiple times to add
# more root certificates.  Tilde expansion is supported.

hkp-cacert /usr/share/gnupg/sks-keyservers.netCA.pem
EOF

Next, configure the program that will be used by GnuPG to ask you for the passphrase(s) protecting your keypair. Since I use KDE, I chose pinentry-qt; if you have another desktop environment, choose the appropriate pinentry variant:

$ cat > ~/.gnupg/gpg-agent.conf <<EOF
# PIN entry program: choose one of /usr/bin/pinentry-{curses,emacs,gnome3,
# gtk-2,qt,tty}.
pinentry-program /usr/bin/pinentry-qt
EOF

Finally, configure GPG to use more secure defaults. A few comments on the settings I chose:

  • I have not specified a default keypair to use (for now); we will set it later after generating it.
  • We will locate public keys in our local keyring first, then on the SKS keyservers pool.
  • I have disabled automatic key retrieval, as that feature might introduce a sort of “web bug”: since key server operators know the keys a user fetches from their machines, they might send messages signed by brand new keys, which would then be automatically obtained from the server, revealing the user’s IP address.
  • I set gwenview as my photo viewer (again, I use KDE, so that makes sense).
  • I enabled a few display options to view additional key/UID details and to suppress copyright notices. It is especially important to set keyid-format to 0xlong and to ask OpenPGP to always show the full key fingerprint: short key IDs are trivially spoofable via preimage attacks, and, even though long IDs are subject to collisions as well, it is better to use them than short ones (of course, you should rely on the fingerprint only when you need to decide whether to trust a given key or not).
  • I asked GnuPG to use digests stronger than SHA-1 and to insert in the preference list of the keys I am going to create a request to use digests belonging to the SHA-256 family for hashing (GnuPG already uses SHA-256 in its most recent version, but I prefer to use a longer hash), AES for encryption and BZIP2/ZLIB/ZIP compression, whenever possible.
  • The charset used by GnuPG will be UTF-8 and the program will use gpg-agent to cache my passphrase.
$ cat > ~/gnupg/gpg.conf <<EOF
# Options for GnuPG
# Copyright 1998-2003, 2010 Free Software Foundation, Inc.
# Copyright 1998-2003, 2010 Werner Koch
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Unless you specify which option file to use (with the command line
# option "--options filename"), GnuPG uses the file ~/.gnupg/gpg.conf
# by default.
#
# An options file can contain any long options which are available in
# GnuPG. If the first non white space character of a line is a '#',
# this line is ignored.  Empty lines are also ignored.
#
# See the gpg man page for a list of options.


# If you have more than 1 secret key in your keyring, you may want to
# uncomment the following option and set your preferred keyid.

#default-key 621CC013


# If you do not pass a recipient to gpg, it will ask for one.  Using
# this option you can encrypt to a default key.  Key validation will
# not be done in this case.  The second form uses the default key as
# default recipient.

#default-recipient some-user-id
#default-recipient-self


# Group names may be defined like this:
#   group mynames = paige 0x12345678 joe patti
#
# Any time "mynames" is a recipient (-r or --recipient), it will be
# expanded to the names "paige", "joe", and "patti", and the key ID
# "0x12345678".  Note there is only one level of expansion - you
# cannot make an group that points to another group.  Note also that
# if there are spaces in the recipient name, this will appear as two
# recipients.  In these cases it is better to use the key ID.

#group mynames = paige 0x12345678 joe patti


# GnuPG can automatically locate and retrieve keys as needed using
# this option.  This happens when encrypting to an email address (in
# the "user@@example.com" form) and there are no keys matching
# "user@example.com" in the local keyring.  This option takes any
# number mechanisms which are tried in the given order.  The default
# is "--auto-key-locate local" to search for keys only in the local
# key database.  Uncomment the next line to locate a missing key using
# two DNS based mechanisms.

auto-key-locate local,keyserver


# Common options for keyserver functions:
# (Note that the --keyserver option has been moved to dirmngr.conf)
#
# include-disabled = when searching, include keys marked as "disabled"
#                    on the keyserver (not all keyservers support this).
#
# no-include-revoked = when searching, do not include keys marked as
#                      "revoked" on the keyserver.
#
# verbose = show more information as the keys are fetched.
#           Can be used more than once to increase the amount
#           of information shown.
#
# auto-key-retrieve = automatically fetch keys as needed from the keyserver
#                     when verifying signatures or when importing keys that
#                     have been revoked by a revocation key that is not
#                     present on the keyring.
#
# no-include-attributes = do not include attribute IDs (aka "photo IDs")
#                         when sending keys to the keyserver.

keyserver-options no-auto-key-retrieve


# Uncomment this line to display photo user IDs in key listings and
# when a signature from a key with a photo is verified.

#show-photos


# Use this program to display photo user IDs
#
# %i is expanded to a temporary file that contains the photo.
# %I is the same as %i, but the file isn't deleted afterwards by GnuPG.
# %k is expanded to the key ID of the key.
# %K is expanded to the long OpenPGP key ID of the key.
# %t is expanded to the extension of the image (e.g. "jpg").
# %T is expanded to the MIME type of the image (e.g. "image/jpeg").
# %f is expanded to the fingerprint of the key.
# %% is %, of course.
#
# If %i or %I are not present, then the photo is supplied to the
# viewer on standard input.  If your platform supports it, standard
# input is the best way to do this as it avoids the time and effort in
# generating and then cleaning up a secure temp file.
#
# The default program is "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"
# On Mac OS X and Windows, the default is to use your regular JPEG image
# viewer.
#
# Some other viewers:
# photo-viewer "qiv %i"
# photo-viewer "ee %i"
# photo-viewer "display -title 'KeyID 0x%k'"
#
# This one saves a copy of the photo ID in your home directory:
# photo-viewer "cat > ~/photoid-for-key-%k.%t"
#
# Use your MIME handler to view photos:
# photo-viewer "metamail -q -d -b -c %T -s 'KeyID 0x%k' -f GnuPG"
photo-viewer "gwenview %i"

# Because some mailers change lines starting with "From " to ">From "
# it is good to handle such lines in a special way when creating
# cleartext signatures; all other PGP versions do it this way too.
# To enable full OpenPGP compliance you may want to use this option.
no-escape-from-lines

# Uncomment the following option to get rid of the copyright notice
no-greeting

# When outputting certificates, view user IDs distinctly from keys
fixed-list-mode

# Disable inclusion of the version string in ASCII armored output
no-emit-version

# Disable comment string in clear text signatures and ASCII armored messages
no-comments

# Always show complete key IDs and fingerprints.
keyid-format 0xlong
with-fingerprint

# Display the calculated validity of user IDs during key listings
list-options show-usage show-uid-validity
verify-options show-policy-urls show-uid-validity

# When verifying a signature made from a subkey, ensure that the cross
# certification "back signature" on the subkey is present and valid.
# This protects against a subtle attack against subkeys that can sign.
# Defaults to --no-require-cross-certification.  However for new
# installations it should be enabled.
require-cross-certification

# Use stronger digests.
personal-digest-preferences SHA512
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES BZIP2 ZLIB ZIP Uncompressed

# If you do not use the Latin-1 (ISO-8859-1) charset, you should tell
# GnuPG which is the native character set.  Please check the man page
# for supported character sets.  This character set is only used for
# metadata and not for the actual message which does not undergo any
# translation.  Note that future version of GnuPG will change to UTF-8
# as default character set.
charset utf-8

# Try to use the GnuPG-Agent. With this option, GnuPG first tries to connect to
# the agent before it asks for a passphrase.
use-agent
EOF

Generating the master key

Now, let’s start generating our new keypair. We will employ:

  • a single master key (to be kept offline), used exclusively to sign…
  • …three subkeys, one for encryption purposes, another one for signing and a third one for authentication.

Start GnuPG in expert key generation mode:

$ gpg --full-generate-key
gpg: keybox '~/.gnupg/pubring.kbx' created

I chose to create a 4096-bit RSA master key as I find it to be more future proof than 2048-bit ones: the latter are believed to be safe until 2030, a point in time which is not too far away (while a master key should, ideally, last for a medium to long timespan before being rotated).

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (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

I also decided to set its validity to one year: since you can always set the expiration date of a key in the future, this acts as a “dead man’s switch” in case you lose access to the associated private key, and also forces your recipients to refresh the key from a public key server more often, allowing them to spot any revocations.

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Mon Jan 22 16:01:49 2018 CET
Is this correct? (y/N) y

Enter your name and your main e-mail address. Do not enter a comment: you might make it more difficult for the people willing to certify your key to do so, and there is virtually no additional information you can’t specify in another field or using a specific OpenPGP feature.

GnuPG needs to construct a user ID to identify your key.

Real name: Alessandro Menti
Email address: alessandro.menti@alessandromenti.it
Comment:
You selected this USER-ID:
    "Alessandro Menti <alessandro.menti@alessandromenti.it>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

GnuPG should now prompt you for a passphrase.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: ~/.gnupg/trustdb.gpg: trustdb created
gpg: key 0xBF334213F5C5CA03 marked as ultimately trusted
gpg: directory '~/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '~/.gnupg/openpgp-revocs.d/6731DDC28357BEC38E342AAFBF334213F5C5CA03.rev'
public and secret key created and signed.

Note that this key cannot be used for encryption.  You may want to use
the command "--edit-key" to generate a subkey for this purpose.
pub   rsa4096/0xBF334213F5C5CA03 2017-01-22 [SC] [expires: 2018-01-22]
      Key fingerprint = 6731 DDC2 8357 BEC3 8E34  2AAF BF33 4213 F5C5 CA03
uid                              Alessandro Menti <alessandro.menti@alessandromenti.it>

Configuring the master key

At this point, I decided to add my photo to the key, as well as a secondary e-mail address. The GnuPG authors suggest to use a JPEG image roughly 240x288 in size and weighing less than 6 KiB, a limit that appears hard to reach. Thanks to the experiments performed by Simon Josefsson and a suggestion by Francois Marier, this was actually easy to do:

  1. I opened a high-quality photo of myself in GIMP and cropped it to 240x288.
  2. I exported it as a JPEG using most of the settings recommended by Josefsson:
    • Save EXIF data: disabled
    • Save thumbnail: disabled
    • Save XMP data: disabled
    • Subsampling: 4:2:0 (chroma quality)
    • Optimize: disabled, Progressive: enabled
    • Quality: left to 100% (it will be reduced in the next step)
  3. I ran jpegoptim --strip-all --size=5 to strip all extra data and lower the image quality to make the final image about 5 KB in size.

The result was pleasant and the loss in quality was minimal (the image is just a little blurrier and there are a few, barely noticeable compression artifacts).

I then ran GnuPG to add the photo:

$ gpg --edit-key 0xBF334213F5C5CA03
Secret key is available.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2018-01-22
sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>

gpg> addphoto

Pick an image to use for your photo ID.  The image must be a JPEG file.
Remember that the image is stored within your public key.  If you use a
very large picture, your key will become very large as well!
Keeping the image close to 240x288 is a good size to use.

Enter JPEG filename for photo ID: ~/GPGAvatar.jpg
Is this photo correct (y/N/q)? y

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  [jpeg image of size 5165]

Note that the newly added photo ID will be marked as having an unknown trust level: it will be marked as ultimately trusted only after a GnuPG restart.

I added the secondary e-mail address, marked the first one as primary and specified a preferred keyserver:

gpg> adduid
Real name: Alessandro Menti
Email address: alessandro.menti@hotmail.it
Comment:
You selected this USER-ID:
    "Alessandro Menti <alessandro.menti@hotmail.it>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1)  Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  [jpeg image of size 5165]
[ unknown] (3). Alessandro Menti <alessandro.menti@hotmail.it>

gpg> uid 1

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1)* Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  [jpeg image of size 5165]
[ unknown] (3)  Alessandro Menti <alessandro.menti@hotmail.it>

gpg> primary

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1)* Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  [jpeg image of size 5165]
[ unknown] (3)  Alessandro Menti <alessandro.menti@hotmail.it>

gpg> keyserver
Enter your preferred keyserver URL: hkps://hkps.pool.sks-keyservers.net

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1)* Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  [jpeg image of size 5165]
[ unknown] (3)  Alessandro Menti <alessandro.menti@hotmail.it>

I checked that our cipher/digest/compression/keyserver preferences were stored correctly into the key, then saved it:

gpg> showpref
[ultimate] (1)* Alessandro Menti <alessandro.menti@alessandromenti.it>
     Cipher: AES256, AES192, AES, 3DES
     Digest: SHA512, SHA384, SHA256, SHA224, SHA1
     Compression: BZIP2, ZLIB, ZIP, Uncompressed
     Features: MDC, Keyserver no-modify
     Preferred keyserver: hkps://hkps.pool.sks-keyservers.net
[ unknown] (2)  [jpeg image of size 5165]
     Cipher: AES256, AES192, AES, 3DES
     Digest: SHA512, SHA384, SHA256, SHA224, SHA1
     Compression: BZIP2, ZLIB, ZIP, Uncompressed
     Features: MDC, Keyserver no-modify
     Preferred keyserver: hkps://hkps.pool.sks-keyservers.net
[ unknown] (3)  Alessandro Menti <alessandro.menti@hotmail.it>
     Cipher: AES256, AES192, AES, 3DES
     Digest: SHA512, SHA384, SHA256, SHA224, SHA1
     Compression: BZIP2, ZLIB, ZIP, Uncompressed
     Features: MDC, Keyserver no-modify
     Preferred keyserver: hkps://hkps.pool.sks-keyservers.net

gpg> save

Adding the signing, encryption and authentication subkeys

We will now add the subkeys meant for daily use. Start GnuPG again in expert mode, so that we will be able to specify the subkey capabilities at creation time, and add the signing subkey:

$ gpg --expert --edit-key 0xBF334213F5C5CA03
Secret key is available.

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ultimate] (2)  Alessandro Menti <alessandro.menti@hotmail.it>
[ultimate] (3)  [jpeg image of size 5165]

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
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
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Mon Jan 22 17:34:54 2018 CET
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x467AC0B4D6A6F336
     created: 2017-01-22  expires: 2018-01-22  usage: S
[ultimate] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ultimate] (2)  Alessandro Menti <alessandro.menti@hotmail.it>
[ultimate] (3)  [jpeg image of size 5165]

Repeat the procedure for the encryption and authentication keys:

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 6
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
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Mon Jan 22 17:38:41 2018 CET
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x467AC0B4D6A6F336
     created: 2017-01-22  expires: 2018-01-22  usage: S
ssb  rsa4096/0x399420510192D5B8
     created: 2017-01-22  expires: 2018-01-22  usage: E
[ultimate] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ultimate] (2)  Alessandro Menti <alessandro.menti@hotmail.it>
[ultimate] (3)  [jpeg image of size 5165]

gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 8

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

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

Your selection? s

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

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

Your selection? e

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

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

Your selection? a

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

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

Your selection? q
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
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Mon Jan 22 17:43:35 2018 CET
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

sec  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x467AC0B4D6A6F336
     created: 2017-01-22  expires: 2018-01-22  usage: S
ssb  rsa4096/0x399420510192D5B8
     created: 2017-01-22  expires: 2018-01-22  usage: E
ssb  rsa4096/0x90FD1486279861B7
     created: 2017-01-22  expires: 2018-01-22  usage: A
[ultimate] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ultimate] (2)  Alessandro Menti <alessandro.menti@hotmail.it>
[ultimate] (3)  [jpeg image of size 5165]

gpg> save

Checking that best practices are followed

Before splitting the master key from the subkeys, it is better to check that we did, indeed, follow all recommended OpenPGP practices.

Install hopenpgp-tools and run hokey lint on the new keypair:

$ gpg --export 0xBF334213F5C5CA03 | hokey lint
hokey (hopenpgp-tools) 0.19.4
Copyright (C) 2012-2016  Clint Adams
hokey comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions.

Key has potential validity: good
Key has fingerprint: 6731 DDC2 8357 BEC3 8E34  2AAF BF33 4213 F5C5 CA03
Checking to see if key is OpenPGPv4: V4
Checking to see if key is RSA or DSA (>= 2048-bit): RSA 4096
Checking user-ID- and user-attribute-related items:
  Alessandro Menti <alessandro.menti@hotmail.it>:
    Self-sig hash algorithms: [SHA-512]
    Preferred hash algorithms: [SHA-512, SHA-384, SHA-256, SHA-224]
    Key expiration times: [11m30d16200s = Mon Jan 22 15:02:01 UTC 2018]
    Key usage flags: [[sign-data, certify-keys]]
  Alessandro Menti <alessandro.menti@alessandromenti.it>:
    Self-sig hash algorithms: [SHA-512]
    Preferred hash algorithms: [SHA-512, SHA-384, SHA-256, SHA-224]
    Key expiration times: [11m30d16200s = Mon Jan 22 15:02:01 UTC 2018]
    Key usage flags: [[sign-data, certify-keys]]
  <uat:[jpeg:5165:7a7647b51cb5]>:
    Self-sig hash algorithms: [SHA-512]
    Preferred hash algorithms: [SHA-512, SHA-384, SHA-256, SHA-224]
    Key expiration times: [11m30d16200s = Mon Jan 22 15:02:01 UTC 2018]
    Key usage flags: [[sign-data, certify-keys]]
Checking subkeys:
  one of the subkeys is encryption-capable: True
  fpr: BAF2 0A23 583C 849B 034D  D484 467A C0B4 D6A6 F336
    version: v4
    timestamp: 20170122-163441
    algo/size: RSA 4096
    binding sig hash algorithms: [SHA-512]
    usage flags: [[sign-data]]
    embedded cross-cert: True
    cross-cert hash algorithms: [SHA-512]
  fpr: 7188 2755 4424 0B0E AAE7  47D9 3994 2051 0192 D5B8
    version: v4
    timestamp: 20170122-163837
    algo/size: RSA 4096
    binding sig hash algorithms: [SHA-512]
    usage flags: [[encrypt-storage, encrypt-communications]]
    embedded cross-cert: False
    cross-cert hash algorithms: [SHA-512]
  fpr: 71FA 9F12 D4C5 24CD DE54  597D 90FD 1486 2798 61B7
    version: v4
    timestamp: 20170122-164326
    algo/size: RSA 4096
    binding sig hash algorithms: [SHA-512]
    usage flags: [[auth]]
    embedded cross-cert: False
    cross-cert hash algorithms: [SHA-512]

The only warning emitted is the absence of the embedded cross-certification in the authentication subkey, but, since this is needed only for signing subkeys, we are good to go.

Splitting the master key from the subkeys

We will now split the master key from the subkeys we created. Since GnuPG is currently unable to use non-imported private keys, it does not make sense to export the master key and import it later into a temporary keyring when we will need to use it; instead, we will just kill the GnuPG agent and copy the .gnupg directory to a safe place (e.g. a USB key):

$ gpgconf --kill gpg-agent
$ cp -r .gnupg /your/USB/key/.gnupg

Make sure that all private key and subkeys are present in the backup. Kill again the agent at the end, otherwise it might use the backup keystore for some operations, yielding undesired results:

$ GNUPGHOME=/your/USB/key/.gnupg gpg --list-secret-keys
/your/USB/key/.gnupg/pubring.kbx
--------------------------------
sec   rsa4096/0xBF334213F5C5CA03 2017-01-22 [SC] [expires: 2018-01-22]
      Key fingerprint = 6731 DDC2 8357 BEC3 8E34  2AAF BF33 4213 F5C5 CA03
uid                   [ultimate] Alessandro Menti <alessandro.menti@alessandromenti.it>
uid                   [ultimate] Alessandro Menti <alessandro.menti@hotmail.it>
uid                   [ultimate] [jpeg image of size 5165]
ssb   rsa4096/0x467AC0B4D6A6F336 2017-01-22 [S] [expires: 2018-01-22]
ssb   rsa4096/0x399420510192D5B8 2017-01-22 [E] [expires: 2018-01-22]
ssb   rsa4096/0x90FD1486279861B7 2017-01-22 [A] [expires: 2018-01-22]

$ gpgconf --kill gpg-agent

Since we are using GnuPG 2.1.x, to remove the master key from the keyring we will just need to get its keygrip and delete the corresponding file:

$ gpg --with-keygrip --list-key 0xBF334213F5C5CA03
pub   rsa4096/0xBF334213F5C5CA03 2017-01-22 [SC] [expires: 2018-01-22]
      Key fingerprint = 6731 DDC2 8357 BEC3 8E34  2AAF BF33 4213 F5C5 CA03
      Keygrip = 8CB347C4EDD361FB31EE0E9BA820F8E11C7F190D
uid                   [ultimate] Alessandro Menti <alessandro.menti@alessandromenti.it>
uid                   [ultimate] Alessandro Menti <alessandro.menti@hotmail.it>
uid                   [ultimate] [jpeg image of size 5165]
sub   rsa4096/0x467AC0B4D6A6F336 2017-01-22 [S] [expires: 2018-01-22]
      Keygrip = 8EE0D532477B1ED79FE58B599B1CB0F8ABF96345
sub   rsa4096/0x399420510192D5B8 2017-01-22 [E] [expires: 2018-01-22]
      Keygrip = AAE3DAD217A6F4137BC025293393D9329B6D2E94
sub   rsa4096/0x90FD1486279861B7 2017-01-22 [A] [expires: 2018-01-22]
      Keygrip = C70228C7592B1F6972971F0E1E13413199FC103C

$ rm .gnupg/private-keys-v1.d/8CB347C4EDD361FB31EE0E9BA820F8E11C7F190D.key
$ gpg --list-secret-keys
~/.gnupg/pubring.kbx
--------------------------------
sec#  rsa4096/0xBF334213F5C5CA03 2017-01-22 [SC] [expires: 2018-01-22]
      Key fingerprint = 6731 DDC2 8357 BEC3 8E34  2AAF BF33 4213 F5C5 CA03
uid                   [ultimate] Alessandro Menti <alessandro.menti@alessandromenti.it>
uid                   [ultimate] Alessandro Menti <alessandro.menti@hotmail.it>
uid                   [ultimate] [jpeg image of size 5165]
ssb   rsa4096/0x467AC0B4D6A6F336 2017-01-22 [S] [expires: 2018-01-22]
ssb   rsa4096/0x399420510192D5B8 2017-01-22 [E] [expires: 2018-01-22]
ssb   rsa4096/0x90FD1486279861B7 2017-01-22 [A] [expires: 2018-01-22]

Note that sec# is shown next to the master secret key, which means it is not present in our keyring anymore. Kill the agent, list the secret keys in the backup and kill the agent again to check that the backup copy contains all keys, including the master one:

$ gpgconf --kill gpg-agent
$ GNUPGHOME=/your/USB/key/.gnupg gpg --list-secret-keys
/your/USB/key/.gnupg/pubring.kbx
------------------------------------------
sec   rsa4096/0xBF334213F5C5CA03 2017-01-22 [SC] [expires: 2018-01-22]
      Key fingerprint = 6731 DDC2 8357 BEC3 8E34  2AAF BF33 4213 F5C5 CA03
uid                   [ultimate] Alessandro Menti <alessandro.menti@alessandromenti.it>
uid                   [ultimate] Alessandro Menti <alessandro.menti@hotmail.it>
uid                   [ultimate] [jpeg image of size 5165]
ssb   rsa4096/0x467AC0B4D6A6F336 2017-01-22 [S] [expires: 2018-01-22]
ssb   rsa4096/0x399420510192D5B8 2017-01-22 [E] [expires: 2018-01-22]
ssb   rsa4096/0x90FD1486279861B7 2017-01-22 [A] [expires: 2018-01-22]

$ gpgconf --kill gpg-agent

You should now change the passphrase on your keyring so that, should it be compromised, the private master key will remain safe from someone with access to the backup:

$ gpg --edit-key 0xBF334213F5C5CA03
Secret key is available.

pub  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x467AC0B4D6A6F336
     created: 2017-01-22  expires: 2018-01-22  usage: S
ssb  rsa4096/0x399420510192D5B8
     created: 2017-01-22  expires: 2018-01-22  usage: E
ssb  rsa4096/0x90FD1486279861B7
     created: 2017-01-22  expires: 2018-01-22  usage: A
[ultimate] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ultimate] (2)  Alessandro Menti <alessandro.menti@hotmail.it>
[ultimate] (3)  [jpeg image of size 5165]

gpg> passwd
gpg: key 0xBF334213F5C5CA03/0xBF334213F5C5CA03: error changing passphrase: No secret key

gpg> quit

GnuPG will (obviously) warn you about the absence of the master key, but that’s fine.

Preparing a transition statement and signing the new key with the old one

We will now make a public statement about the transition, including the fingerprints of the old and the new public keys, and sign it using both of them; this way, the people in possession of our old public key will be able to check that the new keypair was, indeed, created by us and not by an impostor (or an attacker).

I used Stefano Zacchiroli’s transition statement as a template and saved my statement in a file named key-transition-2017-01-26.txt.

Let’s sign the new key with the old one and sign the transition statement:

$ gpgconf --kill gpg-agent
$ gpg --export-secret-subkeys -a 0xBF334213F5C5CA03 > /tmp/secret.asc
$ gpgconf --kill gpg-agent
$ GNUPGHOME=~/.gnupg_old/ gpg --import < /tmp/secret.asc
gpg: key 0xBF334213F5C5CA03: public key "Alessandro Menti <alessandro.menti@alessandromenti.it>" imported
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key 0xBF334213F5C5CA03: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:  secret keys unchanged: 1
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   2  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   2  signed:   0  trust: 1-, 0q, 0n, 0m, 1f, 0u
gpg: next trustdb check due at 2017-01-28

$ rm /tmp/secret.asc
$ GNUPGHOME=~/.gnupg_old/ gpg --default-key 0x8CEFF2C1E97587C2 --edit-key 0xBF334213F5C5CA03
Secret key is available.

pub  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: unknown       validity: full
ssb  rsa4096/0x467AC0B4D6A6F336
     created: 2017-01-22  expires: 2018-01-22  usage: S
ssb  rsa4096/0x399420510192D5B8
     created: 2017-01-22  expires: 2018-01-22  usage: E
ssb  rsa4096/0x90FD1486279861B7
     created: 2017-01-22  expires: 2018-01-22  usage: A
[ unknown] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  Alessandro Menti <alessandro.menti@hotmail.it>

gpg> trust
pub  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: unknown       validity: full
ssb  rsa4096/0x467AC0B4D6A6F336
     created: 2017-01-22  expires: 2018-01-22  usage: S
ssb  rsa4096/0x399420510192D5B8
     created: 2017-01-22  expires: 2018-01-22  usage: E
ssb  rsa4096/0x90FD1486279861B7
     created: 2017-01-22  expires: 2018-01-22  usage: A
[ unknown] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  Alessandro Menti <alessandro.menti@hotmail.it>

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

  1 = I don't know or won't say
  2 = I do NOT trust
  3 = I trust marginally
  4 = I trust fully
  5 = I trust ultimately
  m = back to the main menu

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

pub  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: full
ssb  rsa4096/0x467AC0B4D6A6F336
     created: 2017-01-22  expires: 2018-01-22  usage: S
ssb  rsa4096/0x399420510192D5B8
     created: 2017-01-22  expires: 2018-01-22  usage: E
ssb  rsa4096/0x90FD1486279861B7
     created: 2017-01-22  expires: 2018-01-22  usage: A
[ unknown] (1). Alessandro Menti <alessandro.menti@alessandromenti.it>
[ unknown] (2)  Alessandro Menti <alessandro.menti@hotmail.it>
Please note that the shown key validity is not necessarily correct
unless you restart the program.

gpg> sign 0x8CEFF2C1E97587C2
Really sign all text user IDs? (y/N) y
gpg: all values passed to '--default-key' ignored

pub  rsa4096/0xBF334213F5C5CA03
     created: 2017-01-22  expires: 2018-01-22  usage: SC
     trust: ultimate      validity: sconosciuto
 Primary key fingerprint: 6731 DDC2 8357 BEC3 8E34  2AAF BF33 4213 F5C5 CA03

     Alessandro Menti <alessandro.menti@alessandromenti.it>
     Alessandro Menti <alessandro.menti@hotmail.it>
     [jpeg image of size 5165]

This key will expire on 2018-01-22.
Are you sure that you want to sign this key with your
key "Alessandro Menti <alessandro.menti@hotmail.it>" (0x8CEFF2C1E97587C2)

Really sign? (y/N) y

gpg> save
$ GNUPGHOME=~/.gnupg_old/ gpg --export -a 0xBF334213F5C5CA03 > /tmp/signedkey
$ GNUPGHOME=~/.gnupg_old/ gpg -u 0x8CEFF2C1E97587C2 -u 0xBF334213F5C5CA03 --clearsign key-transition-2017-01-26.txt
$ mv key-transition-2017-01-26.txt.asc key-transition-2017-01-26.txt
$ gpgconf --kill gpg-agent
$ gpg --import < /tmp/signedkey
gpg: key 0xBF334213F5C5CA03: "Alessandro Menti <alessandro.menti@alessandromenti.it>" 3 new signatures
gpg: Total number processed: 1
gpg:         new signatures: 3
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2018-01-22
$ rm /tmp/signedkey

Our transition statement, signed by both keys, is now in key-transition-2017-01-26.txt: you can get mine here.

Send the new public key to the public keyservers

Submit your new key to a public keyserver, such as the SKS keyservers pool:

$ gpg --send-keys 0xBF334213F5C5CA03

Some finishing touches

Edit ~/.gnupg/gpg.conf and change the default-key preference to the ID of the new key:

default-key 0xBF334213F5C5CA03

gpg-agent also requires the GPG_TTY variable to be set, so add it to your .bashrc:

$ cat >> ~/.bashrc << EOF
export GPG_TTY=$(tty)
EOF
$ . ~/.bashrc

Finally, import the old public keys that were exported earlier from the old keyring:

$ gpg --import < ~/allpubkeys.asc

Next steps: getting our key signed by others

Our keys are now ready for use; however, since anyone can generate a keypair in our name and publish them, how can we prove ― publicly ― that the new key is, in fact, tied to our identity? The answer to this problem is keysigning, that is, meeting (usually in person) with other people, asking them to assert that the key belongs indeed to us. We will discuss how to do this (the right way) in a future blog post.