cmd: add handling of HS keys

Signed-off-by: Paul Greenberg <greenpau@outlook.com>
This commit is contained in:
Paul Greenberg 2022-02-08 14:17:53 -05:00
parent d0c0939ff8
commit c6a6a5f45b
2 changed files with 117 additions and 7 deletions

View File

@ -1,19 +1,97 @@
`jwt` command-line tool # `jwt` command-line tool
=======================
This is a simple tool to sign, verify and show JSON Web Tokens from This is a simple tool to sign, verify and show JSON Web Tokens from
the command line. the command line.
## Getting Started
The following will create and sign a token, then verify it and output the original claims: The following will create and sign a token, then verify it and output the original claims:
echo {\"foo\":\"bar\"} | ./jwt -key ../../test/sample_key -alg RS256 -sign - | ./jwt -key ../../test/sample_key.pub -alg RS256 -verify - ```bash
echo {\"foo\":\"bar\"} | ./jwt -key ../../test/sample_key -alg RS256 -sign - | ./jwt -key ../../test/sample_key.pub -alg RS256 -verify -
```
Key files should be in PEM format. Other formats are not supported by this tool. Key files should be in PEM format. Other formats are not supported by this tool.
To simply display a token, use: To simply display a token, use:
echo $JWT | ./jwt -show - ```bash
echo $JWT | ./jwt -show -
```
You can install this tool with the following command: You can install this tool with the following command:
go install github.com/golang-jwt/jwt/v4/cmd/jwt ```bash
go install github.com/golang-jwt/jwt/v4/cmd/jwt
```
## Sign/Verify with Shared Secret
First, create a JSON document with token payload, e.g. `~/experimental/jwt/data`.
```json
{
"email": "jsmith@foo.bar",
"aud": "foo.bar",
"exp": 2559489932,
"iat": 1612805132,
"iss": "foo.bar",
"sub": "jsmith"
}
```
Then, create a file with shared secret key, e.g. `~/experimental/jwt/token.key`.
```
foobarbaz
```
Next, sign the token:
```bash
./jwt -key ~/experimental/jwt/token.key -alg HS512 -sign ~/experimental/jwt/data > ~/experimental/jwt/token.jwt
```
After that, review the token:
```bash
./jwt -show ~/experimental/jwt/token.jwt
```
The expected output follows:
```
Header:
{
"alg": "HS512",
"typ": "JWT"
}
Claims:
{
"aud": "foo.bar",
"email": "jsmith@foo.bar",
"exp": 2559489932,
"iat": 1612805132,
"iss": "foo.bar",
"sub": "jsmith"
}
```
Subsequently, validate the token:
```bash
./jwt -key ~/experimental/jwt/token.key -alg HS512 -verify ~/experimental/jwt/token.jwt
```
The expected output follows:
```
{
"aud": "foo.bar",
"email": "jsmith@foo.bar",
"exp": 2559489932,
"iat": 1612805132,
"iss": "foo.bar",
"sub": "jsmith"
}
```

View File

@ -7,6 +7,7 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
@ -16,6 +17,7 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"unicode"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
@ -142,6 +144,8 @@ func verifyToken() error {
return jwt.ParseRSAPublicKeyFromPEM(data) return jwt.ParseRSAPublicKeyFromPEM(data)
} else if isEd() { } else if isEd() {
return jwt.ParseEdPublicKeyFromPEM(data) return jwt.ParseEdPublicKeyFromPEM(data)
} else if isHs() {
return parseHSKey(data)
} }
return data, nil return data, nil
}) })
@ -196,9 +200,19 @@ func signToken() error {
// get the key // get the key
var key interface{} var key interface{}
if isNone() { switch {
case isNone():
key = jwt.UnsafeAllowNoneSignatureType key = jwt.UnsafeAllowNoneSignatureType
} else { case isHs():
kb, err := loadData(*flagKey)
if err != nil {
return fmt.Errorf("couldn't read key: %w", err)
}
key, err = parseHSKey(kb)
if err != nil {
return err
}
default:
key, err = loadData(*flagKey) key, err = loadData(*flagKey)
if err != nil { if err != nil {
return fmt.Errorf("couldn't read key: %w", err) return fmt.Errorf("couldn't read key: %w", err)
@ -292,6 +306,10 @@ func showToken() error {
return nil return nil
} }
func isHs() bool {
return strings.HasPrefix(*flagAlg, "HS")
}
func isEs() bool { func isEs() bool {
return strings.HasPrefix(*flagAlg, "ES") return strings.HasPrefix(*flagAlg, "ES")
} }
@ -342,3 +360,17 @@ func (l ArgList) Set(arg string) error {
l[parts[0]] = parts[1] l[parts[0]] = parts[1]
return nil return nil
} }
func parseHSKey(b []byte) ([]byte, error) {
if len(b) == 0 {
return nil, fmt.Errorf("shared key is empty")
}
f := func(c rune) bool {
return unicode.IsSpace(c)
}
i := bytes.IndexFunc(b, f)
if i < 0 {
return b, nil
}
return b[:i], nil
}