gpt4 book ai didi

java - 启用加密和签名时 JWT 中的解析器异常

转载 作者:行者123 更新时间:2023-11-30 08:11:36 25 4
gpt4 key购买 nike

我是 JWT 的新手,通过独立代码学习以了解 JWT API。下面的代码从发送方对 JWT token 进行签名和加密,并在接收方进行验证。

库:JOSE 0.4.1

package com.one00bytes.jwt;

public class JWTSignEncryption {

public static void main(String[] args) throws Exception {

/***************************SENDER'S END ***********************************/

JwtClaims claims = new JwtClaims();
claims.setAudience("Admins");
claims.setIssuer("CA");
claims.setSubject("users");
claims.setClaim("email", "users@test.com");
claims.setClaim("Country", "Antartica");
System.out.println(claims.toJson());

//SIGNING
RsaJsonWebKey jsonSignKey = RsaJwkGenerator.generateJwk(2048);
JsonWebSignature jws = new JsonWebSignature();
jws.setKey(jsonSignKey.getPrivateKey());
jws.setPayload(claims.toJson());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA512);
String signedJwt = jws.getCompactSerialization();
System.out.println("Signed ::" + signedJwt);


//ENCRYPTING
RsaJsonWebKey keyEncrypt = RsaJwkGenerator.generateJwk(2048);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey contentEncryptKey = keyGen.generateKey();

JsonWebEncryption jwe = new JsonWebEncryption();
jwe.setKey(keyEncrypt.getPublicKey());
jwe.setPayload(signedJwt);
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256);
jwe.setContentEncryptionKey(contentEncryptKey.getEncoded());
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);
SecureRandom iv = SecureRandom.getInstance("SHA1PRNG");
jwe.setIv(iv.generateSeed(32));
String encryptedJwt = jwe.getCompactSerialization();
System.out.println("Encrypted ::" + encryptedJwt);


/***************************RECEIVER'S END ***********************************/

JwtConsumer consumer = new JwtConsumerBuilder()
.setExpectedAudience("Admins")
.setExpectedIssuer("CA")
.setRequireSubject()
.setDecryptionKey(keyEncrypt.getPrivateKey())
.setVerificationKey(jsonSignKey.getPublicKey())
.build();
JwtClaims receivedClaims = consumer.processToClaims(encryptedJwt);
System.out.println("SUCESS :: JWT Validation :: " + receivedClaims);

}

运行此程序时观察到以下异常:

Exception in thread "main" org.jose4j.jwt.consumer.InvalidJwtException: Unable to parse JWT Claim Set JSON: eyJhbGciOiJSUzUxMiJ9.eyJhdWQiOiJBZG1pbnMiLCJpc3MiOiJDQSIsInN1YiI6InVzZXJzIiwiaWF0IjoxNDM0NTM0MDgxLCJleHAiOjE0MzQ1MzQ2ODEsImp0aSI6IjJxUUpuMDVGY3RrLWF1VG1vVktuWXciLCJuYmYiOjE0MzQ1MzM5NjEsImVtYWlsIjoidXNlcnNAMTAwYnl0ZXMuY29tIiwiQ291bnRyeSI6IkFudGFydGljYSIsImhvYmJpZXMiOlsiQmxvZ2dpbmciLCJQbGF5aW5nIGNhcmRzIiwiR2FtZXMiXX0.soY_5Hbam569I-CnUW1F4GWdaqprh-XAOtAMOcb7zZSiRcIhXYUdJjEslrDbwphAP135SvmoXO4nVaVmo-d8oWREFYUeXEDzHbrqHNp7pp5pH6hGTJ5C4uE1UVzZ4bis3g_KEgZvEn31NnV4RcU_oRn2Q4inkrTlYKY-juEtCmpPQ0sSP4GiDbwVIfCj-kxZsKh_i9n28SSK890K3DIGiFWOUDwrnY4Yfr1UffsUS9ovyhtqrOcN4YsJR4XzGPaLehlR-qD7eOdAdmVb8RDtGKufNuCd7Q9OFfeKzBmGITHsvd6IPVYLLCfSCzO6PqQSIzkupl5D6HqoOqID8JZLxA
at org.jose4j.jwt.JwtClaims.<init>(JwtClaims.java:50)
at org.jose4j.jwt.JwtClaims.parse(JwtClaims.java:56)
at org.jose4j.jwt.consumer.JwtConsumer.process(JwtConsumer.java:267)
at org.jose4j.jwt.consumer.JwtConsumer.processToClaims(JwtConsumer.java:115)
at com.one00bytes.jwt.JWTSignEncryption.main(JWTSignEncryption.java:76)
Caused by: org.jose4j.lang.JoseException: Parsing error: org.jose4j.json.internal.json_simple.parser.ParseException: Unexpected character (e) at position 0.
at org.jose4j.json.JsonUtil.parseJson(JsonUtil.java:66)
at org.jose4j.jwt.JwtClaims.<init>(JwtClaims.java:45)
... 4 more
Caused by: org.jose4j.json.internal.json_simple.parser.ParseException: Unexpected character (e) at position 0.
at org.jose4j.json.internal.json_simple.parser.Yylex.yylex(Yylex.java:612)
at org.jose4j.json.internal.json_simple.parser.JSONParser.nextToken(JSONParser.java:269)
at org.jose4j.json.internal.json_simple.parser.JSONParser.parse(JSONParser.java:118)
at org.jose4j.json.internal.json_simple.parser.JSONParser.parse(JSONParser.java:81)
at org.jose4j.json.JsonUtil.parseJson(JsonUtil.java:62)
... 5 more

签名的 JWT

eyJhbGciOiJSUzUxMiJ9.eyJhdWQiOiJBZG1pbnMiLCJpc3MiOiJDQSIsInN1YiI6InVzZXJzIiwiZW1haWwiOiJ1c2Vyc0B0ZXN0LmNvbSIsIkNvdW50cnkiOiJBbnRhcnRpY2EifQ.5Xu7v2MosIQmtAOlqfM2PE9eJeT0iZzL9x6RIvqx_PAHKer0ylo-0wT9eON_qX1H_QZekTWMf8ok4fxdZNv2KP_AkNqSKLXYJ65TjPnfcX8-dooDJM9txfRWOFqJWx4yj4CTMPNR6rNhizkC9jUaLisPIjogc_a_61qTSnvHXFnuaYmkovN2Y3WfuXjhUZCH98hodRL_ATg1_SpO0bPb7_N1Z76yrcv0RYQan0Y5kICWYdhHlk8Dw6I2fLMVsl3HiYiRq4XBJE8AY_g742Uq5kTS62PKohg3IjfRa-g2rjgKo1XW2sRLVc7vnns2L3TqESo5vgvorTjKnCTQKuHpIg

加密 JWT

eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.lZ2nqCeiPzsPmJShsrDD3uA55-06A649CMtwOyuY9nNzMtUGyzV-G8qc4w4ui1uWrtzypBs5Eyq4GfjnTtVHbcDVkS1HVc3tfxNAPY8dfjVrWNz59HyKt4bCjBdqqhBOdZezLtWB9aoWIwZoHLf4D8aUcVUtDsFELVcScmiQNtzHwvpDHZb4oxRfPl-OuOTkKA23C8lnnDMO1KUy8ZXHD4p0jQKAcaV877gYm8NbHDwOBEf-ItWJOGx2jV60apWd0hKqwfFR2QKD9wmGgXpbFZ08ro7X2fj8rTgKWhDgoBT_JVZdVFhVI4T4RLRDrCJqkyeciXhLm7W_xNhWBXAMrA.94SuB596ZLuUtw53wrofwN5jZXfT5f-ZarJhQc9Mj0M.0Ow5DXfilYX3ty49H4lNMNPljlWAFASc49zljhRSIIUSlmUHLZo0SAezn-n_FdxexAIYLk_FtRgnkMHDEyxJ1V1yHhqa1Jvdb36lTYyptqCJhMkOV1XGn58L4Z9QQmdrIZnn5iHxZ9-N1Jfjs0eoKiLBgR9O7ZEcs7QrWZVT6n_HrGrIloYQu_lFgmk5O7k47_15CVXaFqIohpHXETejoHEwjQj-iTToNRaHWNFAKvlpUBz4mUgk9RSIQCxK1GxxS8wxP44w5G4HdOIjFNwTsRDXeSZy0mU9zTNUCmDEUT9MFESfmVU1nPurdT-VoiPvVklbJZW8Sas0hWgqQkdQdP35nFY1sjCgfMB9iYUeEU-TCE219wkm1XXrLJwLEYZclL_4ckl4zExo2wb3Czwd8f5iO9fBQQWZ4mdwThK4VtZaPs1JEkxwGLI0SHA8Jr-e2PsDrkGEnxs74FsJ5MKluU2ZKvKcGXyQPaaTRa0ecJLD5-YYBuTtxOnU3gM_5aZm97pd_wiPk_h81r5aiwjSfRF3Ihxp37KNPfNOMJoA9xe2F51m1AvmjrOUgSM156LwmFyJFebVfarb9NPtJ_q1wU891sCu2Vmv520BR4QfIc-ayIwTVxLgZSN-BP7PhEJb_x8.XhZpINBxRdFFEgwPTcAgJg

相同的代码分别运行用于签名和加密,但如果我同时包含两者则不会运行。

请帮助我理解我做错了什么。

提前致谢

最佳答案

对于嵌套的 JWT(即 JWE[JWS[JSON Claims]],这是你想要的),JWE 的“cty”(内容类型) header 应该具有“JWT”的值表明有效载荷本身就是一个 JWT。 JWT 规范中“cty”的定义,RFC 7519 ,多谈一点。它帮助消费者/接收者了解如何处理事物。

您看到的异常是库尝试将 JWS 紧凑序列化(即 JWE 的有效负载)解析为 JSON 的结果。

根据规范,您确实应该在 JWE 上将 cty header 设置为“JWT”,这表明 JWE 负载本身就是一个 JWT。从 v0.4.2 开始,这可以通过 jwe.setHeader(HeaderParameterNames.CONTENT_TYPE, "JWT");jwe.setContentTypeHeaderValue("JWT") 来完成。

您还可以告诉 JwtConsumer 在处理时更加自由一些,并在 cty header 不存在且有效负载未解析为 JSON 但可以解析时尽最大努力到 JOSE 对象中。这可以通过 JwtConsumerBuilder 上的 .setEnableLiberalContentTypeHandling() 来完成。

结合更多观察:

您无需在 JWE 上设置内容加密 key 或 IV。该库使用安全随机数为您生成具有适当长度的那些。所以以下应该足够了,

JsonWebEncryption jwe = new JsonWebEncryption();
jwe.setHeader(HeaderParameterNames.CONTENT_TYPE, "JWT");
jwe.setKey(keyEncrypt.getPublicKey());
jwe.setPayload(signedJwt);
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256);
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);
String encryptedJwt = jwe.getCompactSerialization();
System.out.println("Encrypted ::" + encryptedJwt);

另外,根据 RSA_OAEP_256 和 AES_256_GCM 的使用,我猜测您正在使用 Bouncy CaSTLe。我强烈建议升级到 jose4j 0.4.4,因为在将库与 Bouncy CaSTLe 安全提供程序一起使用时发现了一个安全漏洞。有关详细信息,请参阅 v 0.4.4 的发行说明 https://bitbucket.org/b_c/jose4j/wiki/Release%20Notes#!jose4j-044-july-24-2015

关于java - 启用加密和签名时 JWT 中的解析器异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30888180/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com