Blog

Assinatura digital e hash: o que cada um garante.

A confusão entre hash e assinatura digital é a mais cara que eu vejo em desenho de sistema, porque ela não produz erro visível. O sistema funciona, passa em teste, e não garante o que todo mundo acha que garante.

O que cada um responde

Hash responde uma pergunta: esse conteúdo é o mesmo de antes?

Qualquer pessoa calcula o hash de qualquer coisa. Não existe segredo envolvido. Se você guarda o documento e o hash no mesmo lugar, quem alterar o documento recalcula o hash e a verificação passa.

Assinatura digital responde duas: esse conteúdo mudou, e quem o produziu?

Ela usa um par de chaves. A privada assina, e só quem a possui consegue. A pública verifica, e qualquer pessoa pode.

A diferença prática: hash é uma afirmação sobre o conteúdo. Assinatura é uma afirmação sobre o conteúdo feita por alguém identificável, que não pode negar depois.

Como funciona por baixo

Assinatura é hash mais criptografia assimétrica:

import { createSign, createVerify } from "node:crypto";

// quem emite
const signer = createSign("SHA256");
signer.update(canonicalBytes);
const signature = signer.sign(privateKeyPem, "base64");

// qualquer pessoa que tenha a chave pública
const verifier = createVerify("SHA256");
verifier.update(canonicalBytes);
const ok = verifier.verify(publicKeyPem, signature, "base64");

Primeiro se calcula o hash do conteúdo, depois se assina o hash. Isso existe por eficiência: assinar 40MB seria lento, assinar 32 bytes é instantâneo.

O que isso implica: a força da assinatura depende da força do hash. Uma assinatura sobre SHA-1 herda a fraqueza do SHA-1, e é por isso que documento assinado com algoritmo antigo perde valor com o tempo.

Onde a troca acontece

O caso mais comum: “guardamos o hash do documento pra garantir que ele não seja adulterado”.

Isso protege contra corrupção acidental. Contra adulteração intencional por alguém com acesso ao seu banco, não faz nada, porque essa pessoa tem os dois valores.

O segundo caso: webhook validado por hash.

// não é assinatura, é HMAC
const expected = createHmac("sha256", SHARED_SECRET).update(rawBody).digest("hex");
const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(receivedHeader));

HMAC fica no meio do caminho: prova que quem enviou conhece o segredo compartilhado. Como as duas partes conhecem o mesmo segredo, você não consegue provar pra um terceiro que a mensagem veio deles e não de você.

Para validar webhook entre duas partes que confiam uma na outra, HMAC é suficiente e é o padrão da indústria. Para provar origem a alguém de fora, não serve.

A propriedade que só a assinatura tem

Não repúdio.

Com HMAC, você e o parceiro podem gerar a mesma prova. Se houver disputa, nenhum dos dois consegue demonstrar que foi o outro.

Com assinatura, só quem tem a chave privada consegue produzir aquela assinatura. Se ela verifica com a chave pública do parceiro, foi o parceiro, a menos que a chave dele tenha vazado.

Essa é a propriedade que importa em contrato, certificado, nota fiscal e qualquer coisa que possa virar disputa.

O que a assinatura não resolve

Quando. Você assina hoje e afirma que é de ontem. Se anterioridade importa, precisa de carimbo de tempo por terceiro ou de âncora pública, como escrevi em como provar que um documento não mudou.

Se ainda vale. A assinatura continua válida depois de o documento ser revogado. Validade e integridade são coisas separadas, e o sistema precisa das duas respostas.

Confidencialidade. Assinado não é cifrado. O conteúdo continua legível por quem o tiver.

A chave privada é o sistema inteiro

Uma assinatura vale o quanto a proteção da chave privada vale.

Chave em variável de ambiente é chave que vaza em log, em despejo de memória, em painel de configuração compartilhado. Chave no repositório é chave pública com passos extras.

O lugar certo é KMS ou HSM, onde a chave nunca sai: você manda o hash e recebe a assinatura. O serviço assina, você nunca vê o material da chave.

Isso também resolve auditoria: quem pediu assinatura, quando, e para qual conteúdo, tudo registrado fora da sua aplicação.

E rotação precisa estar planejada desde o começo. Cada assinatura precisa registrar qual chave a produziu, senão você não consegue verificar documento antigo depois de rotacionar.

{
  "signature": "...",
  "key_id": "prod-signing-2026-01",
  "algorithm": "RS256",
  "hash_version": "v2"
}

A decisão em três perguntas

Preciso detectar alteração acidental? Hash.

Preciso que quem recebe confirme que veio de mim, e essa pessoa confia em mim? HMAC com segredo compartilhado.

Preciso que um terceiro confirme a origem, ou preciso que a origem não possa negar depois? Assinatura digital, com a chave num serviço dedicado.

A maioria dos sistemas precisa da primeira, acha que precisa da terceira, e implementa algo que não é nenhuma das três.

← Voltar pro blog