Pular para o conteúdo
ecdsa.com
Java · Kotlin

Tink (Java)

O Tink para Java, do Google, uma API de criptografia resistente a uso indevido, construída em torno de keysets e templates: as assinaturas padrão trazem um prefixo Tink de 5 bytes que nenhuma outra biblioteca entende, e os nomes dos templates escondem qual codificação você recebe.
Licença
Apache-2.0
Versão verificada
1.24.0 ·
Código-fonte
github.com/tink-crypto/tink-java
Documentação
developers.google.com/tink

Visão geral da compatibilidade

ECDSAEdDSA

Curvas ECDSA
P-256P-384P-521
EdDSA
Ed25519

O Tink substitui chaves individuais por keysets — conjuntos de chaves com IDs, uma delas marcada como primária — e algoritmos por templates com nome, como ECDSA_P256 ou ED25519. As primitivas (PublicKeySign, PublicKeyVerify) fazem a rotação de chaves de forma transparente, o que é o objetivo do design e a origem de suas surpresas de interoperabilidade. Os keysets são armazenados como JSON do Tink ou protobuf binário, não como PEM.

O template padrão ECDSA_P256 antepõe um prefixo de saída: 0x01 e o ID da chave, de 4 bytes, seguidos de uma assinatura DER. O JDK rejeitou uma assinatura assim com Invalid encoding for signature, e o mesmo fará qualquer verificador que não seja o Tink. Os templates terminados em _WITHOUT_PREFIX ou _RAW removem o prefixo, mas os nomes também escondem a codificação: ECDSA_P256_RAW produziu 64 bytes de r‖s IEEE P1363, não DER — a assinatura foi verificada com o SHA256withECDSAinP1363Format do JDK e falhou com SHA256withECDSA. Escolha o template pela codificação exata que o outro lado espera, como o exemplo faz com ECDSA_P256_IEEE_P1363_WITHOUT_PREFIX, e confira os bytes com o conversor DER ⇄ bruto.

A importação de uma chave pública externa passa por SignaturePemKeysetReader, e a chave resultante só verificou assinaturas DER: a mesma assinatura bruta que a chave P1363 do próprio Tink aceitou falhou com a chave importada de PEM até ser recodificada como DER. As assinaturas usam nonces aleatórios, o s não é normalizado e a verificação aceita high-S; uma entrada inválida lança GeneralSecurityException: invalid signature.

O Tink para Java 1.24.0 oferece templates de ECDSA na P-256, na P-384 e na P-521, de Ed25519 e de RSA (PKCS#1 v1.5 e PSS); nenhum template de ML-DSA ou SLH-DSA está registrado nesta versão (Name ML_DSA_65 does not exist). Para assinaturas pós-quânticas na JVM hoje, veja o Java JCA ou o Bouncy Castle.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/tink/run.sh executou Tink (Java) 1.24.0 na imagem de contêiner maven:3.9.16-eclipse-temurin-25 em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Codificação da assinatura ECDSA

    der

    As assinaturas ECDSA_P256 são DER depois do prefixo de saída.

    Registrado pela verificação
    fact ecdsa-signature-encoding = der
  • Prefixo da assinatura

    tink-5-byte

    Os templates padrão antepõem um prefixo de 5 bytes — 0x01 e o ID da chave —, que o JDK rejeitou com Invalid encoding for signature.

    Registrado pela verificação
    fact output-prefix = tink-5-byte
    evidence output-prefix: ECDSA_P256 signature starts with 0x01 followed by the 4-byte key id, then the DER signature
    evidence output-prefix: ECDSA_P256_IEEE_P1363 signature: 69 bytes (5-byte prefix + 64)
    evidence output-prefix: ECDSA_P256 signature given to the JDK with the prefix: SignatureException: Invalid encoding for signature
  • Codificação do modelo RAW

    ieee-p1363

    ECDSA_P256_RAW produziu 64 bytes de r‖s IEEE P1363 (sem prefixo, não DER); a assinatura foi verificada com SHA256withECDSAinP1363Format.

    Registrado pela verificação
    fact raw-template-encoding = ieee-p1363
    evidence raw-template-encoding: ECDSA_P256_RAW signature: 64 bytes, parses as DER: false
    evidence raw-template-encoding: ECDSA_P256_RAW signature verifies in the JDK's SHA256withECDSAinP1363Format: true
  • Codificação da assinatura após importar PEM

    der

    Uma chave pública importada de PEM por meio de SignaturePemKeysetReader só verificou assinaturas DER; uma assinatura r‖s bruta falhou até ser recodificada como DER.

    Registrado pela verificação
    fact pem-import-encoding = der
  • Nonce do ECDSA

    random

    Nonce aleatório: duas assinaturas da mesma mensagem são diferentes.

    Registrado pela verificação
    fact ecdsa-nonce = random
  • Low-S ao assinar

    either

    O s não é normalizado: valores high-S apareceram entre 64 assinaturas.

    Registrado pela verificação
    fact ecdsa-low-s-sign = either
  • High-S ao verificar

    accepts-high-s

    Uma assinatura com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Entrada do hash na chamada de assinatura

    hashes-message

    As primitivas assinam a mensagem; não há API para assinar um digest.

    Registrado pela verificação
    fact ecdsa-prehash = hashes-message
  • Assinatura ECDSA malformada

    throws

    Uma assinatura malformada lança GeneralSecurityException: invalid signature.

    Registrado pela verificação
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: GeneralSecurityException: invalid signature
  • Curvas ECDSA

    P-256P-384P-521

    Assinatura e verificação funcionaram com os templates P-256, P-384 e P-521.

    Registrado pela verificação
    fact ecdsa-curves = P-256, P-384, P-521
  • EdDSA

    Ed25519

    Templates Ed25519, com e sem o prefixo de saída.

    Registrado pela verificação
    fact eddsa = Ed25519
  • Algoritmos pós-quânticos

    none

    Nenhum template de ML-DSA ou SLH-DSA está registrado no Tink para Java 1.24.0.

    Registrado pela verificação
    fact pqc = none
    evidence pqc: ML_DSA_65: GeneralSecurityException: Name ML_DSA_65 does not exist
    evidence pqc: SLH_DSA_SHA2_128S: GeneralSecurityException: Name SLH_DSA_SHA2_128S does not exist
  • Formatos de chave pública

    spki-pem

    SPKI PEM por meio de SignaturePemKeysetReader; fora isso, as chaves ficam em keysets do Tink.

    Registrado pela verificação
    fact public-key-formats = spki-pem
    evidence public-key-formats: SPKI PEM through SignaturePemKeysetReader, then verifying the RAW template's raw signature: GeneralSecurityException: invalid signature
    evidence public-key-formats: the same signature re-encoded as DER against the PEM-imported key: ok
    evidence public-key-formats: Tink JSON public keyset round trip: ok
  • Formato do conjunto de chaves (keyset)

    tink-jsontink-binary

    Os keysets são serializados como JSON do Tink ou protobuf binário; o keyset público fez o ciclo completo de ida e volta pelo JSON.

    Registrado pela verificação
    fact keyset-format = tink-json, tink-binary

Uso

Compare uma assinatura padrão com prefixo e uma assinatura portável de 64 bytes, gerada por um template sem o prefixo.
import com.google.crypto.tink.KeyTemplates;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.PublicKeySign;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.RegistryConfiguration;
import com.google.crypto.tink.signature.SignatureConfig;
import java.nio.charset.StandardCharsets;

public class Usage {
    public static void main(String[] args) throws Exception {
        SignatureConfig.register();
        byte[] message = "order #1042 approved".getBytes(StandardCharsets.UTF_8);

        // Default templates prepend a 5-byte key-id prefix that no other library expects.
        KeysetHandle tinkOnly = KeysetHandle.generateNew(KeyTemplates.get("ECDSA_P256"));
        byte[] prefixed = tinkOnly.getPrimitive(RegistryConfiguration.get(), PublicKeySign.class).sign(message);

        // For signatures other systems verify, pick a *_WITHOUT_PREFIX / *_RAW template.
        KeysetHandle portable = KeysetHandle.generateNew(KeyTemplates.get("ECDSA_P256_IEEE_P1363_WITHOUT_PREFIX"));
        byte[] raw = portable.getPrimitive(RegistryConfiguration.get(), PublicKeySign.class).sign(message);
        portable.getPublicKeysetHandle().getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class).verify(raw, message);

        System.out.println("prefixed first byte: 0x0" + prefixed[0] + ", portable signature: " + raw.length + " bytes, verified");
    }
}

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner maven:3.9.16-eclipse-temurin-25, e a execução falha se a saída não corresponder.

Configuração
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Comando
java -cp 'lib/*' Usage.java 2>/dev/null
A saída deve corresponder a (expressão regular)
prefixed first byte: 0x01, portable signature: 64 bytes, verified

Bibliotecas