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.
__ __
___ ____/ / _____ ____ _______ ___/ /
/ _ `/ _ / |/ / _ `/ _ \/ __/ -_) _ /
\_,_/\_,_/|___/\_,_/_//_/\__/\__/\_,_/
____ ____ ____
/ ___|_ __ _ _| _ \ / ___|
| | _| '_ \| | | | |_) | | _
| |_| | | | | |_| | __/| |_| |
\____|_| |_|\__,_|_| \____|
A little deeper introduction to the commandline
usage of gpg
.
» pwd
» echo $GNUPGHOME
» tree
result:
$ pwd
/tmp/tmp.Z1pF6IQjoe
$ echo /tmp/tmp.Z1pF6IQjoe/gpg
/tmp/tmp.Z1pF6IQjoe/gpg
$ tree
.
└── gpg
2 directories, 0 files
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é!
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×┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
gpg -d --passphrase="SuperSecret" --batch --yes message.enc
result:
I am looking forward to meet you in Rick's Café!
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é!
This was symmetric encryption.
We used a shared secret - the passphrase
-c
to "crypt" ≈ --symmetric
-d
≈ --decrypt
Want more? ;)
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
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.
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
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'
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
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
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#)
))
+-------------------+ _
/ | 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)
+----------------------+
| ... |
+-------------------+
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
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
--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
-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
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
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>
... 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
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××××ו┊•×ו|×ו│
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
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]
... 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é!
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
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-----
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
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>
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
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
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
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
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
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]
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
Backup your key(s)!
All your GnuPG data is stored here:
or there
or maybe here
Just back up the whole folder.
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/
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
Creating a key automatically generates a revocation certificate in this folder:
$GNUPGHOME/openpgp-revocs.d/<FINGERPRINT>.rev
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
$ 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
--with-subkey-fingerprint
--with-keygrip
--with-sig-list
--with-sig-check
also checks the signatures for validity.» 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]
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
The manual is exhaustive.
man gpg | wc -l
result:
3975
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
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.
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.
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
See also:
» 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
(groups, adsk, hide, random, smartcards, programming, bonus, compress)
Use groups to encrypt to several keys.
Don't mix this up with the similar Kleopatra feature.
Define a group either:
group beta=Bob
group beta=Charly
or
group team@foo.org = <fingerprint1> <fingerprints...>
or use it just as an alias
group b@another.domain=bob@another.domain
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
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
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:
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
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
--throw-keyids
--hidden-recipient
, or short -R
--hidden-encrypt-to
in the config for yourselfgpg -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
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
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'
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é!
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).
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
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
(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
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:
The # indicates the missing secret key
gpg -K --with-keygrip Charly 2>&1
echo
result:
gpg: error reading key: No secret key
Overview:
--batch
, --with-colons
gpg -K --batch --with-colons Bob
gpgme-json
uses JSON notationresult:
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:
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
}
]
}
]
}
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)
» 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
Anyway, you can tell gnupg what to do by using --compress-algo <name>
where name is one of the following:
-z 0
– fastest ;)
You can set your preference with the parameter
personal-compress-preferences ...
Deactivating compression does not have an impact on the security.
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
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
________ __ __ __ __
/_ __/ /_ ____ _____ / /__ \ \/ /___ __ __/ /
/ / / __ \/ __ `/ __ \/ //_/ \ / __ \/ / / / /
/ / / / / / /_/ / / / / ,< / / /_/ / /_/ /_/
/_/ /_/ /_/\__,_/_/ /_/_/|_| /_/\____/\__,_(_)
___ _ _ ___
/ _ \ _ _ ___ __| |_(_)___ _ _ __|__ \.
| (_) | || / -_|_-< _| / _ \ ' \(_-< /_/
\__\_\\_,_\___/__/\__|_\___/_||_/__/(_)
Contact me: alexander@kulbartsch.de
This presentation was made with:
#!bash
echo "$" $*
$*
Copyright (c) 2024 Alexander Kulbartsch
This work is licensed under: CC BY 4.0