privacy-guides/how-to-encrypt-sign-and-decrypt-messages-using-gnupg-on-macos

How to encrypt, sign and decrypt messages using GnuPG on macOS

How to encrypt, sign and decrypt messages using GnuPG on macOS

Heads-up: this is a “getting started” guide. Learn how to harden GnuPG here.

Requirements

  • Computer running macOS Big Sur or Monterey

Caveats

  • When copy/pasting commands that start with $, strip out $ as this character is not part of the command

Setup guide

Step 1: install Homebrew

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

$ uname -m | grep arm64 && echo 'export PATH=$PATH:/opt/homebrew/bin' >> ~/.zshrc && source ~/.zshrc

Step 2: disable Homebrew analytics

brew analytics off

Step 3: install GnuPG

brew install gnupg

Step 4: generate PGP key pair

$ gpg --full-generate-key
gpg (GnuPG) 2.3.4; Copyright (C) 2021 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: directory '/Users/sunknudsen/.gnupg' created
gpg: keybox '/Users/sunknudsen/.gnupg/pubring.kbx' created
Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 9
Please select which elliptic curve you want:
   (1) Curve 25519 *default*
   (4) NIST P-384
   (6) Brainpool P-256
Your selection? 1
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) 0
Key does not expire at all
Is this correct? (y/N) y

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

Real name: John Doe
Email address: john@example.net
Comment:
You selected this USER-ID:
    "John Doe <john@example.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, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /Users/sunknudsen/.gnupg/trustdb.gpg: trustdb created
gpg: directory '/Users/sunknudsen/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/Users/sunknudsen/.gnupg/openpgp-revocs.d/1ADDDBA409558A8E80A4DF381535F6A0BB6BD636.rev'
public and secret key created and signed.

pub   ed25519 2021-12-29 [SC]
      1ADDDBA409558A8E80A4DF381535F6A0BB6BD636
uid                      John Doe <john@example.net>
sub   cv25519 2021-12-29 [E]

Step 5: back up ~/.gnupg folder (learn how here)

Heads-up: files stored in ~/.gnupg include private keys which, if lost, results in loosing ones cryptographic identity (safeguard backup mindfully).

👍


Usage guide

Export PGP public key

Heads-up: replace john@example.net and johndoe with email and name from step 4.

gpg --armor --export john@example.net > ~/johndoe.asc

Import Suns PGP public key using key server…

$ gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x8C9CA674C47CA060
gpg: key 8C9CA674C47CA060: public key "Sun Knudsen <hello@sunknudsen.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

imported: 1

👍

…or using PGP public key URL

Heads-up: verify web of trust to list missing keys.

$ curl https://sunknudsen.com/sunknudsen.asc | gpg --import
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2070  100  2070    0     0   1881      0  0:00:01  0:00:01 --:--:--  1899
gpg: key 8C9CA674C47CA060: 1 signature not checked due to a missing key
gpg: key 8C9CA674C47CA060: public key "Sun Knudsen <hello@sunknudsen.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u

imported: 1

👍

Verify Suns PGP public key using fingerprint

$ gpg --fingerprint hello@sunknudsen.com
pub   ed25519 2021-12-28 [C]
      E786 274B C92B 47C2 3C1C  F44B 8C9C A674 C47C A060
uid           [ unknown] Sun Knudsen <hello@sunknudsen.com>
sub   ed25519 2021-12-28 [S] [expires: 2022-12-28]
sub   cv25519 2021-12-28 [E] [expires: 2022-12-28]
sub   ed25519 2021-12-28 [A] [expires: 2022-12-28]

Open https://sunknudsen.com/, https://github.com/sunknudsen/pgp-public-key and https://www.youtube.com/sunknudsen/about and make sure above fingerprint (“E786 274B C92B 47C2 3C1C  F44B 8C9C A674 C47C A060”) matches published fingerprints.

👍

Verify Suns PGP public key using web of trust

Heads-up: 0xC1323A377DE14C8B is Suns legacy public key.

$ gpg --list-signatures hello@sunknudsen.com
pub   ed25519 2021-12-28 [C]
      E786274BC92B47C23C1CF44B8C9CA674C47CA060
uid           [ unknown] Sun Knudsen <hello@sunknudsen.com>
sig 3        8C9CA674C47CA060 2021-12-28  Sun Knudsen <hello@sunknudsen.com>
sig 3        C1323A377DE14C8B 2021-12-28  [User ID not found]
sub   ed25519 2021-12-28 [S] [expires: 2022-12-28]
sig          8C9CA674C47CA060 2021-12-28  Sun Knudsen <hello@sunknudsen.com>
sub   cv25519 2021-12-28 [E] [expires: 2022-12-28]
sig          8C9CA674C47CA060 2021-12-28  Sun Knudsen <hello@sunknudsen.com>
sub   ed25519 2021-12-28 [A] [expires: 2022-12-28]
sig          8C9CA674C47CA060 2021-12-28  Sun Knudsen <hello@sunknudsen.com>

Paste, encrypt and sign message (enter line break and ctrl+d to quit edit mode)

$ gpg --encrypt --sign --armor --output ~/Desktop/encrypted.asc --recipient john@example.net --recipient hello@sunknudsen.com
gpg: F56809CDE05DB014: There is no assurance this key belongs to the named user

sub  cv25519/F56809CDE05DB014 2021-12-28 Sun Knudsen <hello@sunknudsen.com>
 Primary key fingerprint: E786 274B C92B 47C2 3C1C  F44B 8C9C A674 C47C A060
      Subkey fingerprint: F375 2162 E3A4 3F6E 2762  D50B F568 09CD E05D B014

It is NOT certain that the key belongs to the person named
in the user ID.  If you *really* know what you are doing,
you may answer the next question with yes.

Use this key anyway? (y/N) y
This is a test!

Decrypt message to stdout and decode quoted-printable characters

$ gpg --decrypt ~/Desktop/encrypted.asc | perl -MMIME::QuotedPrint -0777 -nle 'print decode_qp($_)'
gpg: encrypted with cv25519 key, ID F56809CDE05DB014, created 2021-12-28
      "Sun Knudsen <hello@sunknudsen.com>"
gpg: encrypted with cv25519 key, ID F9220AB453F9B6E3, created 2021-12-29
      "John Doe <john@example.net>"
gpg: Signature made Wed 29 Dec 08:24:05 2021 EST
gpg:                using EDDSA key 1ADDDBA409558A8E80A4DF381535F6A0BB6BD636
gpg: Good signature from "John Doe <john@example.net>" [ultimate]
This is a test!

Good signature

👍

Clear passphrase from GnuPG cache

gpg-connect-agent reloadagent /bye

Heads-up: when sending encrypted messages, dont forget to include your public key.