golang JWT 无签名。 “加密/rsa:验证错误”

Posted

技术标签:

【中文标题】golang JWT 无签名。 “加密/rsa:验证错误”【英文标题】:golang JWT No Signature. "crypto/rsa: verification error" 【发布时间】:2015-09-20 15:01:40 【问题描述】:

我正在使用JWT。这就是我创建令牌的方式。

func createToken(user User) (string, error) 
    token := jwt.New(jwt.GetSigningMethod("RS256"))

    token.Claims["Name"] = user.Name
    token.Claims["Email"] = user.Email
    //token.Claims["ExpDate"] = time.Now().Add(time.Hour * 1).Unix()

    tokenString, err := token.SignedString([]byte(config.PrivateKey))
    if err != nil 
        return "", err
    

    return tokenString, nil

这就是我验证令牌的方式。

token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface, error)     
        return []byte(config.PublicKey), nil
    )

我用这个 python 代码生成了我的公钥和私钥

from Crypto.PublicKey import RSA

private = RSA.generate(1024)
public  = private.publickey()

priv = private.exportKey()
pub = public.exportKey()

我收到crypto/rsa: verification error。当我打印解析的令牌时,一切看起来都很好,除了 token.Valid 是假的,token.Signature 是空的。

type Token struct 
    Raw       string                 // The raw token.  Populated when you Parse a token
    Method    SigningMethod          // The signing method used or to be used
    Header    map[string]interface // The first segment of the token
    Claims    map[string]interface // The second segment of the token
    Signature string                 // The third segment of the token.  Populated when you Parse a token
    Valid     bool                   // Is the token valid?  Populated when you Parse/Verify a token


tokenString--> eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6InRlc3RAaG90bWFpbC5jb20iLCJOYW1lIjoidGVzdE5hbWUifQ.fgd1h4LB1zzAiPFLKMOJrQu12rTLeXBDKHdnqiNc04NRn-1v7cHEQpDNawvScMIGrcQLbZo6WrldZQT9ImYWpUyy3CcD2uMO95I5PN6aXOSPb26nNGQpmIi1HNZrq5359hKZ6BWEJnW9iTg7RgmMvZGmIqlGLsOY2a6UiiwBsI0
token.Raw--> eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJFbWFpbCI6InRlc3RAaG90bWFpbC5jb20iLCJOYW1lIjoidGVzdE5hbWUifQ.fgd1h4LB1zzAiPFLKMOJrQu12rTLeXBDKHdnqiNc04NRn-1v7cHEQpDNawvScMIGrcQLbZo6WrldZQT9ImYWpUyy3CcD2uMO95I5PN6aXOSPb26nNGQpmIi1HNZrq5359hKZ6BWEJnW9iTg7RgmMvZGmIqlGLsOY2a6UiiwBsI0
token.Header--> map[alg:RS256 typ:JWT]
token.Claims--> map[Email:test@hotmail.com Name:testName]
token.Signature-->           
token.Valid--> false

PS:我没有任何 SSL 证书。

【问题讨论】:

我强烈建议不要对 JSON Web 令牌使用 RSA(尤其是 1024 位密钥)。与仅使用 jwt.GetSigningMethod("HS256")(即 HMAC-SHA-256)相比,它确实没有提供实际的好处。 RSA 更容易出错。 @elithrar RSA vs HMAC ~ 公钥 vs 对称密钥;哪个更好取决于用例。 RSA 以何种方式“更容易出错”? JWT 库应该完成繁重的工作。 HMAC-SHA-256 很简单:你提供一个密钥,它会 MAC 化它。 RSA 更依赖于密钥强度(1024 不会削减它),最近的许多 JWT 漏洞都与 RSA 密钥有关,并且它增加了更多的复杂性(解析证书/私钥。除非您对客户端有一些迫切需要用你的公钥自我验证令牌,几乎没有什么收获。 @elithrar 有些用例需要与客户端验证令牌无关的公钥算法,例如依赖方为第三方的情况下。您不能向第三方提供您的 HMAC 机密。提问者没有说明他们生成的令牌是如何使用的。 很清楚。我想说的是“如果您必须询问如何生成 RSA 密钥并将它们与 SSL 混为一谈,那么您很可能不需要使用 RSA 密钥。”这些用例通常定义明确(正如您所指出的)。带有 React 或 Angular 的 SPA 的 JWT 不需要 RSA 签名的 JWT。 【参考方案1】:

这对我有用。我使用这些命令生成了 private.pem 和 public.pem。

openssl genrsa -des3 -out private.pem 1024
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

代码:

package main

import (
    "fmt"
    "github.com/dgrijalva/jwt-go"
    "io/ioutil"
    "log"
    "time"
)

//Claims can have user id.. etc for Identification purpose
type AppClaims struct 
    UserId string `json:"userId"`
    jwt.StandardClaims


var (
    privateKey []byte
    publicKey  []byte
    err        error
)

const (
    longForm = "Jan 2, 2006 at 3:04pm (MST)"
)

func errLog(err error) 
    if err != nil 
        log.Fatal("Error:", err.Error())
    


func init() 
    privateKey, err = ioutil.ReadFile("../private.pem")
    errLog(err)
    publicKey, err = ioutil.ReadFile("../public.pem")
    errLog(err)


func jwtTokenGen() (interface, error) 
    privateRSA, err := jwt.ParseRSAPrivateKeyFromPEM(privateKey)
    if err != nil 
        return nil, err
    
    claims := AppClaims
        "RAJINIS*",
        jwt.StandardClaims
            ExpiresAt: time.Now().Add(time.Minute * 15).Unix(),
            Issuer:    "test",
        ,
    
    token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
    ss, err := token.SignedString(privateRSA)
    return ss, err


func jwtTokenRead(inToken interface) (interface, error) 
    publicRSA, err := jwt.ParseRSAPublicKeyFromPEM(publicKey)
    if err != nil 
        return nil, err
    
    token, err := jwt.Parse(inToken.(string), func(token *jwt.Token) (interface, error) 
        if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok 
            return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
        
        return publicRSA, err
    )

    if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid 
        return claims, nil
     else 
        return nil, err
    


func getTokenRemainingValidity(timestamp interface) int 
    expireOffset := 0
    if validity, ok := timestamp.(float64); ok 
        tm := time.Unix(int64(validity), 0)
        remainder := tm.Sub(time.Now())
        if remainder > 0 
            fmt.Println(remainder)
            return int(remainder.Seconds()) + expireOffset
        
    
    return expireOffset


func main() 
    signedString, err := jwtTokenGen()
    fmt.Println(signedString, err)
    claims, err := jwtTokenRead(signedString)
    if err != nil 
        errLog(err)
    
    claimValue := claims.(jwt.MapClaims)
    fmt.Println(claimValue["iss"], claimValue["exp"], claimValue["userId"])
    //  t, _ := time.Parse(longForm, string(claimValue["exp"].(float64)))
    fmt.Println(getTokenRemainingValidity(claimValue["exp"]))

【讨论】:

以上是关于golang JWT 无签名。 “加密/rsa:验证错误”的主要内容,如果未能解决你的问题,请参考以下文章

为啥 JWT 是无状态认证?

shiro(13)-JWT(Token的生成)

解码时jwt签名异常

JWT 签名与本地计算的签名不匹配。 JWT 有效性不能被断言,也不应该被信任

.NET 库中的 JWT 无效签名

.NET:RS256 签名的 JWT 中的签名无效