8

I have a /token endpoint that is working with a password grant. Because it's encoding a JWT token, it has high latency of about 1 second. Is there a way to make signing the JWT faster?

I'm using Go with the github.com/dgrijalva/jwt-go library.

package main

import (
    "crypto/rsa"
    "git.snappfood.ir/golang/framework/assert"
    "io/ioutil"
    "time"

    
    "github.com/dgrijalva/jwt-go"
    "github.com/sirupsen/logrus"
)

var (
    private *rsa.PrivateKey
    public  *rsa.PublicKey
)

func main() {
    var err error
    var priv, pub []byte
    pub, err = ioutil.ReadFile("public.pem")
    if err!=nil{
        panic(err)
    }
    priv, err = ioutil.ReadFile("private.pem")
    if err!=nil{
        panic(err)
    }

    public, err = jwt.ParseRSAPublicKeyFromPEM(pub)
    if err!=nil{
        panic(err)
    }

    private, err = jwt.ParseRSAPrivateKeyFromPEM(priv)
    if err!=nil{
        panic(err)
    }

    data := map[string]interface{}{
        "jti": "dara",
        "scopes": func() []string {
            return []string{"sara", "dara"}
        }(),
        "aud": "aud",
        "sub": "",
    }
    cl := jwt.MapClaims{}

    for k, v := range data {
        cl[k] = v
    }
    cl["iat"] = time.Now().Add(-6 * time.Hour).Unix()
    cl["exp"] = time.Now().UTC().Add(1 * time.Hour).Unix()

    t := jwt.NewWithClaims(jwt.GetSigningMethod("RS256"), cl)

    t2 := time.Now()
    tokenString, err := t.SignedString(private)
    assert.Nil(err)
    logrus.Warn(time.Since(t2))
    logrus.Warn(tokenString)

}
Matthew Rankin
  • 457,139
  • 39
  • 126
  • 163
slifer2015
  • 682
  • 6
  • 12
  • 6
    SignedString boils down to a call to [rsa.SignPKCS1v15](https://golang.org/pkg/crypto/rsa/#SignPKCS1v15) in your case. The timing semantics of this function are controlled by its `rand.Reader` -- its (first) argument. In the case of `jwt-go`, this is [`crypto/rand.Reader`](https://golang.org/pkg/crypto/rand/#pkg-variables). The implementation of `crypto/rand.Reader` depends on your platform. It may be blocking for a long time because of low entropy on your system. Try measuring across platforms. – thwd Jun 07 '20 at 11:40
  • Thanks for your description @thwd . I'm using Ubuntu server and I want to reduce the time for encoding , how can I increase the entropy ? or is there another way ? – slifer2015 Jun 08 '20 at 04:52
  • 3
    How did you reach the conclusion that it was the JWT generation that took 1 second? That sounds very unlikely to me. Did you see in profiling that the JWT encoding is what takes the most of time? – Song Gao Sep 04 '21 at 06:02
  • 1
    I cant tell why it takes 1 second which is really slow. I am also signing JWTs in a project and there is no measurable impact. As a hint your used library `github.com/dgrijalva/jwt-go` is archived and no longer maintend. I would suggest switching to [https://github.com/golang-jwt/jwt](https://github.com/golang-jwt/jwt) even though this might not affect your problem. – fabs Oct 05 '22 at 08:29

2 Answers2

3

Without jumping into library details, it's small but you don't need to initialize a map[string]interface then loop over it to populate a jwt.MapClaims{}. You can just start with a jwt.MapClaims{} like this:

cl := jwt.MapClaims{
    "jti": "dara",
    "scopes": func() []string {
        return []string{"sara", "dara"}
    }(),
    "aud": "aud",
    "sub": "",
    // no need to do these separately:
    "iat": time.Now().Add(-6 * time.Hour).Unix(),
    "exp": time.Now().UTC().Add(1 * time.Hour).Unix(),
}

Consider using Benchmark Tests to experiment with different approaches.


EDIT:

I think time.Now() can be slow sometimes depending on the machine/os and can be a possible factor. So make sure to benchmark time.Now() and look for issues such as this if you want to dig deeper

offchance
  • 642
  • 7
  • 13
-2

In Golang you can always step down to C level. But with that comes a large increase of complexity of shipping your app. But if any ms counts for you, it might be the way out.

But at the same time, (I have not profiled anything) it is still Go. You could easily parallelise the private/public ParseRSAPrivateKeyFromPEM calls to shave a few ms off

CESCO
  • 7,305
  • 8
  • 51
  • 83