Pular para o conteúdo
Novo

DeepSeek V4.1 Flash grátis por 7 dias: 10M tokens por dia após qualquer recarga. Até 30/11.

Como funciona

Chaves pública e privada explicadas: assinaturas que você pode verificar

Chaves pública e privada do zero: o que uma assinatura prova, por que a chave privada fica em segredo e um lab Ed25519 em que um arquivo alterado falha.

Iniciante10 min de leituraPor NotHoly

A criptografia de chave pública resolve um problema que segredos compartilhados não resolvem. Duas partes que nunca se viram podem combinar um segredo por uma rede que qualquer pessoa consegue ler. Alguém também pode provar que escreveu um arquivo, sem compartilhar nenhum segredo com quem confere. As duas coisas funcionam porque um par de chaves tem duas metades matematicamente ligadas. A metade pública pode ser compartilhada livremente. A metade privada fica com o dono.

Se você já usou TLS ou SSH, já dependeu dessa ideia. O cadeado do navegador e o aviso de impressão digital no cliente SSH dependem de chaves públicas. Este artigo explica a ideia por trás dessas ferramentas. Você vai gerar um par de chaves Ed25519 com o OpenSSL, assinar um arquivo, verificar a assinatura e, depois, alterar uma linha do arquivo e ver a verificação falhar. Releases de software assinados usam esse mesmo mecanismo, e ele é a base das chaves SSH que tratamos no próximo artigo.

O que é um par de chaves

Um par de chaves são dois valores gerados juntos. A chave privada é secreta. A chave pública pode ser copiada para um arquivo, enviada por e-mail ou colada na configuração de um servidor. As duas estão ligadas por uma matemática que funciona em um sentido só. Derivar a chave pública a partir da privada é fácil. Recuperar a chave privada a partir da pública é, por projeto, inviável.

Os criptógrafos chamam isso de criptografia assimétrica porque as duas partes usam chaves diferentes. A criptografia simétrica, como o AES, usa um segredo para as duas direções. Um esquema assimétrico divide o trabalho: uma chave executa uma operação, e a outra a desfaz ou a verifica.

Uma imagem simples ajuda. Pense na chave pública como uma fenda de correio. Qualquer pessoa pode colocar uma carta ali, e só o dono tem a chave da caixa. Nas assinaturas, os papéis se invertem. O dono carimba um documento com um selo que só ele consegue produzir, e qualquer pessoa com a referência pública pode conferir o selo. A imagem não é perfeita, porque chaves são números e não metal. O que vale guardar é o sentido de cada operação.

Criptografar e assinar fazem trabalhos opostos

As pessoas misturam essas duas coisas o tempo todo, então vale separá-las.

Criptografar para uma chave pública permite que qualquer pessoa com a sua chave pública crie uma mensagem que só a sua chave privada abre. O objetivo é a confidencialidade. Qualquer um pode escrever para você, e só você pode ler o que foi escrito.

Uma assinatura é um valor que o detentor da chave privada calcula sobre alguns dados. Qualquer pessoa com a chave pública pode verificá-lo. O objetivo é a autenticidade e a integridade: os dados vieram do detentor da chave, e ninguém os alterou depois da assinatura. Uma assinatura não esconde nada. Um arquivo assinado continua totalmente legível para quem o tiver.

O Ed25519, algoritmo deste laboratório, é só um esquema de assinatura. Ele não consegue criptografar uma mensagem. A criptografia com chave pública usa outras construções, como o RSA-OAEP, ou um acordo de chaves em curva elíptica seguido de uma cifra simétrica. Por isso, "chave pública" nomeia uma forma de chave, não um único trabalho. Aprenda primeiro o trabalho e depois o algoritmo.

O TLS usa os dois trabalhos. Em um handshake TLS 1.3, o servidor assina dados com a chave privada correspondente à chave pública do certificado, o que mostra que ele controla essa chave. Um acordo de chaves separado deriva então chaves simétricas que criptografam a sessão. O SSH segue a mesma divisão. Uma chave de host assina parte do handshake, e uma chave de usuário assina um desafio de autenticação. Depois que você separa os dois trabalhos, os protocolos ficam mais fáceis de ler.

Por que a chave privada nunca deve sair do dono

A chave privada é a prova. Quem a tem consegue produzir assinaturas que verificam como aquela identidade e consegue decifrar mensagens cifradas para ela. O verificador não vê seu rosto nem seu nome. Ele confere uma assinatura com a chave pública em que confia. Se alguém copiar a sua chave privada, todo verificador que confia na sua chave pública passa a aceitar a cópia como se fosse você.

Disso saem quatro regras práticas:

  • Gere a chave privada na máquina que vai usá-la, ou em um token de hardware feito para isso. Não baixe chave privada de site nem a receba em conversa.
  • Restrinja as permissões do arquivo. Proteja a chave com senha quando a ferramenta permitir, ou guarde-a em um armazenamento apoiado por hardware.
  • Mantenha a chave fora do Git, de tickets, de capturas de tela e de pedidos de suporte. Os scanners de segredos procuram cabeçalhos PEM. Quando uma chave entra no histórico de um repositório, trate-a como exposta, mesmo depois de apagar o arquivo.
  • Se suspeitar de exposição, aposente a chave. Crie um novo par, publique a nova chave pública por um canal em que seus verificadores confiem e revogue a antiga. Apagar o arquivo local não remove as cópias que já saíram da sua máquina.

Compartilhar a chave pública é seguro por construção. O problema difícil é o outro sentido. O verificador precisa saber que uma chave pública realmente pertence à pessoa ou ao projeto que ela diz representar. Autoridades certificadoras, chaves de release fixadas e impressões digitais de host SSH existem para resolver exatamente isso. Uma assinatura válida prova que o detentor da chave assinou aqueles bytes. Ela não prova que o detentor é quem você esperava.

Como assinar e verificar, passo a passo

A sequência de um release assinado é esta:

  1. O publicador gera um par de chaves uma vez e mantém a chave privada em segredo.
  2. O publicador compartilha a chave pública por um canal em que o verificador confia, como uma página que ele já conhece, uma impressão digital publicada em um segundo lugar ou uma chave verificada antes.
  3. A cada release, o publicador assina os bytes exatos do arquivo com a chave privada. Uma assinatura Ed25519 tem 64 bytes.
  4. O publicador distribui o arquivo e a assinatura juntos.
  5. O verificador executa a checagem com os bytes do arquivo, a assinatura e a chave pública confiável. O resultado é válido ou não válido.

Dois detalhes importam mais do que parecem. Primeiro, a assinatura cobre bytes exatos. Um espaço a mais, uma quebra de linha diferente ou um pacote recompactado é outra mensagem, e a verificação falha. Segundo, a assinatura Ed25519 da RFC 8032 é determinística. A mesma chave e a mesma mensagem sempre produzem a mesma assinatura, então assinar não precisa de um número aleatório novo a cada vez. Isso elimina uma fonte comum de falha. A FIPS 186-5 lista o EdDSA ao lado do ECDSA e do RSA como algoritmos de assinatura digital aprovados.

Laboratório prático: assine um arquivo e veja a verificação falhar

Você precisa do OpenSSL 3.x. A saída deste laboratório veio de um Windows 11 com Git Bash e OpenSSL 3.2.4. Confira sua versão com openssl version antes de começar. Versões antigas podem não aceitar as mesmas opções.

Use o Git Bash, um shell Linux ou o Terminal do macOS. O Windows PowerShell 5.1 grava UTF-16 quando você usa >, e isso muda os bytes que você assina. Execute o laboratório em uma pasta vazia da sua máquina. Ele não se conecta a nenhum servidor.

Gere o par de chaves

bash
openssl genpkey -algorithm ed25519 -out priv.pem
openssl pkey -in priv.pem -pubout -out pub.pem

Os dois comandos não imprimem nada quando dão certo. Confira o resultado:

bash
ls -l priv.pem pub.pem

Na nossa máquina, priv.pem tinha 122 bytes e pub.pem tinha 116 bytes. Abra o pub.pem e você verá o bloco -----BEGIN PUBLIC KEY-----. Não imprima nem cole o priv.pem. Neste laboratório ele é a metade secreta da identidade, e acostumar-se a nunca exibi-lo é um hábito que você também vai precisar com as chaves SSH.

Assine o arquivo

bash
echo 'release v1.0' > msg.txt
openssl pkeyutl -sign -inkey priv.pem -rawin -in msg.txt -out msg.sig
ls -l msg.sig

A opção -rawin diz ao OpenSSL para assinar o conteúdo do arquivo diretamente. O Ed25519 calcula o hash da entrada dentro do próprio algoritmo, então você não calcula o hash do arquivo antes. O comando de assinatura não imprime nada. Nosso msg.sig tinha 64 bytes.

Verifique com a chave pública

bash
openssl pkeyutl -verify -pubin -inkey pub.pem -rawin -in msg.txt -sigfile msg.sig

Saída:

text
Signature Verified Successfully

A opção -pubin indica que o arquivo de chave tem só a metade pública. O verificador não precisa de mais nada, então esta checagem pode rodar em outra máquina que nunca viu o priv.pem.

Altere uma linha e verifique de novo

bash
echo 'release v1.1' > msg.txt
openssl pkeyutl -verify -pubin -inkey pub.pem -rawin -in msg.txt -sigfile msg.sig

Saída:

text
Signature Verification Failure

O comando termina com status 1. A assinatura em msg.sig foi feita sobre release v1.0, então ela não pode garantir release v1.1. O OpenSSL não diz o que mudou. Ele informa apenas que os bytes e a assinatura não batem. Um verificador deve tratar esse resultado como uma parada obrigatória, não como um aviso para ignorar.

Teste as bordas

Estas checagens mostram exatamente o que a assinatura cobre. Rodamos cada uma na mesma máquina, na mesma pasta.

Assinar o mesmo arquivo duas vezes gera os mesmos bytes:

bash
echo 'release v1.0' > msg.txt
openssl pkeyutl -sign -inkey priv.pem -rawin -in msg.txt -out a.sig
openssl pkeyutl -sign -inkey priv.pem -rawin -in msg.txt -out b.sig
cmp a.sig b.sig && echo "identical"

Saída:

text
identical

Um arquivo sem a quebra de linha final falha. O echo adiciona uma quebra de linha, então este arquivo fica um byte menor:

bash
printf 'release v1.0' > nonl.txt
openssl pkeyutl -verify -pubin -inkey pub.pem -rawin -in nonl.txt -sigfile a.sig

Saída:

text
Signature Verification Failure

O msg.txt tem 13 bytes e o nonl.txt tem 12. A assinatura foi feita sobre a versão de 13 bytes, então o arquivo mais curto falha.

Um par de chaves diferente também falha, mesmo com o arquivo original:

bash
openssl genpkey -algorithm ed25519 -out other.pem
openssl pkey -in other.pem -pubout -out other.pub
openssl pkeyutl -verify -pubin -inkey other.pub -rawin -in msg.txt -sigfile a.sig

Saída:

text
Signature Verification Failure

O arquivo e a assinatura estão corretos. A chave pública pertence a outro par, então a checagem falha.

Erros comuns

  • Tratar assinatura como criptografia. Um arquivo assinado continua legível para qualquer pessoa. Se você precisa de sigilo, precisa de criptografia, que é outra construção.
  • Confiar em qualquer chave pública que encontrar. Uma assinatura válida prova que o detentor da chave assinou os bytes. Ela não prova quem é esse detentor. Compare impressões digitais por um segundo canal.
  • Verificar bytes diferentes dos assinados. Um pipeline que recodifica, recomprime ou muda quebras de linha quebra a verificação. Assine o artefato final e verifique esse mesmo artefato.
  • Assinar em um shell que reescreve bytes. O redirecionamento do Windows PowerShell 5.1 é um exemplo. Editores que salvam com outra codificação ou outra quebra de linha são outro.
  • Reusar uma chave para tudo. Chaves separadas para assinar releases, entrar por SSH e criptografar limitam o estrago quando uma delas vaza.
  • Achar que uma chave apagada sumiu. Cópias em backups, pastas sincronizadas, histórico do shell ou logs de conversa sobrevivem a um delete local.
  • Escolher primitivas pelo nome. "RSA" ou "ECC" não dizem qual padding, curva ou modo está em uso. Leia os parâmetros na norma, não só o rótulo.

Como continuar

O próximo artigo, O que é SSH e como funcionam as chaves SSH?, usa essa mesma ideia de assinatura para autenticar logins. Se você tem arquivos id_ed25519, eles são um par exatamente desse tipo. Leia também o artigo sobre hash de senhas, porque senhas e chaves resolvem partes diferentes da autenticação, e vale mantê-las separadas na cabeça.

Para se aprofundar, leia a RFC 8032 para ver como o Ed25519 estrutura as assinaturas e por que ele é determinístico. Leia a FIPS 186-5 para entender onde o EdDSA se encaixa entre os algoritmos de assinatura aprovados. Leia as páginas de manual do genpkey e do pkeyutl do OpenSSL para conhecer as outras opções, como a escolha de digest em algoritmos que precisam dela. Depois, verifique um release de um projeto que você já usa, com a chave que o próprio projeto publica no site. Execute apenas arquivos baixados da fonte oficial e só depois de conferi-los.

Fontes

As normas e as páginas de manual acima são as referências primárias deste artigo:

  • RFC 8032, Edwards-Curve Digital Signature Algorithm (EdDSA)
  • FIPS 186-5, Digital Signature Standard (DSS)
  • Documentação do OpenSSL, openssl-genpkey
  • Documentação do OpenSSL, openssl-pkeyutl

Fontes

  1. RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)
  2. FIPS 186-5: Digital Signature Standard (DSS)
  3. OpenSSL documentation: openssl-genpkey
  4. OpenSSL documentation: openssl-pkeyutl

Mais em Criptografia

Criptografia

Senhas e hash com Argon2id

Por que hashes rápidos falham em senhas, o que fazem o salt e o fator de trabalho, e como gerar e verificar hashes com Argon2id em Python.

Intermediário11 min de leitura