Signing security.txt with GnuPG
The guide lists the commands for keys and signature in short form. This page explains them in detail for anyone who has rarely or never used GnuPG: what the commands do, how to keep the keys safe and what the error messages mean when you verify.
Installing GnuPG
GnuPG (gpg for short) is the widely used free implementation of OpenPGP. The commands on this page need version 2.2 or later.
- Linux: usually preinstalled, otherwise
sudo apt install gnupg(Debian, Ubuntu) orsudo dnf install gnupg2(Fedora, RHEL). - macOS:
brew install gnupgwith Homebrew, or GPG Suite. - Windows: Gpg4win from gpg4win.org. It includes the graphical tool Kleopatra and the command line program
gpg, which you run in PowerShell or the command prompt.
Then check the version:
gpg --version
The first line must show gpg (GnuPG) 2.2 or higher. If it reports version 1.4, the old release is installed alongside; call gpg2 instead of gpg.
In Windows PowerShell, redirecting with > writes files in UTF-16, which makes .asc files unusable. Always use --output file instead, as all commands on this page do.
Primary keys and subkeys
An OpenPGP key is really a bundle: one primary key and any number of subkeys. The primary key carries the identity (name and mail address) and determines the fingerprint. Subkeys are attached to it and take on individual tasks. The letters in square brackets show which:
| Flag | Meaning | Use for security.txt |
|---|---|---|
[S] | Sign | signing the file |
[C] | Certify | changing subkeys and expiry dates; always held by the primary key |
[E] | Encrypt | receiving encrypted reports at psirt@ and csirt@ |
This is how the PSIRT key from the guide looks in gpg --list-keys:
pub nistp256 2026-10-12 [SC] [expires: 2028-10-11]
E2FF9EC5A4DF56E538D887930993B9F74AFE756D
uid [ultimate] Example Ltd PSIRT <psirt@example.com>
sub nistp256 2026-10-12 [E] [expires: 2028-10-11]
pub is the primary key, sub the encryption subkey. The signing key for security.txt needs no subkey: its primary key can already sign and shows only [SC].
Why separate keys? The TR recommends a dedicated key used only to sign the security.txt (4.2.10 b). There is a practical reason: everyone who reads reports needs the decryption key, but only the one or two people who publish the file need the signing key. The fewer people and machines hold a key, the lower the risk that someone uses it to sign a forged security.txt.
Creating keys, command by command
gpg --quick-generate-key "Example Ltd security.txt <security@example.com>" nistp256 sign 2y
- Name and address in quotes: the identity. A role name such as "Example Ltd security.txt" works better than a person's name, because the key stays when people change.
nistp256: the algorithm, here ECDSA on the NIST P-256 curve. AlternativelybrainpoolP256r1. Do not leave it out: without it GnuPG picks Ed25519, which the TR does not allow (4.2.10 d).sign: signing only, no encryption.2y: valid for two years. The TR allows at most five (4.2.10 e). Shorter is better, because a lost key then becomes invalid sooner on its own; you can extend it at any time.
GnuPG asks for a passphrase. Choose a long one and store it in your team's password manager (see Storing keys safely). Export the public key like this:
gpg --armor --export --output security-txt-signing.asc security@example.com
--armor produces text instead of binary data, as the TR requires for the .asc file (4.5.3 c). This file is public and belongs on your website. Never export the secret key to your website.
Reading and publishing the fingerprint
gpg --fingerprint security@example.com
pub nistp256 2026-10-12 [SC] [expires: 2028-10-11]
E4C8 1F53 AF10 FCDC D272 C43A A918 CA58 5C9D 57C2
The fingerprint is the second line: 40 characters from 0 to 9 and A to F, shown in ten groups of four for readability. It is a checksum of the public key. Whoever downloads the key compares its fingerprint with the one you published. If they match, it is your key and not a substituted one.
- Always give it in full. The short key IDs made of the last 8 or 16 characters (here
5C9D57C2) can be forged deliberately and are not enough. - As text, not as an image, so it can be copied and compared.
- In a different place from the key itself. Under the TR, on your reporting page (4.5.3 d for the signing key, 4.3.2 b for the mailbox keys). Someone who takes over your website could swap both; a fingerprint also in your mail signature, or certified via Governikus (see the guide), makes that harder.
Storing keys safely and preparing revocation
The TR does not say how to store secret keys. Four things have proven useful:
- Keep the passphrase. Without it the key cannot be used, so a stolen computer or a copied backup alone is not enough. The security.txt is signed a few times a year at most; that does not justify a key without a passphrase. If you automate signing, keep the passphrase in the secret store of your build environment, not in a script.
- Sign on a workstation, not on the web server. Only the finished, signed file is uploaded. The secret key has no business on the web server.
- One offline backup. Otherwise the key is gone when the computer dies.
- Clear ownership. Record who on the team holds which key. Everyone who handles the mailbox needs the decryption key; only whoever publishes the file needs the signing key. When someone leaves, this belongs on the checklist.
Create the backup like this and keep it on a USB stick in a safe or in the password manager:
gpg --armor --export-secret-keys --output secret-security-txt.asc security@example.com
The file is protected by the passphrase, but it still does not belong in a ticket, an e-mail or a Git repository. Restore it with gpg --import secret-security-txt.asc.
The revocation certificate is at least as important. It lets you declare a key invalid even if you no longer have it. GnuPG creates one automatically when you generate the key, in the folder openpgp-revocs.d of your GnuPG directory (~/.gnupg on Linux and macOS, %APPDATA%\gnupg on Windows), named after the fingerprint. Copy this file along with the backup, but keep it separate from the secret key. To create your own certificate with a stated reason:
gpg --output revocation-security-txt.asc --gen-revoke security@example.com
Whoever holds the revocation certificate can make your key unusable, even without the passphrase. Keep it just as carefully.
Signing and verifying the signature
Sign with gpg --clearsign as described in the guide. Then verify the file the way reporters do: fetch the public key, compare the fingerprint, verify the signature.
curl -sO https://www.example.com/.well-known/security-txt-signing.asc
gpg --import security-txt-signing.asc
curl -s https://www.example.com/.well-known/security.txt | gpg --verify
A valid signature looks like this:
gpg: using ECDSA key E4C81F53AF10FCDCD272C43AA918CA585C9D57C2
gpg: Good signature from "Example Ltd security.txt <security@example.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: E4C8 1F53 AF10 FCDC D272 C43A A918 CA58 5C9D 57C2
The warning alarms many people but is normal: GnuPG only knows that the signature matches the key, not whether the key really belongs to you. Comparing the last line with the published fingerprint settles exactly that. On systems set to another language the messages appear translated.
| Message | Cause | What to do |
|---|---|---|
Good signature | The signature matches the file. | Compare the fingerprint, done. |
This key is not certified with a trusted signature | The key is imported but not certified by you. | Normal. Compare the fingerprint. |
BAD signature | The file was changed after signing, often by editing, a BOM or an encoding conversion during upload. | Sign again and upload in binary mode. |
Can't check signature: No public key | The public key is not in your keyring. | Import the .asc file, see above. |
Note: This key has expired! | The key's validity period is over. | Extend it, export the .asc again and upload it. |
This key has been revoked by its owner | The key was revoked. | Sign with a new key, see below. |
no valid OpenPGP data found | The file is not signed, or an HTML page came back instead of the file. | Look at the response with curl -s. |
signing failed: Unusable secret key | When signing: your own key has expired or been revoked. | Extend it or create a new key. |
signing failed: No secret key | The secret key is not on this machine, or --local-user names a different address. | Import the backup or check the address. |
signing failed: Inappropriate ioctl for device | GnuPG cannot ask for the passphrase, typically in an SSH session. | Run export GPG_TTY=$(tty) first. |
When a key expires, gets lost or falls into the wrong hands
- Expired: no drama. The validity can be extended even afterwards, and the fingerprint stays the same:
gpg --quick-set-expire <fingerprint> 2y, for subkeys additionally with'*'at the end. Then export the .asc again and upload it. Better before it expires, see Maintain. - Lost (computer broken, no backup): you need a new key. Upload the new .asc file, put the new fingerprint on the reporting page, sign the security.txt again. Declare the old key invalid with the revocation certificate so nobody trusts it any more. Without a certificate it stays valid until it expires, one more reason for short lifetimes.
- In the wrong hands (computer stolen, someone with access left on bad terms): revoke immediately, because whoever has the key can validly sign a security.txt with their own contact addresses. Then proceed as for a lost key.
To revoke, import the certificate and export the public key again. With the file created automatically in openpgp-revocs.d, first delete the colon in front of -----BEGIN PGP PUBLIC KEY BLOCK-----; it is there to prevent accidental import.
gpg --import revocation-security-txt.asc
gpg --armor --export --output security-txt-signing.asc <fingerprint>
Your usual address then serves the new key. Publish the revoked one as well, for example as security-txt-signing-old.asc or via keys.openpgp.org (see below). Anyone who imports it sees the warning "revoked by its owner" when verifying signatures made with that key.
Optional: keys.openpgp.org and WKD
For security.txt, the .asc file on your own website is enough. Two further ways make the key discoverable for mail programs:
- keys.openpgp.org: a key directory that publishes mail addresses only after confirmation by e-mail. Upload through the website or with
gpg --export psirt@example.com | curl -T - https://keys.openpgp.org; the response contains the confirmation link. Most useful for the mailbox keys that reporters encrypt to. - Web Key Directory (WKD): the key sits at a fixed address on your own domain, and programs such as Thunderbird or
gpg --locate-keys psirt@example.comfind it from the mail address alone. The GnuPG wiki describes the setup.
Sources
- BSI TR-03183-3 Vulnerability Reports and Notifications, version 1.0.0, sections 4.2.10, 4.3.1, 4.3.2 and 4.5.3, 20 August 2025.
- RFC 9116: A File Format to Aid in Security Vulnerability Disclosure, section 2.3, IETF, April 2022.
- RFC 9580: OpenPGP, IETF, July 2024.
- The GNU Privacy Guard Manual, gnupg.org.
- Gpg4win, GnuPG for Windows.
- keys.openpgp.org: Usage.
- GnuPG wiki: Web Key Directory.