{"id":127,"date":"2026-06-17T21:06:12","date_gmt":"2026-06-17T21:06:12","guid":{"rendered":"https:\/\/shattered.io\/pt\/2026\/06\/17\/assinaturas-digitais-nodejs-ecdsa-ed25519\/"},"modified":"2026-06-17T21:08:49","modified_gmt":"2026-06-17T21:08:49","slug":"assinaturas-digitais-nodejs-ecdsa-ed25519","status":"publish","type":"post","link":"https:\/\/shattered.io\/pt\/assinaturas-digitais-nodejs-ecdsa-ed25519\/","title":{"rendered":"Assinaturas Digitais em Node.js: ECDSA e Ed25519 em 12 Passos [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Assinaturas digitais garantem que uma mensagem ou ficheiro n\u00e3o foi alterado e que foi criado por quem afirma ser o autor. Em Node.js, o m\u00f3dulo nativo <code>crypto<\/code> suporta dois algoritmos modernos: <strong>ECDSA<\/strong> (Elliptic Curve Digital Signature Algorithm) e <strong>Ed25519<\/strong>. Este tutorial mostra como implementar ambos do zero, com c\u00f3digo funcional, tabelas comparativas e 12 passos concretos para um projeto completo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ed25519 verifica assinaturas <strong>cerca de 10 vezes mais r\u00e1pido do que RSA-2048<\/strong> e gera chaves p\u00fablicas de apenas 32 bytes. ECDSA com a curva P-256 domina os ecossistemas TLS e JWT. Saber qual algoritmo usar e implement\u00e1-lo corretamente evita vulnerabilidades cr\u00edticas como reutiliza\u00e7\u00e3o de nonce e ataques de curva inv\u00e1lida, dois dos erros mais explorados em criptografia assim\u00e9trica.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"o-que-sao-assinaturas-digitais-e-por-que-importam-em-2026\">O Que S\u00e3o Assinaturas Digitais e Por Que Importam em 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Uma assinatura digital \u00e9 o equivalente criptogr\u00e1fico de uma assinatura manuscrita, mas com garantias matem\u00e1ticas verific\u00e1veis. Funciona com um par de chaves: a <strong>chave privada<\/strong> assina os dados, e a <strong>chave p\u00fablica<\/strong> verifica a assinatura. Qualquer pessoa com a chave p\u00fablica pode confirmar que o detentor da chave privada assinou aquele conte\u00fado espec\u00edfico, sem nunca ter acesso \u00e0 chave privada em si.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Em 2026, assinaturas digitais est\u00e3o presentes em praticamente toda infraestrutura moderna. O protocolo TLS 1.3 usa ECDSA ou Ed25519 para autenticar certificados durante o handshake. JWTs assinados com o algoritmo ES256 (ECDSA + SHA-256) protegem APIs REST em todo o mundo. O sistema de assinatura de c\u00f3digo do npm verifica a integridade dos pacotes antes da instala\u00e7\u00e3o. SSH usa Ed25519 por padr\u00e3o nas configura\u00e7\u00f5es mais recentes por ser mais r\u00e1pido e seguro do que RSA.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A diferen\u00e7a entre ECDSA e Ed25519 \u00e9 mais do que t\u00e9cnica. ECDSA requer um n\u00famero aleat\u00f3rio \u00fanico, o nonce <code>k<\/code>, a cada assinatura. Se esse n\u00famero se repetir, mesmo uma \u00fanica vez, a chave privada pode ser matematicamente recuperada. Ed25519 \u00e9 determin\u00edstico: dado o mesmo par chave privada e mensagem, produz sempre a mesma assinatura, eliminando por completo a depend\u00eancia de um gerador de n\u00fameros aleat\u00f3rios de boa qualidade durante a assinatura.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O NIST publicou o FIPS 186-5 em 2023, atualizando os padr\u00f5es de assinaturas digitais para incluir curvas EdDSA (que inclui Ed25519) ao lado das curvas ECDSA tradicionais. A <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc8032\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 8032<\/a> especifica Ed25519 em detalhe. A <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc7518\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 7518<\/a> define como usar ECDSA em JWTs (algoritmos ES256, ES384 e ES512). Qualquer implementa\u00e7\u00e3o s\u00e9ria de assinaturas digitais em Node.js parte destas tr\u00eas refer\u00eancias normativas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O m\u00f3dulo <code>crypto<\/code> do Node.js, baseado em OpenSSL, suporta todos estes algoritmos nativamente desde o Node.js 12. N\u00e3o precisa de instalar nenhuma depend\u00eancia externa para ECDSA ou Ed25519. A API estabilizou nas vers\u00f5es recentes, com o Node.js 26.0.0 a adicionar suporte ao par\u00e2metro de contexto para Ed25519, relevante para protocolos que vinculam assinaturas a um dom\u00ednio espec\u00edfico. A documenta\u00e7\u00e3o completa est\u00e1 dispon\u00edvel em <a href=\"https:\/\/nodejs.org\/api\/crypto.html\" target=\"_blank\" rel=\"noopener noreferrer\">nodejs.org\/api\/crypto.html<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pre-requisitos-versoes-e-conhecimentos-necessarios\">Pr\u00e9-requisitos: Vers\u00f5es e Conhecimentos Necess\u00e1rios<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de come\u00e7ar, certifique-se de que o seu ambiente cumpre estes requisitos:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Requisito<\/th><th>Vers\u00e3o M\u00ednima<\/th><th>Recomendada<\/th><th>Motivo<\/th><\/tr><\/thead><tbody><tr><td>Node.js<\/td><td>18.x LTS<\/td><td>22.x LTS<\/td><td>Ed25519 est\u00e1vel e API crypto completa<\/td><\/tr><tr><td>npm<\/td><td>9.x<\/td><td>10.x<\/td><td>Suporte a workspaces e lockfile v3<\/td><\/tr><tr><td>OpenSSL<\/td><td>3.0<\/td><td>3.x mais recente<\/td><td>Inclu\u00eddo com Node.js 18+<\/td><\/tr><tr><td>Sistema Operativo<\/td><td>Linux \/ macOS \/ Windows 10<\/td><td>Linux ou macOS<\/td><td>Melhor compatibilidade de terminal<\/td><\/tr><tr><td>Editor<\/td><td>Qualquer<\/td><td>VS Code<\/td><td>Autocompletar para m\u00f3dulo crypto<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Conhecimentos recomendados: JavaScript moderno (async\/await, m\u00f3dulos), conceitos b\u00e1sicos de criptografia assim\u00e9trica (par de chaves p\u00fablica\/privada) e familiaridade com o terminal. N\u00e3o \u00e9 necess\u00e1rio conhecer a matem\u00e1tica das curvas el\u00edpticas, mas ajuda a compreender os erros de seguran\u00e7a mais comuns.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Verifique a vers\u00e3o instalada com <code>node --version<\/code> e <code>openssl version<\/code>. O m\u00f3dulo <code>crypto<\/code> est\u00e1 dispon\u00edvel em todos os builds padr\u00e3o do Node.js, mas pode estar ausente em builds minimalistas. Confirme com o seguinte comando: se retornar um n\u00famero superior a 20, o suporte a curvas el\u00edpticas est\u00e1 completo.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node --version\n# v22.x ou superior recomendado\n\nopenssl version\n# OpenSSL 3.x (inclu\u00eddo com Node.js 18+)\n\nnode -e \"const c = require('crypto'); console.log('Curvas dispon\u00edveis:', c.getCurves().length)\"\n# Curvas dispon\u00edveis: 77 (ou n\u00famero similar)\n\nnode -e \"\nconst c = require('crypto');\n['prime256v1','secp384r1','secp256k1'].forEach(curve =>\n  console.log(curve + ':', c.getCurves().includes(curve) ? 'OK' : 'NAO SUPORTADO')\n);\"\n# prime256v1: OK\n# secp384r1: OK\n# secp256k1: OK<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"ecdsa-vs-ed25519-comparacao-de-desempenho-e-casos-de-uso\">ECDSA vs Ed25519: Compara\u00e7\u00e3o de Desempenho e Casos de Uso<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de escrever c\u00f3digo, perceba as diferen\u00e7as entre os dois algoritmos. A escolha errada n\u00e3o compromete apenas a performance, pode criar vulnerabilidades de seguran\u00e7a dif\u00edceis de detetar. A tabela abaixo compara os quatro algoritmos de assinatura mais usados em Node.js:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Caracter\u00edstica<\/th><th>Ed25519<\/th><th>ECDSA P-256<\/th><th>ECDSA P-384<\/th><th>RSA-2048<\/th><\/tr><\/thead><tbody><tr><td>N\u00edvel de Seguran\u00e7a<\/td><td>~128-bit<\/td><td>~128-bit<\/td><td>~192-bit<\/td><td>~112-bit<\/td><\/tr><tr><td>Equival\u00eancia RSA<\/td><td>RSA-3072<\/td><td>RSA-3072<\/td><td>RSA-7680<\/td><td>Base de compara\u00e7\u00e3o<\/td><\/tr><tr><td>Tamanho da Chave P\u00fablica<\/td><td>32 bytes<\/td><td>64 bytes<\/td><td>96 bytes<\/td><td>256 bytes<\/td><\/tr><tr><td>Tamanho da Assinatura<\/td><td>64 bytes (fixo)<\/td><td>70-72 bytes (DER)<\/td><td>96-104 bytes (DER)<\/td><td>256 bytes<\/td><\/tr><tr><td>Velocidade de Verifica\u00e7\u00e3o<\/td><td>~10x mais r\u00e1pido que RSA<\/td><td>Mais r\u00e1pido que RSA<\/td><td>Moderado<\/td><td>Base de compara\u00e7\u00e3o<\/td><\/tr><tr><td>Determin\u00edstico<\/td><td>Sim<\/td><td>N\u00e3o<\/td><td>N\u00e3o<\/td><td>Sim (PKCS#1 v1.5)<\/td><\/tr><tr><td>Nonce necess\u00e1rio<\/td><td>N\u00e3o<\/td><td>Sim (cr\u00edtico)<\/td><td>Sim (cr\u00edtico)<\/td><td>N\u00e3o<\/td><\/tr><tr><td>Uso em JWTs<\/td><td>EdDSA<\/td><td>ES256<\/td><td>ES384<\/td><td>RS256<\/td><\/tr><tr><td>Algoritmo de Hash Interno<\/td><td>SHA-512 (interno)<\/td><td>SHA-256<\/td><td>SHA-384<\/td><td>SHA-256<\/td><\/tr><tr><td>Suporte WebCrypto<\/td><td>Sim<\/td><td>Sim<\/td><td>Sim<\/td><td>Sim<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Use <strong>Ed25519<\/strong> para APIs internas, autentica\u00e7\u00e3o SSH, sistemas peer-to-peer e qualquer contexto onde controla ambos os lados da comunica\u00e7\u00e3o. \u00c9 mais simples, mais seguro operacionalmente e mais r\u00e1pido. Use <strong>ECDSA P-256 (ES256)<\/strong> para JWTs p\u00fablicos, certificados TLS, assinatura de c\u00f3digo e interoperabilidade com sistemas que exigem curvas NIST, como muitas bibliotecas Java e Go que ainda n\u00e3o suportam Ed25519. Use <strong>ECDSA P-384<\/strong> em contextos que exigem seguran\u00e7a de 192-bit, como infraestruturas governamentais ou sistemas de sa\u00fade com requisitos regulat\u00f3rios espec\u00edficos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para a maioria dos projetos Node.js novos em 2026, Ed25519 \u00e9 a escolha padr\u00e3o. Para JWTs compat\u00edveis com bibliotecas externas, ECDSA P-256 continua a ser a op\u00e7\u00e3o mais interoper\u00e1vel. Nunca use RSA-2048 para novas implementa\u00e7\u00f5es: oferece apenas 112-bit de seguran\u00e7a, abaixo do m\u00ednimo de 128-bit recomendado pelo NIST para sistemas com vida \u00fatil superior a 2030.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-1-e-2-estrutura-do-projeto-e-configuracao-inicial\">Passo 1 e 2: Estrutura do Projeto e Configura\u00e7\u00e3o Inicial<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Crie a estrutura de diret\u00f3rios do projeto antes de escrever qualquer c\u00f3digo de criptografia. Uma organiza\u00e7\u00e3o clara evita misturar chaves de produ\u00e7\u00e3o com chaves de teste, um erro que frequentemente exp\u00f5e chaves privadas sens\u00edveis em reposit\u00f3rios git.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Passo 1: Criar o projeto\nmkdir digital-signatures-nodejs\ncd digital-signatures-nodejs\nnpm init -y\nmkdir keys signatures utils\n\n# Estrutura final do projeto:\n# digital-signatures-nodejs\/\n# \u251c\u2500\u2500 keys\/           # chaves PEM (NUNCA commitar para git)\n# \u251c\u2500\u2500 signatures\/     # ficheiros de assinatura exportados\n# \u251c\u2500\u2500 utils\/          # fun\u00e7\u00f5es auxiliares reutiliz\u00e1veis\n# \u251c\u2500\u2500 ecdsa.js        # implementa\u00e7\u00e3o ECDSA\n# \u251c\u2500\u2500 ed25519.js      # implementa\u00e7\u00e3o Ed25519\n# \u251c\u2500\u2500 jwt-sign.js     # assinatura de JWTs com ES256\n# \u251c\u2500\u2500 api.js          # API Express de verifica\u00e7\u00e3o\n# \u2514\u2500\u2500 package.json<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Configure o <code>.gitignore<\/code> imediatamente. Chaves privadas n\u00e3o devem nunca entrar no reposit\u00f3rio git, mesmo em reposit\u00f3rios privados. Uma \u00fanica exposi\u00e7\u00e3o \u00e9 suficiente para comprometer toda a infraestrutura de assinaturas.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Passo 2: Criar .gitignore\ncat > .gitignore << 'EOF'\nkeys\/\n*.pem\n*.key\n*.p8\nnode_modules\/\n.env\nsignatures\/\nEOF\n\n# Instalar depend\u00eancias opcionais para passos avan\u00e7ados\nnpm install jsonwebtoken express\n# jsonwebtoken@9.x: assinar JWTs com ES256 e EdDSA\n# express@4.x: API de verifica\u00e7\u00e3o de assinaturas no Passo 12<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Adicione ao <code>package.json<\/code> o campo <code>\"type\": \"module\"<\/code> para usar sintaxe ESM moderna ou mantenha sem o campo e use <code>require()<\/code> como nos exemplos seguintes (CommonJS). Os exemplos deste tutorial usam <code>require()<\/code> para m\u00e1xima compatibilidade com Node.js 18+.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Confirme que o ambiente est\u00e1 pronto com um teste r\u00e1pido de gera\u00e7\u00e3o de par de chaves:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node -e \"\nconst crypto = require('crypto');\nconst { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519', {\n  privateKeyEncoding: { type: 'pkcs8', format: 'pem' },\n  publicKeyEncoding:  { type: 'spki', format: 'pem' }\n});\nconsole.log('Ed25519 OK - Chave p\u00fablica gerada em', publicKey.length, 'bytes de PEM');\n\"\n# Ed25519 OK - Chave p\u00fablica gerada em 119 bytes de PEM<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-3-e-4-gerar-par-de-chaves-ecdsa-p-256-e-guardar-em-pem\">Passo 3 e 4: Gerar Par de Chaves ECDSA P-256 e Guardar em PEM<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O Node.js usa o nome interno <code>prime256v1<\/code> para a curva NIST P-256. Este nome vem do registo OID do OpenSSL. Nos padr\u00f5es JWT e TLS, a mesma curva aparece como <code>P-256<\/code> ou <code>secp256r1<\/code>. S\u00e3o nomes diferentes para o mesmo objeto matem\u00e1tico. N\u00e3o confunda com <code>secp256k1<\/code>, que \u00e9 a curva usada em Bitcoin e Ethereum, com propriedades diferentes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ ecdsa.js\nconst crypto = require('crypto');\nconst fs = require('fs');\nconst path = require('path');\n\n\/\/ Passo 3: Gerar par de chaves ECDSA P-256\nfunction gerarChavesECDSA(curva = 'prime256v1') {\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n    namedCurve: curva,          \/\/ 'prime256v1' = P-256, 'secp384r1' = P-384\n    privateKeyEncoding: {\n      type: 'pkcs8',            \/\/ formato padr\u00e3o para interoperabilidade\n      format: 'pem'\n    },\n    publicKeyEncoding: {\n      type: 'spki',             \/\/ SubjectPublicKeyInfo - formato universal\n      format: 'pem'\n    }\n  });\n  return { privateKey, publicKey };\n}\n\n\/\/ Passo 4: Guardar chaves em ficheiros PEM com permiss\u00f5es seguras\nfunction guardarChaves(prefixo, privateKey, publicKey) {\n  const keysDir = path.join(__dirname, 'keys');\n  if (!fs.existsSync(keysDir)) fs.mkdirSync(keysDir, { recursive: true });\n\n  const privPath = path.join(keysDir, `${prefixo}-private.pem`);\n  const pubPath  = path.join(keysDir, `${prefixo}-public.pem`);\n\n  \/\/ mode 0o600: apenas o dono pode ler - obrigat\u00f3rio para chaves privadas\n  fs.writeFileSync(privPath, privateKey, { mode: 0o600 });\n  fs.writeFileSync(pubPath, publicKey);\n\n  console.log(`Chave privada guardada: ${privPath}`);\n  console.log(`Chave p\u00fablica guardada: ${pubPath}`);\n  return { privPath, pubPath };\n}\n\n\/\/ Executar\nconst { privateKey, publicKey } = gerarChavesECDSA('prime256v1');\nguardarChaves('ecdsa-p256', privateKey, publicKey);\n\nconsole.log('\\n--- Chave P\u00fablica ECDSA P-256 (PEM) ---');\nconsole.log(publicKey);\n\/\/ -----BEGIN PUBLIC KEY-----\n\/\/ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE... (88 bytes em base64)\n\/\/ -----END PUBLIC KEY-----\n\nmodule.exports = { gerarChavesECDSA, guardarChaves };<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Execute com <code>node ecdsa.js<\/code>. O ficheiro <code>keys\/ecdsa-p256-public.pem<\/code> pode ser partilhado livremente. O ficheiro <code>keys\/ecdsa-p256-private.pem<\/code> \u00e9 a chave do reino: proteja-o com permiss\u00f5es 600 e nunca o inclua em reposit\u00f3rios ou logs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A diferen\u00e7a entre os formatos <code>pkcs8<\/code> e <code>sec1<\/code> para chaves privadas EC \u00e9 relevante para interoperabilidade: <code>pkcs8<\/code> \u00e9 compat\u00edvel com Java, Go, Python e OpenSSL moderno. <code>sec1<\/code> \u00e9 o formato EC nativo mas menos universal. Para qualquer novo projeto, use sempre <code>pkcs8<\/code>. Para gerar chaves P-384, substitua apenas o argumento: <code>gerarChavesECDSA('secp384r1')<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-5-e-6-assinar-e-verificar-dados-com-ecdsa-em-node-js\">Passo 5 e 6: Assinar e Verificar Dados com ECDSA em Node.js<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Com o par de chaves gerado, implemente as fun\u00e7\u00f5es de assinatura e verifica\u00e7\u00e3o. O Node.js oferece duas APIs: a API de baixo n\u00edvel com <code>crypto.createSign()<\/code>\/<code>crypto.createVerify()<\/code> e a API de alto n\u00edvel com <code>crypto.sign()<\/code>\/<code>crypto.verify()<\/code>. Use sempre a API de alto n\u00edvel em c\u00f3digo novo, pois \u00e9 mais simples e menos propensa a erros de implementa\u00e7\u00e3o.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ utils\/ecdsa-ops.js\nconst crypto = require('crypto');\nconst fs = require('fs');\nconst path = require('path');\n\n\/\/ Passo 5: Assinar dados com ECDSA P-256 + SHA-256\nfunction assinarECDSA(dados, caminhoChavePrivada) {\n  const privateKeyPem = fs.readFileSync(caminhoChavePrivada, 'utf8');\n\n  const assinatura = crypto.sign(\n    'sha256',                   \/\/ hash: sha256 para P-256, sha384 para P-384\n    Buffer.from(dados, 'utf8'), \/\/ dados como Buffer (encoding expl\u00edcito!)\n    {\n      key: privateKeyPem,\n      dsaEncoding: 'der'        \/\/ DER: formato padr\u00e3o, tamanho vari\u00e1vel 70-72 bytes\n      \/\/ Use 'ieee-p1363' para WebCrypto\/JOSE: tamanho fixo 64 bytes para P-256\n    }\n  );\n\n  console.log('Assinatura ECDSA (base64):', assinatura.toString('base64'));\n  console.log('Tamanho:', assinatura.length, 'bytes (DER, varia 70-72)');\n  return assinatura;\n}\n\n\/\/ Passo 6: Verificar assinatura ECDSA\nfunction verificarECDSA(dados, assinatura, caminhoChavePublica) {\n  const publicKeyPem = fs.readFileSync(caminhoChavePublica, 'utf8');\n\n  const valida = crypto.verify(\n    'sha256',\n    Buffer.from(dados, 'utf8'), \/\/ MESMO encoding usado na assinatura\n    {\n      key: publicKeyPem,\n      dsaEncoding: 'der'        \/\/ MESMO dsaEncoding usado na assinatura\n    },\n    assinatura\n  );\n\n  console.log('Assinatura v\u00e1lida:', valida);\n  return valida;\n}\n\n\/\/ Demonstra\u00e7\u00e3o\nconst mensagem = 'Pagamento de 250 EUR para IBAN PT50000201231234567890154';\nconst keysDir  = path.join(__dirname, '..', 'keys');\nconst privKey  = path.join(keysDir, 'ecdsa-p256-private.pem');\nconst pubKey   = path.join(keysDir, 'ecdsa-p256-public.pem');\n\nconst sig = assinarECDSA(mensagem, privKey);\n\nconsole.log('\\n--- Verificar mensagem original ---');\nverificarECDSA(mensagem, sig, pubKey); \/\/ true\n\nconsole.log('\\n--- Verificar mensagem adulterada ---');\nverificarECDSA('Pagamento de 999 EUR para IBAN PT50000201231234567890154', sig, pubKey); \/\/ false\n\nmodule.exports = { assinarECDSA, verificarECDSA };<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Execute com <code>node utils\/ecdsa-ops.js<\/code>. A assinatura tem entre 70 e 72 bytes em formato DER para ECDSA P-256. A varia\u00e7\u00e3o de tamanho acontece porque os valores <code>r<\/code> e <code>s<\/code> da assinatura s\u00e3o inteiros de tamanho vari\u00e1vel na codifica\u00e7\u00e3o ASN.1: quando o bit mais significativo \u00e9 1, o DER adiciona um byte 0x00 de padding para evitar interpreta\u00e7\u00e3o como n\u00famero negativo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A verifica\u00e7\u00e3o com dados adulterados retorna <code>false<\/code> sem lan\u00e7ar exce\u00e7\u00e3o. Nunca ignore o valor de retorno de <code>crypto.verify()<\/code>: <code>false<\/code> significa assinatura inv\u00e1lida, n\u00e3o um erro de execu\u00e7\u00e3o. Tratar apenas exce\u00e7\u00f5es e ignorar o retorno booleano \u00e9 um erro de seguran\u00e7a comum que pode deixar passar assinaturas inv\u00e1lidas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-7-e-8-implementar-ed25519-com-assinaturas-deterministicas\">Passo 7 e 8: Implementar Ed25519 com Assinaturas Determin\u00edsticas<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ed25519 tem uma API ligeiramente diferente de ECDSA no Node.js. A diferen\u00e7a mais importante: o par\u00e2metro do algoritmo em <code>crypto.sign()<\/code> e <code>crypto.verify()<\/code> deve ser <code>null<\/code> ou <code>undefined<\/code>. O Node.js determina o esquema de assinatura a partir do tipo da chave, n\u00e3o de um par\u00e2metro expl\u00edcito. Passar <code>'sha256'<\/code> com uma chave Ed25519 gera o erro <code>ERR_CRYPTO_INVALID_DIGEST<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ ed25519.js\nconst crypto = require('crypto');\nconst fs = require('fs');\nconst path = require('path');\n\n\/\/ Passo 7: Gerar par de chaves Ed25519\nfunction gerarChavesEd25519() {\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519', {\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' },\n    publicKeyEncoding:  { type: 'spki',  format: 'pem' }\n  });\n\n  const keysDir = path.join(__dirname, 'keys');\n  if (!fs.existsSync(keysDir)) fs.mkdirSync(keysDir, { recursive: true });\n\n  fs.writeFileSync(path.join(keysDir, 'ed25519-private.pem'), privateKey, { mode: 0o600 });\n  fs.writeFileSync(path.join(keysDir, 'ed25519-public.pem'), publicKey);\n\n  console.log('Par de chaves Ed25519 gerado e guardado.');\n  return { privateKey, publicKey };\n}\n\n\/\/ Passo 8a: Assinar com Ed25519\n\/\/ REGRA CR\u00cdTICA: o primeiro par\u00e2metro DEVE ser null (n\u00e3o 'sha256' ou qualquer hash)\nfunction assinarEd25519(dados, privateKeyPem) {\n  const assinatura = crypto.sign(\n    null,                       \/\/ null obrigat\u00f3rio - Ed25519 usa SHA-512 internamente\n    Buffer.from(dados, 'utf8'),\n    privateKeyPem\n  );\n\n  console.log('Assinatura Ed25519 (base64url):', assinatura.toString('base64url'));\n  console.log('Tamanho:', assinatura.length, 'bytes (sempre 64 bytes)');\n  return assinatura;\n}\n\n\/\/ Passo 8b: Verificar com Ed25519\nfunction verificarEd25519(dados, assinatura, publicKeyPem) {\n  const valida = crypto.verify(\n    null,                       \/\/ null obrigat\u00f3rio para Ed25519\n    Buffer.from(dados, 'utf8'),\n    publicKeyPem,\n    assinatura\n  );\n\n  console.log('Assinatura Ed25519 v\u00e1lida:', valida);\n  return valida;\n}\n\n\/\/ Demonstra\u00e7\u00e3o completa\nconst { privateKey, publicKey } = gerarChavesEd25519();\nconst mensagem = 'Transacao #TX-2026-001 | Valor: 1500 EUR | Timestamp: 1750000000';\n\nconsole.log('\\n--- Assinar com Ed25519 ---');\nconst sig = assinarEd25519(mensagem, privateKey);\n\nconsole.log('\\n--- Verificar assinatura original ---');\nverificarEd25519(mensagem, sig, publicKey); \/\/ true\n\nconsole.log('\\n--- Verificar mensagem adulterada ---');\nverificarEd25519(mensagem + ' (adulterado)', sig, publicKey); \/\/ false\n\n\/\/ Demonstrar determinismo: a mesma mensagem gera sempre a mesma assinatura\nconst sig2 = assinarEd25519(mensagem, privateKey);\nconsole.log('\\n--- Confirmar determinismo ---');\nconsole.log('Assinaturas id\u00eanticas:', sig.equals(sig2)); \/\/ true (imposs\u00edvel em ECDSA)\n\nmodule.exports = { gerarChavesEd25519, assinarEd25519, verificarEd25519 };<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Execute com <code>node ed25519.js<\/code>. A sa\u00edda confirma que Ed25519 produz sempre exatamente <strong>64 bytes<\/strong> de assinatura, independentemente do tamanho da mensagem. A linha \"Assinaturas id\u00eanticas: true\" demonstra o determinismo: dado o mesmo par chave privada e mensagem, Ed25519 gera sempre o mesmo resultado.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Este determinismo \u00e9 uma vantagem de seguran\u00e7a importante. Em ECDSA, se o gerador de n\u00fameros aleat\u00f3rios produzir o mesmo nonce <code>k<\/code> duas vezes (por bug, seed fraco ou falha de entropia), a chave privada fica exposta. Ed25519 elimina completamente esta classe de vulnerabilidade porque o nonce \u00e9 derivado deterministicamente da mensagem e da chave privada usando SHA-512.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-9-codificacao-der-vs-ieee-p1363-para-interoperabilidade\">Passo 9: Codifica\u00e7\u00e3o DER vs IEEE P1363 para Interoperabilidade<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A codifica\u00e7\u00e3o da assinatura ECDSA \u00e9 a fonte de um dos erros mais frequentes na integra\u00e7\u00e3o com outros sistemas. O Node.js usa DER por padr\u00e3o, mas muitas bibliotecas, incluindo a WebCrypto API do browser, JOSE e algumas implementa\u00e7\u00f5es Java, esperam o formato IEEE P1363. Compreender a diferen\u00e7a evita horas de debugging em produ\u00e7\u00e3o.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Formato DER:<\/strong> a assinatura \u00e9 uma estrutura ASN.1 que codifica os dois inteiros <code>r<\/code> e <code>s<\/code> com cabe\u00e7alhos de tipo e comprimento. Para ECDSA P-256, o tamanho varia entre 70 e 72 bytes, dependendo dos valores de <code>r<\/code> e <code>s<\/code>. \u00c9 o formato padr\u00e3o para TLS, certificados X.509 e OpenSSL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Formato IEEE P1363:<\/strong> concatena\u00e7\u00e3o direta de <code>r || s<\/code>, com cada valor preenchido com zeros \u00e0 esquerda at\u00e9 ao tamanho fixo da curva. Para P-256, s\u00e3o sempre 64 bytes (32 + 32). \u00c9 o formato esperado pela WebCrypto API e por implementa\u00e7\u00f5es JOSE.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ utils\/encoding-demo.js\nconst crypto = require('crypto');\n\nfunction demonstrarFormatos() {\n  \/\/ Gerar par de chaves tempor\u00e1rio para o exemplo\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n    namedCurve: 'prime256v1',\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' },\n    publicKeyEncoding:  { type: 'spki', format: 'pem' }\n  });\n\n  const dados = Buffer.from('mensagem de teste para comparar formatos', 'utf8');\n\n  \/\/ Assinatura em DER (padr\u00e3o Node.js)\n  const sigDer = crypto.sign('sha256', dados, { key: privateKey, dsaEncoding: 'der' });\n  console.log('DER:', sigDer.length, 'bytes (70-72 bytes para P-256)');\n  console.log('DER hex (in\u00edcio):', sigDer.subarray(0, 4).toString('hex'));\n  \/\/ 30 44 02 20 ... (0x30 = SEQUENCE, 0x44 = comprimento, 0x02 = INTEGER, 0x20 = 32 bytes)\n\n  \/\/ Assinatura em IEEE P1363 (tamanho fixo)\n  const sigP1363 = crypto.sign('sha256', dados, { key: privateKey, dsaEncoding: 'ieee-p1363' });\n  console.log('IEEE P1363:', sigP1363.length, 'bytes (sempre 64 bytes para P-256)');\n\n  \/\/ Verifica\u00e7\u00e3o: usar SEMPRE o mesmo dsaEncoding\n  const vDer    = crypto.verify('sha256', dados, { key: publicKey, dsaEncoding: 'der' },    sigDer);\n  const vP1363  = crypto.verify('sha256', dados, { key: publicKey, dsaEncoding: 'ieee-p1363' }, sigP1363);\n  const vMixed  = crypto.verify('sha256', dados, { key: publicKey, dsaEncoding: 'ieee-p1363' }, sigDer);\n\n  console.log('\\nResultados:');\n  console.log('DER com DER:', vDer);          \/\/ true\n  console.log('P1363 com P1363:', vP1363);    \/\/ true\n  console.log('DER com P1363 (errado):', vMixed); \/\/ false - encoding misto falha!\n}\n\ndemonstrarFormatos();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A linha \"DER com P1363 (errado): false\" demonstra o problema real. Quando um servi\u00e7o Node.js assina com o padr\u00e3o DER e envia para um browser que usa WebCrypto (que espera IEEE P1363), a verifica\u00e7\u00e3o falha silenciosamente, retornando apenas <code>false<\/code>. <strong>Regra pr\u00e1tica:<\/strong> use <code>dsaEncoding: 'ieee-p1363'<\/code> para todas as integra\u00e7\u00f5es com WebCrypto no browser ou com JOSE\/JWT. Use <code>der<\/code> (padr\u00e3o) para TLS, certificados e OpenSSL.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-10-assinar-jwts-com-es256-ecdsa-sha-256\">Passo 10: Assinar JWTs com ES256 (ECDSA + SHA-256)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O algoritmo ES256 definido na RFC 7518 usa ECDSA P-256 com SHA-256 para assinar JSON Web Tokens. \u00c9 a alternativa mais segura ao RS256 (RSA) para JWTs: chaves menores, assinaturas mais pequenas e verifica\u00e7\u00e3o mais r\u00e1pida. A biblioteca <code>jsonwebtoken<\/code> vers\u00e3o 9.0+ suporta tamb\u00e9m o algoritmo EdDSA (Ed25519) nativamente.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ jwt-sign.js\nconst jwt    = require('jsonwebtoken');\nconst crypto = require('crypto');\nconst fs     = require('fs');\nconst path   = require('path');\n\n\/\/ Gerar chaves ECDSA P-256 espec\u00edficas para JWT\nconst { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {\n  namedCurve: 'prime256v1',\n  privateKeyEncoding: { type: 'pkcs8', format: 'pem' },\n  publicKeyEncoding:  { type: 'spki', format: 'pem' }\n});\n\nconst keysDir = path.join(__dirname, 'keys');\nif (!fs.existsSync(keysDir)) fs.mkdirSync(keysDir);\nfs.writeFileSync(path.join(keysDir, 'jwt-private.pem'), privateKey, { mode: 0o600 });\nfs.writeFileSync(path.join(keysDir, 'jwt-public.pem'), publicKey);\n\n\/\/ Passo 10a: Emitir JWT com ES256\nfunction emitirToken(payload, expiracaoSegundos = 3600) {\n  const token = jwt.sign(payload, privateKey, {\n    algorithm: 'ES256',         \/\/ ECDSA P-256 + SHA-256 (ES256 em mai\u00fasculas!)\n    expiresIn: expiracaoSegundos,\n    issuer:    'api.shattered.io',\n    audience:  'api-clients'\n  });\n\n  \/\/ Inspecionar o token\n  const [header, body] = token.split('.').slice(0, 2)\n    .map(p => JSON.parse(Buffer.from(p, 'base64url').toString('utf8')));\n\n  console.log('Header JWT:', JSON.stringify(header));\n  \/\/ {\"alg\":\"ES256\",\"typ\":\"JWT\"}\n  console.log('Payload JWT:', JSON.stringify(body));\n  console.log('Token (primeiros 80 chars):', token.substring(0, 80) + '...');\n  return token;\n}\n\n\/\/ Passo 10b: Verificar JWT com valida\u00e7\u00e3o expl\u00edcita de algoritmo\nfunction verificarToken(token) {\n  try {\n    const payload = jwt.verify(token, publicKey, {\n      algorithms: ['ES256'],    \/\/ OBRIGAT\u00d3RIO: previne ataque alg:none e downgrade\n      issuer:    'api.shattered.io',\n      audience:  'api-clients'\n    });\n    console.log('JWT valido. Utilizador:', payload.sub, '| Expira:', new Date(payload.exp * 1000).toISOString());\n    return payload;\n  } catch (err) {\n    console.error('JWT invalido:', err.message);\n    return null;\n  }\n}\n\n\/\/ Demonstra\u00e7\u00e3o\nconsole.log('\\n--- Emitir token ES256 ---');\nconst token = emitirToken({ sub: 'user-12345', role: 'admin', email: 'user@example.com' });\n\nconsole.log('\\n--- Verificar token original ---');\nverificarToken(token);\n\nconsole.log('\\n--- Verificar token adulterado ---');\nconst tokenAdulterado = token.slice(0, -5) + 'XXXXX';\nverificarToken(tokenAdulterado);\n\nmodule.exports = { emitirToken, verificarToken };<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Execute com <code>node jwt-sign.js<\/code>. O header confirma <code>\"alg\":\"ES256\"<\/code>. A verifica\u00e7\u00e3o do token adulterado imprime \"JWT invalido: invalid signature\".<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O campo <code>algorithms: ['ES256']<\/code> na verifica\u00e7\u00e3o \u00e9 obrigat\u00f3rio. Omiti-lo permite que um atacante altere o header do JWT para <code>\"alg\":\"none\"<\/code> e submeta um token sem assinatura v\u00e1lido. Este ataque, documentado desde 2015, ainda afeta implementa\u00e7\u00f5es que omitem a valida\u00e7\u00e3o expl\u00edcita do algoritmo. Para usar Ed25519 em JWTs, substitua <code>'ES256'<\/code> por <code>'EdDSA'<\/code> e gere chaves com <code>crypto.generateKeyPairSync('ed25519')<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-11-gestao-de-chaves-pem-em-producao\">Passo 11: Gest\u00e3o de Chaves PEM em Produ\u00e7\u00e3o<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Em produ\u00e7\u00e3o, as chaves n\u00e3o s\u00e3o geradas em runtime. S\u00e3o geradas uma vez, armazenadas com seguran\u00e7a e carregadas na inicializa\u00e7\u00e3o da aplica\u00e7\u00e3o. Uma estrat\u00e9gia correta inclui rota\u00e7\u00e3o peri\u00f3dica, separa\u00e7\u00e3o entre ambientes (dev\/staging\/prod) e nunca armazenar chaves privadas em texto simples em sistemas de CI p\u00fablicos.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ utils\/key-manager.js\nconst crypto = require('crypto');\nconst fs     = require('fs');\n\n\/\/ Carregar chave privada de ficheiro ou vari\u00e1vel de ambiente\n\/\/ Em produ\u00e7\u00e3o: KEY_PRIVATE_B64=base64_da_chave_pem\nfunction carregarChavePrivada(caminhoOuEnvVar) {\n  if (process.env[caminhoOuEnvVar]) {\n    \/\/ Vari\u00e1vel de ambiente em base64 (para Kubernetes secrets, AWS SSM, etc.)\n    const pem = Buffer.from(process.env[caminhoOuEnvVar], 'base64').toString('utf8');\n    return crypto.createPrivateKey(pem);\n  }\n  \/\/ Ficheiro local (desenvolvimento e staging)\n  const pem = fs.readFileSync(caminhoOuEnvVar, 'utf8');\n  return crypto.createPrivateKey(pem);\n}\n\nfunction carregarChavePublica(caminhoOuEnvVar) {\n  if (process.env[caminhoOuEnvVar]) {\n    const pem = Buffer.from(process.env[caminhoOuEnvVar], 'base64').toString('utf8');\n    return crypto.createPublicKey(pem);\n  }\n  const pem = fs.readFileSync(caminhoOuEnvVar, 'utf8');\n  return crypto.createPublicKey(pem);\n}\n\n\/\/ Exportar chave p\u00fablica em m\u00faltiplos formatos\nfunction exportarChavePublica(pubKeyObj) {\n  const pem = pubKeyObj.export({ type: 'spki', format: 'pem' });\n  const jwk = pubKeyObj.export({ format: 'jwk' });\n\n  console.log('Tipo de chave:', pubKeyObj.asymmetricKeyType);         \/\/ 'ec' ou 'ed25519'\n  console.log('Detalhes:', pubKeyObj.asymmetricKeyDetails);           \/\/ { namedCurve: 'prime256v1' }\n  console.log('JWK:', JSON.stringify(jwk, null, 2));\n  \/\/ Para ECDSA P-256:\n  \/\/ { \"kty\": \"EC\", \"crv\": \"P-256\", \"x\": \"...\", \"y\": \"...\" }\n  \/\/ Para Ed25519:\n  \/\/ { \"kty\": \"OKP\", \"crv\": \"Ed25519\", \"x\": \"...\" }\n\n  return { pem, jwk };\n}\n\nmodule.exports = { carregarChavePrivada, carregarChavePublica, exportarChavePublica };<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O formato JWK (JSON Web Key) \u00e9 particularmente \u00fatil para expor chaves p\u00fablicas via endpoint JWKS (JSON Web Key Set), o padr\u00e3o OAuth 2.0 que permite a servi\u00e7os externos verificar JWTs sem receber a chave p\u00fablica por canal privado. O endpoint convencional \u00e9 <code>\/.well-known\/jwks.json<\/code>. Para rota\u00e7\u00e3o de chaves sem downtime, mantenha m\u00faltiplas chaves no JWKS com campos <code>kid<\/code> diferentes e inclua o <code>kid<\/code> no header de cada JWT emitido.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para produ\u00e7\u00e3o com requisitos de seguran\u00e7a elevados, considere AWS KMS ou Azure Key Vault: as opera\u00e7\u00f5es de assinatura ocorrem dentro do HSM (Hardware Security Module) sem nunca exportar a chave privada. O Node.js suporta esta integra\u00e7\u00e3o atrav\u00e9s dos respetivos SDKs, sem altera\u00e7\u00f5es ao c\u00f3digo de verifica\u00e7\u00e3o.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-12-api-express-completa-de-verificacao-de-assinaturas\">Passo 12: API Express Completa de Verifica\u00e7\u00e3o de Assinaturas<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O projeto completo culmina numa API Express que exp\u00f5e quatro endpoints: assinar dados, verificar assinaturas, publicar a chave p\u00fablica em formato JWK, e um health check. Esta API demonstra como integrar assinaturas digitais numa aplica\u00e7\u00e3o real com boas pr\u00e1ticas de seguran\u00e7a.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ api.js - API Express de assinaturas digitais com Ed25519\nconst express = require('express');\nconst crypto  = require('crypto');\nconst fs      = require('fs');\nconst path    = require('path');\n\nconst app = express();\napp.use(express.json({ limit: '10kb' })); \/\/ limitar tamanho do body\n\n\/\/ Carregar chaves Ed25519 na inicializa\u00e7\u00e3o (uma vez, n\u00e3o por request)\nconst keysDir = path.join(__dirname, 'keys');\nlet privKey, pubKey, pubKeyObj;\ntry {\n  privKey   = fs.readFileSync(path.join(keysDir, 'ed25519-private.pem'), 'utf8');\n  pubKey    = fs.readFileSync(path.join(keysDir, 'ed25519-public.pem'), 'utf8');\n  pubKeyObj = crypto.createPublicKey(pubKey); \/\/ parse uma vez para reutilizar\n  console.log('Chaves Ed25519 carregadas. Pronto.');\n} catch (err) {\n  console.error('Erro ao carregar chaves:', err.message);\n  console.error('Execute primeiro: node ed25519.js');\n  process.exit(1);\n}\n\n\/\/ POST \/sign - assinar payload com Ed25519\napp.post('\/sign', (req, res) => {\n  const { dados } = req.body;\n  if (!dados || typeof dados !== 'string' || dados.length > 4096) {\n    return res.status(400).json({ erro: 'Campo \"dados\" obrigatorio (string, max 4096 chars)' });\n  }\n\n  const assinatura = crypto.sign(null, Buffer.from(dados, 'utf8'), privKey);\n\n  res.json({\n    dados,\n    assinatura: assinatura.toString('base64url'), \/\/ base64url sem padding\n    algoritmo:  'Ed25519',\n    tamanho_bytes: assinatura.length              \/\/ sempre 64\n  });\n});\n\n\/\/ POST \/verify - verificar assinatura Ed25519\napp.post('\/verify', (req, res) => {\n  const { dados, assinatura } = req.body;\n  if (!dados || !assinatura) {\n    return res.status(400).json({ erro: 'Campos \"dados\" e \"assinatura\" obrigatorios' });\n  }\n\n  let sigBuffer;\n  try {\n    sigBuffer = Buffer.from(assinatura, 'base64url');\n    if (sigBuffer.length !== 64) throw new Error('Tamanho invalido');\n  } catch {\n    return res.status(400).json({ erro: 'Assinatura base64url invalida (deve ter 64 bytes)' });\n  }\n\n  \/\/ crypto.verify() usa comparacao em tempo constante internamente\n  const valida = crypto.verify(null, Buffer.from(dados, 'utf8'), pubKeyObj, sigBuffer);\n\n  res.json({ valida, algoritmo: 'Ed25519' });\n});\n\n\/\/ GET \/public-key - chave publica em formato JWKS\napp.get('\/public-key', (_req, res) => {\n  const jwk = pubKeyObj.export({ format: 'jwk' });\n  res.json({\n    keys: [{ ...jwk, use: 'sig', kid: 'ed25519-2026-01', alg: 'EdDSA' }]\n  });\n});\n\n\/\/ GET \/health\napp.get('\/health', (_req, res) => res.json({ estado: 'ok', algoritmo: 'Ed25519' }));\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`API de assinaturas em http:\/\/localhost:${PORT}`);\n  console.log('Endpoints: POST \/sign | POST \/verify | GET \/public-key | GET \/health');\n});\n\nmodule.exports = app;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Execute com <code>node api.js<\/code> e teste com curl:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Assinar dados\ncurl -s -X POST http:\/\/localhost:3000\/sign \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"dados\":\"Transacao TX-001: 500 EUR\"}' | python3 -m json.tool\n\n# Resposta esperada:\n# {\n#   \"dados\": \"Transacao TX-001: 500 EUR\",\n#   \"assinatura\": \"abc123...xyz\" (86 caracteres base64url para 64 bytes Ed25519),\n#   \"algoritmo\": \"Ed25519\",\n#   \"tamanho_bytes\": 64\n# }\n\n# Verificar (substituir ASSINATURA pelo valor retornado pelo \/sign)\ncurl -s -X POST http:\/\/localhost:3000\/verify \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"dados\":\"Transacao TX-001: 500 EUR\",\"assinatura\":\"ASSINATURA\"}' | python3 -m json.tool\n\n# Obter chave p\u00fablica em JWKS\ncurl -s http:\/\/localhost:3000\/public-key | python3 -m json.tool<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"5-erros-comuns-que-comprometem-implementacoes-de-assinaturas-digitais\">5 Erros Comuns que Comprometem Implementa\u00e7\u00f5es de Assinaturas Digitais<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A maioria das vulnerabilidades em sistemas de assinaturas digitais n\u00e3o vem de fraquezas matem\u00e1ticas, mas de erros de implementa\u00e7\u00e3o. Estes s\u00e3o os cinco erros mais frequentes em c\u00f3digo Node.js e como evit\u00e1-los:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Erro 1: Passar 'sha256' como algoritmo para chaves Ed25519.<\/strong> <code>crypto.sign('sha256', dados, ed25519Key)<\/code> lan\u00e7a <code>Error [ERR_CRYPTO_INVALID_DIGEST]: Invalid digest algorithm sha256 for key type ed25519<\/code>. Ed25519 incorpora SHA-512 internamente e n\u00e3o permite sele\u00e7\u00e3o de hash pelo utilizador. Use sempre <code>crypto.sign(null, ...)<\/code> e <code>crypto.verify(null, ...)<\/code> com chaves Ed25519. ECDSA aceita um hash como primeiro argumento; Ed25519 n\u00e3o.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Erro 2: Reutilizar nonce em implementa\u00e7\u00f5es manuais de ECDSA.<\/strong> O Node.js usa OpenSSL, que gera nonces seguros automaticamente. Mas se alguma vez implementar ECDSA manualmente (por exemplo, em c\u00f3digo de blockchain) com um nonce constante ou um PRNG fraco, a chave privada pode ser extra\u00edda matematicamente de apenas duas assinaturas. Este ataque extraiu a chave de consolas PlayStation 3 em 2010. Solu\u00e7\u00f5es: confie no m\u00f3dulo crypto nativo para ECDSA, ou migre para Ed25519 que \u00e9 determin\u00edstico por design.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Erro 3: Misturar formatos DER e IEEE P1363.<\/strong> Uma assinatura ECDSA gerada com <code>dsaEncoding: 'der'<\/code> retorna <code>false<\/code> quando verificada com <code>dsaEncoding: 'ieee-p1363'<\/code>, mesmo com chave e dados corretos. Documente sempre o formato de codifica\u00e7\u00e3o como parte da especifica\u00e7\u00e3o da API. A regra simples: DER para OpenSSL\/TLS\/certificados, IEEE P1363 para WebCrypto e JOSE.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Erro 4: Encoding inconsistente entre assinatura e verifica\u00e7\u00e3o.<\/strong> Se assinou com <code>Buffer.from(dados)<\/code> (UTF-8 por padr\u00e3o) mas verifica com <code>Buffer.from(dados, 'base64')<\/code>, a verifica\u00e7\u00e3o falha. O encoding dos dados deve ser id\u00eantico em ambos os lados. Documente o encoding como parte do protocolo e teste explicitamente com dados que contenham caracteres n\u00e3o-ASCII (acentos, emoji) para detetar problemas de encoding precocemente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Erro 5: Aceitar a chave p\u00fablica no mesmo payload que os dados e a assinatura.<\/strong> Verificar uma assinatura com a chave p\u00fablica errada retorna <code>false<\/code>, mas c\u00f3digo que aceita chaves p\u00fablicas de fontes n\u00e3o autenticadas pode ser enganado por um atacante que forne\u00e7a a sua pr\u00f3pria chave. A chave p\u00fablica usada na verifica\u00e7\u00e3o deve vir sempre de uma fonte autenticada: certificado X.509, endpoint JWKS autenticado ou configura\u00e7\u00e3o local imut\u00e1vel. Nunca aceite chaves p\u00fablicas no mesmo canal n\u00e3o autenticado que transporta os dados assinados.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"resolucao-de-problemas-8-erros-frequentes-em-node-js\">Resolu\u00e7\u00e3o de Problemas: 8 Erros Frequentes em Node.js<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Esta sec\u00e7\u00e3o cobre as mensagens de erro de runtime mais comuns e como resolver cada uma.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 1: <code>Error: error:0909006C:PEM routines:get_name:no start line<\/code><\/strong><br>O ficheiro PEM est\u00e1 corrompido, truncado ou cont\u00e9m caracteres extra (BOM, espa\u00e7os antes do cabe\u00e7alho). Verifique com <code>openssl pkey -in private.pem -noout<\/code>. O ficheiro deve come\u00e7ar exatamente com <code>-----BEGIN PRIVATE KEY-----<\/code> sem qualquer car\u00e1cter anterior. Se o ficheiro foi criado no Windows, podem existir carriage returns (<code>\\r<\/code>) que corrompem a leitura. Regenere as chaves em ambiente limpo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 2: <code>Error [ERR_CRYPTO_INVALID_DIGEST]: Invalid digest algorithm sha256 for key type ed25519<\/code><\/strong><br>Passou <code>'sha256'<\/code> como primeiro argumento de <code>crypto.sign()<\/code> com uma chave Ed25519. Mude para <code>null<\/code>. Esta \u00e9 a diferen\u00e7a mais comum entre desenvolvedores que migram de ECDSA para Ed25519.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 3: <code>JsonWebTokenError: invalid algorithm<\/code><\/strong><br>O algoritmo passado \u00e0 biblioteca <code>jsonwebtoken<\/code> est\u00e1 em formato errado. Use <code>'ES256'<\/code> em mai\u00fasculas, n\u00e3o <code>'es256'<\/code>. Para Ed25519, use <code>'EdDSA'<\/code>, n\u00e3o <code>'ed25519'<\/code>. A biblioteca \u00e9 sens\u00edvel ao case dos nomes de algoritmo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 4: <code>crypto.verify()<\/code> retorna <code>false<\/code> com assinatura aparentemente correta<\/strong><br>Causas por ordem de frequ\u00eancia: (1) encoding diferente entre assinatura e verifica\u00e7\u00e3o (DER vs P1363), (2) encoding dos dados diferente (UTF-8 vs base64), (3) assinatura truncada na transmiss\u00e3o HTTP porque foi enviada como base64 com <code>+<\/code> e <code>\/<\/code> que foram alterados por URL encoding, use base64url em vez de base64 para URLs, (4) chave p\u00fablica diferente da usada na assinatura.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 5: <code>Error: error:06089094:digital envelope routines:EVP_DigestInit_ex:invalid digest<\/code><\/strong><br>Usando SHA-1 ou MD5 com ECDSA. O OpenSSL 3.x, inclu\u00eddo no Node.js 18+, desativou estes algoritmos por padr\u00e3o. Use <code>'sha256'<\/code> para P-256 ou <code>'sha384'<\/code> para P-384. Se o c\u00f3digo usa SHA-1 por legado, a migra\u00e7\u00e3o para SHA-256 \u00e9 obrigat\u00f3ria.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 6: Chave gerada com formato <code>sec1<\/code> n\u00e3o funciona com <code>jsonwebtoken<\/code><\/strong><br>A biblioteca espera chaves privadas EC em formato PKCS8. Se gerou com <code>type: 'sec1'<\/code>, converta: <code>openssl pkcs8 -topk8 -nocrypt -in sec1-key.pem -out pkcs8-key.pem<\/code>. Para evitar este problema, use sempre <code>type: 'pkcs8'<\/code> na gera\u00e7\u00e3o.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 7: Performance lenta com muitas verifica\u00e7\u00f5es por segundo<\/strong><br>Criar o objeto de chave (<code>crypto.createPublicKey()<\/code>) a cada verifica\u00e7\u00e3o tem overhead de parse PEM. Em c\u00f3digo de alto throughput, crie o objeto uma vez na inicializa\u00e7\u00e3o e reutilize-o. O parse PEM adiciona aproximadamente 0,1ms por opera\u00e7\u00e3o: com 10.000 verifica\u00e7\u00f5es\/segundo, s\u00e3o 1 segundo de overhead por segundo totalmente evit\u00e1vel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Problema 8: <code>ERR_OSSL_EVP_UNSUPPORTED<\/code> no Node.js 17 ou superior<\/strong><br>O OpenSSL 3.0 inclu\u00eddo a partir do Node.js 17 desativou algoritmos legados. A vari\u00e1vel de ambiente <code>NODE_OPTIONS=--openssl-legacy-provider<\/code> \u00e9 uma solu\u00e7\u00e3o tempor\u00e1ria de migra\u00e7\u00e3o, nunca de produ\u00e7\u00e3o. A solu\u00e7\u00e3o correta \u00e9 atualizar o c\u00f3digo para usar algoritmos modernos (SHA-256, SHA-384, AES-256-GCM).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"dicas-avancadas-para-implementacoes-em-producao\">Dicas Avan\u00e7adas para Implementa\u00e7\u00f5es em Produ\u00e7\u00e3o<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Compara\u00e7\u00e3o em tempo constante para verifica\u00e7\u00e3o de tokens.<\/strong> A fun\u00e7\u00e3o <code>crypto.verify()<\/code> do Node.js usa compara\u00e7\u00e3o em tempo constante internamente, o que protege contra ataques de timing. Se implementar verifica\u00e7\u00e3o personalizada que compara buffers byte a byte, use sempre <code>crypto.timingSafeEqual(a, b)<\/code> em vez de <code>a.equals(b)<\/code> ou operadores de igualdade normais. A compara\u00e7\u00e3o normal termina mais cedo quando encontra o primeiro byte diferente, revelando informa\u00e7\u00e3o sobre o conte\u00fado esperado.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Rota\u00e7\u00e3o de chaves sem downtime com JWKS.<\/strong> Publique v\u00e1rias chaves p\u00fablicas no endpoint JWKS com diferentes valores de <code>kid<\/code> (Key ID). O header do JWT inclui o <code>kid<\/code> que identifica qual chave usar na verifica\u00e7\u00e3o. Quando rodar chaves, adicione a nova chave ao JWKS antes de come\u00e7ar a emitir tokens com ela. Mantenha a chave antiga no JWKS durante o per\u00edodo de expira\u00e7\u00e3o m\u00e1xima dos tokens existentes (tipicamente 24 a 48 horas).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Verifica\u00e7\u00e3o ass\u00edncrona com WebCrypto API.<\/strong> O Node.js exp\u00f5e a WebCrypto API atrav\u00e9s de <code>require('crypto').webcrypto<\/code> ou <code>globalThis.crypto<\/code> no Node.js 19+. Esta API \u00e9 ass\u00edncrona e compat\u00edvel com browsers, ideal para servidores com alto volume de verifica\u00e7\u00f5es que n\u00e3o devem bloquear o event loop. Use <code>webcrypto.subtle.verify({name: 'Ed25519'}, ...<\/code>) para Ed25519 ou <code>{name: 'ECDSA', hash: 'SHA-256'}<\/code> para P-256.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Assinaturas sobre streams para ficheiros grandes.<\/strong> Para assinar ficheiros grandes sem carregar tudo na mem\u00f3ria, use a API de stream: <code>const sign = crypto.createSign('sha256')<\/code>, seguido de <code>sign.update(chunk)<\/code> para cada fragmento do ficheiro, e finalmente <code>sign.sign(privateKey)<\/code>. Esta abordagem processa o conte\u00fado incrementalmente com footprint de mem\u00f3ria constante, independentemente do tamanho do ficheiro. Ed25519 n\u00e3o suporta streaming nativo, por isso para ficheiros grandes com Ed25519 calcule primeiro o hash SHA-512 do ficheiro e assine o hash.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Contexto de assinatura Ed25519 no Node.js 26+.<\/strong> O Node.js 26.0.0 adicionou suporte ao par\u00e2metro de contexto para Ed25519, definido na RFC 8032. Este par\u00e2metro vincula a assinatura a um contexto espec\u00edfico (por exemplo, <code>'autenticacao-api-v2'<\/code>), impedindo que assinaturas v\u00e1lidas de um contexto sejam reutilizadas noutro. Esta funcionalidade \u00e9 relevante para sistemas multi-contexto onde o mesmo par de chaves \u00e9 usado em diferentes protocolos ou vers\u00f5es de API.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conformidade-com-padroes-nist-e-rfc-em-2026\">Conformidade com Padr\u00f5es NIST e RFC em 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para implementa\u00e7\u00f5es em ambientes regulados, \u00e9 importante alinhar as escolhas de algoritmo com os padr\u00f5es oficiais em vigor. O <a href=\"https:\/\/csrc.nist.gov\/publications\/detail\/fips\/186\/5\/final\" target=\"_blank\" rel=\"noopener noreferrer\">NIST FIPS 186-5<\/a>, publicado em fevereiro de 2023, \u00e9 o documento de refer\u00eancia atual para assinaturas digitais aprovadas pelo governo americano. Inclui ECDSA com curvas NIST (P-256, P-384, P-521) e EdDSA incluindo Ed25519 e Ed448. RSA-1024 e DSA foram descontinuados; RSA-2048 ainda \u00e9 permitido mas desencorajado para novas implementa\u00e7\u00f5es.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A WebCrypto API W3C, especificada em <a href=\"https:\/\/www.w3.org\/TR\/WebCryptoAPI\/\" target=\"_blank\" rel=\"noopener noreferrer\">w3.org\/TR\/WebCryptoAPI<\/a>, define a interface padr\u00e3o para opera\u00e7\u00f5es criptogr\u00e1ficas em browsers e ambientes JavaScript. O Node.js implementa esta especifica\u00e7\u00e3o atrav\u00e9s de <code>crypto.webcrypto<\/code>, garantindo c\u00f3digo port\u00e1vel entre servidor e cliente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para implementa\u00e7\u00f5es p\u00f3s-qu\u00e2nticas futuras, o NIST selecionou em 2024 os algoritmos ML-DSA (baseado em Dilithium) e SLH-DSA para assinaturas resistentes a computadores qu\u00e2nticos. Node.js ainda n\u00e3o suporta estes algoritmos nativamente em 2026, mas a migra\u00e7\u00e3o ser\u00e1 necess\u00e1ria em contextos de longa dura\u00e7\u00e3o. ECDSA e Ed25519 s\u00e3o adequados para todos os casos de uso atuais.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq-perguntas-frequentes-sobre-assinaturas-digitais-em-node-js\">FAQ: Perguntas Frequentes sobre Assinaturas Digitais em Node.js<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"ed25519-e-compativel-com-todos-os-sistemas-que-verificam-jwts\">Ed25519 \u00e9 compat\u00edvel com todos os sistemas que verificam JWTs?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">N\u00e3o. O algoritmo EdDSA para JWTs \u00e9 suportado por <code>jsonwebtoken<\/code> 9.0+, <code>jose<\/code> 4.0+, e bibliotecas modernas em Go, Python e Java. Sistemas mais antigos ou algumas implementa\u00e7\u00f5es empresariais suportam apenas RS256 e ES256. Se a interoperabilidade com sistemas externos \u00e9 necess\u00e1ria, verifique o suporte a EdDSA antes de adotar. Para sistemas internos onde controla ambos os lados, Ed25519 \u00e9 sempre a escolha prefer\u00edvel.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"qual-a-diferenca-entre-assinar-e-encriptar-dados\">Qual a diferen\u00e7a entre assinar e encriptar dados?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Assinar e encriptar s\u00e3o opera\u00e7\u00f5es distintas com objetivos diferentes. Assinar garante autenticidade e integridade: qualquer pessoa com a chave p\u00fablica pode verificar a origem dos dados. Encriptar garante confidencialidade: apenas o titular da chave privada pode decifrar. \u00c9 poss\u00edvel fazer ambos em simult\u00e2neo (assinar primeiro, depois encriptar), mas s\u00e3o passos independentes. Para proteger dados em repouso, use <a href=\"\/aes-256-encryption-nodejs\/\">AES-256<\/a>. Para autenticar a origem, use assinaturas digitais.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"quanto-tempo-demora-uma-operacao-de-assinatura-ed25519-em-node-js\">Quanto tempo demora uma opera\u00e7\u00e3o de assinatura Ed25519 em Node.js?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Em hardware moderno (CPU x86-64), Ed25519 assina em menos de 0,1 milissegundos e verifica em menos de 0,2 milissegundos. Isto permite mais de 10.000 opera\u00e7\u00f5es por segundo num \u00fanico thread Node.js. RSA-2048 assina em cerca de 1 milissegundo e verifica em cerca de 0,05 milissegundos (verifica\u00e7\u00e3o mais r\u00e1pida porque usa o expoente p\u00fablico pequeno). ECDSA P-256 fica entre Ed25519 e RSA em performance de assinatura. Para APIs REST com centenas de pedidos por segundo, qualquer um dos tr\u00eas \u00e9 suficientemente r\u00e1pido; a diferen\u00e7a torna-se relevante em sistemas que processam milhares de assinaturas por segundo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"e-seguro-expor-a-chave-publica-num-endpoint-publico\">\u00c9 seguro expor a chave p\u00fablica num endpoint p\u00fablico?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sim. A chave p\u00fablica pode e deve ser publicada. \u00c9 matematicamente imposs\u00edvel derivar a chave privada a partir da chave p\u00fablica com Ed25519 ou ECDSA P-256, mesmo com os computadores mais potentes dispon\u00edveis em 2026. O endpoint JWKS (<code>\/.well-known\/jwks.json<\/code>) \u00e9 um padr\u00e3o OAuth 2.0 concebido precisamente para publicar chaves p\u00fablicas. O que deve ser protegido com cuidado extremo \u00e9 a chave privada, que nunca deve sair do servidor onde \u00e9 usada.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"devo-usar-ecdsa-p-256-ou-p-384-para-novos-projetos\">Devo usar ECDSA P-256 ou P-384 para novos projetos?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">P-256 oferece 128-bit de seguran\u00e7a, equivalente a RSA-3072, e \u00e9 suficiente para a vasta maioria das aplica\u00e7\u00f5es at\u00e9 pelo menos 2030 segundo estimativas NIST. P-384 oferece 192-bit de seguran\u00e7a e \u00e9 exigido por regulamenta\u00e7\u00f5es de alguns setores (defesa e sa\u00fade em certas jurisdi\u00e7\u00f5es). Para APIs e JWTs em projetos comerciais normais, P-256 (ES256) \u00e9 a escolha padr\u00e3o pela melhor performance e compatibilidade universal. Para ambientes com requisitos regulat\u00f3rios espec\u00edficos de 192-bit, use P-384 (ES384). Para novos projetos sem restri\u00e7\u00f5es de interoperabilidade, Ed25519 supera ambos.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"como-verificar-assinaturas-de-webhooks-de-servicos-como-stripe-ou-github\">Como verificar assinaturas de webhooks de servi\u00e7os como Stripe ou GitHub?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Stripe e GitHub usam HMAC-SHA256, n\u00e3o ECDSA ou Ed25519, para assinar webhooks. O processo usa uma chave secreta partilhada entre o servi\u00e7o e o seu servidor, n\u00e3o um par de chaves assim\u00e9trico. O Node.js verifica com <code>crypto.createHmac('sha256', secret).update(payload).digest('hex')<\/code> e compara\u00e7\u00e3o em tempo constante com <code>crypto.timingSafeEqual()<\/code>. Consulte o artigo de <a href=\"\/hmac-sha256-nodejs\/\">HMAC-SHA256 em Node.js<\/a> para um guia completo sobre verifica\u00e7\u00e3o de webhooks com HMAC.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"as-assinaturas-digitais-sao-vulneraveis-a-computacao-quantica\">As assinaturas digitais s\u00e3o vulner\u00e1veis \u00e0 computa\u00e7\u00e3o qu\u00e2ntica?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sim. Um computador qu\u00e2ntico suficientemente potente poderia quebrar ECDSA e Ed25519 usando o algoritmo de Shor. No entanto, computadores qu\u00e2nticos com capacidade criptograficamente relevante n\u00e3o existem em 2026 e as estimativas mais conservadoras apontam para 2030 ou posterior. O NIST est\u00e1 a padronizar algoritmos p\u00f3s-qu\u00e2nticos (ML-DSA, SLH-DSA) que resistem a ataques qu\u00e2nticos. Para novos projetos de longa dura\u00e7\u00e3o, monitorize a disponibilidade destes algoritmos no OpenSSL e Node.js. Para implementa\u00e7\u00f5es atuais, ECDSA e Ed25519 s\u00e3o seguros.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"cobertura-relacionada\">Cobertura Relacionada<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para aprofundar os temas abordados neste tutorial, consulte estes recursos do arquivo shattered.io:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/nodejs-crypto-module\/\">M\u00f3dulo Crypto do Node.js: 12 Passos, 30 Min [2026]<\/a> - guia completo ao m\u00f3dulo nativo com hashing, cifragem sim\u00e9trica e HMAC<\/li>\n<li><a href=\"\/hmac-sha256-nodejs\/\">HMAC-SHA256 em Node.js: 10 Passos, 20 Min [2026]<\/a> - autentica\u00e7\u00e3o de mensagens com chave secreta partilhada para webhooks<\/li>\n<li><a href=\"\/rsa-encryption-nodejs\/\">Encripta\u00e7\u00e3o RSA em Node.js: 11 Passos [2026]<\/a> - criptografia assim\u00e9trica com RSA-2048 e RSA-4096 para compara\u00e7\u00e3o com ECDSA<\/li>\n<li><a href=\"\/jwt-authentication-nodejs\/\">Autentica\u00e7\u00e3o JWT em Node.js: 10 Passos [2026]<\/a> - tokens de sess\u00e3o seguros com RS256 e ES256 em aplica\u00e7\u00f5es Express<\/li>\n<li><a href=\"\/aes-256-encryption-nodejs\/\">Encripta\u00e7\u00e3o AES-256 em Node.js: 12 Passos [2026]<\/a> - cifra sim\u00e9trica de alto desempenho com GCM para dados em repouso<\/li>\n<li><a href=\"\/autenticacao-ssh-chaves\/\">Autentica\u00e7\u00e3o SSH com Chaves Ed25519: 12 Passos [2026]<\/a> - uso pr\u00e1tico de Ed25519 para acesso seguro a servidores remotos<\/li>\n<li><a href=\"\/cryptography\/\">Criptografia: Guia Completo [2026]<\/a> - pilar do cluster com todos os recursos sobre criptografia moderna<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Assinaturas digitais garantem que uma mensagem ou ficheiro n\u00e3o foi alterado e que foi criado por quem afirma ser o autor. Em Node.js, o m\u00f3dulo nativo crypto suporta dois algoritmos\u2026<\/p>\n","protected":false},"author":9,"featured_media":128,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-127","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cryptography"],"_links":{"self":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts\/127","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/comments?post=127"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts\/127\/revisions"}],"predecessor-version":[{"id":129,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts\/127\/revisions\/129"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/media\/128"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/media?parent=127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/categories?post=127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/tags?post=127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}