Sign a release artefact¶
Sign a checksum manifest with an RSA key held in AWS KMS:
sigillum sign \
--backend aws-kms \
--kms-region eu-west-2 \
--key-id alias/release-signing-v1 \
--public-key release.asc \
--output checksums.txt.sig \
checksums.txt
That writes checksums.txt.sig, an ASCII-armored OpenPGP detached signature
that gpg --verify accepts. The private key never leaves the backend: signing
is one round-trip to the KMS (or a read of the local PEM) through the
crypto.Signer interface.
sigillum ships two backends: aws-kms (production, the key stays in KMS) and
local (development / air-gapped, an on-disk PEM key). This guide covers
both, plus verification.
This guide covers the OpenPGP lane only, which is RSA-only. For the minisign lane that cargo-binstall and rtb-update verify, see Sign an artefact for Rust consumers.
Prerequisites¶
- sigillum installed (see Sign and verify your first release).
- The public-key file (
release.asc) that corresponds to the signing key, already produced bykeys mintorkeys generate.sigillum signreads the signing identity (creation time, UID, fingerprint) from this file and refuses to proceed if the backend's public half does not match it. - For the
aws-kmsbackend: AWS credentials in the standard SDK chain. In a local terminal, a configured profile (AWS_PROFILE) oreval "$(aws configure export-credentials --format env)"does it. In CI it means OIDC web identity (see below).
Sign with AWS KMS (production)¶
The command at the top of this page is the production form. Its flags:
--key-idis the KMS key ID, ARN, or alias. Prefer an alias: it can be repointed at a new key without changing the pipeline.--kms-regionis contributed by the AWS KMS backend, and is available onsign,keys mintandkeys minisignbecause sigillum compiles that backend in.--outputdefaults to<input>.sigif omitted.signrefuses to write the signature over its own input. The guard compares cleaned paths and falls back toos.SameFile, so a different spelling of the same file (a symlink, an absolute or relative form) is still refused.
The INFO log line includes the public-key fingerprint so you can confirm it
matches your trust anchor before the .sig ships.
Reproducible signatures¶
Pass --created <rfc3339> to pin the signature's creation timestamp. Two runs
over the same content with the same key and the same --created produce
byte-identical .sig files, which a reproduce-from-scratch (SLSA) chain
needs:
sigillum sign \
--backend aws-kms \
--kms-region eu-west-2 \
--key-id alias/release-signing-v1 \
--public-key release.asc \
--created 2026-07-28T15:11:32Z \
checksums.txt
Sign with the local backend (development)¶
For development, tutorials, or air-gapped builds, sign with an on-disk PEM key:
sigillum keys generate --algorithm rsa --rsa-bits 4096 \
--name "Test Signer" --email test@example.org \
--output release.asc --private-output release.pem
sigillum sign \
--backend local \
--key-id ./release.pem \
--public-key ./release.asc \
checksums.txt
The local backend accepts unencrypted PKCS#1 and PKCS#8 PEM private keys
holding either an RSA or an Ed25519 key, but only the RSA ones are
usable here, because OpenPGP signing is RSA-only. An Ed25519 PEM fails with
computing signature: DetachSign: ed25519.PublicKey: unsupported key type: only
RSA is supported; that key belongs to the
minisign lane.
The backend does not decrypt encrypted PEMs, and rejects ECDSA outright. Protect
the key file with filesystem-level encryption (LUKS, age) or use the aws-kms
backend so the key never leaves the HSM.
Dual-sign during key rotation (--append)¶
During a key-rotation overlap window you can carry two signatures in one armored
file. Sign with the old key, then --append the new key's signature into the
same --output file:
sigillum sign --backend aws-kms --key-id alias/release-signing-v1 \
--public-key v1.asc --output checksums.txt.sig checksums.txt
sigillum sign --backend aws-kms --key-id alias/release-signing-v2 \
--public-key v2.asc --output checksums.txt.sig --append checksums.txt
The result is one armored block with multiple signature packets. Verifiers skip
signature packets from issuers they do not know, so a single file serves
consumers that trust either key. --append is a no-op when --output does not
yet exist.
Verify¶
sigillum sign produces what gpg --verify consumes. Import the published
public key into a clean keyring and verify:
TMPGNUPG=$(mktemp -d)
gpg --homedir "$TMPGNUPG" --import release.asc
gpg --homedir "$TMPGNUPG" --verify checksums.txt.sig checksums.txt
# Expect: "Good signature from <UID>" with the published fingerprint.
rm -rf "$TMPGNUPG"
The same output also verifies under any modern OpenPGP implementation and under
the in-tool verifier in gitlab.com/phpboyscout/go/signing/verify, which
downstream tools use during self-update.
CI integration: GitLab + AWS OIDC¶
sigillum has no OIDC code of its own. It reads whatever the AWS SDK default
credential chain provides. One way to fill that chain in CI is an assume-role
step that exports session credentials before sigillum sign runs:
sign:
id_tokens:
AWS_WEB_IDENTITY_TOKEN:
aud: sts.amazonaws.com
variables:
AWS_ROLE_ARN: arn:aws:iam::<account>:role/release-signing-v1-signer
before_script:
- |
creds=$(aws sts assume-role-with-web-identity \
--role-arn "$AWS_ROLE_ARN" \
--role-session-name "release-${CI_COMMIT_TAG}" \
--web-identity-token "$AWS_WEB_IDENTITY_TOKEN" \
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
--output text)
export AWS_ACCESS_KEY_ID=$(echo "$creds" | awk '{print $1}')
export AWS_SECRET_ACCESS_KEY=$(echo "$creds" | awk '{print $2}')
export AWS_SESSION_TOKEN=$(echo "$creds" | awk '{print $3}')
script:
- sigillum sign --backend aws-kms --kms-region eu-west-2
--key-id alias/release-signing-v1
--public-key release.asc checksums.txt
The default chain also understands web identity on its own: with AWS_ROLE_ARN
and AWS_WEB_IDENTITY_TOKEN_FILE (a path to the token, not the token) set, it
assumes the role without the before_script.
Neither form needs Go or gtb in the pipeline: install the binary and sign.
Related¶
- Generate or mint a signing key
- Publish a WKD tree
- Sign an artefact for Rust consumers
signcommand reference: every flag, default and refused combination- Architecture
- What sigillum does not do