Golang: JWT: key is of invalid type

Всем привет!

Осваиваю JWT на golang

Есть вот такой тест

func TestJWTService_getAccess(t *testing.T) {
    finish, service, _ := prepare(t)
    defer finish()
    access, err := service.getAccess("123")
    assert.Nil(t, err)
    assert.Len(t, *access, 164)
    token, err := jwt.Parse(*access, func(token *jwt.Token) (interface{}, error) {
        return token, nil
    })
    fmt.Println("access: ", access)
    fmt.Println("err: ", err)
    fmt.Println("token: ", token.Claims)
    assert.Nil(t, err)
}

вот код самого метода

func (g *JWTService) getAccess(id string) (*string, error) {
    token := jwt.NewWithClaims(jwt.SigningMethodES256,
        &claims.Claims{
            ExpiresAt: time.Now().Add(15 * time.Minute).Unix(),
            Id:        id,
        })
    access, err := token.SignedString(g.TLSKey.Key)
    if err != nil {
        return nil, err
    }
    return &access, nil
}

вот ключ

-----BEGIN PRIVATE KEY-----
MHcCAQEEIAl3OVDPI+DZga26bSGBoIuYsXHYBMuBdhHqT/zdrYoFoAoGCCqGSM49
AwEHoUQDQgAESUiOTkfLNuzOGVolROj356z4pa585PcjLZnCqI8TtfZ8yqMinwbY
gFtba0RdYLuKTBZuKHycSxO8irgaegOJYQ==
-----END PRIVATE KEY-----

в строке fmt.Println("err: ", err) выдает

err:  key is of invalid type

строка fmt.Println("access: ", jwtToken.Access) выдает

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTk4MjU1NTAsImp0aSI6IjEyMyJ9.lnrXSK-kBy9b6zp6BShsjgwrILNCOmXd3Se5CjWNNGu2aaajggBdZIQKpOqoYiBSbvk5d19thZV-H_vtNYh_8Q

строка fmt.Println("token: ", token.Claims) выдает

map[exp:1.69982555e+09 jti:123]

падает на строке assert.Nil(t, err) с ошибкой key is of invalid type

Не могу понять что я делаю не так и где ошибка?


Update: генерировал ключ эти кодом:

func TestGenKey(t *testing.T) {
    // Генерируем новый ECDSA ключ
    privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        fmt.Println("Ошибка при генерации ключа:", err)
        return
    }

    // Кодируем приватный ключ в формат PEM
    privateKeyBytes, err := x509.MarshalECPrivateKey(privateKey)
    if err != nil {
        fmt.Println("Ошибка при кодировании приватного ключа:", err)
        return
    }

    privateKeyPEM := &pem.Block{
        Type:  "EC PRIVATE KEY",
        Bytes: privateKeyBytes,
    }

    // Создаем файл и сохраняем в него ключ
    file, err := os.Create("../../../private.key")
    if err != nil {
        fmt.Println("Ошибка при создании файла:", err)
        return
    }
    defer file.Close()

    err = pem.Encode(file, privateKeyPEM)
    if err != nil {
        fmt.Println("Ошибка при кодировании PEM-блока:", err)
        return
    }
    fmt.Println("Приватный ключ сохранен в файл private.key")
}

Ответы (1 шт):

Автор решения: Pak Uula

Сорри, я не сразу понял, на какой из ключей у вас ругается jwt.

Функция-параметр для jwt.Parse должна возвращать публичный ключ, соответствующий приватному, который использовался для создания токена. Эта информация должна храниться где-то отдельно от токена. Например, в базе данных сервера, который сгенерировал токен.

В вашем случае нужно возвращать публичный ключ

    token, err := jwt.Parse(*access, func(token *jwt.Token) (interface{}, error) {
        return &service.TLSKey.Key.PublicKey, nil
    })

Ваш пример был неполон, я его упростил:

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "os"
    "time"

    "github.com/golang-jwt/jwt"
)

type JWTService struct {
    TLSKey *ecdsa.PrivateKey
}

func main() {
    service := &JWTService{
        TLSKey: TestGenKeyMust(),
    }

    access, err := service.getAccess("123")
    if err != nil {
        panic(err)
    }
    token, err := jwt.Parse(access, func(token *jwt.Token) (interface{}, error) {
        return &service.TLSKey.PublicKey, nil
    })
    if err != nil {
        panic(err)
    }
    fmt.Println("access: ", access)
    fmt.Println("err: ", err)
    fmt.Println("token: ", token.Claims)

}

func (g *JWTService) getAccess(id string) (string, error) {
    token := jwt.NewWithClaims(jwt.SigningMethodES256,
        &jwt.StandardClaims{
            ExpiresAt: time.Now().Add(15 * time.Minute).Unix(),
            Id:        id,
        })
    access, err := token.SignedString(g.TLSKey)
    if err != nil {
        return "", err
    }
    return access, nil
}

func TestGenKeyMust() *ecdsa.PrivateKey {
    key, err := TestGenKey()
    if err != nil {
        panic(err)
    }
    return key
}
func TestGenKey() (*ecdsa.PrivateKey, error) {
    // Генерируем новый ECDSA ключ
    privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        fmt.Println("Ошибка при генерации ключа:", err)
        return nil, err
    }

    // Кодируем приватный ключ в формат PEM
    privateKeyBytes, err := x509.MarshalECPrivateKey(privateKey)
    if err != nil {
        fmt.Println("Ошибка при кодировании приватного ключа:", err)
        return nil, err
    }

    privateKeyPEM := &pem.Block{
        Type:  "EC PRIVATE KEY",
        Bytes: privateKeyBytes,
    }

    // Создаем файл и сохраняем в него ключ
    // file, err := os.Create("private.key")
    file, err := os.Stdout, nil
    if err != nil {
        fmt.Println("Ошибка при создании файла:", err)
        return nil, err
    }
    // defer file.Close()

    err = pem.Encode(file, privateKeyPEM)
    if err != nil {
        fmt.Println("Ошибка при кодировании PEM-блока:", err)
        return nil, err
    }
    fmt.Println("Приватный ключ сохранен в файл private.key")

    return privateKey, nil
}

Запустить: https://go.dev/play/p/LxONs3Pyw2t

Когда jwt.Parse получает правильный ключ, он верифицирует токен без ошибки.

    token, err := jwt.Parse(access, func(token *jwt.Token) (interface{}, error) {
        return &service.TLSKey.PublicKey, nil
    })
→ Ссылка