Aller au contenu
ecdsa.com
Go

go-jose

go-jose, l’implémentation JOSE en Go qu’utilisent des projets comme Kubernetes, Vault et Dex : depuis la v4, une liste d’algorithmes est obligatoire à l’analyse, la vérification de la signature et la validation des claims sont deux appels distincts, et la marge de tolérance est d’une minute par défaut.
Licence
Apache-2.0
Version vérifiée
4.1.6 ·
Code source
github.com/go-jose/go-jose
Documentation
pkg.go.dev/github.com/go-jose/go-jose/v4

Compatibilité en un coup d’œil

JOSE et JWT

Algorithmes JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

github.com/go-jose/go-jose/v4 (la suite du go-jose de Square) implémente JWS, JWE, JWK et JWT, ce qui explique sa présence partout où des services Go échangent des jetons chiffrés ou signés. La 4.1.6 a signé et vérifié les familles HS, RS, PS et ES ainsi qu’EdDSA ; ES256K, Ed25519 et Ed448 ne sont pas des noms d’algorithme reconnus.

La version 4 a rendu la liste d’algorithmes obligatoire : jwt.ParseSigned(token, nil) échoue avec no signature algorithms specified, si bien qu’un jeton ne peut être analysé qu’avec des algorithmes que vous avez choisis. Les jetons non signés peuvent être analysés si none figure dans cette liste, mais ils ne passent jamais la vérification de Claims(key) : la seule façon d’en lire un est la méthode au nom explicite UnsafeClaimsWithoutVerification.

La vérification est scindée en deux, et c’est là le piège. token.Claims(key, &claims) vérifie la signature et remplit la structure : cet appel a renvoyé sans aucune erreur les claims d’un jeton expiré depuis une heure. L’expiration, la date de début de validité, l’émetteur et l’audience ne sont contrôlés que par claims.Validate() avec une valeur jwt.Expected ou par ValidateWithLeeway, et un jeton sans exp passe même ce contrôle : vérifiez donc que Expiry est renseigné, comme le fait l’exemple. Validate tolère jwt.DefaultLeeway, soit une minute, ce que les essais ont confirmé.

Les clés sont typées. Un jeton HS256 dont la clé HMAC était le texte PEM a échoué face à la clé *ecdsa.PublicKey avec error in cryptographic primitive, mais a été vérifié quand ces mêmes octets PEM étaient passés comme clé : conservez les clés sous forme de clés Go analysées, pas d’octets. ES256 refuse une clé P-384 avec expected 256 bit key, got 384 bits instead. Une signature ES256 encodée en DER échoue avec le même error in cryptographic primitive générique qu’une signature falsifiée ; le débogueur JWT affiche la longueur de la signature.

Pour les jetons chiffrés, l’inspecteur JWE affiche l’en-tête et les algorithmes ; pour les jeux de clés, le vérificateur JWKS montre ce que publie un fournisseur et le convertisseur JWK fait passer les clés de JWK à PEM et inversement.

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/go-jose/run.sh a exécuté go-jose 4.1.6 dans l’image de conteneur golang:1.27.3-trixie le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Algorithmes JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512EdDSA

    Signature et vérification réussies avec HS, RS, PS et ES en 256/384/512 bits et EdDSA ; ES256K, Ed25519 et Ed448 échouent avec unknown/unsupported algorithm.

    Relevé par la vérification
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA
    evidence jws-algorithms: ES256K: go-jose/go-jose: unknown/unsupported algorithm
    evidence jws-algorithms: Ed25519: go-jose/go-jose: unknown/unsupported algorithm
    evidence jws-algorithms: Ed448: go-jose/go-jose: unknown/unsupported algorithm
  • Encodage des signatures ES256

    raw

    Les signatures ES256 font 64 octets, en r‖s brut.

    Relevé par la vérification
    fact jws-ecdsa-encoding = raw
  • Signature DER dans un JWS

    rejected

    Une signature ES256 encodée en DER échoue avec go-jose/go-jose: error in cryptographic primitive.

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: go-jose/go-jose: error in cryptographic primitive
  • Liste d’algorithmes autorisés

    required

    jwt.ParseSigned exige la liste des algorithmes autorisés ; une liste vide échoue avec no signature algorithms specified.

    Relevé par la vérification
    fact jwt-algorithm-allowlist = required
    evidence jwt-algorithm-allowlist: ParseSigned with an empty algorithm list: go-jose/go-jose: no signature algorithms specified
  • Jetons non signés (alg none)

    rejected

    Avec none dans la liste, un jeton non signé est analysé, mais Claims() échoue avec une clé EC comme avec un secret HMAC ; seul UnsafeClaimsWithoutVerification permet de le lire.

    Relevé par la vérification
    fact jwt-alg-none = rejected
    evidence jwt-alg-none: ParseSigned with none in the list: <nil>
    evidence jwt-alg-none: Claims(*ecdsa.PublicKey): go-jose/go-jose: error in cryptographic primitive
    evidence jwt-alg-none: Claims([]uint8): go-jose/go-jose: error in cryptographic primitive
    evidence jwt-alg-none: UnsafeClaimsWithoutVerification: <nil>, sub fact-check
  • Clé publique utilisée comme secret HMAC

    rejected

    Un jeton HS256 dont la clé HMAC était le texte PEM a échoué face à la clé *ecdsa.PublicKey ; il n’a été vérifié que lorsque les octets PEM eux-mêmes ont été passés comme clé.

    Relevé par la vérification
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: Claims with the *ecdsa.PublicKey: go-jose/go-jose: error in cryptographic primitive
    evidence jwt-hmac-public-key: Claims with the PEM bytes as key: <nil>
  • Courbe vérifiée par rapport à alg

    enforced

    La signature ES256 avec une clé P-384 échoue avec expected 256 bit key, got 384 bits instead.

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: go-jose/go-jose: expected 256 bit key, got 384 bits instead
  • Claims validés au décodage

    no

    Claims() a renvoyé sans erreur les claims d’un jeton expiré depuis une heure ; les contrôles de dates et d’émetteur n’ont lieu que dans Validate.

    Relevé par la vérification
    fact jwt-claims-validated-on-decode = no
    evidence jwt-claims-validated-on-decode: Claims() on a token expired an hour ago: <nil>
  • Décalage d’horloge toléré par défaut

    60s

    Validate tolère jwt.DefaultLeeway, soit une minute : un jeton expiré depuis 57 secondes a été accepté et un jeton expiré depuis 63 secondes a été refusé.

    Relevé par la vérification
    fact jwt-clock-skew = 60s
    evidence jwt-clock-skew: jwt.DefaultLeeway = 1m0s
  • Claim d’expiration obligatoire

    no

    Un jeton sans exp a passé Validate ; vérifiez vous-même que Expiry n’est pas nil.

    Relevé par la vérification
    fact jwt-exp-required = no

Utilisation

Signer des claims enregistrés en ES256, analyser avec la liste d’algorithmes, vérifier la signature, puis valider séparément les claims.
package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"fmt"
	"time"

	jose "github.com/go-jose/go-jose/v4"
	"github.com/go-jose/go-jose/v4/jwt"
)

func main() {
	key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: key}, (&jose.SignerOptions{}).WithType("JWT"))
	raw, _ := jwt.Signed(signer).Claims(jwt.Claims{Subject: "user-42", Issuer: "https://auth.example.com", Expiry: jwt.NewNumericDate(time.Now().Add(10 * time.Minute))}).Serialize()

	// v4 requires the allowed algorithms when parsing.
	token, _ := jwt.ParseSigned(raw, []jose.SignatureAlgorithm{jose.ES256})
	claims := jwt.Claims{}
	sigErr := token.Claims(&key.PublicKey, &claims) // checks the signature only
	// Validate checks exp, nbf and iss (with jwt.DefaultLeeway of one minute).
	valErr := claims.Validate(jwt.Expected{Issuer: "https://auth.example.com", Time: time.Now()})
	fmt.Println("valid:", sigErr == nil && valErr == nil && claims.Expiry != nil, "sub:", claims.Subject)
}

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

Commande
GOFLAGS=-mod=mod go run .
La sortie doit correspondre à cette expression régulière
valid: true sub: user-42

Bibliothèques