Saltar al contenido
ecdsa.com

Sigstore o GPG: firmar software con o sin claves duraderas

Una firma GPG dice que firmó el titular de una clave concreta. Una firma de Sigstore dice que firmó una identidad concreta, a menudo un flujo de trabajo de CI, en un momento registrado y con una clave que existió durante unos minutos.
Qué se comparaSigstoreOpenPGP

Sigstore frente a GPG es la elección entre dos formas de firmar los artefactos de una versión, los contenedores y los paquetes. Con OpenPGP (GnuPG y herramientas compatibles), el mantenedor conserva una clave privada de larga duración, publica su huella digital y distribuye una firma separada junto a cada archivo. Con Sigstore, el firmante se autentica con OpenID Connect, la autoridad de certificación Fulcio emite un certificado que vincula una clave nueva a esa identidad durante diez minutos, la firma queda anotada en el registro de transparencia Rekor y la clave se desecha. Los verificadores comprueban una identidad, no una clave.

De un vistazo

Sigstore (keyless)OpenPGP (GPG)
En qué confía el verificadorUna identidad OIDC (correo electrónico o URI del flujo de trabajo) y su emisor, a través de la raíz de FulcioUna clave pública concreta, obtenida y comprobada fuera de banda
Vida útil de la clave de firmaEfímera; certificado válido durante 10 minutosAños; el propietario fija y prorroga la caducidad
Gestión de clavesNinguna para los firmantesGenerar, hacer copia de seguridad, proteger, rotar, revocar
Prueba del momento de la firmaEntrada en el registro de Rekor (y sello de tiempo RFC 3161 opcional)Hora de creación de la firma que declara el propio firmante
TransparenciaTodas las firmas se registran públicamenteNinguna
RevocaciónInnecesaria con claves efímeras; las identidades comprometidas se gestionan mediante políticasCertificado de revocación que el propietario debe publicar
Firma sin conexiónNo (necesita Fulcio y Rekor, públicos o privados)Sí
Formato del artefactoBundle de Sigstore (.sigstore.json): certificado, firma o sobre DSSE, prueba del registroArchivo .asc o .sig
Herramientas habitualescosign, sigstore-python, sigstore-js, gitsign, attest-build-provenance de GitHubgpg, Sequoia (sq, sqv), herramientas de rpm y apt
Dónde es el estándarnpm provenance (disponibilidad general en septiembre de 2023), atestaciones de PyPI (noviembre de 2024), atestaciones de artefactos de GitHub (disponibilidad general en junio de 2024), CPython desde la 3.11 (única opción a partir de la 3.14), Kubernetes y muchas imágenes de contenedorMaven Central (obligatorio), repositorios Debian y RPM, etiquetas del kernel de Linux, muchos tarballs de código fuente
PrivacidadLa identidad del firmante (correo electrónico o repositorio) es pública en el registroLa clave y el ID de usuario solo son públicos si el firmante los publica

Qué contiene una firma keyless

npm guarda bundles de Sigstore para los paquetes publicados con procedencia. El script siguiente descarga las atestaciones de @noble/curves 2.3.0 desde la API pública del registro y decodifica el certificado de firma (20 de octubre de 2026, curl, jq 1.8.1, OpenSSL 3.6.5).

Inspeccionar el bundle de Sigstore de una atestación de procedencia de npm
#!/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
Salida
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

El paquete lleva dos atestaciones: la atestación de publicación propia de npm y una declaración de procedencia SLSA firmada en la CI. El certificado es válido exactamente diez minutos y se registró en Rekor en el mismo segundo en que se emitió. Su sujeto no es una persona ni una clave, sino el flujo de trabajo que se ejecutó, aquí un flujo de trabajo de publicación compartido que vive en otro repositorio y está fijado a un commit; otras extensiones de Fulcio registran el repositorio de origen, el commit, la etiqueta y la ejecución. El verificador de bundles de Sigstore y el comprobador de procedencia de npm decodifican los mismos datos de cualquier paquete.

En qué se diferencia la verificación

Verificar una firma GPG es un solo comando, gpg --verify file.asc file, pero lo que significa depende de todo lo anterior: cómo obtuvo la clave el verificador, si comprobó la huella digital con una fuente de confianza y si la clave se revocó después. La mayoría de los resultados «Good signature» en los pipelines de CI confían en cualquier clave que se importara unas líneas antes.

Verificar una firma de Sigstore exige indicar la identidad esperada, lo que lleva la cuestión de la política al propio comando. El script siguiente comprueba tres veces el archivo de sumas de comprobación de la propia versión v3.1.3 de cosign con la imagen de contenedor oficial de cosign v3.1.3: con la identidad que el proyecto Sigstore documenta para sus versiones (una cuenta de servicio de Google, emisor https://accounts.google.com), con una identidad de flujo de trabajo de GitHub Actions que no firmó el archivo y después de añadir un byte al final del archivo.

Verificación keyless de un archivo publicado con 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
Salida (20 de octubre de 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

En el primer resultado, cosign comprobó la cadena de certificados hasta la raíz de Sigstore (distribuida mediante TUF), la firma sobre el archivo, la prueba de inclusión en Rekor y que la hora del registro cae dentro de la ventana de diez minutos del certificado. La segunda ejecución es la razón de ser de la fijación de identidades: la firma es criptográficamente válida, pero no la hizo la identidad que pidió el verificador, así que se rechaza; la tercera falla en la propia firma. En los artefactos compilados en GitHub Actions, la identidad es el archivo del flujo de trabajo con su ref, como el flujo de trabajo release.yml@5320e209… del certificado de procedencia de npm de más arriba, y el emisor es https://token.actions.githubusercontent.com.

Los ecosistemas de paquetes integran estas comprobaciones en sus propias herramientas. npm verifica tanto las firmas de su registro como las atestaciones de Sigstore de todo lo instalado:

Verificar las firmas del registro y la procedencia de los paquetes npm instalados
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
Salida
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 hace lo mismo con las atestaciones de artefactos de GitHub, y PyPI comprueba las atestaciones frente al publicador de confianza (trusted publisher) del proyecto cuando se suben.

Contrapartidas que conviene conocer

  • La confianza pasa a los proveedores de identidad y a la infraestructura de Sigstore: un atacante que controle la cuenta de GitHub del mantenedor o el flujo de trabajo de publicación puede obtener firmas válidas. La transparencia hace que esto sea detectable (los mantenedores pueden vigilar el registro en busca de su identidad), no imposible.
  • Metadatos públicos: las identidades de correo electrónico personales aparecen en el registro público; las identidades de flujo de trabajo revelan nombres de repositorios. Los despliegues privados de Sigstore evitan ambas cosas.
  • Disponibilidad: para firmar, Fulcio y Rekor tienen que estar en línea. La verificación de un bundle funciona sin conexión una vez que la raíz de confianza está en caché.
  • La operación evoluciona: Rekor v2, un registro basado en teselas (tiles), tiene disponibilidad general desde octubre de 2025, pero la instancia pública mantiene Rekor v1 como opción predeterminada mientras los mantenedores preparan la transición poscuántica.
  • Los problemas de GPG son humanos: las claves se pierden, caducan sin que nadie lo note o los usuarios aguas abajo nunca las verifican, y esa es la razón principal por la que Python, npm y PyPI lo abandonaron.

Cuándo elegir cada uno

  • Paquetes compilados en GitHub Actions o GitLab CI para npm, PyPI, crates o contenedores: Sigstore, mediante la publicación de confianza y la procedencia.
  • Imágenes de contenedor: firmas y atestaciones de cosign, verificadas por políticas de admisión.
  • Maven Central: OpenPGP es obligatorio; añade también un bundle de Sigstore, que Central valida desde enero de 2025.
  • Paquetes y repositorios de distribuciones Linux: OpenPGP, como esperan las herramientas de la distribución.
  • Firma en entornos aislados (air-gapped) o sin conexión: OpenPGP (o un despliegue privado de Sigstore con tu propio proveedor de identidad).
  • Commits y etiquetas de Git: claves SSH u OpenPGP, o gitsign para commits keyless (consulta firma de commits con SSH o GPG).

Notas de migración

  1. Firma en la CI, no en equipos personales: lleva la publicación de versiones a un flujo de trabajo con un token OIDC (id-token: write en GitHub Actions) y firma allí.
  2. Publica ambas firmas durante un periodo de transición: distribuye archivos .asc y .sigstore.json, y documenta la identidad y el emisor esperados junto a la huella digital de la clave antigua.
  3. Usa los mecanismos propios del registro donde existan: --provenance de npm y la publicación de confianza, los publicadores de confianza de PyPI, las atestaciones de artefactos de GitHub.
  4. Fija las identidades con precisión en las políticas de verificación: repositorio, archivo del flujo de trabajo y etiqueta o rama, no solo la organización.
  5. Vigila el registro en busca de tus identidades y conserva el certificado de revocación de tu clave OpenPGP hasta que la última versión firmada con ella deje de tener soporte.

Referencias

Todas las comparaciones