使用OpenSSL验证JWT(RS256)

3
我的要求是使用公钥(RS256)验证JWT。检查应仅基于本机OpenSSL。
我使用JWT.IO的初始内容进行测试。此令牌已生成:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA

当我输入带有点号分隔的标题+有效负载+签名(如下图中的项目1和2),接着我输入公钥(如下图中的项目3)。 结果,我看到消息“签名已验证”(如下图中的项目4)。 现在我想使用OpenSSL获得相同的结果。
我已经执行了以下步骤:
1. 创建文本文件/tmp/pub.pem,并将以下内容粘贴到其中(从JWT.IO测试用例复制):
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0 e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9 MwIDAQAB -----END PUBLIC KEY-----
公钥
创建文本文件 /tmp/data.txt 并将以下内容粘贴到其中。这是标题和载荷,由点分隔(图片中的项目1):

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0

  1. 创建文本文件 /tmp/signature.txt,并将下面的内容粘贴到其中(这是图片中的第2项):

POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA

  1. I run OpenSSL command:

    openssl dgst -verify /tmp/pub.pem -keyform PEM -sha256 -signature /tmp/signature.txt -binary /tmp/data.txt
    

    But get result:

    Verification Failure.

我到底做错了什么?
2个回答

1
JWT的签名是base64url编码的,需要先解码。建议重复只涉及base64编码的签名,而openssl似乎无法处理base64url编码。
如果您正在Windows系统上工作,可以使用certutil解码签名文件,它可以直接解码bas64url:
certutil -decode signature.txt signature.sha256

然后将signature.sha256作为openssl的输入:

openssl.exe dgst -sha256 -verify pubkey.pem -signature signature.sha256 data.txt

然后你应该得到结果:

验证通过


(保留html)

很遗憾,它不起作用。 这个命令 certutil -decode signature.txt signature.sha256 创建了一个新文件 signature.sha256,长度为256字节。没问题。 但是验证命令 openssl.exe dgst -sha256 -verify pubkey.pem -signature signature.sha256 data.txt 返回结果为 验证失败 - undefined
是的,OpenSSL无法解码Base64URL,这就是为什么我写了我的答案。请再次检查您的文件是否正确。在我写答案之前,我已经检查过了,并且我刚刚又检查了一遍,结果显示验证通过。我使用了您问题中的所有数据,尤其是公钥文件应该按照正确格式,每64个字符后有换行符,并且头部和尾部在单独的行上。 - undefined
我还尝试运行命令openssl enc -d -A -base64 -in signature.txt -out signature.sha1,结果生成了文件signature.sha1。但是该文件长度为0字节(似乎是无效文件)。 当我运行验证命令时,返回读取签名文件signature.sha1出错 - undefined
你之前已经发过那个评论了。是的,我也看到结果是0字节,当然会出错(因为文件是空的!)。请再仔细阅读我上面的评论。在回答之前,我实际上测试了这些命令,它们确实有效。也许在其中一个文件中有一个小的复制/粘贴错误,缺少了一个字符或其他什么问题? - undefined
1
非常感谢!我已经删除了之前的所有文件,并且重复了所有步骤。我不知道上次为什么失败了,但是现在您关于命令certutil -decode signature.txt signature.sha256的回答解决了问题。验证成功。再次感谢您! - undefined
啊,谢谢你的反馈,很高兴听到它对你有用!别担心,总会有些小问题出现。你介意点击答案左边的勾选标记为已接受吗? - undefined

1
我发现这个Gist对于从初始JWT到分解其各部分、解码某些位,然后根据签名验证有效载荷非常有用。它使用openssl和perl,这应该包含在大多数Bash实现中。
我将贴出我“简短”的版本。在我的情况下,我正在验证Auth0的JWT。
# The JWT itself
jwt=eyJ...

# Get Auth0's public certificate
curl https://your_auth0_domain/pem > auth0publiccert.pem

# Extract the public key from the public certificate
openssl x509 -pubkey -noout -in auth0publiccert.pem > auth0publickey.pem

# Extract the input payload
input=${jwt%.*}
echo -n $input > payload.txt

# Extract the signature portion
encSig=${jwt##*.}

# Decode the signature
echo -n $encSig | perl -ne 'tr|-_|+/|; print "$1\n" while length>76 and s/(.{0,76})//; $_ .= ("", "", "==", "=")[length($_) % 4]; print' | openssl enc -base64 -d > signature.dat

# Finally, verify
openssl dgst -sha256 -verify auth0publickey.pem -signature signature.dat payload.txt
#Output should be "Verified OK"

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