O código Java assina por meio de java.security.Signature, KeyPairGenerator e KeyFactory, e os próprios provedores do JDK fazem o trabalho: o SunEC para curvas elípticas e EdDSA, além dos provedores mais novos de ML-KEM (javax.crypto.KEM, JDK 24) e de ML-DSA. Android, Kotlin e todos os frameworks da JVM se apoiam na mesma API, então os padrões dela aparecem no Spring, no Keycloak e em inúmeros SDKs.
O nome do algoritmo decide a codificação. SHA256withECDSA produz e espera ASN.1 DER; SHA256withECDSAinP1363Format usa o r‖s bruto de 64 bytes de que o JWS e o WebCrypto precisam, como no exemplo. Trocar um pelo outro tem efeitos assimétricos: uma assinatura bruta passada a SHA256withECDSA lança SignatureException, enquanto uma assinatura DER passada à variante P1363 simplesmente retorna false. NONEwithECDSA assina um digest que você calculou — e essa assinatura foi verificada com SHA256withECDSA contra a mensagem —, e o nome do hash é independente da curva; por isso, SHA256withECDSA com uma chave P-384 é permitido e falha com contrapartes que esperam SHA-384.
O SunEC do JDK 25 oferece apenas secp256r1, secp384r1 e secp521r1 para ECDSA: P-224, secp256k1 e as curvas Brainpool geraram erros, e é por isso que o código de Bitcoin e Ethereum na JVM usa o Bouncy Castle ou o web3j. Os nonces são aleatórios, o s não é normalizado e a verificação aceita s em qualquer uma das duas metades; por isso, as assinaturas destinadas a contrapartes que exigem low-S estrito precisam ter o s substituído por n − s.
As chaves entram como X.509 SPKI DER (X509EncodedKeySpec), PKCS#8 DER (PKCS8EncodedKeySpec) ou componentes brutos (ECPrivateKeySpec). O suporte a PEM chegou como API em versão prévia no JDK 25 (java.security.PEMDecoder, JEP 470), que no teste só decodificou uma chave pública com --enable-preview; o código de produção ainda remove a armadura e decodifica o base64. O suporte pós-quântico é real: o encapsulamento com ML-KEM-512/768/1024 e a assinatura e a verificação com ML-DSA-44/65/87 funcionaram; o SLH-DSA não está neste JDK.
Compare codificações de assinatura com o conversor DER ⇄ bruto e inspecione JARs assinados com o inspetor de assinaturas JAR.