- Add AGE.md with policies
- Add age-recipient.txt for `age -R`
- Add ceremonies/2026-09-13_age_key.md as the signed record
of which recipient is valid from which date
- List the recipient in KEYS.md
- Describe AGE.md and age-recipient.txt in README.md
- Extend the scripts:
+ sign.sh signs AGE.md by default
+ verify.sh verifies AGE.md by default
and cross-checks age-recipient.txt,
the only unsigned artifact,
against the recipient in the signed KEYS.md and AGE.md
132 lines
3.9 KiB
Markdown
132 lines
3.9 KiB
Markdown
# Keys
|
|
|
|
Public record of the cryptographic keys of Alexander Hess
|
|
(i.e., "me" in this repository):
|
|
What they are, how they are handled, and how to verify them.
|
|
|
|
There are two audiences for this repository:
|
|
|
|
- **I** want a how-to:
|
|
[`OPENPGP.md`](OPENPGP.md) and [`AGE.md`](AGE.md) hold the policies and ceremonies,
|
|
and the [`scripts/`](scripts/) run them.
|
|
- **Readers** want to confirm my keys and my identity:
|
|
Everything needed for that is in the [Verify](#verify) section below.
|
|
|
|
|
|
|
|
## Verify
|
|
|
|
The fingerprint of my [current PGP key](public-key.asc) is in [`KEYS.md`](KEYS.md),
|
|
which is signed.
|
|
|
|
Compare the fingerprint with at least one source outside this repository
|
|
to confirm my identity:
|
|
|
|
- [WKD](https://wiki.gnupg.org/WKD),
|
|
for example, via `gpg --locate-keys ah@alexander-hess.com`
|
|
- The keyservers listed in [`OPENPGP.md`](OPENPGP.md),
|
|
for example, `gpg --keyserver hkps://keys.openpgp.org --search-keys alexander@webartifex.de`
|
|
- [`KEYS.md`](KEYS.md) obtained from my websites:
|
|
- [alexander-hess.com](https://www.alexander-hess.com/KEYS.md)
|
|
- [webartifex.de](https://www.webartifex.de/KEYS.md)
|
|
|
|
|
|
Then:
|
|
|
|
```bash
|
|
gpg --import public-key.asc
|
|
|
|
gpg --verify KEYS.md.asc KEYS.md
|
|
gpg --verify OPENPGP.md.asc OPENPGP.md
|
|
gpg --verify AGE.md.asc AGE.md
|
|
```
|
|
|
|
Each `gpg --verify` will print two things:
|
|
|
|
1. `Good signature from "Alexander Hess <...>"`:
|
|
The file is intact and was signed by this key.
|
|
2. `WARNING: This key is not certified with a trusted signature!`:
|
|
Because `gpg` likely has no record that this key is mine.
|
|
|
|
So, a good signature plus a matching `Primary key fingerprint` line is the full result.
|
|
|
|
To silence the warning on your machine,
|
|
tell `gpg` about your check:
|
|
|
|
```bash
|
|
gpg --lsign-key <FPR> # after comparing the fingerprint yourself
|
|
```
|
|
|
|
|
|
If you trusted one of the two keys I retired in 2026,
|
|
they certified the current one,
|
|
and the supersession is recorded in [`ceremonies/`](ceremonies/):
|
|
|
|
```bash
|
|
gpg --import history/344EA5AB10D868E0-public.asc history/6D11C1E91D3B0265-public.asc
|
|
gpg --check-signatures 8B1140ADB5185334E9FA513B75681C8167312680
|
|
gpg --verify ceremonies/2026-07-08_supersession.md.asc ceremonies/2026-07-08_supersession.md
|
|
```
|
|
|
|
|
|
|
|
## Contents
|
|
|
|
The files in this repository are described below.
|
|
|
|
|
|
### OpenPGP
|
|
|
|
How I implement [OpenPGP](https://www.openpgp.org/):
|
|
|
|
- [`KEYS.md`](KEYS.md):
|
|
Current keys, signed
|
|
- [`KEYS.md.asc`](KEYS.md.asc):
|
|
Detached signature, renewed at every ceremony
|
|
- [`OPENPGP.md`](OPENPGP.md):
|
|
OpenPGP policies, ceremonies, and history
|
|
- [`OPENPGP.md.asc`](OPENPGP.md.asc):
|
|
Detached signature, renewed at every ceremony
|
|
- [`public-key.asc`](public-key.asc):
|
|
Armored export of the current OpenPGP key
|
|
- [`snapshots/`](snapshots/):
|
|
Yearly exports of the current OpenPGP key
|
|
- [`ceremonies/`](ceremonies/):
|
|
Signed records of exceptional events
|
|
- [`history/`](history/):
|
|
Armored public keys of all previous OpenPGP keys
|
|
|
|
|
|
### `age`
|
|
|
|
Besides OpenPGP,
|
|
I also use the modern [`age`](https://github.com/FiloSottile/age) encryption:
|
|
|
|
- [`AGE.md`](AGE.md):
|
|
`age` policies
|
|
- [`AGE.md.asc`](AGE.md.asc):
|
|
Detached signature, renewed at every ceremony
|
|
- [`age-recipient.txt`](age-recipient.txt):
|
|
`age` public key for `age -R`
|
|
- [`ceremonies/`](ceremonies/):
|
|
Signed record of when a recipient became valid or rotated
|
|
because `age` keys carry no metadata of their own
|
|
|
|
|
|
### Scripts
|
|
|
|
`sh` scripts for my own use:
|
|
|
|
- [`bootstrap.sh`](scripts/bootstrap.sh):
|
|
Imports and trusts all keys on a new machine,
|
|
then verifies all files
|
|
- [`sign.sh`](scripts/sign.sh):
|
|
Renews the detached signatures
|
|
- [`verify.sh`](scripts/verify.sh):
|
|
Verifies all signed files against the pinned signing subkey,
|
|
warns when the key is about to expire, and
|
|
cross-checks `age-recipient.txt`
|
|
- [`snapshot.sh`](scripts/snapshot.sh):
|
|
Exports the current key to `snapshots/` and `public-key.asc`
|
|
- [`publish.sh`](scripts/publish.sh):
|
|
Pushes the current key to the keyservers
|