Saltar al contenido
ecdsa.com

SSH o GPG para firmar commits en Git: qué clave usar

Las dos opciones guardan en el objeto commit una firma que Git y las forjas de código pueden verificar. La firma SSH reutiliza claves que los desarrolladores ya tienen; OpenPGP aporta caducidad, revocación y un modelo de confianza propio.

Elegir entre SSH y GPG para firmar commits es elegir qué clave usa Git cuando ejecutas git commit -S o git tag -s. Con OpenPGP (la opción predeterminada, normalmente GnuPG), Git guarda una firma OpenPGP en el encabezado gpgsig del commit. Con SSH (gpg.format=ssh, Git 2.34 y posteriores), Git llama a ssh-keygen -Y sign y guarda una firma SSHSIG en el mismo lugar. En ambos casos la firma cubre el objeto commit, que nombra el árbol, los padres, el autor y el mensaje, de modo que una sola firma autentica todo el historial hasta ese commit.

De un vistazo

Firma SSHFirma OpenPGP (GPG)
Compatibilidad en GitGit 2.34 (noviembre de 2021), gpg.format=sshgit commit -S desde Git 1.7.9 (2012); las etiquetas firmadas, mucho antes
Herramienta de firmassh-keygen -Y sign, OpenSSH 8.1 o posterior (evita la 8.7)gpg u otra implementación de OpenPGP
Formato de la firmaSSHSIG con el espacio de nombres git, con armadura BEGIN SSH SIGNATUREPaquete de firma OpenPGP, con armadura BEGIN PGP SIGNATURE
Tamaño en el objeto commit (Ed25519, medido)306 bytes279 bytes
En quién se confíaLas claves que figuran en gpg.ssh.allowedSignersFileLas claves del anillo de claves con suficiente confianza en el propietario
Caducidad y rotaciónvalid-after y valid-before por clave en el archivo de firmantes permitidos (OpenSSH 8.8+)Fecha de caducidad dentro de la clave, prorrogable; subclaves
Revocacióngpg.ssh.revocationFile (lista de claves o KRL)Certificado de revocación publicado con la clave
Claves de hardwareLlaves FIDO2 (ed25519-sk, ecdsa-sk), agentes respaldados por enclaves segurosTarjetas inteligentes OpenPGP y llaves de seguridad con un applet OpenPGP
Autoridades de certificaciónCertificados SSH mediante líneas cert-authorityNinguna; las identidades las certifican otras claves
«Verified» en GitHubDesde el 23 de agosto de 2022Desde abril de 2016
«Verified» en GitLabDesde la 15.7 (diciembre de 2022), con disponibilidad general en la 15.8Sí

Firmar y verificar de las dos formas

El script siguiente crea una clave SSH y una clave OpenPGP para la misma identidad en un directorio temporal, firma un commit con cada una y verifica ambos (Git 2.54, OpenSSH 10.3, GnuPG 2.5.21). La configuración global de Git está desactivada, así que no cambia nada en tu equipo.

Un repositorio, un commit firmado con SSH y otro firmado con OpenPGP
#!/bin/sh
base=$(pwd)
dir=$(mktemp -d "$base/commitdemo.XXXXXX")
cd "$dir" || exit 1
GNUPGHOME=$(mktemp -d /tmp/gpg.XXXXXX)   # short path: gpg-agent sockets need one
export GNUPGHOME GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
export GIT_AUTHOR_NAME=Alice GIT_AUTHOR_EMAIL=alice@example.org
export GIT_COMMITTER_NAME=Alice GIT_COMMITTER_EMAIL=alice@example.org

# One SSH key with an allowed_signers entry, one OpenPGP key
ssh-keygen -q -t ed25519 -N '' -C alice@example.org -f alice
echo "alice@example.org namespaces=\"git\" $(cat alice.pub)" > allowed_signers
gpg --batch --quiet --passphrase '' --quick-gen-key "Alice <alice@example.org>" ed25519 sign 1y
fpr=$(gpg --list-keys --with-colons alice@example.org 2>/dev/null | awk -F: '/^fpr/ { print $10; exit }')

# Two commits: the first signed with SSH, the second with OpenPGP
git init -q repo && cd repo || exit 1
git config gpg.ssh.allowedSignersFile ../allowed_signers
echo one > file && git add file
git -c gpg.format=ssh -c user.signingkey=../alice.pub commit -q -S -m "signed with SSH"
echo two >> file
git commit -q -a -S"$fpr" -m "signed with OpenPGP" 2>/dev/null

for c in HEAD~1 HEAD; do
  echo "== $(git log -1 --format=%s "$c")"
  git verify-commit "$c" 2>&1 | grep -E "Good|using"
  git cat-file commit "$c" | awk '/^gpgsig/,/END/' > sig.txt
  echo "$(head -1 sig.txt | cut -c8-) ($(wc -c < sig.txt | tr -d ' ') bytes in the commit object)"
done
gpgconf --kill gpg-agent
cd "$base" && rm -rf "$dir" "$GNUPGHOME"
Salida
== signed with SSH
Good "git" signature for alice@example.org with ED25519 key SHA256:7z1vKkx5UJmp2Qrc5ucSDqImz8+r8pfG21roc32f/2A
-----BEGIN SSH SIGNATURE----- (306 bytes in the commit object)
== signed with OpenPGP
gpg:                using EDDSA key 6A4E1CC8308072568C9E240CDDBB1B4BE59BFC00
gpg: Good signature from "Alice <alice@example.org>" [ultimate]
-----BEGIN PGP SIGNATURE----- (279 bytes in the commit object)

La verificación SSH nombra el principal de allowed_signers y la huella digital de la clave; la verificación OpenPGP nombra el ID de usuario e informa del nivel de confianza que indica el anillo de claves. Los tamaños son parecidos porque las dos llevan una firma Ed25519; SSHSIG además incluye la clave pública completa, y OpenPGP solo su huella digital. El verificador de firmas SSH y el inspector de OpenPGP decodifican cualquiera de los dos bloques que muestra git cat-file commit.

Cómo decidir qué firmas cuentan

Una firma válida solo importa si el verificador sabe qué claves pertenecen a quién. Con SSH, esa correspondencia es el archivo allowed_signers: una línea por principal y clave, restringida opcionalmente al espacio de nombres git y a una ventana de validez. Git compara valid-after y valid-before con el momento en que se hizo la firma, de modo que una clave retirada puede quedarse en el archivo con una fecha de fin y los commits antiguos siguen siendo verificables. Una línea con cert-authority confía en toda clave certificada por la autoridad de certificados SSH de una organización, lo que conviene a las empresas que ya emiten certificados SSH de corta duración. El archivo puede vivir en el repositorio, así que sus cambios también se revisan y se firman.

Con OpenPGP, la correspondencia vive en el anillo de claves y en la base de datos de confianza de cada verificador, y por eso los sistemas de CI y las forjas importan las claves de forma explícita. La red de confianza puede conectar a desconocidos, pero rara vez se usa fuera de comunidades con mucha trayectoria. Las claves llevan sus propias fechas de caducidad y sus certificados de revocación, y las subclaves permiten a un desarrollador guardar la clave principal sin conexión y rotar las subclaves de firma sin cambiar de identidad.

Las forjas añaden su propia capa: GitHub y GitLab muestran «Verified» cuando la clave de firma está registrada en la cuenta a la que pertenece la dirección de correo electrónico verificada del committer. Esa insignia depende de los registros de la forja; la verificación persistente de GitHub, en versión preliminar desde noviembre de 2024, guarda el resultado en el momento del push, de modo que eliminar la clave más tarde no lo cambia.

Cuándo elegir cada uno

  • Desarrolladores individuales en GitHub o GitLab: SSH, idealmente con una clave ed25519-sk en una llave de seguridad, para que firmar exija un toque.
  • Equipos y empresas: SSH con un archivo allowed_signers en el repositorio, o con certificados SSH emitidos por tu CA actual.
  • Proyectos que verifican con OpenPGP las etiquetas de upstream (kernel de Linux, muchas distribuciones, responsables de publicación con claves publicadas): OpenPGP.
  • Commits hechos en la CI o por bots: ninguna de las dos claves de larga duración; usa la firma keyless con certificados de corta duración (por ejemplo, gitsign de Sigstore; consulta Sigstore frente a GPG) o las firmas de bots de la propia forja.
  • Entornos regulados con tarjetas inteligentes X.509: gpg.format=x509 con una herramienta de firma S/MIME, compatible desde Git 2.19.

Notas de migración

  1. Configura la firma SSH: git config --global gpg.format ssh, user.signingkey con la ruta de tu archivo de clave pública (o key:: seguido de la clave), y commit.gpgsign true y tag.gpgsign true.
  2. Registra la clave como clave de firma en tu forja; GitHub y GitLab aceptan la misma clave para la autenticación y para la firma, pero una clave de firma independiente es más fácil de rotar.
  3. Publica los firmantes permitidos en el repositorio (por ejemplo, .gitsigners), apunta gpg.ssh.allowedSignersFile a ese archivo en la CI y ejecuta git verify-commit en los merges; añade valid-after cuando incorpores una clave y valid-before cuando retires una.
  4. Conserva las claves públicas OpenPGP antiguas a disposición de los verificadores y en las cuentas de la forja, para que los commits firmados antes del cambio sigan verificándose.
  5. Exige commits firmados en las ramas protegidas (conjuntos de reglas de GitHub, reglas de push de GitLab) cuando todos los committers hayan hecho el cambio.

Referencias

Todas las comparaciones