3

I'm using JWT. This is how I create the token.

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
}

This is how I verify the token.

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

I generated my public and private keys with this python code

from Crypto.PublicKey import RSA

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

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

I get crypto/rsa: verification error. When I print the parsed token everything looks fine except token.Valid which is false and token.Signature is empty.

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:[email protected] Name:testName]
token.Signature-->           
token.Valid--> false

PS: I don't have any SSL-Certificates.

5
  • 1
    I highly recommend against using RSA (esp. with a 1024 bit key) for a JSON Web Token. It really doesn't offer practical benefits over just using jwt.GetSigningMethod("HS256") - which is HMAC-SHA-256. RSA is just easier to get wrong. Commented Jul 2, 2015 at 22:11
  • @elithrar RSA vs HMAC ~ public key vs symmetric key; which is preferable depends on use case. And in what way is RSA "just easier to get wrong"? The JWT library should do the heavy lifting. Commented Jul 3, 2015 at 2:23
  • HMAC-SHA-256 is simple: you provide a key, it MACs it. RSA is more reliant on key strength (1024 won't cut it), many of the recent JWT vulns have been related to RSA keys, and it adds more complexity (parsing certs/private keys. Unless you have some pressing need for the client to self-verify the token with your public key, there's little to be gained. Commented Jul 3, 2015 at 3:18
  • @elithrar there are use cases which require public key algorithms that have nothing to do with clients validating tokens, e.g. where the relying party is a third-party. You can't give a third-party your HMAC secret. The questioner has said nothing about how the tokens they are generating are used. Commented Jul 4, 2015 at 5:34
  • Well aware. What I'm trying to say is that "if you have to ask how to generate RSA keys and conflate them with SSL, there's a high likelihood that you don't need to use RSA keys." Those use cases are typically well defined (as you point out). JWTs for a SPA with React or Angular don't need RSA signed JWTs. Commented Jul 4, 2015 at 6:39

1 Answer 1

0

This worked for me. I generated the private.pem and public.pem using these commands.

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

Code:

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"]))
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.