gpg

Getting Started

brew install gpg

GPG vs PGP

GNU Privacy Guard (GPG) is open source software which implements OpenPGP standard RFC4880, which specifies a protocol for how to encrypt and decrypt files. Pretty Good Privacy (PGP) is proprietary software written by Symantec, and is another implementation of OpenPGP. Both are compatible with each other, and the reason why is where it gets confusing.

In 1997, Symantec released OpenPGP, an open source set of standards for encryption software. Even though PGP is not open source, OpenPGP is. The GNU Privacy Guard GPG implements the set of standards outlined in OpenPGP.

For this reason, it is effectively synonymous to say “GPG key” and “PGP key” since they’re both “OpenPGP keys”. With this in mind, technically a “key-pair” is what refers to a public and private key, in the industry it’s common for “key” to mean “key-pair”

At this point, GPG has been around a long time. It’s currently on version 2, which is not compatible with version 1. However, version 1 is only required if you’re trying to decrypt PGP keys from 20+ years ago.

Anatomy of a GPG Key

  • User ID (UID): The name and email corresponding with a key.
  • Key ID: A hexadecimal string that identifies a key.
  • Key Certificate: An assertion that a certain key belongs to a certain entity.
  • Key-Pair: A private key and it’s corresponding public key.
  • Private Key: A key that can be used to decrypt any messages previously encrypted with the corresponding public key.
  • Public Key: A key that can be used to encrypt messages that can only be decrypted with the corresponding private key.

Generating a new key

gpg --quick-generate-key
gpg --generate-key
gpg --full-generate-key

Listing all keys in the keyring

gpg -K

Listing the public keys in the keyring

gpg -k

Importing keys

  • Import a key file directly
gpg --import example.key
  • Download someone’s public GPG key from GitHub
user="tommytrojan" # their GitHub username
curl https://api.github.com/users/${user}/gpg_keys | jp '[0].raw_key' | gpg --import

Exporting keys

  • Exporting the public key specified by its email address to STDOUT

    gpg --armor --export example@pm.me > example.asc
    
  • Exporting the public key specified by its comment “GitHub” to /dev/stdout

    gpg -a --export GitHub
    
  • Exporting the public key to a file as binary data

    gpg --output ./example.key --export example@pm.me
    
  • Exporting the public key to a file as armored ASCII

    gpg -o example.asc -a --export example@pm.me
    
  • Export the private key as binary file

    gpg --export-secret-keys example@pm.me > ./example.key
    
  • Export the private key as armored ASCII

    gpg -a --export-secret-keys example@pm.me > ./example.asc
    

Tip: Use the -a flag when exporting keys. This flag will encrypt the file into a readable cipher of jumbled ASCII text, instead of a binary file filed with 0s and 1s.

  • By default, exporting a key will be directed to the standard output. It is common convention to give a binary key file the .key extension and ASCII armored key files the .asc extension.

Changing a key’s passphrase

Sometimes you want to change the passphrase that you’re prompted for when using a particular secret key. Maybe you’re using the same password for the key as you are for your computer (and if so, shame on you, who would do such a thing? Definitely not me, that’s for sure).

  • Change the password of a private key

    gpg --passwd tommy@pm.me
    

Encrypting a file

Use the -r flag to specify the recipient of the file. You can use this flag multiple times to specify more than one recipient. If you specify multiple recipients, any of the corresponding secret keys will be able to decrypt the file.

  • Encrypt a file for a single recipient

    gpg -r John -se file.txt
    
  • Encrypt a file for multiple recipients

    gpg -r John -r Cam -se file.txt
    

Tip: It might be a good idea to specify your own key ID so that you can decrypt the file later on as well.

Decrypting a file

  • Decrypt the file example.txt.gpg

    gpg -d example.txt.gpg
    

    Tip: If you have multiple private keys, you don’t need to specify which one to decrypt a file. gpg can figure out which key to use.

Signing a message

It’s important to sign a file with your key when you’re encrypting it for your recipient. This signature tells gpg to provide a proof of origin, specifying where the file came from.

Signatures serve a useful purpose: Since signature is unique, if a file contains your signature, it must be from you. This helps add trust to someone when they’re decrypting a file.

Imagine there is a hacker, who gains access to your email. He knows your friend’s public key, so he sends a message to your friend with malicious intent, claiming to be you.

Your friend, who sees that the message came from your email address, decrypts the file, and is tricked into believing that the file was sent from you. Since the file lacks a signature, he has no way of knowing who encrypted it using his public key.

Your private key is the only one that can provide this unique signature. If a file is signed with a private key, you’re certifying that it came from you. If, later down the line, the file you encrypted was altered by a hacker in a “man in the middle” attack, your original signature and the current state of the file would no longer match up. If someone wanted to check and see that the file was from you, it would no longer appear to be so.

Specifying which key to sign with

If you have multiple private keys on your keyring, you may want to encrypt a document using a particular key. Use the -u flag to tell gpg which key to use for signing the encrypted file.

  • Encrypt the file file.txt for recipient friend@pm.me, and sign it using the private key of example@pm.me

    gpg -u example@pm.me -r friend@pm.me -se file.txt
    

Trusting a key

If you import somebody’s public key, that doesn’t mean you trust them, it just tells gpg about the key. If your friend gives you his key, you should tell gpg that you trust it by adding your key signature to the public key.

Note: The vocab thrown around on the internet can be a little confusing so it’s important to clarify some terms here. Declaring that you trust a key is known as “certification”, “certifying a key”, “key signing”, or “signing a key”.

# import a friend's key
gpg --import friend.key
# list keyring's public key info (to find the associated key ID)
gpg -k
# sign a friend's key
gpg --sign-key friend@pm.me

This isn’t inherently useful, but it becomes useful if you send that public key back to them. It would mean that if your friend sends a file to your boss, who also trusts your key, then he can trust your friend’s signature as well.

Why can he trust your friend? Because you signed off on it with your key, thereby telling gpg that you believe your friend’s key is trustworthy. If your boss trusts you, and you trust your friend, then your boss trusts your friend too.

Outputting to a specific filename

Use the -o flag to specify output to a particular file, instead of the default output.

By default, encrypting example.txt will create example.txt.gpg but this can be altered by specifying the output file with the -o flag.

gpg -o boring_paperwork.gpg -se illegal_activities.txt

Generating a key-pair

$ gpg --full-generate-key --no-emit-version

Listing Keys

# list fingerprints for keys
$ gpg --fingerprint

# list all public keys
$ gpg -k

# list all secret keys
$ gpg -K

Fingerprints & Key IDs

A public key fingerprint is a short sequence of bytes used to identify a longer public key. Fingerprints are created by applying a cryptographic hash function to a public key. Since fingerprints are shorter than the keys they refer to, they can be used to simplify certain key management tasks.

Some operations on keys require you to provide a fingerprint or key ID. The keys are prefixed with the hex-value indicator, “0x”

  • a short key ID is the last 8 chars, e.g.: 0xA4FF2279
  • a long key ID is the last 16 chars, e.g.: 0x4E1F799AA4FF2279

Deleting Keys

# Delete a friend's public key
gpg --delete-keys friend@noreply.github.com
# Delete your secret & public key-pair
gpg --delete-secret-and-public-keys me@noreply.github.com

ssh with gpg key

  • Add this to your shell startup file.

    # Enable support for GPG encryption of echo command
    export GPG_TTY=$(tty)
    
    # Launch the GPG agent, unless one is already running
    gpg-agent --daemon &>/dev/null
    
    # Identifies the path of a UNIX-domain socket
    # Used to communicate with the SSH agent
    export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
    
  • Add an authentication subkey to your keyring

    gpg --expert --edit-key <keyID>
    addkey
    # press 8 <Enter>
    # press S <Enter>
    # press E <Enter>
    # press A <Enter>
    # press Q <Enter>
    # press 4096 <Enter>
    # press 0 <Enter>
    
  • Copy your authentication subkey’s keygrip to ~/.gnupg/sshcontrol

    gpg -k --with-keygrip
    # 4EC68884AECA658DD0523C66E6C70FD9A1B61790
    
    • The authentication subkey is the one whose header line resembles the pattern rsa4096/0x85B21AADAE7C8359 2019-07-10 [A]
  • Add this line to the file ~/.gnupg/gpg-agent.conf

    enable-ssh-support
    
  • Check if SSH can detect this key

    # View the MD5 fingerprint of the SSH key
    ssh-add -l -E md5
    
  • Check if these two commands produce matching output

    ssh-add -L
    gpg --export-ssh-key <keyID>
    
  • If you ever need to kill the GPG agent, you can do so by running this command

    gpgconf --kill gpg-agent
    
  • Checking the message digest of a key file

    gpg --print-mds key.asc
    gpg --print-md md5 key.asc
    gpg --print-md sha256 key.asc
    gpg --print-md sha1 key.asc
    

Ways to Specify User ID

user ID can be specified many ways

  • By email (partial or full) e.g. usc.edu e.g. @ttrojan e.g. <ttrojan@usc.edu>

  • By name (partial or full) e.g. Tommy e.g. Tommy Trojan

  • By short key ID (optionally prefix the key-id with 0x (8 hex digits long) e.g. 2B2F8910 e.g. 022B2F8910

  • By long key ID (optionally prefix the key-id with 0x (16 hex digits long) e.g. 2F6F37E42B2F8910 e.g. 0x2F6F37E42B2F8910

  • By fingerprint (optionally prefix with 0x) e.g. 438FB6FEFCA0744F279E42192F6F37E42B2F8910 e.g. 0x438FB6FEFCA0744F279E42192F6F37E42B2F8910

  • By keygrip (must be prepended with an ampersand e.g. &D75F22C3F86E355877348498CDC92BD21010A480

  • By exact match of an OpenPGP UserID e.g. =Tommy Trojan <ttrojan@usc.edu>

Hash Algorithms

  • MD5 stands for Merkle–Damgård 5, but it’s easier to pretend it stands for “Message Digest 5”

  • MD5’s digest length is 128 bits
  • SHA1’s digest length is 160 bits
  • SHA256’s digest length is 256 bits
  • SHA512’s digest length is 512 bits

Expired Keys

  • Remove the expiration date of a key (even if it already happened)
# Disable expiration for a key, even if it's already expired
gpg --quick-set-expire <key fingerprint> 0
  • Remove all expired keys from your keyring
# TODO fix
gpg -k --with-colons \
	| grep '^...:e' \
	| awk -F ':' '{ print $5 }' \
	| awk -v ORS=' ' 'NF' \
	| read -A array; gpg --delete-secret-and-public-keys ${array}

GPG and SSH

  • Launching a GPG agent that can support SSH compatibility
# Launch the GPG agent if one isn't already running
# if there is an existing one running already, then ignore the message
# that the GPG agent reports
gpg-agent --enable-ssh-support --daemon &> /dev/null

  • Transfering control of the SSH socket from the SSH agent to the GPG agent
# Allow GPG's socket to manage the `ssh` authentication process
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
  • Export GPG key as an SSH public key

    # Using GPG
    gpg --export-ssh-key ttrojan@usc.edu > ~/.ssh/id_rsa.pub