GPG Key Management

Primary Key Strategy

Primary key should be used solely to sign subkeys grant and revoke access.

daily subkeys
create separate subkeys for Signing, Encryption and Authentication.
cold storage
export the primary key and save it securely offline; primary key should be removed from daily workstation.

GPG Usage

Generate a primary key

gpg --quick-generate-key "Carlos Augusto Lima Borges <gpg@carlosborges.dev>" ed25519 cert 2y

Generate subkeys

gpg --quick-add-key $KEY_ID ed25519 sign 1y
gpg --quick-add-key $KEY_ID cv25519 encr 1y
gpg --quick-add-key $KEY_ID ed25519 auth 1y

List public keys

gpg --list-keys --keyid-format LONG

List secret keys

gpg --list-secret-keys --keyid-format LONG

Backup keys

Exports live in a keys/ directory, the same layout the S server uses.

mkdir -p keys
gpg --export --armor $KEY_ID > keys/public-key.asc
gpg --export-secret-subkeys --armor $KEY_ID > keys/keys.asc
gpg --export-secret-keys --armor $KEY_ID > keys/master-key.asc
gpg --gen-revoke $KEY_ID > keys/revoke-certificate.asc

gpg --symmetric --cipher-algo AES256 keys/keys.asc
gpg --symmetric --cipher-algo AES256 keys/master-key.asc
gpg --symmetric --cipher-algo AES256 keys/revoke-certificate.asc
rm keys/keys.asc keys/master-key.asc keys/revoke-certificate.asc
keys/public-key.asc
public key; not secret, keep it unencrypted.
keys/keys.asc.gpg
secret subkeys only; restore this on daily workstations.
keys/master-key.asc.gpg
full secret key (primary + subkeys); cold storage only.
keys/revoke-certificate.asc.gpg
encrypted because anyone holding it can revoke the key.

Restore keys

Daily workstation (subkeys only)

GPG_TTY must be set (put it in ~/.zshrc) or pinentry fails inside pipes with "Inappropriate ioctl for device".

export GPG_TTY=$(tty)
gpg -d keys/keys.asc.gpg | gpg --import

gpg --edit-key $KEY_ID
gpg> trust # you should trust ultimately in this key
gpg> quit
gpg -K --keyid-format LONG # expect: sec# (primary offline), ssb (subkeys present)

Full restore (includes primary key)

export GPG_TTY=$(tty)
gpg -d keys/master-key.asc.gpg | gpg --import
gpg -K --keyid-format LONG # expect: sec (primary present)

Extend expiration

export GNUPGHOME=$(mktemp -d)
gpg -d keys/master-key.asc.gpg | gpg --import # secret export also carries the public parts
gpg --quick-set-expire $KEY_ID 2y # extend master key
gpg --quick-set-expire $KEY_ID 1y "*" # extend all subkeys
gpg --export --armor $KEY_ID > keys/public-key.asc
rm -rf $GNUPGHOME
unset GNUPGHOME
gpg --import keys/public-key.asc
gpg --keyserver hkps://keys.openpgp.org --send-keys $KEY_ID

Revoke a primary key

gpg -d keys/revoke-certificate.asc.gpg | gpg --import # or regenerate: gpg --gen-revoke <KEY_ID>
gpg --keyserver hkps://keys.openpgp.org --send-keys $KEY_ID
gpg --delete-secret-keys $KEY_ID
gpg --delete-keys $KEY_ID

Revoke a subkey

gpg --edit-key gpg@carlosborges.dev
gpg> list
gpg> key <N>
gpg> revkey
gpg> save
gpg --keyserver hkps://keys.openpgp.org --send-keys $KEY_ID

Output symbols

sec
secret master key present on local system
sec#
secret master key is offline / stripped
ssb
secret subkey present
pub
public key
sub
public subkey

Keyserver

Use hkps://keys.openpgp.org to publish and search public keys.

Send your public key

gpg --keyserver hkps://keys.openpgp.org --send-keys $KEY_ID

Search public key

gpg --keyserver hkps://keys.openpgp.org --search-keys gpg@carlosborges.dev

Fetch a key

gpg --keyserver hkps://keys.openpgp.org --recv-keys $KEY_ID

Refresh keyring

gpg --refresh-keys

Integration

Import in git server

gpg --armor --export $KEY_ID