启用加密和签名时JWT中的分析器异常

时间:2015-06-17 09:54:43

标签: java json jwt jose4j

我是JWT的新手,通过独立代码学习了解JWT API。代码签名并从发件人端加密JWT令牌,并在接收端验证。

图书馆: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

相同的代码单独运行以进行签名和加密,但如果我同时包含这两个代码,则无法运行。

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

提前致谢

2 个答案:

答案 0 :(得分:2)

对于嵌套的JWT(即JWE [JWS [JSON Claims]],这就是你的东西),JWE的“cty”(内容类型)标题应该具有值“JWT”到表明有效载荷本身就是JWT。 JWT规范中的“cty”的定义RFC 7519,更多地讨论了这个问题。它有助于消费者/接收者知道如何处理事物。

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

根据规范,您确实应该在JWE上将cty标头设置为“JWT”,这表明JWE有效负载本身就是JWT。对于v0.4.2,可以使用jwe.setHeader(HeaderParameterNames.CONTENT_TYPE, "JWT");jwe.setContentTypeHeaderValue("JWT")来完成此操作。

您还可以告诉JwtConsumer在处理时更加自由,并且当cty头不存在且有效负载不解析为JSON但可以解析为JOSE时尽力而为宾语。这可以通过.setEnableLiberalContentTypeHandling()上的JwtConsumerBuilder来完成。

更多观察结果:

您无需在JWE上设置内容加密密钥或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安全提供程序一起使用时会发现一个安全漏洞。有关详细信息https://bitbucket.org/b_c/jose4j/wiki/Release%20Notes#!jose4j-044-july-24-2015

,请参阅v 0.4.4的发行说明

答案 1 :(得分:0)

JWT的有效负载或消息声明集的UTF-8表示。来自RFC 7519

  

让Message成为UTF-8表示的八位字节          JWT索赔集。

对于签名的JWT(JWS对象)和加密的JWT(使用JWE)都是如此:

  

如果JWT是JWE,请使用Message as创建JWE             JWE的明文; JWE中指定的所有步骤             必须遵循创建JWE。

因此,为了验证加密的JWT,有效载荷被解释为声明集:

  

否则,如果JWT是JWE,请按照中指定的步骤操作              JWE用于验证JWE。让消息成为结果              明文。

您在程序中犯的错误是使用已签名的JWT的序列化作为JWE的有效负载,但随后尝试将生成的对象作为n 加密 JWE处理。因此,库尝试将序列化签名JWT( JWS Flattened Serialization )解释为序列化JWT声明集(JSON对象)。这解释了您获得的例外情况:

Caused by: org.jose4j.lang.JoseException: Parsing error:
    org.jose4j.json.internal.json_simple.parser.ParseException:
    Unexpected character (e) at position 0.

您似乎正在尝试生成经过加密和身份验证的JWT。所有JWE算法都是经过身份验证的加密算法,因此无需使用JWS来实现此目的 - 加密的JWT就足够了。