O crypto.subtle é a interface de criptografia padrão da plataforma web. As mesmas chamadas — generateKey, importKey, sign, verify, exportKey — funcionam no Chromium, no Firefox e no WebKit e em runtimes de servidor como o Node.js, o que faz dele a escolha natural para código que precisa rodar dos dois lados. Ele só existe em contextos seguros: em uma página servida por http simples, crypto.subtle é undefined.
A especificação fixa o formato da assinatura ECDSA em r‖s bruto, 64 bytes na P-256, e o teste encontrou exatamente isso nos quatro motores. Isso coincide com JWS e JWT, então tokens ES256 não precisam de conversão, mas não coincide com o OpenSSL, o SHA256withECDSA do Java, o SignASN1 do Go nem o node:crypto, que usam todos ASN.1 DER. Como uma assinatura DER passada a verify() retorna um simples false em vez de um erro, a incompatibilidade parece uma falsificação; converta com o conversor DER ⇄ bruto ou teste as duas formas no verificador de assinaturas.
As chaves são a outra fonte comum de atrito. importKey("spki") recebe bytes DER, nunca texto PEM; "raw" recebe o ponto SEC1, e a forma comprimida é aceita pelo Node.js, pelo Chromium e pelo Firefox, mas recusada pelo WebKit — por isso, em código portável, envie o ponto não comprimido. Chaves privadas só entram como PKCS#8 DER ou JWK — os arquivos SEC1 EC PRIVATE KEY que o OpenSSL grava por padrão precisam ser convertidos para PKCS#8 antes, por exemplo com o conversor de JWK.
A lista de curvas se resume a P-256, P-384 e P-521: secp256k1 e Brainpool geram NotSupportedError em todos os motores, então chaves de Bitcoin e Ethereum precisam de uma biblioteca como a @noble/curves. O Ed25519 funciona nos quatro motores; o Ed448, só no Node.js. O suporte pós-quântico é desigual: o Node.js 24.22 implementa ML-DSA e ML-KEM com um aviso de recurso experimental, o Chromium 156 aceita ML-DSA e ML-KEM-768/1024, mas não ML-KEM-512, e o Firefox 157 e o WebKit 27.2 não têm nenhum dos dois — detecte o suporte antes de depender deles e experimente os algoritmos no playground de assinaturas pós-quânticas.
O exemplo mostra o padrão portável: informe o hash no objeto de algoritmo, importe chaves PEM externas após a decodificação para DER e lembre-se de que a saída de 64 bytes precisa ser encapsulada em DER antes que um verificador no estilo do OpenSSL consiga lê-la.