JWT,如何验证签名?

问题描述 投票:0回答:2

我有这个智威汤逊:

eyJhbGciOiJSUzI1NiIsImtpZCI6IjVkMzQwZGRiYzNjNWJhY2M0Y2VlMWZiOWQxNmU5ODM3ZWM2MTYzZWIiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiemFnYWxvIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3Byb2ZlcHQtM2M0NzkiLCJhdWQiOiJwcm9mZXB0LTNjNDc5IiwiYXV0aF90aW1lIjoxNjY2MjkxNDAzLCJ1c2VyX2lkIjoiZ1JtdnFYb0tySE85T0RLUURCYTBWNnRaNTBLMiIsInN1YiI6ImdSbXZxWG9LckhPOU9ES1FEQmEwVjZ0WjUwSzIiLCJpYXQiOjE2NjYyOTE0MDMsImV4cCI6MTY2NjI5NTAwMywiZW1haWwiOiJyc2p1bGlhb0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicnNqdWxpYW9AZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.ZkBqE8GCSGt9FX_LxoaLNgHcPx19EDMq3ARmZaJ_R1_FiBcQAp8T_AEmleVu68lqw7SdcM2aAjZ1kZbfkZ48hgfhW0LI03VC_6Dc4sq9pgCHWarteCeUz4fE1B6nl4nIbKI3nPQorKYTu82SXEzaRiEwHQCVayiMmnkjzj4d-2YVp4WA8If_h3jNHBe8giskjwkB2t6hB39vYLqvcM5sEeSBRpVT8zA-hmp2AeImcXagCK4Av7JIt_iBNuwT9dwMLtA6addoXcDYTJuRZ3GhVrbL8x_is9u2XDDLWDWdrj1yAjkq7pTPwC7KPft8Md2PKxqYR5bid_VRSjPIeb_k8A

还有这个公钥

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3CTs4UeO1cS3FJAWDTcO
HHcAeEfZOCFmxju1xC+kSAw9RVQTykKzgNAREUQyGFvp1WwC51r1QHMwNyk+wsDG
/h4mbDIgECMeEEGh2qnHgFIVWJ12H5oP/WHVvho/GgVuOkJzCuHTTVYGSaKi43IR
VZqO7784VfzHsHl/caUqv/pOu8MjsynD8QVzac0XrdXHTqYUMWm0rFCrEm+UWFHK
KQK2skzQxFTUTcI2NtG+TjNFiHGs3ZzAfd+N6PuW3FpX3TsNN0fWmFbqgUH0oduV
9Qd2XhZ2TtnAK4+FVCLJDuqk8XkAe9Ibmgelz+aKtwFGN1bx8TilswsvepGjDpMj
AwIDAQAB
-----END PUBLIC KEY-----

使用网站 https://jwt.io/,可以运行签名验证,一切顺利OK

现在为了更好地了解正在发生的事情,我想自己生成签名。

我的第一步是使用此标头 json 作为输入来生成标头:

{"alg":"RS256","kid":"5d340ddbc3c5bacc4cee1fb9d16e9837ec6163eb","typ":"JWT"}

只需使用 base64UrlEncode 进行翻译即可得到:

eyJhbGciOiJSUzI1NiIsImtpZCI6IjVkMzQwZGRiYzNjNWJhY2M0Y2VlMWZiOWQxNmU5ODM3ZWM2MTYzZWIiLCJ0eXAiOiJKV1QifQ

对表示有效负载的 json 进行相同的转换,我们得到了正确的结果!

{"name":"zagalo","iss":"https://securetoken.google.com/profept-3c479","aud":"profept-3c479","auth_time":1666291403,"user_id":"gRmvqXoKrHO9ODKQDBa0V6tZ50K2","sub":"gRmvqXoKrHO9ODKQDBa0V6tZ50K2","iat":1666291403,"exp":1666295003,"email":"[email protected]","email_verified":false,"firebase":{"identities":{"email":["[email protected]"]},"sign_in_provider":"password"}}

eyJuYW1lIjoiemFnYWxvIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL3Byb2ZlcHQtM2M0NzkiLCJhdWQiOiJwcm9mZXB0LTNjNDc5IiwiYXV0aF90aW1lIjoxNjY2MjkxNDAzLCJ1c2VyX2lkIjoiZ1JtdnFYb0tySE85T0RLUURCYTBWNnRaNTBLMiIsInN1YiI6ImdSbXZxWG9LckhPOU9ES1FEQmEwVjZ0WjUwSzIiLCJpYXQiOjE2NjYyOTE0MDMsImV4cCI6MTY2NjI5NTAwMywiZW1haWwiOiJyc2p1bGlhb0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicnNqdWxpYW9AZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0

最后一部分:

根据 jwt.io,签名是:

RSASHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload))

我们假设:
H=base64UrlEncode(标头)

P=base64UrlEncode(有效负载)
所以, SIGNATURE=RSASHA256( H+'.'+P )

如何获得签名? 我一直在尝试各种策略来产生正确的结果,但我没有成功。

我必须做什么才能得到正确的结果? 我的主要策略是获取 SHA256 的结果,并将其作为参数与我上面的公钥一起传递给 RSA,但结果并不相同。我做错了什么? 我必须学什么来解决我缺乏知识的问题?

jwt rsa sha256
2个回答
1
投票

当您只有 RSA 公钥时,无法生成消息的签名。您需要私钥来生成签名。使用 JWT 的“RS256”算法的签名算法是“使用 SHA-256 的 RSASSA-PKCS1-v1_5”,如 RFC 7518 - 3.3 中所定义。使用 RSASSA-PKCS1-v1_5 进行数字签名:

      +-------------------+---------------------------------+
      | "alg" Param Value | Digital Signature Algorithm     |
      +-------------------+---------------------------------+
      | RS256             | RSASSA-PKCS1-v1_5 using SHA-256 |
      | RS384             | RSASSA-PKCS1-v1_5 using SHA-384 |
      | RS512             | RSASSA-PKCS1-v1_5 using SHA-512 |
      +-------------------+---------------------------------+

数字签名算法“RSASSA-PKCS1-v1_5”本身在 RFC 3447 - 8.2 中定义。 RSASSA-PKCS1-v1_5。实际的签名算法定义在RFC 3447 - 8.2.1签名生成操作:

RSASSA-PKCS1-V1_5-SIGN (K, M)

Input:
K        signer's RSA private key
M        message to be signed, an octet string

Output:
S        signature, an octet string of length k, where k is the
         length in octets of the RSA modulus n

如您所见,您需要“签名者的 RSA 私钥”

K
来生成消息的签名
M
(这将是您的 JWT 标头和负载)。

您只能使用公钥来验证给定的签名是否有效,而不能创建新的签名。只有私钥的所有者才能这样做。


0
投票

以下是如何验证 RS256 签名的示例:

var bn = ...;                               // BigInteger with your public n
var be = ...;                               // BigInteger with your public e
var base64jwt = ...;                        // your base64 encoded JWT token
var parts = base64jwt.split("\\.");         // the 3 parts
var sContent = parts[0] + "." + parts[1];   // this is hashed
var sha = MessageDigest.getInstance("SHA256");
var hash = sha.digest(data.getBytes());     // the expected hash

现在解密签名的哈希值:

byte[] h = Mime.decode(parts[2].getBytes());// use some base64 decoder
var bh = new BigInteger(1, h);              // create a BigInteger
var bv = bh.modPow(be, bn);                 // apply the public key
var v = bv.toByteArray();                   // the resulting data

对于 sha256,最后 32 个字节是相关的。您还应该检查数据是否正确...

var z = new byte[32];
System.arraycopy(v, v.length - 32, z, 0, 32);
var ok = Arrays.equals(hash, z);

到目前为止一切顺利。使用一些可用的库可以缓解这一问题,并为所有签名变体提供全面支持。例如,使用 nimbus-jose-jwt 您可以使用 jwks_uri 检索所有密钥(您可以通过 openid 提供者的 .well-known/openid-configuration 获得此信息)。

var parts = base64jwt.split("\\.");         // the 3 parts
var selector = JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(new URL(jwksUrl));
var sjwt = new SignedJWT(new Base64URL(parts[0]), new Base64URL(parts[1]), new Base64URL(parts[2]));
var processor = new DefaultJWTProcessor<>();
processor.setJWSKeySelector(selector);
var claims = processor.process(sjwt, null);

如果出现错误,您将得到一些异常,如果成功,您将获得一个可以轻松访问所有属性的声明对象。

© www.soinside.com 2019 - 2024. All rights reserved.