Zum Inhalt springen
ecdsa.com

Sigstore vs. GPG: Software-Releases signieren

Eine GPG-Signatur besagt, dass der Inhaber eines bestimmten Schlüssels signiert hat. Eine Sigstore-Signatur besagt, dass eine bestimmte Identität, oft ein CI-Workflow, zu einem protokollierten Zeitpunkt signiert hat – mit einem Schlüssel, der nur Minuten lang existierte.
Im VergleichSigstoreOpenPGP

Bei Sigstore vs. GPG geht es um die Wahl zwischen zwei Arten, Release-Artefakte, Container und Pakete zu signieren. Bei OpenPGP (GnuPG und kompatible Tools) bewahrt ein Maintainer einen langlebigen privaten Schlüssel auf, veröffentlicht dessen Fingerabdruck und liefert neben jeder Datei eine abgetrennte Signatur aus. Bei Sigstore authentifiziert sich der Signierer per OpenID Connect, und die Zertifizierungsstelle Fulcio stellt ein Zertifikat aus, das einen frischen Schlüssel für zehn Minuten an diese Identität bindet. Die Signatur wird im Transparenzlog Rekor erfasst, danach wird der Schlüssel verworfen. Verifizierer prüfen eine Identität, keinen Schlüssel.

Auf einen Blick

Sigstore (keyless)OpenPGP (GPG)
Wem der Verifizierer vertrautEiner OIDC-Identität (E-Mail-Adresse oder Workflow-URI) und ihrem Aussteller, abgesichert über die Root von FulcioEinem bestimmten öffentlichen Schlüssel, über einen separaten Kanal beschafft und geprüft
Lebensdauer des SignaturschlüsselsEphemer; Zertifikat 10 Minuten gültigJahre; Ablauf vom Besitzer festgelegt und verlängert
SchlüsselmanagementFür Signierer keinesErzeugen, sichern, schützen, rotieren, widerrufen
Nachweis des SignaturzeitpunktsRekor-Logeintrag (und optional ein Zeitstempel nach RFC 3161)Vom Signierer angegebener Erstellungszeitpunkt der Signatur
TransparenzJede Signatur wird öffentlich protokolliertKeine
WiderrufBei ephemeren Schlüsseln nicht nötig; kompromittierte Identitäten werden über Richtlinien behandeltWiderrufszertifikat, das der Besitzer veröffentlichen muss
Offline signierenNein (braucht Fulcio und Rekor, öffentlich oder privat)Ja
ArtefaktformatSigstore-Bundle (.sigstore.json): Zertifikat, Signatur oder DSSE-Envelope, Log-NachweisDatei .asc oder .sig
Typische Toolscosign, sigstore-python, sigstore-js, gitsign, GitHub-Action attest-build-provenancegpg, Sequoia (sq, sqv), Tools von rpm und apt
Wo es Standard istnpm-Provenance (GA im September 2023), PyPI-Attestierungen (November 2024), Artefakt-Attestierungen von GitHub (GA im Juni 2024), CPython seit 3.11 (ab 3.14 die einzige Option), Kubernetes und viele Container-ImagesMaven Central (Pflicht), Debian- und RPM-Repositorys, Tags des Linux-Kernels, viele Quell-Tarballs
DatenschutzIdentität des Signierers (E-Mail-Adresse oder Repository) ist im Log öffentlichSchlüssel und User-ID nur öffentlich, wenn der Signierer sie veröffentlicht

Was eine Keyless-Signatur enthält

npm speichert Sigstore-Bundles für Pakete, die mit Provenance veröffentlicht wurden. Das folgende Skript ruft die Attestierungen für @noble/curves 2.3.0 über die öffentliche Registry-API ab und dekodiert das Signaturzertifikat (20. Oktober 2026, curl, jq 1.8.1, OpenSSL 3.6.5).

Sigstore-Bundle hinter einer npm-Provenance-Attestierung untersuchen
#!/bin/sh
curl -s "https://registry.npmjs.org/-/npm/v1/attestations/@noble%2fcurves@2.3.0" -o att.json
jq -r '.attestations[].predicateType' att.json
bundle='.attestations[] | select(.predicateType == "https://slsa.dev/provenance/v1") | .bundle'
jq -r "$bundle | .mediaType" att.json
jq -r "$bundle | .verificationMaterial.tlogEntries[0] | \"Rekor log index \(.logIndex), integrated \(.integratedTime | tonumber | todate)\"" att.json
jq -r "$bundle | .verificationMaterial.certificate.rawBytes" att.json | openssl base64 -d -A > leaf.der
openssl x509 -inform DER -in leaf.der -noout -issuer -dates -ext subjectAltName
openssl x509 -inform DER -in leaf.der -noout -text | grep -A1 "57264.1.1:" | tail -1 | sed 's/^ */OIDC issuer: /'
rm -f att.json leaf.der
Ausgabe
https://github.com/npm/attestation/tree/main/specs/publish/v0.1
https://slsa.dev/provenance/v1
application/vnd.dev.sigstore.bundle.v0.3+json
Rekor log index 2357213216, integrated 2026-08-06T07:37:48Z
issuer=O=sigstore.dev, CN=sigstore-intermediate
notBefore=Aug  6 07:37:48 2026 GMT
notAfter=Aug  6 07:47:48 2026 GMT
X509v3 Subject Alternative Name: critical
    URI:https://github.com/paulmillr/jsbt/.github/workflows/release.yml@5320e209ca8cc9b1b0b569675e164bdd2a139d71
OIDC issuer: https://token.actions.githubusercontent.com

Das Paket trägt zwei Attestierungen: die eigene Publish-Attestierung von npm und ein in CI signiertes SLSA-Provenance-Statement. Das Zertifikat ist genau zehn Minuten gültig und wurde in der Sekunde seiner Ausstellung in Rekor protokolliert. Sein Subject ist weder eine Person noch ein Schlüssel, sondern der Workflow, der gelaufen ist – hier ein gemeinsam genutzter Release-Workflow in einem anderen Repository, gepinnt auf einen bestimmten Commit; weitere Fulcio-Erweiterungen halten Quell-Repository, Commit, Tag und Lauf fest. Der Sigstore-Bundle-Verifizierer und der npm-Provenance-Checker dekodieren dieselben Daten für jedes beliebige Paket.

Wie sich die Prüfung unterscheidet

Eine GPG-Signatur zu prüfen ist ein einziger Befehl, gpg --verify file.asc file, doch was das Ergebnis aussagt, hängt von allem ab, was davor geschieht: wie der Verifizierer an den Schlüssel gekommen ist, ob der Fingerabdruck mit einer vertrauenswürdigen Quelle abgeglichen wurde und ob der Schlüssel inzwischen widerrufen wurde. Die meisten Ergebnisse „Good signature“ in CI-Pipelines vertrauen schlicht dem Schlüssel, der ein paar Zeilen weiter oben importiert wurde.

Wer eine Sigstore-Signatur prüft, muss die erwartete Identität nennen – damit landet die Frage nach der Prüfrichtlinie direkt im Befehl. Das folgende Skript prüft die Prüfsummendatei aus dem Release v3.1.3 von cosign selbst dreimal mit dem offiziellen Container-Image von cosign v3.1.3: gegen die Identität, die das Sigstore-Projekt für seine Releases dokumentiert (ein Dienstkonto von Google, Aussteller https://accounts.google.com), gegen eine Workflow-Identität von GitHub Actions, die die Datei nicht signiert hat, und nachdem ein Byte an die Datei angehängt wurde.

Keyless-Prüfung einer öffentlichen Release-Datei mit cosign v3.1.3
url=https://github.com/sigstore/cosign/releases/download/v3.1.3
curl -sSLO "$url/cosign_checksums.txt"
curl -sSLO "$url/cosign_checksums.txt.sigstore.json"
cosign() { docker run --rm -v "$PWD":/work:ro -w /work ghcr.io/sigstore/cosign/cosign:v3.1.3 "$@"; }

echo "== expected identity"
cosign verify-blob cosign_checksums.txt \
  --bundle cosign_checksums.txt.sigstore.json \
  --certificate-identity keyless@projectsigstore.iam.gserviceaccount.com \
  --certificate-oidc-issuer https://accounts.google.com

echo "== any other identity"
cosign verify-blob cosign_checksums.txt \
  --bundle cosign_checksums.txt.sigstore.json \
  --certificate-identity "https://github.com/sigstore/cosign/.github/workflows/release.yml@refs/tags/v3.1.3" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

echo "== modified file"
printf 'x' >> cosign_checksums.txt
cosign verify-blob cosign_checksums.txt \
  --bundle cosign_checksums.txt.sigstore.json \
  --certificate-identity keyless@projectsigstore.iam.gserviceaccount.com \
  --certificate-oidc-issuer https://accounts.google.com
Ausgabe (20. Oktober 2026)
== expected identity
Verified OK
== any other identity
Error: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value "https://github.com/sigstore/cosign/.github/workflows/release.yml@refs/tags/v3.1.3", got "keyless@projectsigstore.iam.gserviceaccount.com"
error during command execution: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value "https://github.com/sigstore/cosign/.github/workflows/release.yml@refs/tags/v3.1.3", got "keyless@projectsigstore.iam.gserviceaccount.com"
== modified file
Error: failed to verify signature: could not verify message: invalid signature when validating ASN.1 encoded signature
error during command execution: failed to verify signature: could not verify message: invalid signature when validating ASN.1 encoded signature

Für das erste Ergebnis hat cosign die Zertifikatskette bis zur Sigstore-Root (verteilt über TUF) geprüft, außerdem die Signatur über die Datei, den Inclusion Proof von Rekor und ob die Log-Zeit in das Zehn-Minuten-Fenster des Zertifikats fällt. Der zweite Lauf zeigt, worum es beim Identitäts-Pinning geht: Die Signatur ist kryptografisch gültig, stammt aber nicht von der Identität, die der Verifizierer verlangt hat, und wird deshalb abgelehnt; der dritte Lauf scheitert an der Signatur selbst. Bei Artefakten, die in GitHub Actions gebaut werden, ist die Identität die Workflow-Datei samt Ref, etwa der Workflow release.yml@5320e209… im npm-Provenance-Zertifikat oben, und der Aussteller ist https://token.actions.githubusercontent.com.

Paket-Ökosysteme verpacken diese Prüfungen in eigene Tools. npm prüft sowohl seine Registry-Signaturen als auch die Sigstore-Attestierungen aller installierten Pakete:

Registry-Signaturen und Provenance installierter npm-Pakete prüfen
cd "$(mktemp -d)" && npm init -y > /dev/null
npm install --ignore-scripts --no-fund --no-audit @noble/curves@2.3.0 @noble/hashes@2.3.0
npm audit signatures
Ausgabe
added 2 packages in 589ms
audited 2 packages in 0s

2 packages have verified registry signatures

2 packages have verified attestations
(use --json --include-attestations to view attestation details)

gh attestation verify erledigt dasselbe für Artefakt-Attestierungen von GitHub, und PyPI prüft Attestierungen beim Hochladen gegen den Trusted Publisher des Projekts.

Abwägungen, die du kennen solltest

  • Das Vertrauen verlagert sich auf Identity Provider und die Infrastruktur von Sigstore: Ein Angreifer, der das GitHub-Konto des Maintainers oder den Release-Workflow unter seiner Kontrolle hat, kann gültige Signaturen erhalten. Transparenz macht das erkennbar (Maintainer können das Log auf ihre Identität hin überwachen), nicht unmöglich.
  • Öffentliche Metadaten: Persönliche E-Mail-Identitäten erscheinen im öffentlichen Log; Workflow-Identitäten verraten Namen von Repositorys. Private Sigstore-Instanzen vermeiden beides.
  • Verfügbarkeit: Zum Signieren müssen Fulcio und Rekor online sein. Die Prüfung eines Bundles funktioniert offline, sobald die Trust Root zwischengespeichert ist.
  • Der Betrieb entwickelt sich weiter: Rekor v2, ein Tile-basiertes Log, ist seit Oktober 2025 allgemein verfügbar, doch die öffentliche Instanz nutzt weiterhin Rekor v1 als Standard, während die Maintainer an der Post-Quanten-Tauglichkeit arbeiten.
  • Bei GPG liegen die Kosten beim Menschen: Schlüssel gehen verloren, laufen unbemerkt ab oder werden von Downstream-Nutzern nie geprüft – der Hauptgrund, warum sich Python, npm und PyPI davon abgewandt haben.

Wann welches Verfahren passt

  • Pakete, die in GitHub Actions oder GitLab CI für npm, PyPI, crates oder Container gebaut werden: Sigstore über Trusted Publishing und Provenance.
  • Container-Images: Signaturen und Attestierungen mit cosign, geprüft durch Admission Policies.
  • Maven Central: OpenPGP ist Pflicht; füge zusätzlich ein Sigstore-Bundle hinzu, das Central seit Januar 2025 validiert.
  • Pakete und Repositorys von Linux-Distributionen: OpenPGP, wie es die Tools der Distribution erwarten.
  • Signieren in Air-Gap-Umgebungen oder offline: OpenPGP (oder eine private Sigstore-Instanz mit deinem eigenen Identity Provider).
  • Git-Commits und -Tags: SSH- oder OpenPGP-Schlüssel oder gitsign für Keyless-Commits (siehe SSH vs. GPG für Commit-Signaturen).

Hinweise zur Migration

  1. In CI signieren, nicht auf Laptops: Verlagere Releases in einen Workflow mit OIDC-Token (id-token: write in GitHub Actions) und signiere dort.
  2. Für eine Übergangszeit beides veröffentlichen: Liefere Dateien .asc und .sigstore.json aus und dokumentiere die erwartete Identität und den Aussteller neben dem alten Fingerabdruck des Schlüssels.
  3. Registry-eigene Mechanismen nutzen, wo es sie gibt: npm --provenance und Trusted Publishing, Trusted Publishers bei PyPI, Artefakt-Attestierungen von GitHub.
  4. Identitäten genau pinnen: Lege in Prüfrichtlinien Repository, Workflow-Datei und Tag oder Branch fest, nicht nur die Organisation.
  5. Das Log überwachen: Achte auf Einträge zu deinen Identitäten, und bewahre das Widerrufszertifikat deines OpenPGP-Schlüssels auf, bis das letzte damit signierte Release nicht mehr unterstützt wird.

Quellen

Alle Vergleiche