Skip to content

Commit

Permalink
Add support for RSA signing keys
Browse files Browse the repository at this point in the history
  • Loading branch information
kernle32dll committed Nov 14, 2019
1 parent f9eb488 commit a850e5d
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 2 deletions.
2 changes: 1 addition & 1 deletion login/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (c *Config) ConfigureFlagSet(f *flag.FlagSet) {
f.StringVar(&c.LogLevel, "log-level", c.LogLevel, "The log level")
f.BoolVar(&c.TextLogging, "text-logging", c.TextLogging, "Log in text format instead of json")
f.StringVar(&c.JwtSecret, "jwt-secret", c.JwtSecret, "The secret to sign the jwt token")
f.StringVar(&c.JwtAlgo, "jwt-algo", c.JwtAlgo, "The singing algorithm to use (ES256, ES384, ES512, HS512, HS256, HS384, HS512)")
f.StringVar(&c.JwtAlgo, "jwt-algo", c.JwtAlgo, "The singing algorithm to use (ES256, ES384, ES512, RS256, RS384, RS512, HS256, HS384, HS512")
f.DurationVar(&c.JwtExpiry, "jwt-expiry", c.JwtExpiry, "The expiry duration for the jwt token, e.g. 2h or 3h30m")
f.IntVar(&c.JwtRefreshes, "jwt-refreshes", c.JwtRefreshes, "The maximum amount of jwt refreshes. 0 by Default")
f.StringVar(&c.CookieName, "cookie-name", c.CookieName, "The name of the jwt cookie")
Expand Down
13 changes: 12 additions & 1 deletion login/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func (h *Handler) signingInfo() (signingMethod jwt.SigningMethod, key, verifyKey
keyString := h.config.JwtSecret
switch h.config.JwtAlgo {
case "ES256", "ES384", "ES512":
if !strings.Contains(string(keyString), "-----") {
if !strings.Contains(keyString, "-----") {
keyString = "-----BEGIN EC PRIVATE KEY-----\n" + keyString + "\n-----END EC PRIVATE KEY-----"
}

Expand All @@ -334,6 +334,17 @@ func (h *Handler) signingInfo() (signingMethod jwt.SigningMethod, key, verifyKey
}
h.signingKey = key
h.signingVerifyKey = key.Public()
case "RS256", "RS384", "RS512":
if !strings.Contains(keyString, "-----") {
keyString = "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "\n-----END RSA PRIVATE KEY-----"
}

key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(keyString))
if err != nil {
return nil, nil, nil, errors.Wrap(err, "can not parse PEM formated RSA private key")
}
h.signingKey = key
h.signingVerifyKey = key.Public()
default:
h.signingKey = []byte(keyString)
h.signingVerifyKey = h.signingKey
Expand Down
70 changes: 70 additions & 0 deletions login/handler_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package login

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -516,6 +520,72 @@ func TestHandler_signAndVerify_ES256(t *testing.T) {
Equal(t, input, userInfo)
}

func TestHandler_signAndVerify_RSA(t *testing.T) {
h := testHandler()

tt := []int{
256,
384,
512,
}
for _, bits := range tt {
jwtAlgo := fmt.Sprintf("RS%d", bits)
t.Run(jwtAlgo, func(t *testing.T) {
t.Parallel()

key, err := rsa.GenerateKey(rand.Reader, bits*2)
NoError(t, err)

privateKey := &pem.Block{
Type: "PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}

h.config.JwtAlgo = jwtAlgo
h.config.JwtSecret = string(pem.EncodeToMemory(privateKey))

input := model.UserInfo{Sub: "marvin", Expiry: time.Now().Add(time.Second).Unix()}
token, err := h.createToken(input)
NoError(t, err)
r := &http.Request{
Header: http.Header{"Cookie": {h.config.CookieName + "=" + token + ";"}},
}
userInfo, valid := h.GetToken(r)
True(t, valid)
Equal(t, input, userInfo)
})
}
}

func TestHandler_signAndVerify_RSA_headerless(t *testing.T) {
h := testHandler()
h.config.JwtAlgo = "RS256"
h.config.JwtSecret = "" +
"MIICXAIBAAKBgQDSu7M1jiH06fGywhSw5jdjUdfX6b1yw8j2coVjAgT1oB44vU+S" +
"dgvak/tWoBkqG+Gdrn0m+3H/mRtGXWZDmh6VjQ5mnw91OGVJccL2UGdEbb4ub/9g" +
"4Bobn1ANUcbZvXWpmNP0kqyBwsXiaq6iL4TNW5iKdvnat7SwzLyIkGwPkQIDAQAB" +
"AoGAXpshs1Nh7z/v4F69R0WzbAVcL3SiNpmq6Ok09OP9MgB2UOa8iHYykCiLV7J8" +
"Wak2usGRMiUEYslrs0VPGd5hB9X94fDAh0SYC2wmBOJRBY2tU82pSkN5RjE8A3+f" +
"G6uwlZB2UtpYa/sihf7NkJCQh2ibT3YeelDUvEnfwALB6iECQQDck14kDckwi4mt" +
"LwWPqXTWAdKdTN1i6KGXDBt7Bi5lbVk3XFgQy/Z+GzRiBtjcWmcMw2VOUeFC9d/J" +
"WnRv8NklAkEA9JOsxEgzr7utqw3Zd1dDK5weDhAwXuaHiCIS+bDAsGor7pSgWOtU" +
"k+kpDdPe/TmtxJFhorJOsl+49VtYVoy+/QJAWJnlhcv31cUnL2ak8DkcUl53EHJw" +
"tytExVy6qScpedp7rM4uHckgITWiTAH+GD1ECY9vYQ9o0bHcC5CHFvQC9QJBAOwI" +
"2ONVCwy+A4zhgM472QdtU1QfK49qy8IFoGp4un2G+X720Qj/lFBq5MQDhWC9GYZr" +
"B98MVgavesDPtyFQE8ECQCRZaTDF4d5KBAvu5ogoqEATD5r21V4Zj5uZ/QSeI7+v" +
"UVncBYg6g4CIrczoqYpJ3aBF5MVJ0FEU9XCDO/iDvCU="

input := model.UserInfo{Sub: "marvin", Expiry: time.Now().Add(time.Second).Unix()}
token, err := h.createToken(input)
NoError(t, err)
r := &http.Request{
Header: http.Header{"Cookie": {h.config.CookieName + "=" + token + ";"}},
}
userInfo, valid := h.GetToken(r)
True(t, valid)
Equal(t, input, userInfo)
}

func TestHandler_getToken_InvalidSecret(t *testing.T) {
h := testHandler()
input := model.UserInfo{Sub: "marvin"}
Expand Down

0 comments on commit a850e5d

Please sign in to comment.