Sigstore vs GPG, c’est le choix entre deux façons de signer les artefacts de version, les conteneurs et les paquets. Avec OpenPGP (GnuPG et les outils compatibles), un mainteneur conserve une clé privée de longue durée, publie son empreinte et fournit une signature détachée à côté de chaque fichier. Avec Sigstore, le signataire s’authentifie avec OpenID Connect, l’autorité de certification Fulcio délivre un certificat qui lie une clé neuve à cette identité pendant dix minutes, la signature est enregistrée dans le journal de transparence Rekor, puis la clé est jetée. Les vérificateurs contrôlent une identité, pas une clé.
Vue d’ensemble
| Sigstore (sans clé) | OpenPGP (GPG) | |
|---|---|---|
| Ce à quoi le vérificateur fait confiance | Une identité OIDC (e-mail ou URI du workflow) et un émetteur, via la racine de Fulcio | Une clé publique précise, obtenue et vérifiée hors bande |
| Durée de vie de la clé de signature | Éphémère ; certificat valide 10 minutes | Des années ; expiration fixée et prolongée par le propriétaire |
| Gestion des clés | Aucune pour les signataires | Génération, sauvegarde, protection, rotation, révocation |
| Preuve de l’heure de signature | Entrée du journal Rekor (et horodatage RFC 3161 facultatif) | Heure de création de la signature déclarée par le signataire |
| Transparence | Chaque signature consignée dans un journal public | Aucune |
| Révocation | Inutile pour des clés éphémères ; les identités compromises sont traitées par les politiques de vérification | Certificat de révocation que le propriétaire doit publier |
| Signature hors ligne | Non (nécessite Fulcio et Rekor, publics ou privés) | Oui |
| Format de l’artefact | Bundle Sigstore (.sigstore.json) : certificat, signature ou enveloppe DSSE, preuve du journal | Fichier .asc ou .sig |
| Outils courants | cosign, sigstore-python, sigstore-js, gitsign, attest-build-provenance de GitHub | gpg, Sequoia (sq, sqv), outils rpm et apt |
| Où c’est la norme | Provenance npm (GA en septembre 2023), attestations PyPI (novembre 2024), attestations d’artefacts GitHub (GA en juin 2024), CPython depuis la 3.11 (seule option à partir de la 3.14), Kubernetes et de nombreuses images de conteneurs | Maven Central (obligatoire), dépôts Debian et RPM, tags du noyau Linux, nombreuses archives tar de sources |
| Confidentialité | L’identité du signataire (e-mail ou dépôt) est publique dans le journal | Clé et ID utilisateur publics uniquement si le signataire les publie |
Ce que contient une signature sans clé
npm stocke des bundles Sigstore pour les paquets publiés avec provenance. Le script ci-dessous récupère les attestations de @noble/curves 2.3.0 via l’API publique du registre et décode le certificat de signature (20 octobre 2026, curl, jq 1.8.1, OpenSSL 3.6.5).
#!/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
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
Le paquet porte deux attestations : l’attestation de publication propre à npm et une déclaration SLSA de provenance signée en CI. Le certificat est valide exactement dix minutes et a été consigné dans Rekor à la seconde même de sa délivrance. Son sujet n’est ni une personne ni une clé, mais le workflow qui s’est exécuté, ici un workflow de publication partagé, situé dans un autre dépôt et épinglé sur un commit ; d’autres extensions Fulcio enregistrent le dépôt source, le commit, le tag et l’exécution. Le vérificateur de bundles Sigstore et le vérificateur de provenance npm décodent les mêmes données pour n’importe quel paquet.
En quoi la vérification diffère
Vérifier une signature GPG tient en une commande, gpg --verify file.asc file, mais sa portée dépend de tout ce qui précède : la façon dont le vérificateur a obtenu la clé, le contrôle ou non de l’empreinte auprès d’une source digne de confiance, et la révocation éventuelle de la clé depuis. La plupart des résultats « Good signature » dans les pipelines CI font confiance à n’importe quelle clé importée quelques lignes plus tôt.
Vérifier une signature Sigstore impose de nommer l’identité attendue, ce qui fait entrer la question de la politique de vérification dans la commande elle-même. Le script ci-dessous vérifie trois fois, avec l’image de conteneur officielle de cosign v3.1.3, le fichier des sommes de contrôle de cette même version v3.1.3 de cosign : par rapport à l’identité que le projet Sigstore documente pour ses versions (un compte de service Google, émetteur https://accounts.google.com), par rapport à une identité de workflow GitHub Actions qui n’a pas signé le fichier, puis après l’ajout d’un octet à la fin du fichier.
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
== 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
Pour le premier résultat, cosign a contrôlé la chaîne de certificats jusqu’à la racine Sigstore (distribuée via TUF), la signature du fichier et la preuve d’inclusion Rekor, et vérifié que l’heure du journal tombe dans la fenêtre de dix minutes du certificat. Le deuxième essai montre tout l’intérêt de l’épinglage d’identité : la signature est cryptographiquement valide, mais elle n’a pas été produite par l’identité qu’exige le vérificateur, elle est donc rejetée ; le troisième échoue sur la signature elle-même. Pour les artefacts construits dans GitHub Actions, l’identité est le fichier de workflow avec sa référence (ref), comme le workflow release.yml@5320e209… du certificat de provenance npm ci-dessus, et l’émetteur est https://token.actions.githubusercontent.com.
Les écosystèmes de paquets intègrent ces vérifications à leurs propres outils. npm vérifie à la fois les signatures de son registre et les attestations Sigstore de tout ce qui est installé :
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
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 fait de même pour les attestations d’artefacts GitHub, et PyPI vérifie les attestations par rapport à l’éditeur de confiance (trusted publisher) du projet au moment où elles sont envoyées.
Compromis à connaître
- La confiance se déplace vers les fournisseurs d’identité et l’infrastructure de Sigstore : un attaquant qui contrôle le compte GitHub du mainteneur ou le workflow de publication peut obtenir des signatures valides. La transparence rend cela détectable (les mainteneurs peuvent surveiller le journal pour repérer leur identité), pas impossible.
- Métadonnées publiques : les identités e-mail personnelles apparaissent dans le journal public ; les identités de workflow révèlent les noms des dépôts. Les déploiements privés de Sigstore évitent l’un et l’autre.
- Disponibilité : signer nécessite que Fulcio et Rekor soient en ligne. La vérification d’un bundle fonctionne hors ligne une fois la racine de confiance mise en cache.
- L’exploitation évolue : Rekor v2, un journal à base de tuiles (tiles), est en disponibilité générale depuis octobre 2025, mais l’instance publique conserve Rekor v1 par défaut pendant que les mainteneurs préparent le passage au post-quantique.
- Les coûts de GPG sont humains : des clés se perdent, expirent sans que personne s’en aperçoive ou ne sont jamais vérifiées par les utilisateurs en aval, et c’est la principale raison pour laquelle Python, npm et PyPI l’ont abandonné.
Quand choisir l’un ou l’autre
- Paquets construits dans GitHub Actions ou GitLab CI pour npm, PyPI, crates ou des conteneurs : Sigstore, via la publication de confiance et la provenance.
- Images de conteneurs : signatures et attestations cosign, vérifiées par des politiques d’admission.
- Maven Central : OpenPGP est obligatoire ; ajoutez aussi un bundle Sigstore, que Central valide depuis janvier 2025.
- Paquets et dépôts des distributions Linux : OpenPGP, comme l’attendent les outils des distributions.
- Signature isolée du réseau (air-gapped) ou hors ligne : OpenPGP (ou un déploiement privé de Sigstore avec votre propre fournisseur d’identité).
- Commits et tags Git : clés SSH ou OpenPGP, ou gitsign pour des commits sans clé (voir SSH vs GPG pour signer les commits).
Notes de migration
- Signez en CI, pas sur les ordinateurs portables : déplacez la publication des versions vers un workflow doté d’un jeton OIDC (
id-token: writedans GitHub Actions) et signez-y. - Publiez les deux pendant une période de transition : fournissez des fichiers
.ascet.sigstore.json, et documentez l’identité et l’émetteur attendus à côté de l’empreinte de l’ancienne clé. - Utilisez les mécanismes natifs des registres là où ils existent :
--provenanceet la publication de confiance (trusted publishing) de npm, les éditeurs de confiance (trusted publishers) de PyPI, les attestations d’artefacts GitHub. - Épinglez précisément les identités dans les politiques de vérification : dépôt, fichier de workflow et tag ou branche, pas seulement l’organisation.
- Surveillez le journal pour repérer vos identités, et conservez le certificat de révocation de votre clé OpenPGP jusqu’à ce que la dernière version signée avec elle ne soit plus prise en charge.
Références
- Documentation Sigstore : vérification avec cosign et vérification des versions de cosign (l’identité et l’émetteur utilisés ci-dessus)
- Version v3.1.3 de cosign
- Sigstore : disponibilité générale de Rekor v2 et évolution de Rekor
- Provenance SLSA v1
- PEP 740, prise en charge des attestations numériques par l’index et PEP 761, abandon des signatures PGP pour CPython
- GitHub : les attestations d’artefacts en disponibilité générale
- Sonatype : le Central Publisher Portal valide les signatures Sigstore
- RFC 9580, OpenPGP