Files
auth-jwt/signer_ec.go
T

67 lines
1.7 KiB
Go
Raw Permalink Normal View History

package authjwt
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/golang-jwt/jwt/v5"
)
var _ Signer = (*ecSigner)(nil)
type ecSigner struct {
private *ecdsa.PrivateKey
public *ecdsa.PublicKey
}
// NewECSigner returns a Signer backed by ECDSA.
// The signing algorithm is auto-detected from the key's curve:
// P-256→ES256, P-384→ES384, P-521→ES512.
func NewECSigner(privateKey *ecdsa.PrivateKey) Signer {
return &ecSigner{private: privateKey, public: &privateKey.PublicKey}
}
// NewECSignerFromPEM parses a PKCS#8 PEM-encoded ECDSA private key.
func NewECSignerFromPEM(pemKey []byte) (Signer, error) {
block, _ := pem.Decode(pemKey)
if block == nil {
return nil, fmt.Errorf("no PEM block found")
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parse EC private key: %w", err)
}
ecKey, ok := key.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("PEM key is not an EC private key")
}
return NewECSigner(ecKey), nil
}
func (s *ecSigner) Sign(claims jwt.Claims) (string, error) {
return jwt.NewWithClaims(ecAlg(s.private), claims).SignedString(s.private)
}
func (s *ecSigner) Verify(tokenString string) (*jwt.Token, error) {
alg := ecAlg(s.private)
return jwt.Parse(tokenString, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != alg.Alg() {
return nil, fmt.Errorf("unexpected signing method %q", t.Header["alg"])
}
return s.public, nil
}, jwt.WithJSONNumber())
}
func ecAlg(key *ecdsa.PrivateKey) *jwt.SigningMethodECDSA {
switch key.Curve.Params().Name {
case "P-384":
return jwt.SigningMethodES384
case "P-521":
return jwt.SigningMethodES512
default:
return jwt.SigningMethodES256
}
}