Preface

These are the slides for the Advanced GnuPG presentation with pre-generated results of the commands that are usually shown interactively during the presentation. (Some actions with manual input do not have results.)

A german recording of the presentation can be found on peertube.luga.at.

Slightly Advanced GnuPG

          __                          __
 ___ ____/ /  _____ ____  _______ ___/ /
/ _ `/ _  / |/ / _ `/ _ \/ __/ -_) _  /
\_,_/\_,_/|___/\_,_/_//_/\__/\__/\_,_/

     ____             ____   ____
    / ___|_ __  _   _|  _ \ / ___|
   | |  _| '_ \| | | | |_) | |  _
   | |_| | | | | |_| |  __/| |_| |
    \____|_| |_|\__,_|_|    \____|

A little deeper introduction to the commandline usage of gpg.


Fresh environment

» pwd
» echo $GNUPGHOME
» tree

result:

$ pwd
/tmp/tmp.Z1pF6IQjoe
$ echo /tmp/tmp.Z1pF6IQjoe/gpg
/tmp/tmp.Z1pF6IQjoe/gpg
$ tree
.
└── gpg

2 directories, 0 files

Encryption & Decryption - I

have a private message

echo "I am looking forward to meet you in Rick's Café!" > message.txt
» ls -lh message*
» cat message.txt

result:

$ ls -lh message.txt
-rw-r--r-- 1 alexk alexk 50 11. Aug 11:58 message.txt
$ cat message.txt
I am looking forward to meet you in Rick's Café!

Encryption & Decryption - II

encrypt it

cat message.txt | gpg -c --passphrase="SuperSecret" --batch --yes > message.enc
» hexyl message.enc

result:

$ hexyl message.enc
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 8c 0d 04 09 03 08 f7 fe ┊ 7a c7 c7 75 f6 b9 ff d2 │×_•_••××┊z××u××××│
│00000010│ 67 01 88 d8 2e fd 17 9b ┊ 05 57 61 c5 28 14 63 94 │g•××.ו×┊•Wa×(•c×│
│00000020│ e6 18 fb 1b 3d 82 bd f6 ┊ 05 8e 4c 65 7d 0b 8a 74 │וו=×××┊•×Le}•×t│
│00000030│ b6 75 bf 43 51 91 e1 b2 ┊ 23 0b d3 dc 19 7d ef 0c │×u×CQ×××┊#•×ו}×_│
│00000040│ d9 d8 35 6a 8a 40 09 b2 ┊ cc 98 ae 25 da 77 1b dc │××5j×@_×┊×××%×w•×│
│00000050│ 05 90 21 51 a8 5e 2d ad ┊ c5 f1 29 d2 39 58 40 8d │•×!Q×^-×┊××)×9X@×│
│00000060│ 07 08 b4 1e 83 6c 66 f9 ┊ ab d1 bc 87 35 79 ab 9e │••×•×lf×┊××××5y××│
│00000070│ 7b 75 8c d7 95 cf 53 ec ┊                         │{u××××S×┊        │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

Encryption & Decryption - III

decrypt it

gpg -d --passphrase="SuperSecret" --batch --yes message.enc

result:

I am looking forward to meet you in Rick's Café!

Encryption & Decryption - IV

decrypt it (viewing at stderr)

gpg -d --passphrase="SuperSecret" --batch --yes message.enc 2>&1

result:

gpg: AES256.CFB encrypted data
gpg: encrypted with 1 passphrase
I am looking forward to meet you in Rick's Café!

Encryption & Decryption - V

That's it!

This was symmetric encryption.

We used a shared secret - the passphrase

Want more? ;)


Recap of Public Key Encryption - I

keys

Keypair := Secret Key and Public Key

"Public Keys" are made public.

"Secret Key" ≈ "Private Key"

You import public keys into your keyring.

Before use, make sure that the public key belongs to the correct person. You verify this by either:

or


Recap of Public Key Encryption - II

sending emails

Alice sends an encrypted and signed email to Bob:

Bob receives the email from Alice:

=> Checking the signature validates sender and integrity of the message.


Naming Things


Tips for non-interactive usage

Using --batch mode avoids asking things. Useful in scripts.

If you want to avoid interactive requests (e.g. on overwriting files) add --yes to your options.

Command line options ≈ config parameters

Don't do this! (unless you are doing a presentation like this ;)

echo "yes" >> $GNUPGHOME/gpg.conf
cat $GNUPGHOME/gpg.conf

result:

yes

Create a keypair fast - Alice I

You probably know gpg --gen-key or the equivalent gpg --generate-key for interactive key generation.

There is a fast lane:

gpg --quick-gen-key "name <mail>" [ALGO [USAGE [EXPIRE-DATE]]]

ALGO := future-default | default | rsa | ...

USAGE := default | cert

gpg --batch --quick-gen-key --passphrase "" "Alice <alice@example.com>" \
    future-default default 1y   2>&1
Use a strong passphrase!!!

result:

gpg: /tmp/tmp.Z1pF6IQjoe/gpg/trustdb.gpg: trustdb created
gpg: directory '/tmp/tmp.Z1pF6IQjoe/gpg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/tmp/tmp.Z1pF6IQjoe/gpg/openpgp-revocs.d/C066589E554309BA28C06F94396394F741CD25CE.rev'

Create a keypair fast - Alice II

gpg --list-secret-keys

result:

/tmp/tmp.Z1pF6IQjoe/gpg/pubring.kbx
-----------------------------------
sec   ed25519 2024-08-11 [SC] [expires: 2025-08-11]
      C066589E554309BA28C06F94396394F741CD25CE
uid           [ultimate] Alice <alice@example.com>
ssb   cv25519 2024-08-11 [E]
      2E0B1084CEFB6AF4691D3946D7176F405283851C

Create a keypair fast - Alice III

ls -lht $GNUPGHOME/private-keys-v1.d

result:

total 8,0K
-rw------- 1 alexk alexk 234 11. Aug 11:58 AE5130E769E164DE6B402ED9E483A5DE733F5AD9.key
-rw------- 1 alexk alexk 227 11. Aug 11:58 C8A46974AE3F57880868543FCF3470CA91F73B3B.key

Create a keypair fast - Alice IV

cat $GNUPGHOME/private-keys-v1.d/*

result:

Created: 20240811T095855
Key: (private-key (ecc (curve Curve25519)(flags djb-tweak)(q
  #404F351751F0D99D7A4C0CFA934C9E7722FBEFC4D6A413D2E4C462675B24F3850B#)
 (d #72DD59E144C444748317B3DB20D3D49607D43DE411606C67027D25B982E6D1E0#)
 ))
Created: 20240811T095855
Key: (private-key (ecc (curve Ed25519)(flags eddsa)(q
  #4003395919098D90A9AF04A86459BAA5F1E29FD2A7A4CAE7A91BB8EADA86A28427#)
 (d #64415003F3FD45454FAC07A15E024C6B30050662F9AE193E85F83738563FCF6F#)
 ))

Structure and Interpretation of a GnuPG Keyblock - I

        +-------------------+             _
      / | Public-Key        |              \.
     |  |  - Crypto primit. | } #→Key-Grip  > #→Fingerprint
  .--|  |  - Creation date  |             _/
  |  |  +---------------------+
  |  :    | Direct Key Sig.   |  i.e. designated revoker
  |  |  +---------------------+
  |   \ | User-ID ...       |
  |     +----------------------+
  `--->   | Self-Signature     |
          +--------------------+
          | Signatures ...     |
        +----------------------+
        | Sub-Key ...       |    (has a KG + FP as well)
        +----------------------+
          | Self-Signature ... | (signs hash of PK + SK)
        +----------------------+
        | ...               |
        +-------------------+

Structure and Interpretation of a GnuPG Keyblock - II

gpg --export --output alice-pub-key.gpg  alice  2>&1
» gpg --show-key < alice-pub-key.gpg     2>&1

result:

$ gpg --show-key
pub   ed25519 2024-08-11 [SC] [expires: 2025-08-11]
      C066589E554309BA28C06F94396394F741CD25CE
uid                      Alice <alice@example.com>
sub   cv25519 2024-08-11 [E]
      2E0B1084CEFB6AF4691D3946D7176F405283851C

Structure and Interpretation of a GnuPG Keyblock - III

gpg --list-packets < alice-pub-key.gpg 2>&1 | head

result:

# off=0 ctb=98 tag=6 hlen=2 plen=51
:public key packet:
	version 4, algo 22, created 1723370335, expires 0
	pkey[0]: [80 bits] ed25519 (1.3.6.1.4.1.11591.15.1)
	pkey[1]: [263 bits]
	keyid: 396394F741CD25CE
# off=53 ctb=b4 tag=13 hlen=2 plen=25
:user ID packet: "Alice <alice@example.com>"
# off=80 ctb=88 tag=2 hlen=2 plen=153
:signature packet: algo 22, keyid 396394F741CD25CE

Structure and Interpretation of a GnuPG Keyblock - IV

--list-packets is a very useful tool to understand any OpenPGP data structure.
gpg --list-packets < alice-pub-key.gpg 2>&1 | egrep "^:"

result:

:public key packet:
:user ID packet: "Alice <alice@example.com>"
:signature packet: algo 22, keyid 396394F741CD25CE
:public sub key packet:
:signature packet: algo 22, keyid 396394F741CD25CE

Create a keypair fast - Bob

-K is short for --list-secret-keys
gpg --batch --passphrase "" --quick-gen-key "Bob <bob@example.com>"
» gpg -K

result:

$ gpg -K
/tmp/tmp.Z1pF6IQjoe/gpg/pubring.kbx
-----------------------------------
sec   ed25519 2024-08-11 [SC] [expires: 2025-08-11]
      C066589E554309BA28C06F94396394F741CD25CE
uid           [ultimate] Alice <alice@example.com>
ssb   cv25519 2024-08-11 [E]
      2E0B1084CEFB6AF4691D3946D7176F405283851C

sec   ed25519 2024-08-11 [SC] [expires: 2027-08-11]
      F9951D1BE61F218F7B37B3D85F080C1B0B94D17E
uid           [ultimate] Bob <bob@example.com>
ssb   cv25519 2024-08-11 [E]
      CC989542D91A3EF53B27FA4D07D6B9890D0859E3

Create a keypair fast - Charly

Now with RSA:

gpg --batch --passphrase "" \
    --default-new-key-algo rsa3072/cert,sign+rsa3072/encr  \
    --quick-gen-key "Charly <charly@example.com>"  2>&1
» gpg -K Charly

Mind the slowness.

result:

gpg: revocation certificate stored as '/tmp/tmp.Z1pF6IQjoe/gpg/openpgp-revocs.d/799710B175EC82A85E09F7C28AEAEAEBA1C10D64.rev'
$ gpg -K Charly
sec   rsa3072 2024-08-11 [SC] [expires: 2027-08-11]
      799710B175EC82A85E09F7C28AEAEAEBA1C10D64
uid           [ultimate] Charly <charly@example.com>
ssb   rsa3072 2024-08-11 [E]
      991F20A55A69FA0CDCA4D9639797FDA39FBFA00C

Create a keypair interactively

For certain cases you might want to specify the details for your key creation interactively:

gpg --full-generate-key

Then you are asked for:

BTW: A UserID is composed like this:

real name (comment) <email-address>


Using different email addresses

... with one key. :)

gpg --quick-add-uid alice@example.com alice@wolkenbruch.net 2>&1
gpg --update-trustdb
gpg -k alice

result:

pub   ed25519 2024-08-11 [SC] [expires: 2025-08-11]
      C066589E554309BA28C06F94396394F741CD25CE
uid           [ultimate] Alice <alice@example.com>
uid           [ultimate] alice@wolkenbruch.net
sub   cv25519 2024-08-11 [E]
      2E0B1084CEFB6AF4691D3946D7176F405283851C

Sign & Encrypt a message - I

Send to one or more --recipient <id>. Use --sign --encrypt or short

gpg -se --default-key Alice -r Bob message.txt > message.txt.gpg 2>&1
hexyl -n 128  message.txt.gpg

result:

┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 84 5e 03 07 d6 b9 89 0d ┊ 08 59 e3 12 01 07 40 32 │×^••×××_┊•Yו••@2│
│00000010│ 54 f2 46 6c e7 ea 8d f3 ┊ 35 b8 09 92 c5 33 d1 7a │T×Fl××××┊5×_××3×z│
│00000020│ 79 88 fe f9 56 74 41 e8 ┊ 45 65 7c 2d dd 7c 01 30 │y×××VtA×┊Ee|-×|•0│
│00000030│ 0b 48 2e 70 35 da b1 76 ┊ 7d f5 eb aa e5 05 0d bc │•H.p5××v┊}×××ו_×│
│00000040│ 8d bd 31 0e 1c df 7e 07 ┊ 87 29 93 4a 58 41 17 c2 │××1••×~•┊×)×JXA•×│
│00000050│ a8 3d cd 7b ef 1f 02 64 ┊ ab 1f b5 30 7a 40 b0 9e │×=×{ו•d┊ו×0z@××│
│00000060│ d4 c0 52 01 09 02 10 e5 ┊ ef 56 40 85 47 e7 c5 80 │××R•_••×┊×V@×G×××│
│00000070│ 91 75 aa ad dd b7 81 1c ┊ 08 85 ba 0f 7c aa b3 16 │×u××××ו┊•×ו|×ו│
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

Decrypt and Verify - I

Use --decrypt or -d ...

» gpg -d  message.txt.gpg 2>&1

result:

$ gpg -d message.txt.gpg
gpg: encrypted with cv25519 key, ID 07D6B9890D0859E3, created 2024-08-11
      "Bob <bob@example.com>"
I am looking forward to meet you in Rick's Café!
gpg: Signature made So 11 Aug 2024 11:58:55 CEST
gpg:                using EDDSA key C066589E554309BA28C06F94396394F741CD25CE
gpg:                issuer "alice@example.com"
gpg: Good signature from "Alice <alice@example.com>" [ultimate]
gpg:                 aka "alice@wolkenbruch.net" [ultimate]

Decrypt and Verify - II

... or let GnuPG autodetect what to do.

» rm message.txt
» gpg message.txt.gpg 2>&1
» cat message.txt

result:

$ rm message.txt
$ gpg message.txt.gpg
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with cv25519 key, ID 07D6B9890D0859E3, created 2024-08-11
      "Bob <bob@example.com>"
gpg: Signature made So 11 Aug 2024 11:58:55 CEST
gpg:                using EDDSA key C066589E554309BA28C06F94396394F741CD25CE
gpg:                issuer "alice@example.com"
gpg: Good signature from "Alice <alice@example.com>" [ultimate]
gpg:                 aka "alice@wolkenbruch.net" [ultimate]
$ cat message.txt
I am looking forward to meet you in Rick's Café!

Choose from various private keys

The default secret key used is the first key found in the keyring.

With --default-key <name> _name_ is used for signing. Use the fingerprint for this!

This can be overwritten with --local-user <name> (short -u).

To encrypt messages automatically to yourself use --default-recipient-self.

CLI options ≈ config options
echo "default-key alice@example.com" >> $GNUPGHOME/gpg.conf
echo "default-recipient-self" >> $GNUPGHOME/gpg.conf
cat $GNUPGHOME/gpg.conf

result:

yes
default-key alice@example.com
default-recipient-self

Sign & Encrypt a message - II

If want to sent a message via a non binary safe channel use --armor (short -a).

Combine public key and symmetric encryption.

gpg -seca --batch --passphrase "yo" -r Bob message.txt > message.txt.asc 2>&1
» cat message.txt.asc

result:

$ cat message.txt.asc
-----BEGIN PGP MESSAGE-----

hF4DB9a5iQ0IWeMSAQdArhzIrlg3FEGKYOf6qzEqGAlwkx3U/fVkMT7mHQF00mkw
twAjjMejSfNWI/42TdREhlN6cLVcSHtWZ2J8tMswr1YnYje94/uRbHV2T79oshPe
jE0FCQIDCFvN+lELSrMV/8SBfLbl/X26ayXjhIHeA7WkQEjCrfmxqUg9LFz7TXZx
T0fvjsVs9vwRN/A7fZU7Hi8pnfsZNwXt4dKrlR5P9tTAUwEJAhDqnPASDNChGjpB
WnXknUqUsdFppj383JDEeNC0W3kgqU85EP0QJ5V55CsooSKwP4kPYokZTWlr2zNZ
1ibfuI6Ah20AehKqVtXWjzrWvxQL4RHHZgjwoq4GELBNdebR/53YjPVlmAPUNx96
iqDeT3JpngcRpsV6ip9vMhL/+yC2OCq1JTFPSuezuy0wPCazOYKt4vh17EW4GPQQ
QHLiTFerWcZEL/ZvCCV8gFUofaVXbcbrqhRbRmr+dqV192etZwrc28siB++sjv84
Bf9WiqEtcM/WdjBmGuze6BvKeppNU4vgCGfzfgcTHxDWuRRJ4MXeBkyJFS/6+8xd
6NYeBmmNcWS/VOLU39PhdHwoKMU5
=f/2c
-----END PGP MESSAGE-----

Structure and Interpretation of an encrypted message

Combined public key and symmetric encryption.

gpg --list-packets < message.txt.asc 2>&1 | egrep "^:"
see https://www.rfc-editor.org/rfc/rfc4880 chapter 4+5

result:

:pubkey enc packet: version 3, algo 18, keyid 07D6B9890D0859E3
:symkey enc packet: version 5, cipher 9, aead 2, s2k 3, hash 8, encrypted seskey 63 bytes
:aead encrypted packet: cipher=9 aead=2 cb=16
:compressed packet: algo=2
:onepass_sig packet: keyid 396394F741CD25CE
:literal data packet:
:signature packet: algo 22, keyid 396394F741CD25CE

Intermezzo - get more keys

From the website https://ftp-master.debian.org/keys/release-12.asc

» wget --quiet https://ftp-master.debian.org/keys/release-12.asc 2>&1
» gpg --import release-12.asc 2>&1
» gpg -k

result:

$ wget --quiet https://ftp-master.debian.org/keys/release-12.asc
$ gpg --import release-12.asc
gpg: key F8D2585B8783D481: public key "Debian Stable Release Key (12/bookworm) <debian-release@lists.debian.org>" imported
gpg: Total number processed: 1
gpg:               imported: 1
$ gpg -k
/tmp/tmp.Z1pF6IQjoe/gpg/pubring.kbx
-----------------------------------
pub   ed25519 2024-08-11 [SC] [expires: 2025-08-11]
      C066589E554309BA28C06F94396394F741CD25CE
uid           [ultimate] Alice <alice@example.com>
uid           [ultimate] alice@wolkenbruch.net
sub   cv25519 2024-08-11 [E]
      2E0B1084CEFB6AF4691D3946D7176F405283851C

pub   ed25519 2024-08-11 [SC] [expires: 2027-08-11]
      F9951D1BE61F218F7B37B3D85F080C1B0B94D17E
uid           [ultimate] Bob <bob@example.com>
sub   cv25519 2024-08-11 [E]
      CC989542D91A3EF53B27FA4D07D6B9890D0859E3

pub   rsa3072 2024-08-11 [SC] [expires: 2027-08-11]
      799710B175EC82A85E09F7C28AEAEAEBA1C10D64
uid           [ultimate] Charly <charly@example.com>
sub   rsa3072 2024-08-11 [E]
      991F20A55A69FA0CDCA4D9639797FDA39FBFA00C

pub   ed25519 2023-01-23 [SC] [expires: 2031-01-21]
      4D64FEC119C2029067D6E791F8D2585B8783D481
uid           [ unknown] Debian Stable Release Key (12/bookworm) <debian-release@lists.debian.org>

Add another crypto algo (PQC) - I

Kyber is not only for Light-Sabers

NEW: Post Quantum Crypto (PQC)

Available with GnuPG 2.5 (details may change)

gpg --batch --passphrase "" --quick-gen-key "Doro <doro@example.com>"
fp=`gpg --list-options show-only-fpr-mbox -K Doro |cut --delimiter=" " --fields=1`
» gpg --batch --passphrase "" --quick-add-key $fp Kyber
gpg -K Doro

result:

$ gpg --batch --passphrase --quick-add-key B58FAE15981AA8BDF7A27367F9C0FD9B6754C6DF Kyber
sec   ed25519 2024-08-11 [SC] [expires: 2027-08-11]
      B58FAE15981AA8BDF7A27367F9C0FD9B6754C6DF
uid           [ultimate] Doro <doro@example.com>
ssb   cv25519 2024-08-11 [E]
      44A76C416DF77A294779224EF518FCDB286DB555
ssb   ky768_bp256 2024-08-11 [E]
      BA0B5AA4D19D36917B313C05102D5318FD3C0B12DEFB169A0D0AE999D5D6D913

Add another crypto algo (PQC) - II

and another one

gpg --batch --passphrase "" --quick-gen-key "Eve <eve@example.com>"
fp=`gpg --list-options show-only-fpr-mbox -K Eve |cut --delimiter=" " --fields=1`
gpg --batch --passphrase "" --quick-add-key $fp Kyber
gpg -K Eve

result:

sec   ed25519 2024-08-11 [SC] [expires: 2027-08-11]
      F20CC900F35EE47AF644385CF6933D5AD211A4F9
uid           [ultimate] Eve <eve@example.com>
ssb   cv25519 2024-08-11 [E]
      F61D27EF14CDC3404F79B01F6D946FE94B2CF6AF
ssb   ky768_bp256 2024-08-11 [E]
      7E4682581B5C2DD8C42D530A5F7F843174B04B220CC8570D1FAD181ABD9099A5

PQC encryption - I

The --local-user or short -u option overwrites the default-user and is used for signing.

» gpg --batch -se --local-user Doro -r Eve -o pqcenc.gpg \
    --require-pqc-encryption      message.txt

result:

$ gpg --batch -se --local-user Doro -r Eve -o pqcenc.gpg --require-pqc-encryption message.txt

PQC encryption - II

check the packets

gpg --list-packets < pqcenc.gpg 2>&1 | head -n 5
echo
gpg --list-packets < message.txt.gpg 2>&1 | head -n 5

result:

gpg: encrypted with ky768_bp256 key, ID 7E4682581B5C2DD8, created 2024-08-11
      "Eve <eve@example.com>"
gpg: using "alice@example.com" as default secret key for signing
# off=0 ctb=85 tag=1 hlen=3 plen=1211
:pubkey enc packet: version 3, algo 29, keyid 7E4682581B5C2DD8

gpg: encrypted with cv25519 key, ID 07D6B9890D0859E3, created 2024-08-11
      "Bob <bob@example.com>"
gpg: using "alice@example.com" as default secret key for signing
# off=0 ctb=84 tag=1 hlen=2 plen=94
:pubkey enc packet: version 3, algo 18, keyid 07D6B9890D0859E3

Trust someone - I

Check Bob's fingerprint:

gpg --fingerprint Bob

The option --fingerprint shows the fingerprint in blocks for easy comparison.

result:

pub   ed25519 2024-08-11 [SC] [expires: 2027-08-11]
      F995 1D1B E61F 218F 7B37  B3D8 5F08 0C1B 0B94 D17E
uid           [ultimate] Bob <bob@example.com>
sub   cv25519 2024-08-11 [E]
      CC98 9542 D91A 3EF5 3B27  FA4D 07D6 B989 0D08 59E3

Trust someone - II

Sign Bob's key:

gpg --sign-key --batch  Bob
gpg --list-sigs Bob

result:

pub   ed25519 2024-08-11 [SC] [expires: 2027-08-11]
      F9951D1BE61F218F7B37B3D85F080C1B0B94D17E
uid           [ultimate] Bob <bob@example.com>
sig 3        5F080C1B0B94D17E 2024-08-11  [self-signature]
sig          396394F741CD25CE 2024-08-11  Alice <alice@example.com>
sub   cv25519 2024-08-11 [E]
      CC989542D91A3EF53B27FA4D07D6B9890D0859E3
sig          5F080C1B0B94D17E 2024-08-11  [self-signature]

Trust someone - III - cert level

man gpg | grep -A 15 "default-cert-level n"

result:

       --default-cert-level n
              The default to use for the check level when signing a key.

              0  means  you make no particular claim as to how carefully you veri‐
              fied the key.

              1 means you believe the key is owned by the person who claims to own
              it but you could not, or did not verify the key at all. This is use‐
              ful for a "persona" verification, where you sign the key of a  pseu‐
              donymous user.

              2  means  you  did casual verification of the key. For example, this
              could mean that you verified the key  fingerprint  and  checked  the
              user ID on the key against a photo ID.

              3 means you did extensive verification of the key. For example, this

Things go wrong - be prepared!

Backup your key(s)!

Backup Everything

All your GnuPG data is stored here:

or there

or maybe here

Just back up the whole folder.


Backup private Keys

gpg --export-secret-key --armor --output alice_priv_key.asc alice
head alice_priv_key.asc

result:

-----BEGIN PGP PRIVATE KEY BLOCK-----

lFgEZriLXxYJKwYBBAHaRw8BAQdAAzlZGQmNkKmvBKhkWbql8eKf0qekyuepG7jq
2oaihCcAAP9kQVAD8/1FRU+sB6FeAkxrMAUGYvmuGT6F+Dc4Vj/Pbw2FtBlBbGlj
ZSA8YWxpY2VAZXhhbXBsZS5jb20+iJkEExYKAEEWIQTAZlieVUMJuijAb5Q5Y5T3
Qc0lzgUCZriLXwIbAwUJAeEzgAULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAK
CRA5Y5T3Qc0lzr/EAP0QmQc8qn1FlBc1iZqr6fOzQgZIQH9FKogc7lRQZg1DzQEA
xo6AC/5rdJ3b6bWx5nnhCcKmnINx6TnN/7Ro36O+fAi0FWFsaWNlQHdvbGtlbmJy
dWNoLm5ldIiZBBMWCgBBFiEEwGZYnlVDCboowG+UOWOU90HNJc4FAma4i18CGwMF
CQHhM4AFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQOWOU90HNJc75ZgD/

Backup Private Keys - Paperkey

gpg --export-secret-key alice | paperkey | tail

result:

# Each base16 line ends with a CRC-24 of that line.
# The entire block of data ends with a CRC-24 of the entire block of data.

  1: 00 04 C0 66 58 9E 55 43 09 BA 28 C0 6F 94 39 63 94 F7 41 CD 25 CE 79BCE5
  2: 00 25 00 00 FF 64 41 50 03 F3 FD 45 45 4F AC 07 A1 5E 02 4C 6B 30 C223A0
  3: 05 06 62 F9 AE 19 3E 85 F8 37 38 56 3F CF 6F 0D 85 04 2E 0B 10 84 A55D81
  4: CE FB 6A F4 69 1D 39 46 D7 17 6F 40 52 83 85 1C 00 25 00 00 FF 72 A4198E
  5: DD 59 E1 44 C4 44 74 83 17 B3 DB 20 D3 D4 96 07 D4 3D E4 11 60 6C 55E6D1
  6: 67 02 7D 25 B9 82 E6 D1 E0 11 83 9CE996
  7: B8A3C3

Backup Revocation Key

Creating a key automatically generates a revocation certificate in this folder:

ls -l $GNUPGHOME/openpgp-revocs.d/

BTW: you can generate a revocation certificate any time with --gen-revoke.

result:

total 20
-rw------- 1 alexk alexk 1629 11. Aug 11:58 799710B175EC82A85E09F7C28AEAEAEBA1C10D64.rev
-rw------- 1 alexk alexk 1194 11. Aug 11:58 B58FAE15981AA8BDF7A27367F9C0FD9B6754C6DF.rev
-rw------- 1 alexk alexk 1196 11. Aug 11:58 C066589E554309BA28C06F94396394F741CD25CE.rev
-rw------- 1 alexk alexk 1192 11. Aug 11:58 F20CC900F35EE47AF644385CF6933D5AD211A4F9.rev
-rw------- 1 alexk alexk 1192 11. Aug 11:58 F9951D1BE61F218F7B37B3D85F080C1B0B94D17E.rev

Designated Revoker

$ gpg --key-edit ...
> add revoker FINGERPRINT

This allows a user (called "revoker") to revoke someone else's key with the permission of the key holder.

A designated revoker generates a revocation certificate for a key with:

gpg --generate-designated-revocation <name>
  or short
gpg --desig-revoke Bob

List Keys - more Options I

» gpg -k --with-subkey-fingerprint --with-keygrip --with-sig-list Bob

result:

$ gpg -k --with-subkey-fingerprint --with-keygrip --with-sig-list Bob
pub   ed25519 2024-08-11 [SC] [expires: 2027-08-11]
      F9951D1BE61F218F7B37B3D85F080C1B0B94D17E
      Keygrip = 6B01439DDAC3CF7F2BE017389D8AF182E07741DE
uid           [ultimate] Bob <bob@example.com>
sig 3        5F080C1B0B94D17E 2024-08-11  [self-signature]
sig          396394F741CD25CE 2024-08-11  Alice <alice@example.com>
sub   cv25519 2024-08-11 [E]
      CC989542D91A3EF53B27FA4D07D6B9890D0859E3
      Keygrip = F3B70D7F227BFF5EFB8F08DCC5F94A8365294BBD
sig          5F080C1B0B94D17E 2024-08-11  [self-signature]

List Keys - more Options II

A compact list of email-addresses and fingerprints:

» gpg --list-options show-only-fpr-mbox -k

result:

$ gpg --list-options show-only-fpr-mbox -k
C066589E554309BA28C06F94396394F741CD25CE alice@example.com
C066589E554309BA28C06F94396394F741CD25CE alice@wolkenbruch.net
F9951D1BE61F218F7B37B3D85F080C1B0B94D17E bob@example.com
799710B175EC82A85E09F7C28AEAEAEBA1C10D64 charly@example.com
4D64FEC119C2029067D6E791F8D2585B8783D481 debian-release@lists.debian.org
B58FAE15981AA8BDF7A27367F9C0FD9B6754C6DF doro@example.com
F20CC900F35EE47AF644385CF6933D5AD211A4F9 eve@example.com

rtfm - I

The manual is exhaustive.

man gpg | wc -l

result:

3975

rtfm - II

Just search a topic.

man gpg | grep -A 9 "list-options param"

result:

       --list-options parameters
              This is a space or comma delimited string that  gives  options  used
              when listing keys and signatures (that is, --list-keys, --check-sig‐
              natures,  --list-public-keys, --list-secret-keys, and the --edit-key
              functions).  Options can be prepended with  a  no-  (after  the  two
              dashes) to give the opposite meaning.  The options are:

              show-photos
                     Causes  --list-keys,  --check-signatures, --list-public-keys,
                     and --list-secret-keys to display any photo IDs  attached  to

Distribute your Public Key - I

via keyserver

gpg --keyserver keyserver.ubuntu.com --send-keys YOURKEYID
Don't spam the keyserver! :)

Keyservers nowadays often want you to confirm the key by sending you an email with a confirmation link.

Keyservers are problematic. There are other options to prefer.


Distribute your Public Key - II

via signatures

You can easily distribute your signing key by adding it to a gpg signature. You can do so with the gpg option --include-key-block.

If you always want to add your certificate signing key you can add this to the ~/.gnupg/gpg.conf.

On the other hand you can automatically import such certificate signing keys with the option --auto-key-import, respectively adding this to the config.

But be careful with auto-key-import. You might get messages from unknown people with malicious certificates in the signature.

Distribute your Public Key - III

WKD FTW - a very short overview

The Web Key Directory is a web-server based solution to distribute public keys.

The keys are statically stored on your web-server matching your email domain under /.well-known/openpgpkey/hu/... with the hash of the mbox-name.

There are other options as well - just read the docs ;)

get the mbox-name-hash:

» gpg --with-wkd-hash -K Alice

and export it with --no-armor using the WKD-hash as the filename.

result:

$ gpg --with-wkd-hash -K Alice
sec   ed25519 2024-08-11 [SC] [expires: 2025-08-11]
      C066589E554309BA28C06F94396394F741CD25CE
uid           [ultimate] Alice <alice@example.com>
              kei1q4tipxxu1yj79k9kfukdhfy631xe@example.com
uid           [ultimate] alice@wolkenbruch.net
              kei1q4tipxxu1yj79k9kfukdhfy631xe@wolkenbruch.net
ssb   cv25519 2024-08-11 [E]
      2E0B1084CEFB6AF4691D3946D7176F405283851C

Distribute your Public Key - IV

WKD FTW - references

See also:


Distribute your Public Key - V

WKD FTW - Import a key from a Web Key Directory

» gpg --auto-key-locate clear,wkd,nodefault \
    --locate-external-keys  alexander@kulbartsch.de

result:

$ gpg --auto-key-locate clear,wkd,nodefault --locate-external-keys alexander@kulbartsch.de
pub   ed25519 2024-01-07 [SC] [expires: 2029-01-05]
      A74CC79E0A800BF196A36B3E213E2CD3CABCF0B9
uid           [ unknown] Alexander Kulbartsch <alexander@kulbartsch.de>
sub   cv25519 2024-01-07 [E] [expires: 2029-01-05]
      D36ED81350E8CE37288235870ECBED56E55BDFEA

Continue?

There is more to experience

(groups, adsk, hide, random, smartcards, programming, bonus, compress)


Groups - I

define a group

Use groups to encrypt to several keys.

Don't mix this up with the similar Kleopatra feature.

Define a group either:

or

or use it just as an alias


Groups - II

define a group

echo "group beta=Bob" >> $GNUPGHOME/gpg.conf
echo "group beta=Charly" >> $GNUPGHOME/gpg.conf
cat $GNUPGHOME/gpg.conf

result:

yes
default-key alice@example.com
default-recipient-self
group beta=Bob
group beta=Charly

Groups - III

encrypt to a group

gpg -e -a  -r beta  -o to_beta.gpg  message.txt  2>&1
ls -lha *.gpg

result:

-rw-r--r-- 1 alexk alexk  415 11. Aug 11:58 alice-pub-key.gpg
-rw-r--r-- 1 alexk alexk  373 11. Aug 11:58 message.txt.gpg
-rw-r--r-- 1 alexk alexk 1,5K 11. Aug 11:58 pqcenc.gpg
-rw-r--r-- 1 alexk alexk  911 11. Aug 11:58 to_beta.gpg

Groups - IV

look at that group encryption

gpg --list-packets < to_beta.gpg 2>&1 | grep "^:"

result:

:pubkey enc packet: version 3, algo 1, keyid 9797FDA39FBFA00C
:pubkey enc packet: version 3, algo 18, keyid 07D6B9890D0859E3
:aead encrypted packet: cipher=9 aead=2 cb=16
:compressed packet: algo=2
:literal data packet:

ADSK

An Additional Decryption Sub-Key

requests GnuPG to encrypt to an extra key. This is useful to encrypt to

Introduced with GnuPG 2.4.1.

gpg --quick-add-adsk FINGERPRINT...
see https://www.gnupg.org/blog/20230321-adsk.html

Don't tell who are the recipients - I

Usually everyone can see to whom a message is encrypted.

gpg --list-packets < to_beta.gpg 2>&1 | head

result:

gpg: encrypted with cv25519 key, ID 07D6B9890D0859E3, created 2024-08-11
      "Bob <bob@example.com>"
gpg: encrypted with rsa3072 key, ID 9797FDA39FBFA00C, created 2024-08-11
      "Charly <charly@example.com>"
gpg: using "alice@example.com" as default secret key for signing
# off=0 ctb=85 tag=1 hlen=3 plen=396
:pubkey enc packet: version 3, algo 1, keyid 9797FDA39FBFA00C
	data: [3070 bits]
# off=399 ctb=84 tag=1 hlen=2 plen=94
:pubkey enc packet: version 3, algo 18, keyid 07D6B9890D0859E3

Don't tell who are the recipients - II

gpg -e --throw-keyids -r beta -o to_someone.gpg message.txt 2>&1
ls -lh to*

result:

-rw-r--r-- 1 alexk alexk 911 11. Aug 11:58 to_beta.gpg
-rw-r--r-- 1 alexk alexk 627 11. Aug 11:58 to_someone.gpg

Don't tell who are the recipients - III

Now check the recipients ...

This alone is not sufficient. Watch your OpSec
gpg --list-packets < to_someone.gpg 2>&1 | head

result:

gpg: encrypted with ECDH key, ID 0000000000000000
gpg: encrypted with RSA key, ID 0000000000000000
gpg: using "alice@example.com" as default secret key for signing
gpg: anonymous recipient; trying secret key D7176F405283851C ...
gpg: ecdh failed in gcry_cipher_decrypt: Checksum error
gpg: anonymous recipient; trying secret key 07D6B9890D0859E3 ...
gpg: okay, we are the anonymous recipient.
gpg: WARNING: encrypted message has been manipulated!
# off=0 ctb=85 tag=1 hlen=3 plen=396
:pubkey enc packet: version 3, algo 1, keyid 0000000000000000

Session Key - I

There might be a case where you need to or want to pass on the key to decrypt a specific message.

If you hand out your private key, everything ever encrypted to this key would be decryptable.

But every message has it's own session key. You can extract this key for a specific message with --show-only-session-key.

» gpg --show-only-session-key message.txt.gpg 2>&1

result:

$ gpg --show-only-session-key message.txt.gpg
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with cv25519 key, ID 07D6B9890D0859E3, created 2024-08-11
      "Bob <bob@example.com>"
gpg: using "alice@example.com" as default secret key for signing
gpg: session key: '9.2:3AC5CD41CB6879FE0BC2845ED56AC06748B889957486B50A87B8D1779311FECD'

Session Key - II

Decrypt using a session key with --override-session-key:

sk=$(gpg --show-only-session-key message.txt.gpg 2>&1 | grep session | cut -d \' -f2)
gpg -d --override-session-key $sk message.txt.gpg

result:

I am looking forward to meet you in Rick's Café!

get Random

GnuPG has its own very high-quality and, above all, trustworthy random number generator built in.

If randomness is ever needed in a script, you can do the following:

gpg -a --gen-random 2 15

This will output a Base64 encoded random with an effective length of 15 bytes (120 bits).

How much entropy should a very good password have:

A password should consist of at least 120 randomly chosen bits.

If a password is needed, you can easily generate it like this (30 × zBase32 ⇒ 150 bit):

gpg --gen-random 30

result:

wreeqyd6txbnq49u1gn66xtw1tub98

Be smart, use SmartCards - I

You can copy a secret key to a smartcard (or two) and remove it from the keyring.

gpg-card
writekey <slot> <keygrip>
name → GnuPG-Card → Charly
reset
list
reset is needed to update card info on screen
reset a card with factory-reset

(preparing pinentry for this demo:)

echo "pinentry-program /bin/pinentry-tty" >> $GNUPGHOME/gpg-agent.conf
gpg-connect-agent reloadagent /bye

result:

Nothing to see here.

OK

Be smart, use SmartCards - II

copy to smartcard

(gpg-card, writekey n kg, name, reset, list)

gpg -K --with-keygrip Charly 2>&1

result:

sec   rsa3072 2024-08-11 [SC] [expires: 2027-08-11]
      799710B175EC82A85E09F7C28AEAEAEBA1C10D64
      Keygrip = B18C9A16A4A0FD92B22F8EA8DFAD3236E6CC9D6A
uid           [ultimate] Charly <charly@example.com>
ssb   rsa3072 2024-08-11 [E]
      991F20A55A69FA0CDCA4D9639797FDA39FBFA00C
      Keygrip = DEA3C68B8DC05BEF3C60EB366C876F90CE029512

Be smart, use SmartCards - III

delete local secret key

gpg --batch --delete-secret-keys \
    `gpg --list-options show-only-fpr-mbox -K Charly \
      |cut --delimiter=" " --fields=1`

There are also options to --delete-keys (public only) and --delete-secret-and-public-keys.

result:


Be smart, use SmartCards - IV

key is gone

The # indicates the missing secret key
gpg -K --with-keygrip Charly 2>&1
echo

result:

gpg: error reading key: No secret key

Programming with GnuPG

Overview:

gpg -K --batch --with-colons Bob

result:

sec:u:255:22:5F080C1B0B94D17E:1723370335:1817978335::u:::scESC:::+::ed25519:::0:
fpr:::::::::F9951D1BE61F218F7B37B3D85F080C1B0B94D17E:
grp:::::::::6B01439DDAC3CF7F2BE017389D8AF182E07741DE:
uid:u::::1723370335::53B786D9927B2601EBBF42093AAF7D24E6BB9B4E::Bob <bob@example.com>::::::::::0:
ssb:u:255:18:07D6B9890D0859E3:1723370335::::::e:::+::cv25519::
fpr:::::::::CC989542D91A3EF53B27FA4D07D6B9890D0859E3:
grp:::::::::F3B70D7F227BFF5EFB8F08DCC5F94A8365294BBD:

Programming with gpgme-json

echo '{ "op" : "keylist", "keys" : "Alice" }' | gpgme-json -s | jq | tail -n 15

result:

          "revoked": false,
          "invalid": false,
          "validity": "ultimate",
          "uid": "alice@wolkenbruch.net",
          "name": "",
          "email": "alice@wolkenbruch.net",
          "comment": "",
          "address": "alice@wolkenbruch.net",
          "origin": 0,
          "last_update": 0
        }
      ]
    }
  ]
}

Programming with GPGME

Theres a C library that makes it easy to use GnuPG:

The tests folder in the source provide good example code:

In the sources you also find bindings for Common Lisp, C++, Javascript, Python and QT:

Go bindings (extended from proglottis work)


Identifiing GPG files

» file message*
» gpgme-json --identify message.txt.gpg

result:

$ file message.enc message.txt message.txt.asc message.txt.gpg
message.enc:     PGP symmetric key encrypted data - AES with 256-bit key salted & iterated - SHA256 .
message.txt:     Unicode text, UTF-8 text
message.txt.asc: PGP message Public-Key Encrypted Session Key (old)
message.txt.gpg: data
$ gpgme-json --identify message.txt.gpg
PGP-encrypted

To compress or not to compress - I

Anyway, you can tell gnupg what to do by using --compress-algo <name> where name is one of the following:


To compress or not to compress - II

your preference

You can set your preference with the parameter personal-compress-preferences ...

Deactivating compression does not have an impact on the security.

To compress or not to compress - III

with compressed data

Let's generate some already "compressed" data and encrypt it.

head -c 1M < /dev/urandom > compressed.plain
hyperfine "gpg --encrypt -r Bob --output compressed_d.gpg  compressed.plain" \
          "gpg --encrypt -r Bob -z 0  -o compressed_0.gpg  compressed.plain" \
          "gpg --encrypt -r Bob -z 9  -o compressed_9.gpg  compressed.plain"
echo
ls -lhtr compressed*

result:

Benchmark 1: gpg --encrypt -r Bob --output compressed_d.gpg  compressed.plain
  Time (mean ± σ):      25.8 ms ±   3.5 ms    [User: 22.8 ms, System: 3.0 ms]
  Range (min … max):    22.0 ms …  35.8 ms    109 runs
 
Benchmark 2: gpg --encrypt -r Bob -z 0  -o compressed_0.gpg  compressed.plain
  Time (mean ± σ):       4.4 ms ±   0.8 ms    [User: 2.0 ms, System: 2.7 ms]
  Range (min … max):     2.3 ms …   5.8 ms    521 runs
 
Benchmark 3: gpg --encrypt -r Bob -z 9  -o compressed_9.gpg  compressed.plain
  Time (mean ± σ):      26.2 ms ±   3.6 ms    [User: 23.7 ms, System: 2.5 ms]
  Range (min … max):    21.9 ms …  36.0 ms    107 runs
 
Summary
  gpg --encrypt -r Bob -z 0  -o compressed_0.gpg  compressed.plain ran
    5.88 ± 1.28 times faster than gpg --encrypt -r Bob --output compressed_d.gpg  compressed.plain
    5.95 ± 1.31 times faster than gpg --encrypt -r Bob -z 9  -o compressed_9.gpg  compressed.plain

-rw-r--r-- 1 alexk alexk 1,0M 11. Aug 11:58 compressed.plain
-rw-r--r-- 1 alexk alexk 1,1M 11. Aug 11:59 compressed_d.gpg
-rw-r--r-- 1 alexk alexk 1,1M 11. Aug 11:59 compressed_0.gpg
-rw-r--r-- 1 alexk alexk 1,1M 11. Aug 11:59 compressed_9.gpg

To compress or not to compress - IV

with uncompressed/text data

Let's use some uncompressed text and encrypt it.

man gpg > gpg_man.txt; man gpg >> gpg_man.txt; man gpg >> gpg_man.txt
hyperfine "gpg --encrypt -r Bob --output gpg_man_d.gpg  gpg_man.txt" \
          "gpg --encrypt -r Bob -z 0  -o gpg_man_0.gpg  gpg_man.txt" \
          "gpg --encrypt -r Bob -z 9  -o gpg_man_9.gpg  gpg_man.txt"
echo
ls -lhtr gpg_man*

result:

Benchmark 1: gpg --encrypt -r Bob --output gpg_man_d.gpg  gpg_man.txt
  Time (mean ± σ):      30.8 ms ±   4.0 ms    [User: 28.1 ms, System: 2.7 ms]
  Range (min … max):    26.7 ms …  43.6 ms    100 runs
 
Benchmark 2: gpg --encrypt -r Bob -z 0  -o gpg_man_0.gpg  gpg_man.txt
  Time (mean ± σ):       4.0 ms ±   0.8 ms    [User: 1.7 ms, System: 2.7 ms]
  Range (min … max):     2.2 ms …   6.0 ms    718 runs
 
Benchmark 3: gpg --encrypt -r Bob -z 9  -o gpg_man_9.gpg  gpg_man.txt
  Time (mean ± σ):      86.5 ms ±   4.4 ms    [User: 84.0 ms, System: 2.5 ms]
  Range (min … max):    79.9 ms …  96.7 ms    33 runs
 
Summary
  gpg --encrypt -r Bob -z 0  -o gpg_man_0.gpg  gpg_man.txt ran
    7.63 ± 1.74 times faster than gpg --encrypt -r Bob --output gpg_man_d.gpg  gpg_man.txt
   21.47 ± 4.18 times faster than gpg --encrypt -r Bob -z 9  -o gpg_man_9.gpg  gpg_man.txt

-rw-r--r-- 1 alexk alexk 630K 11. Aug 11:59 gpg_man.txt
-rw-r--r-- 1 alexk alexk 180K 11. Aug 11:59 gpg_man_d.gpg
-rw-r--r-- 1 alexk alexk 630K 11. Aug 11:59 gpg_man_0.gpg
-rw-r--r-- 1 alexk alexk 179K 11. Aug 11:59 gpg_man_9.gpg

FIN

  ________                __      __  __            __
 /_  __/ /_  ____ _____  / /__    \ \/ /___  __  __/ /
  / / / __ \/ __ `/ __ \/ //_/     \  / __ \/ / / / /
 / / / / / / /_/ / / / / ,<        / / /_/ / /_/ /_/
/_/ /_/ /_/\__,_/_/ /_/_/|_|      /_/\____/\__,_(_)


      ___              _   _             ___
     / _ \ _  _ ___ __| |_(_)___ _ _  __|__ \.
    | (_) | || / -_|_-<  _| / _ \ ' \(_-< /_/
     \__\_\\_,_\___/__/\__|_\___/_||_/__/(_)

Contact me: alexander@kulbartsch.de


Colophon

This presentation was made with:

#!bash
echo "$" $*
$*

There's more ...

exciting topics to explore


Finally

EOF

Copyright (c) 2024 Alexander Kulbartsch

This work is licensed under: CC BY 4.0