This document provides comprehensive documentation of all signing methods and algorithms supported by the JWT library. Signing methods are responsible for creating and verifying JWT signatures.
type SigningMethod interface {
Verify(signingString string, sig []byte, key any) error
Sign(signingString string, key any) ([]byte, error)
Alg() string
}The core interface that all signing method implementations must satisfy.
Methods:
Verify - Verifies that the signature is valid for the given signing string and keySign - Creates a signature for the given signing string using the provided keyAlg - Returns the algorithm identifier string (e.g., "HS256", "RS256")func GetSigningMethod(alg string) SigningMethodRetrieves a registered signing method by its algorithm name. Returns nil if the algorithm is not registered.
Parameters:
alg - Algorithm identifier (e.g., "HS256", "RS256", "ES256")Returns:
Example:
method := jwt.GetSigningMethod("HS256")
if method == nil {
panic("algorithm not found")
}func RegisterSigningMethod(alg string, f func() SigningMethod)Registers a custom signing method. This allows you to add support for custom algorithms or override built-in ones. The function f is a factory that creates new instances of the signing method.
Parameters:
alg - Algorithm identifier to registerf - Factory function that returns a new SigningMethod instanceExample:
// Register a custom signing method
jwt.RegisterSigningMethod("CUSTOM", func() jwt.SigningMethod {
return &CustomSigningMethod{}
})func GetAlgorithms() []stringReturns a list of all registered algorithm names. Useful for validation and introspection.
Returns:
Example:
algorithms := jwt.GetAlgorithms()
// Returns: ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", ...]HMAC (Hash-based Message Authentication Code) uses symmetric keys and hash functions to create signatures. These algorithms are fast, simple, and suitable for internal services where the same secret can be safely shared.
type SigningMethodHMAC struct {
Name string
Hash crypto.Hash
}Implements HMAC-based signing using SHA-2 hash functions.
Fields:
Name - Algorithm name (e.g., "HS256")Hash - The hash function to use (e.g., crypto.SHA256)Methods:
func (m *SigningMethodHMAC) Alg() stringReturns the algorithm name.
func (m *SigningMethodHMAC) Sign(signingString string, key any) ([]byte, error)Signs the signing string using HMAC with the provided key.
Key Requirements:
[]bytefunc (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key any) errorVerifies the HMAC signature.
var SigningMethodHS256 *SigningMethodHMACHMAC using SHA-256 hash function. Produces 256-bit signatures.
var SigningMethodHS384 *SigningMethodHMACHMAC using SHA-384 hash function. Produces 384-bit signatures.
var SigningMethodHS512 *SigningMethodHMACHMAC using SHA-512 hash function. Produces 512-bit signatures.
import (
"fmt"
"github.com/golang-jwt/jwt/v5"
)
// Create secret key (should be at least 32 bytes)
secretKey := []byte("your-256-bit-secret-key-here!!")
// Create claims
claims := jwt.MapClaims{
"sub": "user123",
"exp": jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
}
// Create token with HS256
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Sign the token
tokenString, err := token.SignedString(secretKey)
if err != nil {
panic(err)
}
fmt.Println("Token:", tokenString)// Create token with stronger hash
token := jwt.NewWithClaims(jwt.SigningMethodHS384, claims)
tokenString, err := token.SignedString(secretKey)// Create token with strongest HMAC hash
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
tokenString, err := token.SignedString(secretKey)[]byte (byte slice)RSA uses asymmetric cryptography with public/private key pairs. These algorithms are suitable when you need to distribute public keys for verification while keeping private keys secure.
type SigningMethodRSA struct {
Name string
Hash crypto.Hash
}Implements RSA PKCS#1 v1.5 signature algorithm.
Fields:
Name - Algorithm name (e.g., "RS256")Hash - The hash function to use (e.g., crypto.SHA256)Methods:
func (m *SigningMethodRSA) Alg() stringReturns the algorithm name.
func (m *SigningMethodRSA) Sign(signingString string, key any) ([]byte, error)Signs using RSA PKCS#1 v1.5 with the provided private key.
Key Requirements:
*rsa.PrivateKey*rsa.PublicKeyfunc (m *SigningMethodRSA) Verify(signingString string, sig []byte, key any) errorVerifies the RSA signature using the public key.
var SigningMethodRS256 *SigningMethodRSARSA signature with SHA-256 hash.
var SigningMethodRS384 *SigningMethodRSARSA signature with SHA-384 hash.
var SigningMethodRS512 *SigningMethodRSARSA signature with SHA-512 hash.
import (
"crypto/rsa"
"github.com/golang-jwt/jwt/v5"
)
// Load RSA private key from PEM file
privateKeyData, err := os.ReadFile("private_key.pem")
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyData)
if err != nil {
panic(err)
}
// Create claims
claims := jwt.RegisteredClaims{
Issuer: "my-service",
Subject: "user456",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
}
// Create and sign token with RS256
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
tokenString, err := token.SignedString(privateKey)
if err != nil {
panic(err)
}// Load RSA public key from PEM file
publicKeyData, err := os.ReadFile("public_key.pem")
if err != nil {
panic(err)
}
publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyData)
if err != nil {
panic(err)
}
// Parse and verify token
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// Validate the algorithm
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return publicKey, nil
})
if err != nil {
panic(err)
}// RS384 - stronger hash
token := jwt.NewWithClaims(jwt.SigningMethodRS384, claims)
tokenString, err := token.SignedString(privateKey)
// RS512 - strongest RSA hash
token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
tokenString, err := token.SignedString(privateKey)*rsa.PrivateKey*rsa.PublicKeycrypto/rsa package or OpenSSLRSA-PSS (Probabilistic Signature Scheme) is a more secure RSA signature scheme with better security proofs than PKCS#1 v1.5. It uses randomized padding for enhanced security.
type SigningMethodRSAPSS struct {
*SigningMethodRSA
Options *rsa.PSSOptions
VerifyOptions *rsa.PSSOptions
}Implements RSA-PSS signature algorithm.
Fields:
*SigningMethodRSA - Embeds base RSA signing methodOptions - PSS options for signing (salt length, hash function)VerifyOptions - PSS options for verificationMethods:
func (m *SigningMethodRSAPSS) Sign(signingString string, key any) ([]byte, error)Signs using RSA-PSS with the provided private key.
Key Requirements:
*rsa.PrivateKey*rsa.PublicKeyfunc (m *SigningMethodRSAPSS) Verify(signingString string, sig []byte, key any) errorVerifies the RSA-PSS signature using the public key.
var SigningMethodPS256 *SigningMethodRSAPSSRSA-PSS signature with SHA-256 hash. Uses default salt length (hash length).
var SigningMethodPS384 *SigningMethodRSAPSSRSA-PSS signature with SHA-384 hash.
var SigningMethodPS512 *SigningMethodRSAPSSRSA-PSS signature with SHA-512 hash.
import (
"crypto/rsa"
"github.com/golang-jwt/jwt/v5"
)
// Load RSA private key
privateKeyData, err := os.ReadFile("private_key.pem")
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyData)
if err != nil {
panic(err)
}
// Create claims
claims := jwt.MapClaims{
"sub": "user789",
"exp": time.Now().Add(2 * time.Hour).Unix(),
}
// Create and sign token with PS256
token := jwt.NewWithClaims(jwt.SigningMethodPS256, claims)
tokenString, err := token.SignedString(privateKey)
if err != nil {
panic(err)
}// PS384
token := jwt.NewWithClaims(jwt.SigningMethodPS384, claims)
tokenString, err := token.SignedString(privateKey)
// PS512 - strongest PSS variant
token := jwt.NewWithClaims(jwt.SigningMethodPS512, claims)
tokenString, err := token.SignedString(privateKey)// Load public key
publicKeyData, err := os.ReadFile("public_key.pem")
if err != nil {
panic(err)
}
publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyData)
if err != nil {
panic(err)
}
// Parse and verify
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// Validate the algorithm
if _, ok := token.Method.(*jwt.SigningMethodRSAPSS); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return publicKey, nil
})*rsa.PrivateKey*rsa.PublicKeyECDSA (Elliptic Curve Digital Signature Algorithm) provides asymmetric signatures with smaller key sizes than RSA while maintaining comparable security. These algorithms are efficient and produce compact signatures.
type SigningMethodECDSA struct {
Name string
Hash crypto.Hash
KeySize int
CurveBits int
}Implements ECDSA signature algorithm.
Fields:
Name - Algorithm name (e.g., "ES256")Hash - The hash function to use (e.g., crypto.SHA256)KeySize - Expected key size in bytesCurveBits - Expected curve bit sizeMethods:
func (m *SigningMethodECDSA) Alg() stringReturns the algorithm name.
func (m *SigningMethodECDSA) Sign(signingString string, key any) ([]byte, error)Signs using ECDSA with the provided private key.
Key Requirements:
*ecdsa.PrivateKey*ecdsa.PublicKeyfunc (m *SigningMethodECDSA) Verify(signingString string, sig []byte, key any) errorVerifies the ECDSA signature using the public key.
var SigningMethodES256 *SigningMethodECDSAECDSA using P-256 curve and SHA-256 hash. Provides approximately 128-bit security.
var SigningMethodES384 *SigningMethodECDSAECDSA using P-384 curve and SHA-384 hash. Provides approximately 192-bit security.
var SigningMethodES512 *SigningMethodECDSAECDSA using P-521 curve and SHA-512 hash. Provides approximately 256-bit security.
import (
"crypto/ecdsa"
"github.com/golang-jwt/jwt/v5"
)
// Load ECDSA private key from PEM file
privateKeyData, err := os.ReadFile("ec_private_key.pem")
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseECPrivateKeyFromPEM(privateKeyData)
if err != nil {
panic(err)
}
// Create claims
claims := jwt.RegisteredClaims{
Subject: "user123",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * time.Minute)),
IssuedAt: jwt.NewNumericDate(time.Now()),
}
// Create and sign token with ES256
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
tokenString, err := token.SignedString(privateKey)
if err != nil {
panic(err)
}// Load ECDSA public key from PEM file
publicKeyData, err := os.ReadFile("ec_public_key.pem")
if err != nil {
panic(err)
}
publicKey, err := jwt.ParseECPublicKeyFromPEM(publicKeyData)
if err != nil {
panic(err)
}
// Parse and verify token
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// Validate the algorithm
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return publicKey, nil
})
if err != nil {
panic(err)
}// ES384 - P-384 curve
token := jwt.NewWithClaims(jwt.SigningMethodES384, claims)
tokenString, err := token.SignedString(privateKey) // Must use P-384 key
// ES512 - P-521 curve (note: curve is P-521, not P-512)
token := jwt.NewWithClaims(jwt.SigningMethodES512, claims)
tokenString, err := token.SignedString(privateKey) // Must use P-521 keyimport (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
)
// Generate P-256 key for ES256
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
// Generate P-384 key for ES384
privateKey384, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
// Generate P-521 key for ES512
privateKey521, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)*ecdsa.PrivateKey*ecdsa.PublicKeyEdDSA (Edwards-curve Digital Signature Algorithm) using the Ed25519 curve is a modern signature scheme offering excellent performance, small key and signature sizes, and strong security guarantees.
type SigningMethodEd25519 struct{}Implements EdDSA signature algorithm using Ed25519 curve.
Methods:
func (m *SigningMethodEd25519) Alg() stringReturns "EdDSA".
func (m *SigningMethodEd25519) Sign(signingString string, key any) ([]byte, error)Signs using Ed25519 with the provided private key.
Key Requirements:
ed25519.PrivateKey (64 bytes)ed25519.PublicKey (32 bytes)func (m *SigningMethodEd25519) Verify(signingString string, sig []byte, key any) errorVerifies the Ed25519 signature using the public key.
var SigningMethodEdDSA *SigningMethodEd25519EdDSA using Ed25519 curve. This is the recommended algorithm for new projects due to its excellent security and performance characteristics.
import (
"crypto/ed25519"
"github.com/golang-jwt/jwt/v5"
)
// Load Ed25519 private key from PEM file
privateKeyData, err := os.ReadFile("ed25519_private_key.pem")
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseEdPrivateKeyFromPEM(privateKeyData)
if err != nil {
panic(err)
}
// Create claims
claims := jwt.MapClaims{
"sub": "user456",
"name": "Jane Doe",
"exp": time.Now().Add(1 * time.Hour).Unix(),
}
// Create and sign token with EdDSA
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims)
tokenString, err := token.SignedString(privateKey)
if err != nil {
panic(err)
}// Load Ed25519 public key from PEM file
publicKeyData, err := os.ReadFile("ed25519_public_key.pem")
if err != nil {
panic(err)
}
publicKey, err := jwt.ParseEdPublicKeyFromPEM(publicKeyData)
if err != nil {
panic(err)
}
// Parse and verify token
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// Validate the algorithm
if _, ok := token.Method.(*jwt.SigningMethodEd25519); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return publicKey, nil
})
if err != nil {
panic(err)
}import (
"crypto/ed25519"
"crypto/rand"
)
// Generate Ed25519 key pair
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
// Use the keys
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims)
tokenString, err := token.SignedString(privateKey)ed25519.PrivateKey)ed25519.PublicKey)The "none" algorithm creates unsecured JWTs with no signature. This should only be used in very specific scenarios where the token is transmitted over a secure channel and signature verification is not needed.
type signingMethodNone struct{}Implements the "none" signature method (unsecured JWTs).
var SigningMethodNone *signingMethodNoneThe "none" signing method. Creates JWTs without signatures.
const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed"A constant that must be used explicitly to allow parsing tokens with "none" algorithm. This is a safety mechanism to prevent accidental acceptance of unsecured tokens.
import (
"github.com/golang-jwt/jwt/v5"
)
// Create claims
claims := jwt.MapClaims{
"sub": "user123",
"data": "public information",
}
// Create token with none algorithm
token := jwt.NewWithClaims(jwt.SigningMethodNone, claims)
// Sign with UnsafeAllowNoneSignatureType constant
tokenString, err := token.SignedString(jwt.UnsafeAllowNoneSignatureType)
if err != nil {
panic(err)
}
// The resulting token has no signature: "header.payload."// Parse token allowing none algorithm
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// Explicitly check for none algorithm
if _, ok := token.Method.(*jwt.signingMethodNone); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// Return the magic constant to allow none
return jwt.UnsafeAllowNoneSignatureType, nil
})
if err != nil {
panic(err)
}Security Considerations:
When to Use:
When NOT to Use:
Use Symmetric (HMAC) when:
Use Asymmetric (RSA/ECDSA/EdDSA) when:
Algorithm Variants:
Recommendations:
| Algorithm | Type | Key Size | Signature Size | Performance | Security Level |
|---|---|---|---|---|---|
| HS256 | Symmetric | 32+ bytes | 32 bytes | Fastest | High |
| HS384 | Symmetric | 48+ bytes | 48 bytes | Fastest | Higher |
| HS512 | Symmetric | 64+ bytes | 64 bytes | Fastest | Highest |
| RS256 | Asymmetric | 2048+ bits | 256 bytes | Medium | High |
| RS384 | Asymmetric | 2048+ bits | 256 bytes | Medium | Higher |
| RS512 | Asymmetric | 2048+ bits | 256 bytes | Medium | Highest |
| PS256 | Asymmetric | 2048+ bits | 256 bytes | Medium | High (Better than RS) |
| PS384 | Asymmetric | 2048+ bits | 256 bytes | Medium | Higher |
| PS512 | Asymmetric | 2048+ bits | 256 bytes | Medium | Highest |
| ES256 | Asymmetric | 256 bits | 64 bytes | Fast | High |
| ES384 | Asymmetric | 384 bits | 96 bytes | Fast | Higher |
| ES512 | Asymmetric | 521 bits | 132 bytes | Fast | Highest |
| EdDSA | Asymmetric | 256 bits | 64 bytes | Very Fast | Very High |
For New Projects:
For Legacy Compatibility:
For High Security:
When migrating algorithms:
// Support multiple algorithms during transition
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
switch token.Method.(type) {
case *jwt.SigningMethodHMAC:
// Legacy HMAC tokens
return hmacSecret, nil
case *jwt.SigningMethodEd25519:
// New EdDSA tokens
return eddsaPublicKey, nil
default:
return nil, fmt.Errorf("unsupported algorithm: %v", token.Header["alg"])
}
})The library provides utility functions for parsing keys from PEM format.
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error)Parses a PEM-encoded Elliptic Curve private key.
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error)Parses a PEM-encoded Elliptic Curve public key.
func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error)Parses a PEM-encoded Ed25519 private key.
func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error)Parses a PEM-encoded Ed25519 public key.
func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)Parses a PEM-encoded RSA private key (PKCS#1 or PKCS#8).
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)Parses a PEM-encoded RSA public key.
func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error)Parses a password-protected PEM-encoded RSA private key. DEPRECATED: Use standard library's encrypted PEM parsing instead.
import (
"os"
"github.com/golang-jwt/jwt/v5"
)
// Load private key
privateKeyPEM, err := os.ReadFile("ec_private.pem")
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseECPrivateKeyFromPEM(privateKeyPEM)
if err != nil {
panic(err)
}
// Load public key
publicKeyPEM, err := os.ReadFile("ec_public.pem")
if err != nil {
panic(err)
}
publicKey, err := jwt.ParseECPublicKeyFromPEM(publicKeyPEM)
if err != nil {
panic(err)
}// Load Ed25519 private key
privateKeyPEM, err := os.ReadFile("ed25519_private.pem")
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseEdPrivateKeyFromPEM(privateKeyPEM)
if err != nil {
panic(err)
}
// Load Ed25519 public key
publicKeyPEM, err := os.ReadFile("ed25519_public.pem")
if err != nil {
panic(err)
}
publicKey, err := jwt.ParseEdPublicKeyFromPEM(publicKeyPEM)
if err != nil {
panic(err)
}// Load RSA private key
privateKeyPEM, err := os.ReadFile("rsa_private.pem")
if err != nil {
panic(err)
}
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyPEM)
if err != nil {
panic(err)
}
// Load RSA public key
publicKeyPEM, err := os.ReadFile("rsa_public.pem")
if err != nil {
panic(err)
}
publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyPEM)
if err != nil {
panic(err)
}Here's a complete example showing how to support multiple signing algorithms in a single application:
package main
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"fmt"
"os"
"time"
"github.com/golang-jwt/jwt/v5"
)
type KeyManager struct {
hmacSecret []byte
rsaPrivate *rsa.PrivateKey
rsaPublic *rsa.PublicKey
ecdsaPrivate *ecdsa.PrivateKey
ecdsaPublic *ecdsa.PublicKey
eddsaPrivate ed25519.PrivateKey
eddsaPublic ed25519.PublicKey
}
func NewKeyManager() (*KeyManager, error) {
km := &KeyManager{}
// Load HMAC secret
km.hmacSecret = []byte("your-256-bit-secret")
// Load RSA keys
rsaPrivPEM, _ := os.ReadFile("rsa_private.pem")
km.rsaPrivate, _ = jwt.ParseRSAPrivateKeyFromPEM(rsaPrivPEM)
rsaPubPEM, _ := os.ReadFile("rsa_public.pem")
km.rsaPublic, _ = jwt.ParseRSAPublicKeyFromPEM(rsaPubPEM)
// Load ECDSA keys
ecPrivPEM, _ := os.ReadFile("ec_private.pem")
km.ecdsaPrivate, _ = jwt.ParseECPrivateKeyFromPEM(ecPrivPEM)
ecPubPEM, _ := os.ReadFile("ec_public.pem")
km.ecdsaPublic, _ = jwt.ParseECPublicKeyFromPEM(ecPubPEM)
// Load EdDSA keys
edPrivPEM, _ := os.ReadFile("ed25519_private.pem")
km.eddsaPrivate, _ = jwt.ParseEdPrivateKeyFromPEM(edPrivPEM)
edPubPEM, _ := os.ReadFile("ed25519_public.pem")
km.eddsaPublic, _ = jwt.ParseEdPublicKeyFromPEM(edPubPEM)
return km, nil
}
func (km *KeyManager) CreateToken(alg string, claims jwt.Claims) (string, error) {
var method jwt.SigningMethod
var key any
switch alg {
case "HS256":
method = jwt.SigningMethodHS256
key = km.hmacSecret
case "HS384":
method = jwt.SigningMethodHS384
key = km.hmacSecret
case "HS512":
method = jwt.SigningMethodHS512
key = km.hmacSecret
case "RS256":
method = jwt.SigningMethodRS256
key = km.rsaPrivate
case "RS384":
method = jwt.SigningMethodRS384
key = km.rsaPrivate
case "RS512":
method = jwt.SigningMethodRS512
key = km.rsaPrivate
case "PS256":
method = jwt.SigningMethodPS256
key = km.rsaPrivate
case "PS384":
method = jwt.SigningMethodPS384
key = km.rsaPrivate
case "PS512":
method = jwt.SigningMethodPS512
key = km.rsaPrivate
case "ES256":
method = jwt.SigningMethodES256
key = km.ecdsaPrivate
case "ES384":
method = jwt.SigningMethodES384
key = km.ecdsaPrivate
case "ES512":
method = jwt.SigningMethodES512
key = km.ecdsaPrivate
case "EdDSA":
method = jwt.SigningMethodEdDSA
key = km.eddsaPrivate
default:
return "", fmt.Errorf("unsupported algorithm: %s", alg)
}
token := jwt.NewWithClaims(method, claims)
return token.SignedString(key)
}
func (km *KeyManager) VerifyToken(tokenString string) (*jwt.Token, error) {
return jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
switch method := token.Method.(type) {
case *jwt.SigningMethodHMAC:
return km.hmacSecret, nil
case *jwt.SigningMethodRSA:
return km.rsaPublic, nil
case *jwt.SigningMethodRSAPSS:
return km.rsaPublic, nil
case *jwt.SigningMethodECDSA:
return km.ecdsaPublic, nil
case *jwt.SigningMethodEd25519:
return km.eddsaPublic, nil
default:
return nil, fmt.Errorf("unexpected signing method: %v", method.Alg())
}
})
}
func main() {
km, err := NewKeyManager()
if err != nil {
panic(err)
}
// Create claims
claims := jwt.RegisteredClaims{
Issuer: "my-service",
Subject: "user123",
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
}
// Test each algorithm
algorithms := []string{"HS256", "RS256", "PS256", "ES256", "EdDSA"}
for _, alg := range algorithms {
// Create token
tokenString, err := km.CreateToken(alg, claims)
if err != nil {
fmt.Printf("Error creating %s token: %v\n", alg, err)
continue
}
// Verify token
token, err := km.VerifyToken(tokenString)
if err != nil {
fmt.Printf("Error verifying %s token: %v\n", alg, err)
continue
}
fmt.Printf("%s: Token valid=%v\n", alg, token.Valid)
}
}Generate keys securely:
Store keys securely:
Rotate keys regularly:
Separate signing and verification:
Always validate algorithm:
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// Check algorithm is expected
if _, ok := token.Method.(*jwt.SigningMethodEd25519); !ok {
return nil, fmt.Errorf("unexpected alg: %v", token.Header["alg"])
}
return publicKey, nil
})Use parser options for additional validation:
parser := jwt.NewParser(
jwt.WithValidMethods([]string{"EdDSA", "ES256"}),
jwt.WithIssuer("trusted-issuer"),
)Check token validity after parsing:
if !token.Valid {
return fmt.Errorf("invalid token")
}