diff --git a/jwt/generate_keys.go b/jwt/generate_keys.go new file mode 100644 index 0000000..094e83a --- /dev/null +++ b/jwt/generate_keys.go @@ -0,0 +1,63 @@ +package jwt + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "errors" + + "github.com/golang-jwt/jwt/v5" +) + +func generateKey(method jwt.SigningMethod) (interface{}, error) { + switch method.Alg() { + case jwt.SigningMethodRS256.Alg(), + jwt.SigningMethodRS384.Alg(), + jwt.SigningMethodRS512.Alg(), + jwt.SigningMethodPS256.Alg(), + jwt.SigningMethodPS384.Alg(), + jwt.SigningMethodPS512.Alg(): + privateKeyRS, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return "", err + } + return privateKeyRS, nil + + case jwt.SigningMethodES256.Alg(): + privateKeyES256, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return "", err + } + return privateKeyES256, nil + + case jwt.SigningMethodES384.Alg(): + privateKeyES384, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + if err != nil { + return "", err + } + return privateKeyES384, nil + + case jwt.SigningMethodES512.Alg(): + privateKeyES512, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + if err != nil { + return "", err + } + return privateKeyES512, nil + + case jwt.SigningMethodHS256.Alg(), + jwt.SigningMethodHS384.Alg(), + jwt.SigningMethodHS512.Alg(): + keyHS := make([]byte, 64) + _, err := rand.Read(keyHS) + if err != nil { + return "", err + } + return keyHS, nil + + case jwt.SigningMethodNone.Alg(): + return nil, nil + } + + return "", errors.New("unsupported signing method") +} diff --git a/jwt/jwt_writer.go b/jwt/jwt_writer.go index e6d60b6..115a808 100644 --- a/jwt/jwt_writer.go +++ b/jwt/jwt_writer.go @@ -11,7 +11,6 @@ type JWTWriter struct { } func NewJWTWriter(token string) (*JWTWriter, error) { - // Parse the original JWT token originalToken, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{}) if err != nil { return nil, err @@ -25,10 +24,8 @@ func NewJWTWriter(token string) (*JWTWriter, error) { } func (j *JWTWriter) SignWithMethodAndKey(method jwt.SigningMethod, key interface{}) (string, error) { - // Create a new token with the new claims newToken := jwt.NewWithClaims(method, j.Token.Claims) - // Sign the new token with the new secret key tokenString, err := newToken.SignedString(key) if err != nil { return "", err @@ -37,6 +34,14 @@ func (j *JWTWriter) SignWithMethodAndKey(method jwt.SigningMethod, key interface return tokenString, nil } +func (j *JWTWriter) SignWithMethodAndRandomKey(method jwt.SigningMethod) (string, error) { + key, err := generateKey(method) + if err != nil { + return "", err + } + return j.SignWithMethodAndKey(method, key) +} + func (j *JWTWriter) SignWithKey(key interface{}) (string, error) { return j.SignWithMethodAndKey(j.Token.Method, key) } diff --git a/jwt/jwt_writer_test.go b/jwt/jwt_writer_test.go new file mode 100644 index 0000000..185da22 --- /dev/null +++ b/jwt/jwt_writer_test.go @@ -0,0 +1,72 @@ +package jwt_test + +import ( + "testing" + + "github.com/cerberauth/vulnapi/jwt" + libjwt "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/assert" +) + +func TestJWTWriter_SignWithMethodAndRandomKey_WhenSigningMethodIsHS256(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + writer, _ := jwt.NewJWTWriter(token) + + token, err := writer.SignWithMethodAndRandomKey(libjwt.SigningMethodHS256) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} + +func TestJWTWriter_SignWithMethodAndRandomKey_WhenSigningMethodIsRS256(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + writer, _ := jwt.NewJWTWriter(token) + + token, err := writer.SignWithMethodAndRandomKey(libjwt.SigningMethodRS256) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} + +func TestJWTWriter_SignWithMethodAndRandomKey_WhenSigningMethodIsRS384(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + writer, _ := jwt.NewJWTWriter(token) + + token, err := writer.SignWithMethodAndRandomKey(libjwt.SigningMethodRS384) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} + +func TestJWTWriter_SignWithMethodAndRandomKey_WhenSigningMethodIsRS512(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + writer, _ := jwt.NewJWTWriter(token) + + token, err := writer.SignWithMethodAndRandomKey(libjwt.SigningMethodRS512) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} + +func TestJWTWriter_SignWithMethodAndRandomKey_WhenSigningMethodIsES256(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + writer, _ := jwt.NewJWTWriter(token) + + token, err := writer.SignWithMethodAndRandomKey(libjwt.SigningMethodES256) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} + +func TestJWTWriter_SignWithMethodAndRandomKey_WhenSigningMethodIsES384(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + writer, _ := jwt.NewJWTWriter(token) + + token, err := writer.SignWithMethodAndRandomKey(libjwt.SigningMethodES384) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} + +func TestJWTWriter_SignWithMethodAndRandomKey_WhenSigningMethodIsES512(t *testing.T) { + token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + writer, _ := jwt.NewJWTWriter(token) + + token, err := writer.SignWithMethodAndRandomKey(libjwt.SigningMethodES512) + assert.NoError(t, err) + assert.NotEmpty(t, token) +} diff --git a/scan/jwt/not_verified.go b/scan/jwt/not_verified.go index ce9f527..271fd95 100644 --- a/scan/jwt/not_verified.go +++ b/scan/jwt/not_verified.go @@ -6,7 +6,6 @@ import ( "github.com/cerberauth/vulnapi/internal/scan" "github.com/cerberauth/vulnapi/jwt" "github.com/cerberauth/vulnapi/report" - jwtlib "github.com/golang-jwt/jwt/v5" ) const ( @@ -22,11 +21,7 @@ func NotVerifiedScanHandler(operation *request.Operation, ss auth.SecurityScheme } valueWriter := ss.GetValidValueWriter().(*jwt.JWTWriter) - method := jwtlib.SigningMethodHS256 - if valueWriter.Token.Method == method { - method = jwtlib.SigningMethodHS384 - } - newToken, err := valueWriter.SignWithMethodAndKey(method, []byte("a")) + newToken, err := valueWriter.SignWithMethodAndRandomKey(valueWriter.Token.Method) if err != nil { return r, err }