Aller au contenu
ecdsa.com
Java · Kotlin

Tink (Java)

Tink pour Java de Google, une API cryptographique résistante aux mauvais usages, bâtie autour de jeux de clés et de modèles : les signatures par défaut portent un préfixe Tink de 5 octets qu’aucune autre bibliothèque ne comprend, et les noms des modèles masquent l’encodage obtenu.
Licence
Apache-2.0
Version vérifiée
1.24.0 ·
Code source
github.com/tink-crypto/tink-java
Documentation
developers.google.com/tink

Compatibilité en un coup d’œil

ECDSAEdDSA

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

Tink remplace les clés individuelles par des jeux de clés (keysets), c’est-à-dire des collections de clés dotées d’identifiants dont l’une est marquée comme principale, et les algorithmes par des modèles nommés (templates) comme ECDSA_P256 ou ED25519. Les primitives (PublicKeySign, PublicKeyVerify) assurent la rotation des clés de façon transparente : c’est tout l’intérêt de cette conception, et aussi la source de ses surprises d’interopérabilité. Les jeux de clés sont stockés en JSON Tink ou en protobuf binaire, pas en PEM.

Le modèle par défaut ECDSA_P256 ajoute en tête un préfixe de sortie : 0x01 et l’identifiant de clé sur 4 octets, suivis d’une signature DER. Le JDK a rejeté une telle signature avec Invalid encoding for signature, comme le fera tout vérificateur autre que Tink. Les modèles dont le nom se termine par _WITHOUT_PREFIX ou _RAW suppriment le préfixe, mais les noms masquent aussi l’encodage : ECDSA_P256_RAW a produit 64 octets de r‖s IEEE P1363, et non du DER ; la signature a été vérifiée avec le SHA256withECDSAinP1363Format du JDK et a échoué avec SHA256withECDSA. Choisissez le modèle selon l’encodage exact qu’attend l’autre côté, comme le fait l’exemple avec ECDSA_P256_IEEE_P1363_WITHOUT_PREFIX, et contrôlez les octets avec le convertisseur DER ⇄ brut.

L’import d’une clé publique externe passe par SignaturePemKeysetReader, et la clé obtenue n’a vérifié que des signatures DER : la même signature brute, acceptée par une clé P1363 propre à Tink, a échoué face à la clé importée depuis PEM tant qu’elle n’avait pas été réencodée en DER. Les signatures utilisent des nonces aléatoires, s n’est pas normalisé et la vérification accepte les signatures high-S ; une entrée invalide lève GeneralSecurityException: invalid signature.

Tink pour Java 1.24.0 propose des modèles ECDSA sur P-256, P-384 et P-521, Ed25519 et RSA (PKCS#1 v1.5 et PSS) ; aucun modèle ML-DSA ou SLH-DSA n’est enregistré dans cette version (Name ML_DSA_65 does not exist). Pour des signatures post-quantiques sur la JVM aujourd’hui, consultez Java JCA ou Bouncy Castle.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/tink/run.sh a exécuté Tink (Java) 1.24.0 dans l’image de conteneur maven:3.9.16-eclipse-temurin-25 le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Encodage des signatures ECDSA

    der

    Les signatures ECDSA_P256 sont en DER après le préfixe de sortie.

    Relevé par la vérification
    fact ecdsa-signature-encoding = der
  • Préfixe de signature

    tink-5-byte

    Les modèles par défaut ajoutent en tête un préfixe de 5 octets (0x01 et l’identifiant de clé), et le JDK a rejeté ces signatures avec Invalid encoding for signature.

    Relevé par la vérification
    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
  • Encodage du modèle RAW

    ieee-p1363

    ECDSA_P256_RAW a produit 64 octets de r‖s IEEE P1363 (sans préfixe, pas en DER) ; la signature a été vérifiée avec SHA256withECDSAinP1363Format.

    Relevé par la vérification
    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
  • Encodage des signatures après import PEM

    der

    Une clé publique importée depuis PEM via SignaturePemKeysetReader n’a vérifié que des signatures DER ; une signature r‖s brute a échoué tant qu’elle n’avait pas été réencodée en DER.

    Relevé par la vérification
    fact pem-import-encoding = der
  • Nonce ECDSA

    random

    Nonce aléatoire : deux signatures du même message diffèrent.

    Relevé par la vérification
    fact ecdsa-nonce = random
  • Low-S à la signature

    either

    s n’est pas normalisé : des valeurs high-S sont apparues parmi 64 signatures.

    Relevé par la vérification
    fact ecdsa-low-s-sign = either
  • High-S à la vérification

    accepts-high-s

    Une signature dont s a été remplacé par n − s a été vérifiée.

    Relevé par la vérification
    fact ecdsa-low-s-verify = accepts-high-s
  • Ce que l’appel de signature hache

    hashes-message

    Les primitives signent le message ; il n’existe aucune API pour signer un condensat.

    Relevé par la vérification
    fact ecdsa-prehash = hashes-message
  • Signature ECDSA mal formée

    throws

    Une signature mal formée lève GeneralSecurityException: invalid signature.

    Relevé par la vérification
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: GeneralSecurityException: invalid signature
  • Courbes ECDSA

    P-256P-384P-521

    Signature et vérification réussies avec les modèles P-256, P-384 et P-521.

    Relevé par la vérification
    fact ecdsa-curves = P-256, P-384, P-521
  • EdDSA

    Ed25519

    Modèles Ed25519, avec et sans préfixe de sortie.

    Relevé par la vérification
    fact eddsa = Ed25519
  • Algorithmes post-quantiques

    none

    Aucun modèle ML-DSA ou SLH-DSA n’est enregistré dans Tink pour Java 1.24.0.

    Relevé par la vérification
    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
  • Formats de clé publique

    spki-pem

    SPKI PEM via SignaturePemKeysetReader ; sinon, les clés résident dans des jeux de clés Tink.

    Relevé par la vérification
    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
  • Format du jeu de clés (keyset)

    tink-jsontink-binary

    Les jeux de clés se sérialisent en JSON Tink ou en protobuf binaire ; le jeu de clés publiques a fait l’aller-retour via JSON.

    Relevé par la vérification
    fact keyset-format = tink-json, tink-binary

Utilisation

Comparer une signature préfixée par défaut avec une signature portable de 64 octets issue d’un modèle sans préfixe.
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");
    }
}

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur maven:3.9.16-eclipse-temurin-25, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
mvn -q -B dependency:copy-dependencies -DoutputDirectory=lib >/dev/null
Commande
java -cp 'lib/*' Usage.java 2>/dev/null
La sortie doit correspondre à cette expression régulière
prefixed first byte: 0x01, portable signature: 64 bytes, verified

Bibliothèques