如何使用OpenSSL shell命令创建Json Web Token (JWT)?

22

我正在尝试在MacOS上使用命令行工具创建JSON Web Token (JWT),但在签名部分遇到了问题。

我受到了这个代码片段的极大启发:https://gist.github.com/indrayam/dd47bf6eef849a57c07016c0036f5207

我的JWT包含:

{"alg":"HS256","typ":"JWT"}

负载:

{"email":"jordan@example.com"}

我的hmac密钥是:

bigsecretisveryhardtoguessbysneakypeopleright

或者以Base64编码:

Ymlnc2VjcmV0aXN2ZXJ5aGFyZHRvZ3Vlc3NieXNuZWFreXBlb3BsZXJpZ2h0Cg==

我在使用以下网站进行验证:https://jwt.io/

我发现如果我使用我的密钥的base64版本将所有内容输入到该网站中,它会生成以下JWT,成功地通过我正在测试的网站进行验证:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvcmRhbkBleGFtcGxlLmNvbSJ9.C3MVjfmnul8dLNIgiv6Dt3jSefD07Y0QtDrOZ5oYSXo

在Bash中,我尝试了以下内容:

jwt_header=$(echo -n '{"alg":"HS256","typ":"JWT"}' | base64 | sed s/\+/-/g | sed 's/\//_/g' | sed -E s/=+$//)

payload=$(echo -n '{"email":"jordan@example.com"}' | base64 | sed s/\+/-/g |sed 's/\//_/g' |  sed -E s/=+$//)

hmac_signature=$(echo -n "${jwt_header}.${payload}" | openssl dgst -sha256 -hmac "${key}" -binary | openssl base64 -e -A | sed s/\+/-/g | sed 's/\//_/g' | sed -E s/=+$//)

jwt="${jwt_header}.${payload}.${hmac_signature}"

它产生了以下结果:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpyZWVkQGV4dG9sZS5jb20ifQ.o426f0XDnsUwActVt14Cr3X3IUqPwfv6yaN5nRaZhew

这个网站不接受作为有效签名的东西。因此,我不确定在openssl命令中做错了什么,以致于没有获得一个有效的HS256签名。


相关链接:https://dev59.com/jaXja4cB1Zd3GeqPW9Lv - Heath Borders
相关链接:https://dev59.com/Zbbna4cB1Zd3GeqPhNCL#58326090 - Heath Borders
1个回答

21

我能够从https://jwt.io/重新创建JWT。

在您的示例中,用户密钥上有一个隐藏的换行符。因此,在下面的示例中,我也添加了该换行符,仅为了重新创建所需的输出。此外,负载中的电子邮件地址不一致,因此在下面我使用jordan@example.com

我对hmac步骤采取了稍微不同的方法。 我将用户密钥转换为十六进制字节,并将其用作密钥(使用HMAC的hexkey选项)。

# Construct the header
jwt_header=$(echo -n '{"alg":"HS256","typ":"JWT"}' | base64 | sed s/\+/-/g | sed 's/\//_/g' | sed -E s/=+$//)

# ans: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

# Construct the payload
payload=$(echo -n '{"email":"jordan@example.com"}' | base64 | sed s/\+/-/g |sed 's/\//_/g' |  sed -E s/=+$//)

# ans: eyJlbWFpbCI6ImpvcmRhbkBleGFtcGxlLmNvbSJ9

# Store the raw user secret (with example of newline at end)
secret=$'bigsecretisveryhardtoguessbysneakypeopleright\n'

# Note, because the secret may have newline, need to reference using form $"" 
echo -n "$secret"

# Convert secret to hex (not base64)
hexsecret=$(echo -n "$secret" | xxd -p | paste -sd "")

# ans: 62696773656372657469737665727968617264746f67756573736279736e65616b7970656f706c6572696768740a

# For debug, also display secret in base64 (for input into https://jwt.io/)
echo -n "$secret" | base64

# ans: Ymlnc2VjcmV0aXN2ZXJ5aGFyZHRvZ3Vlc3NieXNuZWFreXBlb3BsZXJpZ2h0Cg==

# Calculate hmac signature -- note option to pass in the key as hex bytes
hmac_signature=$(echo -n "${jwt_header}.${payload}" |  openssl dgst -sha256 -mac HMAC -macopt hexkey:$hexsecret -binary | base64  | sed s/\+/-/g | sed 's/\//_/g' | sed -E s/=+$//)

# Create the full token
jwt="${jwt_header}.${payload}.${hmac_signature}"

# ans: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvcmRhbkBleGFtcGxlLmNvbSJ9.C3MVjfmnul8dLNIgiv6Dt3jSefD07Y0QtDrOZ5oYSXo

8
由于某种原因,输入命令 paste -sd "" 时出现了错误 paste: no delimiters specified。我改用了 tr -d '\n' 命令,效果非常好。 - Jordan Reed
当我按照这些说明操作时,在 https://jwt.io/ 上运行时,签名未通过验证。为了与他们的工具进行比较,我还尝试选择不同的密钥(没有 \n),并使用 jwt.io 网站的签名生成(HS256)进行验证,但它仍然与 hmac_signature 命令的输出不匹配。 - GM Lucid
@GMLucid ... 我尝试的方法是访问jwt.io,然后(第一步)将明文密钥输入到右下角的框中(不要勾选base64),然后(第二步)将整个令牌粘贴到左侧。最好使用不以换行符结尾的密钥进行此测试。我成功地获得了“签名已验证”的勾号。 - Darren Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接