Saltar al contenido
ecdsa.com
Java · Kotlin

Tink (Java)

Tink para Java de Google, una API criptográfica resistente al mal uso construida en torno a keysets y plantillas: las firmas predeterminadas llevan un prefijo Tink de 5 bytes que ninguna otra biblioteca entiende, y los nombres de las plantillas ocultan qué codificación obtienes.
Licencia
Apache-2.0
Versión comprobada
1.24.0 ·
Código fuente
github.com/tink-crypto/tink-java
Documentación
developers.google.com/tink

Compatibilidad de un vistazo

ECDSAEdDSA

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

Tink sustituye las claves individuales por keysets —colecciones de claves con ID, una de ellas marcada como primaria— y los algoritmos por plantillas con nombre, como ECDSA_P256 o ED25519. Las primitivas (PublicKeySign, PublicKeyVerify) rotan las claves de forma transparente, que es el objetivo del diseño y el origen de sus sorpresas de interoperabilidad. Los keysets se almacenan como JSON de Tink o protobuf binario, no como PEM.

La plantilla predeterminada ECDSA_P256 antepone un prefijo de salida: 0x01 y el ID de clave de 4 bytes, seguidos de una firma DER. El JDK rechazó una firma así con Invalid encoding for signature, y lo mismo hará cualquier verificador que no sea Tink. Las plantillas terminadas en _WITHOUT_PREFIX o _RAW eliminan el prefijo, pero los nombres también ocultan la codificación: ECDSA_P256_RAW produjo 64 bytes de r‖s IEEE P1363, no DER; se verificó con SHA256withECDSAinP1363Format del JDK y falló con SHA256withECDSA. Elige la plantilla según la codificación exacta que espera el otro lado, como hace el ejemplo con ECDSA_P256_IEEE_P1363_WITHOUT_PREFIX, y comprueba los bytes con el conversor DER ⇄ en bruto.

Una clave pública externa se importa mediante SignaturePemKeysetReader, y la clave resultante solo verificó firmas DER: la misma firma en bruto que aceptaba la clave P1363 generada por el propio Tink falló con la clave importada desde PEM hasta que se volvió a codificar como DER. Las firmas usan nonces aleatorios, s no se normaliza y la verificación acepta high-S; una entrada no válida lanza GeneralSecurityException: invalid signature.

Tink para Java 1.24.0 ofrece plantillas de ECDSA en P-256, P-384 y P-521, de Ed25519 y de RSA (PKCS#1 v1.5 y PSS); en esta versión no hay registrada ninguna plantilla ML-DSA ni SLH-DSA (Name ML_DSA_65 does not exist). Para firmas poscuánticas en la JVM hoy, consulta Java JCA o Bouncy Castle.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/tink/run.sh ejecutó Tink (Java) 1.24.0 en la imagen de contenedor maven:3.9.16-eclipse-temurin-25 el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Codificación de la firma ECDSA

    der

    Las firmas ECDSA_P256 son DER después del prefijo de salida.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = der
  • Prefijo de la firma

    tink-5-byte

    Las plantillas predeterminadas anteponen un prefijo de 5 bytes —0x01 y el ID de clave—, que el JDK rechazó con Invalid encoding for signature.

    Registrado por la comprobación
    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
  • Codificación de la plantilla RAW

    ieee-p1363

    ECDSA_P256_RAW produjo 64 bytes de r‖s IEEE P1363 (sin prefijo, no DER); se verificó con SHA256withECDSAinP1363Format.

    Registrado por la comprobación
    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
  • Codificación de la firma tras importar PEM

    der

    Una clave pública importada desde PEM mediante SignaturePemKeysetReader solo verificó firmas DER; una firma r‖s en bruto falló hasta que se volvió a codificar como DER.

    Registrado por la comprobación
    fact pem-import-encoding = der
  • Nonce de ECDSA

    random

    Nonce aleatorio: dos firmas del mismo mensaje son distintas.

    Registrado por la comprobación
    fact ecdsa-nonce = random
  • Low-S al firmar

    either

    s no se normaliza: aparecieron valores high-S entre 64 firmas.

    Registrado por la comprobación
    fact ecdsa-low-s-sign = either
  • High-S al verificar

    accepts-high-s

    Se verificó una firma con s sustituido por n − s.

    Registrado por la comprobación
    fact ecdsa-low-s-verify = accepts-high-s
  • De qué calcula el hash la llamada de firma

    hashes-message

    Las primitivas firman el mensaje; no hay ninguna API para firmar un resumen.

    Registrado por la comprobación
    fact ecdsa-prehash = hashes-message
  • Firma ECDSA mal formada

    throws

    Una firma mal formada lanza GeneralSecurityException: invalid signature.

    Registrado por la comprobación
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: GeneralSecurityException: invalid signature
  • Curvas ECDSA

    P-256P-384P-521

    Con las plantillas P-256, P-384 y P-521 se firmó y se verificó.

    Registrado por la comprobación
    fact ecdsa-curves = P-256, P-384, P-521
  • EdDSA

    Ed25519

    Plantillas Ed25519, con y sin el prefijo de salida.

    Registrado por la comprobación
    fact eddsa = Ed25519
  • Algoritmos poscuánticos

    none

    En Tink para Java 1.24.0 no hay registrada ninguna plantilla ML-DSA ni SLH-DSA.

    Registrado por la comprobación
    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 clave pública

    spki-pem

    SPKI PEM mediante SignaturePemKeysetReader; por lo demás, las claves viven en keysets de Tink.

    Registrado por la comprobación
    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 del conjunto de claves (keyset)

    tink-jsontink-binary

    Los keysets se serializan como JSON de Tink o protobuf binario; el keyset público superó la prueba de ida y vuelta a través de JSON.

    Registrado por la comprobación
    fact keyset-format = tink-json, tink-binary

Uso

Compara una firma predeterminada con prefijo con una firma portable de 64 bytes de una plantilla sin prefijo.
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");
    }
}

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor maven:3.9.16-eclipse-temurin-25, y la ejecución falla si la salida no coincide.

Configuración
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Comando
java -cp 'lib/*' Usage.java 2>/dev/null
La salida debe coincidir (expresión regular)
prefixed first byte: 0x01, portable signature: 64 bytes, verified

Bibliotecas