SSH vs GPG para assinar commits é a escolha de qual chave o Git usa quando você roda git commit -S ou git tag -s. Com o OpenPGP (o padrão, normalmente via GnuPG), o Git guarda uma assinatura OpenPGP no cabeçalho gpgsig do commit. Com o SSH (gpg.format=ssh, Git 2.34 ou posterior), o Git chama ssh-keygen -Y sign e guarda uma assinatura SSHSIG no mesmo lugar. Nos dois casos, a assinatura cobre o objeto de commit, que nomeia a árvore, os pais, o autor e a mensagem, de modo que uma única assinatura autentica todo o histórico até aquele commit.
Visão geral
| Assinatura SSH | Assinatura OpenPGP (GPG) | |
|---|---|---|
| Suporte no Git | Git 2.34 (novembro de 2021), gpg.format=ssh | git commit -S desde o Git 1.7.9 (2012); tags assinadas bem antes |
| Ferramenta de assinatura | ssh-keygen -Y sign, OpenSSH 8.1 ou posterior (evite a 8.7) | gpg ou outra implementação do OpenPGP |
| Formato da assinatura | SSHSIG com namespace git, com armadura ASCII BEGIN SSH SIGNATURE | Pacote de assinatura OpenPGP, com armadura ASCII BEGIN PGP SIGNATURE |
| Tamanho no objeto de commit (Ed25519, medido) | 306 bytes | 279 bytes |
| Em quem se confia | Chaves listadas em gpg.ssh.allowedSignersFile | Chaves do chaveiro com confiança suficiente no dono (owner trust) |
| Expiração e rotação | valid-after e valid-before por chave no arquivo de signatários permitidos (OpenSSH 8.8+) | Data de expiração dentro da chave, prorrogável; subchaves |
| Revogação | gpg.ssh.revocationFile (lista de chaves ou KRL) | Certificado de revogação publicado com a chave |
| Chaves de hardware | Chaves FIDO2 (ed25519-sk, ecdsa-sk), agentes apoiados em enclaves seguros | Cartões inteligentes OpenPGP e chaves de segurança com applet OpenPGP |
| Autoridades certificadoras | Certificados SSH por meio de linhas cert-authority | Nenhuma; identidades certificadas por outras chaves |
| “Verified” no GitHub | Desde 23 de agosto de 2022 | Desde abril de 2016 |
| “Verified” no GitLab | Desde a 15.7 (dezembro de 2022), com disponibilidade geral na 15.8 | Sim |
Assinar e verificar das duas formas
O script abaixo cria uma chave SSH e uma chave OpenPGP para a mesma identidade em um diretório temporário, assina um commit com cada uma e verifica os dois (Git 2.54, OpenSSH 10.3, GnuPG 2.5.21). A configuração global do Git fica desativada, então nada muda na sua máquina.
#!/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"
== 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)
A verificação SSH nomeia o principal do allowed_signers e a impressão digital da chave; a verificação OpenPGP nomeia o ID de usuário e informa a confiança registrada no chaveiro. Os tamanhos são parecidos porque os dois formatos carregam uma assinatura Ed25519; o SSHSIG também embute a chave pública completa, e o OpenPGP, só a impressão digital dela. O verificador de assinaturas SSH e o inspetor de OpenPGP decodificam qualquer um dos dois blocos a partir de git cat-file commit.
Como decidir de quem aceitar assinaturas
Uma assinatura válida só importa se o verificador souber quais chaves pertencem a quem. No SSH, esse mapeamento é o arquivo allowed_signers: uma linha por principal e chave, opcionalmente restrita ao namespace git e a uma janela de validade. O Git confere valid-after e valid-before com o horário em que a assinatura foi feita, então uma chave aposentada pode continuar no arquivo com uma data de fim, e os commits antigos continuam verificáveis. Uma linha com cert-authority confia em toda chave certificada pela autoridade certificadora que emite os certificados SSH de uma organização, o que serve bem a empresas que já emitem certificados SSH de curta duração. O arquivo pode ficar no repositório, e assim as alterações nele também passam por revisão e são assinadas.
No OpenPGP, o mapeamento fica no chaveiro e no banco de dados de confiança de cada verificador, e é por isso que sistemas de CI e plataformas de código importam chaves explicitamente. A rede de confiança consegue conectar desconhecidos, mas raramente é usada fora de comunidades antigas. As chaves trazem as próprias datas de expiração e certificados de revogação, e as subchaves permitem que o desenvolvedor mantenha a chave primária offline e rotacione as subchaves de assinatura sem mudar de identidade.
As plataformas acrescentam uma camada própria: o GitHub e o GitLab mostram “Verified” quando a chave de assinatura está cadastrada na conta a que pertence o endereço de e-mail verificado do committer. Esse selo depende dos registros da plataforma; a verificação persistente do GitHub, em versão prévia desde novembro de 2024, armazena o resultado no momento do push, de modo que remover a chave mais tarde não o altera.
Quando escolher cada um
- Desenvolvedores individuais no GitHub ou no GitLab: SSH, de preferência com uma chave
ed25519-skem uma chave de segurança, para que assinar exija um toque. - Equipes e empresas: SSH com um arquivo
allowed_signersno repositório, ou com certificados SSH emitidos pela CA que você já tem. - Projetos que verificam tags upstream com OpenPGP (kernel Linux, muitas distribuições, gerentes de release com chaves publicadas): OpenPGP.
- Commits feitos no CI ou por bots: nenhuma das duas chaves de longa duração; use assinatura keyless com certificados de curta duração (por exemplo, o gitsign do Sigstore; veja Sigstore vs GPG para assinatura) ou as assinaturas de bot da própria plataforma.
- Ambientes regulados com cartões inteligentes X.509:
gpg.format=x509com uma ferramenta de assinatura S/MIME, com suporte desde o Git 2.19.
Notas de migração
- Configure a assinatura SSH:
git config --global gpg.format ssh,user.signingkeyapontando para o arquivo da sua chave pública (oukey::seguido da chave), além decommit.gpgsign trueetag.gpgsign true. - Cadastre a chave como chave de assinatura na sua plataforma; o GitHub e o GitLab aceitam a mesma chave para autenticação e assinatura, mas uma chave de assinatura separada é mais fácil de rotacionar.
- Publique a lista de signatários permitidos no repositório (por exemplo,
.gitsigners), apontegpg.ssh.allowedSignersFilepara ela no CI e rodegit verify-commitnos merges; acrescentevalid-afterao adicionar uma chave evalid-beforeao aposentá-la. - Mantenha as chaves públicas OpenPGP antigas disponíveis para os verificadores e nas contas das plataformas, para que os commits assinados antes da troca continuem sendo verificados.
- Exija commits assinados nos branches protegidos (conjuntos de regras do GitHub, regras de push do GitLab) quando todos os committers tiverem migrado.
Referências
- Documentação do Git: git-config gpg.format, gpg.ssh.allowedSignersFile, gpg.ssh.revocationFile
- Notas da versão do Git 2.34
- Notas da versão do OpenSSH 8.1 e manual do ssh-keygen, ALLOWED SIGNERS
- OpenSSH PROTOCOL.sshsig
- RFC 9580, OpenPGP
- Changelog do GitHub: verificação de commits SSH
- GitLab: assinar commits e tags com chaves SSH