Creating the Perfect GPG Keypair

Francesco Vezzoli

This article is a summary of various article in internet to create a almost perfect GPG keypair. There is a list of principle sources:

In case you do not know openpgp. OpenPGP is not a simple system to encrypt and sign message, this is a system to create a digital identity that is verified by other people completely decentralized. There no unique authority that will control the identity, is other users who will check the other people.

Please, keep in mind I’m a simple person and not a specialist of cryptography. This is a simple summary of several articles spread around the web. The encryption technology landscape can change rapidly, and is complex. Use this article as base for your search for perfection.

Install the right tools

To create a perfect keyring you must first install the right tool:

Configure the tool

Before start, it’s better to configure the tool to enhance security. There is a configuration of OpenPGP to display more information in key listing and avoid information leaking on how the key was created. In the file end there are some restrictions on the encryption algorithms to use the best and most resistant at this time. You are free to copy this file to ~/.gnupg/gpg.conf (Linux and Mac) or C:\Users[username]\AppData\Roaming\gnupg\gpg.conf (Windows).

# Avoid information leaked
no-emit-version
no-comments
export-options export-minimal

# Displays the long format of the ID of the keys and their fingerprints
#keyid-format 0xlong
with-fingerprint

# Displays the validity of the keys
list-options show-uid-validity
verify-options show-uid-validity

# Limits the algorithms used
personal-cipher-preferences AES256
personal-digest-preferences SHA512
default-preference-list SHA512 SHA384 SHA256 RIPEMD160 AES256 TWOFISH BLOWFISH ZLIB BZIP2 ZIP Uncompressed

cipher-algo AES256
digest-algo SHA512
cert-digest-algo SHA512
compress-algo ZLIB

disable-cipher-algo 3DES
weak-digest SHA1

s2k-cipher-algo AES256
s2k-digest-algo SHA512
s2k-mode 3
s2k-count 65011712

Start to take advantage of subkeys

When create a key with OpenPGP in the basic mode, it will create a key pair to sign and certify. If private key stolen or lost all your digital identity is compromised. To increase the security of your keypair is possible to use a function present in OpenPGP: the subkeys.

OpenPGP allow you to create different subkeys with a specific task: encrypt, sign and authenticate. If you store the master key (the one used to certify the pthrt keys) correctly, in the secure site, and one of the subkeys is stolen the only key to revoke is the unsecure subkey and your identity (the master key) is safe.

Create your digital identity

Let’s start by creation the master key. This key will hold the identity. To use the special function of OpenPGP to use the subkeys we need to create the certification key for Doctor Who. It will allow to certify other keys. It is a special key and is very important, you must keep it safely.

When you create your new keypair, you should use the highest possible values for key length. The storage is cheaper but a message unbreakable today is possible to break in the near future by more powerful computer. Please, don’t use GPG’s default of 2048!

The commands output reported is possible to have difference with yours. The possible difference is due to different version or truncated output to fit in page.

doctor@tardis:~$ gpg2 --expert --full-gen-key
gpg (GnuPG) 2.1.11; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: keybox '/home/doctor/.gnupg/pubring.kbx' created
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)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
Your selection? 8

Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Sign Certify 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 Certify Encrypt Authenticate
Current allowed actions: Sign Certify

   (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 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
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 Wed Apr  3 22:40:20 2019 CEST
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: Doctor Who
E-mail address: doctor@tardis.net
Comment:
You selected this USER-ID:
    "Doctor Who <doctor@tardis.net>"

Change (N)ame, (C)omment, (E)-mail or (O)kay/(Q)uit? o
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, utilise the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /home/doctor/.gnupg/trustdb.gpg: trustdb created
gpg: key FD8607C7 marked as ultimately trusted
gpg: directory '/home/doctor/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/doctor/.gnupg/openpgp-revocs.d/20522982307DF46E5A31ACA808E72BD1FD8607C7.rev'
public and secret key created and signed.

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 2019-04-03
pub   rsa4096/FD8607C7 2018-04-03 [] [expires: 2019-04-03]
      Key fingerprint = 2052 2982 307D F46E 5A31  ACA8 08E7 2BD1 FD86 07C7
uid         [ultimate] Doctor Who <doctor@tardis.net>

If you notice the key validity is set to 1 year to prevent problem if revocation certificate is lost (You store it secure?), and to mantain pratice with OpenPGP. The master key pair is created. Now create the subkeys.

Creating subkeys

Now with the master key, start the most important part of keyring creation: The subkeys creation. The subkeys to create is 3:

  1. Authenticate (A)
  2. Sign (S)
  3. Encrypt (E)

Let’s create them now. We will first list the available keys:

doctor@tardis:~$ gpg2 --list-keys
/home/doctor/.gnupg/pubring.kbx
---------------------------------
pub   rsa4096/FD8607C7 2018-04-03 [C] [expires: 2019-04-03]
      Key fingerprint = 2052 2982 307D F46E 5A31  ACA8 08E7 2BD1 FD86 07C7
uid         [ultimate] Doctor Who <doctor@tardis.net>

Ok, the key is present. Now edit it to add subkeys. To do this is moment to switch to expert mode.

doctor@tardis~$ gpg2 --expert --edit-key FD8607C7
gpg (GnuPG) 2.1.11; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  rsa4096/FD8607C7
     created: 2018-04-03  expires: 2019-04-03  usage: C
     trust: ultimate      validity: ultimate
[ultimate] (1). Doctor Who <doctor@tardis.net>

gpg>

You are now in edit mode. Add the encryption key with the addkey command.

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? 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 Wed Apr  3 22:42:16 2019 CEST
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/FD8607C7
     created: 2018-04-03  expires: 2019-04-03  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/11D8D5C0
     created: 2018-04-03  expires: 2019-04-03  usage: E
[ultimate] (1). Doctor Who <doctor@tardis.net>

gpg>

The key with fingerprint 11D8D5C0 has been created. Repeat for Signing and Authentication key.

In the end, you must have these keys:

sec  rsa4096/FD8607C7
     created: 2018-04-03  expires: 2019-04-03  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/11D8D5C0
     created: 2018-04-03  expires: 2019-04-03  usage: E
ssb  rsa4096/9B825FCF
     created: 2018-04-03  expires: 2019-04-03  usage: S
ssb  rsa4096/5E6B1035
     created: 2018-04-03  expires: 2019-04-03  usage: A
[ultimate] (1). Doctor Who <doctor@tardis.net>

gpg> save

Enter save then quit, and you’re done. Now the Doctor has an OpenPGP key pair to manage his digital identity and subkeys to manage the different capabilitites.

Before continue is important to make a robust backup.

Export the key for long term store

The PGP key is not safe used at this time. In the event of theft of the master key and password, the robber can spoof the digital identity completely. The master key, which allows to certify, will be stored in a cold storage space and totally disconnected from the network.

First action is to create a safe place to store temprary the various file on your machine:

mkdir /tmp/gpg
sudo mount -t ramfs -o size=10M ramfs /tmp/gpg
sudo chown $(logname):$(logname) /tmp/gpg

First, to prevent unauthorized use after keypair compromise, create a revocation of master key:

doctor@tardis:~$ gpg2 --output /tmp/gpg/FD8607C7.rev --gen-revoke FD8607C7

sec  rsa4096/FD8607C7 2018-04-03 Doctor Who <doctor@tardis.net>

Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
  0 = No reason specified
  1 = Key has been compromised
  2 = Key is superseded
  3 = Key is no longer used
  Q = Cancel
(Probably you want to select 1 here)
Your decision? 1
Enter an optional description; end it with an empty line:
>
Reason for revocation: Key has been compromised
(No description given)
Is this okay? (y/N) y
ASCII armoured output forced.
Revocation certificate created.

Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable.  But have some caution:  The print system of
your machine might store the data and make it available to others!

Now pay attention on this file. This file is important to keep in safe place to prevent the its use to block your identity: in this case you need to recreate all your trust chain. Now export all your keypair to prepare the keyring to everyday use.

doctor@tardis:~$ gpg2 --export --armor FD8607C7 > /tmp/gpg/FD8607C7.pub.asc
doctor@tardis:~$ gpg2 --export-secret-keys --armor FD8607C7 > /tmp/gpg/FD8607C7.priv.asc
doctor@tardis:~$ gpg2 --export-secret-subkeys --armor FD8607C7 > /tmp/gpg/FD8607C7.sub_priv.asc

Now FD8607C7.pub.asc will contain all public keys and FD8607C7.priv.asc the private keys of the master key. FD8607C7.sub_priv.asc contains only the private keys of the subkeys.

Delete the master key

As mentioned above the everyday keyring not contains the private key. Let’s delete all private keys:

doctor@tardis:~$ gpg2 --delete-secret-key FD8607C7
gpg (GnuPG) 2.1.11; Copyright (C) 2016 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


sec  rsa4096/FD8607C7 2018-04-03 Doctor Who <doctor@tardis.net>

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

And now import only the subkeys:

doctor@tardis:~$ gpg2 --import /tmp/gpg/FD8607C7.sub_priv.asc
gpg: key FD8607C7: "Doctor Who <doctor@tardis.net>" not changed
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key FD8607C7: secret key imported
gpg: Total number processed: 5
gpg:              unchanged: 1
gpg:       secret keys read: 5
gpg:   secret keys imported: 3

After reimport only the subkeys it’s time to check the correct import:

doctor@tardis:~$ gpg2 --list-secret-keys
/home/doctor/.gnupg/pubring.kbx
---------------------------------
sec#  rsa4096/FD8607C7 2018-04-03 [C] [expires: 2019-04-03]
      Key fingerprint = 2052 2982 307D F46E 5A31  ACA8 08E7 2BD1 FD86 07C7
uid         [ultimate] Doctor Who <doctor@tardis.net>
ssb   rsa4096/11D8D5C0 2018-04-03 [E] [expires: 2019-04-03]
ssb   rsa4096/9B825FCF 2018-04-03 [S] [expires: 2019-04-03]
ssb   rsa4096/5E6B1035 2018-04-03 [A] [expires: 2019-04-03]

The # sign after sec indicates the secret key of the master key is no longer present in the keyring, it’s a stub.

Now the keyring is ready for normal utilize. All the files created will have to kept offline, in the next section we see how to create a safe offline copy.

Create a safe offline copy

OK, now I have a perfet keypair, but where store the private key safely?

Obviously the private key is not safe to keep on daily laptop. It will have to be stored on offline storage space, completely disconnected from the net. The possible storage space is:

  • USB key
  • external hard disk
  • optical disk
  • magnetic tape
  • paper
  • stone

But not all of this is safe as others. The first criteria to choice the right support is the durability over the time.

The digital media support is not long term durability.

If I write the key on the support I want to be sure the data write on now, in 20 years I want to be sure the data is readable without error. On optical media the time is 15 year, and the USB key the problem is in the technology: the data is stored with electrical charge in a cell, the absence of refresh degrade the data.

The other problem is the support manage: think the HD-DVD. This support is used 10 years ago, but now is pratically impossible to find a HW able to read this media.

Giving this criteria the digital storage is not a good choice. Ok, put the key on not digital media: paper or stone. For convenience we will choose the paper.

The problem is the length of the key (4096bit). The simple key print run in problem because the manual entry have high error rate intrinsecally, and the OCR software is possible to have problem to recognize O (capital o) and 0 (zero) for example.

Here some things to reduce the problem:

  • Use a 2D barcode (QR code, Data Matrix) for more short term usage
  • Print an HEX dump of key with checksum for more long term usage

Here is a python script that allows to have the hex dump as well as the checksum of each line of the exported files.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

def crc32(buf, crc=0xFFFFFFFF):
    for c in buf:
        crc = crc ^ ord(c)
        for i in range(8):
            if crc & 1:
                crc = (crc >> 1) ^ 0xedb88320
            else:
                crc = crc >> 1;
    return crc ^ 0xFFFFFFFF;

offset = 0
with open(sys.argv[1]) as fp:
    for chunk in iter(lambda: fp.read(16), b''):
        cksum = crc32(chunk)
        dump = ' '.join('{:02X}'.format(ord(a)) for a in chunk)
        print("%08X | %-47s | %08X | %s" % (offset, dump, cksum, repr(chunk)))
        offset = offset + len(chunk)
    print("%08X" % offset)
    fp.close()

Where the various step is:

  • read file in block of 16 byte
  • print HEX dump
  • for each line display the line and generate checksum for the raw part of line

Sample output:

00000000 | 2D 2D 2D 2D 2D 42 45 47 49 4E 20 50 47 50 20 50 | 4BDAE384 | '-----BEGIN PGP P'
00000010 | 55 42 4C 49 43 20 4B 45 59 20 42 4C 4F 43 4B 2D | E312478B | 'UBLIC KEY BLOCK-'
00000020 | 2D 2D 2D 2D 0A 0A 6D 51 49 4E 42 46 72 44 57 69 | 8195F9F3 | '----\n\nmQINBFrDWi'
00000030 | 34 42 45 41 44 53 44 73 32 54 79 74 33 4B 42 63 | 3A009DA6 | '4BEADSDs2Tyt3KBc'
00000040 | 4B 65 4F 70 77 5A 4B 70 4E 76 61 56 35 74 73 31 | 1ED1B1EA | 'KeOpwZKpNvaV5ts1'

Take the first line as an example. From left to right :

  • index of the first byte of line
  • 16 group of 2 hexadecimal characters. Each group represents an ASCII character
  • the CRC32 checksum of the ASCII value
  • the raw ASCII value with char escape

Restore the physical file

Now the problem is to check the correctly restore of dumped file. The manual input is long and often present error difficult to found. The OCR program as write above present possible error in same letter recognition. Here a simple python script to restore the data from manual input or OCR scan with error notification. After error correction the output is the original file.

    #!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

def crc32(buf, crc=0xFFFFFFFF):
    for c in buf:
        crc = crc ^ ord(c)
        for i in range(8):
            if crc & 1:
                crc = (crc >> 1) ^ 0xedb88320
            else:
                crc = crc >> 1;
    return crc ^ 0xFFFFFFFF;

data = []
with open(sys.argv[1]) as fp:
    for line in fp:
        data.append({
            'offset': line[0:8],
            'value': line[11:58].split(),
            'cksum': line[61:69],
            'string': line[72:(line.rfind("'")+1)]
        })
    fp.close()

ascFile = ""
offset = 0
for chunk in data:
    chunkValue = ""
    for hexdump in chunk['value']:
        chunkValue = chunkValue + (chr(int(hexdump, 16)))
    if (chunk['cksum'] != ''):
        if crc32(chunkValue) == int(chunk['cksum'], 16):
            ascFile = ascFile + chunkValue
            offset = len(ascFile)
        else:
            print("Checksum error at line: " + chunk['offset'])
            restored = repr(chunkValue)
            diff = ""
            for i in range(len(restored)):
                if restored[i] == chunk['string'][i]:
                    diff = diff + "·"
                else:
                    diff = diff + "X"
            print("HEX: %s | ASCII: %s | DIFF: %s" % (restored, chunk['string'], diff))
            sys.exit(1)
    else:
        if offset == int(chunk['offset'], 16):
            print(ascFile)
        else:
            print("File length error!!!!")
            sys.exit(2)

To ensure the perfect backup is OK now is the moment to test. Get the printed dump with QR code and check the correctly reading with no file diff.

After this simple test now get long time and a cup of tea and copy all file, or simply scan the printed dump and using an OCR software (eg. tesseract) read the photo. After this pass the file to above script and check the file.

Conclusion

Through this article, we have created a master PGP key with a set of subkeys dedicated to a particular task.The advantage of this feature is that it is not necessary to revoke the master key, the one that holds our digital identity, in the event of compromise. This strategy offers a much higher level of security.

I think in next post try to explain the signing party and use of hardware key.