PGP / GPG

The "Pretty Good Privacy" asymmetric encryption scheme used in email and sending encrypted or signed messages

GNU Privacy Guard (GPG)

In Linux, a common command-line utility to perform PGP actions is the gpg program. See a small reference here:

Signing and Verifying

Putting a signature under a message can prove that a certain private key owner has written the message. Anyone can verify it with your public key, but only you can create it with your private key. This is often found in the following format:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Hello, world!
-----BEGIN PGP SIGNATURE-----

iQGzBAEBCgAdFiEEQctSPuIHmG7eGC/HYLG3XetyYecFAmSRkqsACgkQYLG3Xety
Yec2fwwAuwOmfZJlttuFxOlLP6RPD1yMD8XDDfRUxg96NvDvzYNnntZhU6Jeevvf
0rdogx5NsRZMPFYb9ysXlO/RDk9ZS4w8vNvZtiqDBQjjOKLblsU/sjgC7SQKLPnD
TZiQYbqBDH3DQIGgkbmU3fiOrps8Atu0dJR7o0y6Kf9/HqTBN2nCvA0jnhhXTA00
R5f6h+KXtBcMw29aHeLSO3c27+8LelTLKSyXEuUNev8ssbt5T7JdorckJzq3cwUG
zkXAtEMOzvOYd9SeftXeThTmMoiT1I8ZaBgJ60MnxVaaJUZAahQSx0wHtIV7NZCz
auNfR14XvAyUSyPbKLDI98+qygn/ljgI0yU5nRj5fvRptuJBDUWywKJF+Zj7iGRL
opPyb6BS5kSOba1PgubgICoKGMyWWWVi33OZjoFqNVq4W4i/tNL3+oQbVBpfqXYo
fRAx5C9tAv6moRRnyyE2gZolCs7grdZVqpbTMOQzMJMjwjOfG+Rmsfc81zi1v3Z3
HzEdXwrj
=RtST
-----END PGP SIGNATURE-----

It is very recognizable and easy to understand. The first part is the readable plaintext message, and the second part is the signature of that text above, signed (encrypted) with the sender's private key. If anyone wants to verify the validity, they would need to take your public key, and decrypt the signature to be left with an exact match of the text above.

In practice, you can sign a message like this:

message.txt
Hello, world!
$ gpg --clearsign message.txt
$ cat message.txt.asc
-----BEGIN PGP SIGNED MESSAGE-----
...

If you don't have a keypair yet, you can generate one with:

$ gpg --gen-key

or import an existing one with:

$ gpg --import [keyfile]

To then verify it, make sure to first have the public key from the sender imported:

$ gpg --import sender.pub

Afterward, you can verify any messages they send came from that key:

$ gpg --verify msg.txt.asc
gpg: Signature made Tue Jun 20 13:51:07 2023 CEST
gpg:                using RSA key 41CB523EE207986EDE182FC760B1B75DEB7261E7
gpg: Good signature from "Jorian" [ultimate]

Encrypting and Decrypting

If a message is intended for only a specific person to be able to read it, you can encrypt it with their public key so only they can decrypt it with their private key. An encrypted PGP message looks like this:

-----BEGIN PGP MESSAGE-----

hQGMA+DFCnh6q6yZAQwAvLRxIVGgqmpLM8OyE7YbW67djAKcq8RtTEGfRzj7dRf/
FRIljcJiqQXYrRlzgQR9qd7lpnfFb5goPVYzaCHA8HRtQo7XfZnRZ7lt821SPxmS
H4hrgf33euSt3fACvGhIIR5kmUz2ExL7n3/tMTwld0y5dgwtaGsl3ikD+TWZKM3f
pVKH+SAj37VermnBv2m4eAVMFWuykYPp82HIruNVRXlbCbFuC6QRzRHzvyhIwf+t
gFyJhiwwq74twHBnpKDgMN+z2uEyMGI9b2FYnfMs6MGFlOWgqALXryUtuVM2XnqM
1KBLReW5AihlvJn984t2joYu5ASeuTaN/Bf5gmVtcoHOVVQCiu1xS36bJmvdz+Q/
Q/EJSsfVn19e6o2FvXWaGA7w7g9uNoZj0oBM1LzQO9gtYsgQel7TfEbrMmT8lWZ4
RUi1jHvpph6+tUGrK7e/nK9z1F1lYuqEn4sZcfv6Y6hFFibI0vunfU3eKulJKVkE
QqCQug2XnKAX4t91uy1f0lABFkBI6X+j7182AjdleTAXWI/XNQgSNk+SN35mX6KO
F61+m5OjcsPer6cq7mylnWND3Ix+PRIynFbVNsHpRN+JOI79mC7sirVOZAKxXU9n
Xg==
=/OLC
-----END PGP MESSAGE-----

It is generated by encrypting the message with the recipient's public key, which means you first need to have imported their key like shown before. Then use the following options:

  • -e: to select encryption

  • -r [name or ID]: to select the recipient public key

  • -a: Add "armor" to the resulting file, meaning it is simply an ASCII format in Base64 and BEGIN/END instead of the regular .gpg binary format

For example:

$ gpg -a -e -r Jorian message.txt  # Using saved name
$ gpg -a -e -r 41CB523EE207986EDE182FC760B1B75DEB7261E7 message.txt  # Using ID

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

hQGMA+DFCnh6q6yZAQv/RNVXrpb4awnHSOQUkr0dn32NvgmwwXXXCKChAr17SpAt
Tu7ppTjSBIfSYDSGvLNHYDQdKBaqHxR+YGfiUKKgJahqTX8n17HgG4FLESWyWJJq
adQpE3sr9d+PpexZ/L1i+dtTk5XYkxXtbXXg4MvgLj/YWKNhPoTEEzlSxYIW1sjB
/7yyZuLcG0Y6DEt6/apnpF2HWh+ygFM/Xhx4RplM0HwpE3fdYZQxNVYcoMgEsm51
PQfaHml5lDAviJNdv7tMpS90wE6jWJMPj1GojOx0oQK4nM+k62Bppj/OVSo5RMkl
N6i1SYngyKcTVN8EF7g60lp3Z6tKd/a781ecsdXAJyNXP0/ccfgkzaMEHuOkfY66
9cig0RbglG11uXBygeU+V5mriKV2lw+I8rW5uIE9ZZgfW8e5fuOkBDfZ44uEYKrO
ZPBCWFXSwKrbKSUw+KOJiiJjk+7An3/0a/rrM2RcQzx9yfIWHeVl5Psi697LO3ps
PsQmjsIDJg/77UzFWUT50lQBydrV62gwxaC2j2i6X2ctffGMUReovang9IDfmQP+
2kDnsSiT9HQtLRMex0I9S+ZIK4CehgjdCWqCW+74HO1Tz/RRhdjoONSBkU8506kK
3d/nhX0=
=LSK8
-----END PGP MESSAGE-----

When the recipient wants to read this message, they have to decrypt it with their private key. Simply using the -d option will automatically find the correct key used and owned by you:

$ gpg -d msg.txt.asc
gpg: encrypted with 3072-bit RSA key, ID E0C50A787AABAC99, created 2023-06-18
      "Jorian"
Hello, world!

You can also encrypt and sign at the same time, using both options ( -s and -e) together

Python

Using the PGPy module you can easily automate any PGP tasks like generating keys, signing/verifying messages and encrypting/decrypting messages.

This can also be useful to generate low-level keys where you can easily control exactly what data is in the name, comment or email. If an application parses these fields in some what it may be worth trying to inject unexpected data in here like any other field on a website.

Here is a simple example of generating a key and signing a message:

import pgpy

from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm

key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)
uid = pgpy.PGPUID.new("Jorian", comment="This is a comment", email="contact@jorianwoltjer.com")

key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
            hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224],
            ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
            compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed])

print(str(key.pubkey))  # -----BEGIN PGP PUBLIC KEY BLOCK----- ...

text = "Hello, world!"
signed_text = key.sign(text)

print(str(signed_text))  # -----BEGIN PGP SIGNATURE----- ...

Last updated