Jason
Jason

Reputation: 1

CAS 6.5 not able to use jose4J package to validate JWT

I am upgrading from 5.3 to 6.5 and getting errors using the Jose4J package to validate the JWTs CAS is generating. From what I can tell after jose4j's JWE decrypts the JWT the content type header “cty” is “JWT” indicating that the JWT is still in a nested state and needs to again be decrypted or unsigned. The code works fine with CAS version 5.3.15. I'm using the cas overlay project and last tried version 6.5.7.

It appears that the inner JWT's content type header "cty" is set to "JWT". From my reading only the outer JWT should have it set. Is this a bug in CAS's JWT implementation?

// Step 1: signature validation
JsonWebSignature jws = new JsonWebSignature();
jws.setCompactSerialization(jwtString);
jws.setKey(new AesKey(jwtConfig.getSigningKey().getBytes(StandardCharsets.UTF_8)));
jws.setAlgorithmConstraints(AlgorithmConstraints.DISALLOW_NONE);
            
if (!jws.verifySignature()) {
                logger.error(String.format("jwt have invalid signature:%s", jwtString));
                return new ValidationDTO(false, false);
}

// Step 2: check if encryption is fine, but possibly a expired token 
final byte[] decodedBytes = Base64.decodeBase64(jws.getEncodedPayload().getBytes(StandardCharsets.UTF_8));
final String decodedPayload = new String(decodedBytes, StandardCharsets.UTF_8);
final JsonWebKey jsonWebKey = JsonWebKey.Factory
                    .newJwk("\n" + "{\"kty\":\"oct\",\n" + " \"k\":\"" + jwtConfig.getEncriptionKey() + "\"\n" + "}");
            
JwtConsumer consumer = new JwtConsumerBuilder()
                    .setSkipAllValidators()
                    .setDisableRequireSignature()
                    .setSkipSignatureVerification()
                    .setDecryptionKey(new AesKey(jsonWebKey.getKey().getEncoded()))
                    .setJweAlgorithmConstraints(
                            new AlgorithmConstraints(ConstraintType.WHITELIST,
                                    KeyManagementAlgorithmIdentifiers.DIRECT))
                    
                    .setJweContentEncryptionAlgorithmConstraints(
                            new AlgorithmConstraints(ConstraintType.WHITELIST,
                                    ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256))  //this have to match CAS configuration
                    .build();
            
JwtContext context = consumer.process(decodedPayload);  // <<<<< Exception thrown here. “Invalid JOSE Compact Serialization"
Invalid JWT:JWT processing failed. Additional details: [[17] Unable to process nested JOSE object (cause: org.jose4j.lang.JoseException: Invalid JOSE Compact Serialization. Expecting either 3 or 5 parts for JWS or JWE respectively but was 14.): {"clientIpAddress":"127.0.0.1","sub":"[email protected]","authenticationDate":1659977730,"successfulAuthenticationHandlers":"careerAuthenticationHandler","iss":"https:\/\/jason.crengland.com\/cas","userAgent":"PostmanRuntime\/7.29.2","credentialType":"UsernamePasswordCredential","aud":"https:\/\/jason.crengland.com\/cas","authenticationMethod":"careerAuthenticationHandler","geoLocation":"unknown","serverIpAddress":"127.0.0.1","exp":1660006530,"iat":1659977730,"jti":"TGT-2-xxxxxxxxx-CREJDR-MBP2022"}]

org.jose4j.jwt.consumer.InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process nested JOSE object (cause: org.jose4j.lang.JoseException: Invalid JOSE Compact Serialization. Expecting either 3 or 5 parts for JWS or JWE respectively but was 14.): {"clientIpAddress":"127.0.0.1","sub":"[email protected]","authenticationDate":1659977730,"successfulAuthenticationHandlers":"careerAuthenticationHandler","iss":"https:\/\/jason.crengland.com\/cas","userAgent":"PostmanRuntime\/7.29.2","credentialType":"UsernamePasswordCredential","aud":"https:\/\/jason.crengland.com\/cas","authenticationMethod":"careerAuthenticationHandler","geoLocation":"unknown","serverIpAddress":"127.0.0.1","exp":1660006530,"iat":1659977730,"jti":"TGT-2-xxxxxxxx-CREJDR-MBP2022"}]
        at org.jose4j.jwt.consumer.JwtConsumer.process(JwtConsumer.java:406) ~[jose4j-0.7.12.jar:na]
        at com.cre.web.security.service.JWTValidationServiceImpl.validate(JWTValidationServiceImpl.java:93) ~[classes/:na]
        at com.cre.web.security.service.JWTValidationServiceImpl$$FastClassBySpringCGLIB$$c0ab6de1.invoke(<generated>) [classes/:na]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) [spring-core-5.3.2.jar:5.3.2]

    …...

        at java.lang.Thread.run(Thread.java:750) [na:1.8.0_332]
Caused by: org.jose4j.lang.JoseException: Invalid JOSE Compact Serialization. Expecting either 3 or 5 parts for JWS or JWE respectively but was 14.
        at org.jose4j.jwx.JsonWebStructure.fromCompactSerialization(JsonWebStructure.java:90) ~[jose4j-0.7.12.jar:na]
        at org.jose4j.jwt.consumer.JwtConsumer.process(JwtConsumer.java:320) ~[jose4j-0.7.12.jar:na]
        ... 70 common frames omitted

Upvotes: 0

Views: 2849

Answers (1)

Brian Campbell
Brian Campbell

Reputation: 2461

Best I can tell, you are correct that the inner JWT has a content type header with "JWT". Which is incorrect and likely a problem with CAS's JWT implementation. The JwtConsumer sees that cty and tries to process the payload as a JWT (JWS or JWE) and that fails b/c the payload is JSON.

Side note that won't help with the cty issue but JwtConsumer can deal with (properly) nested JWTs so you shouldn't have to do any of the JsonWebSignature stuff. Rather build the JwtConsumer with the HMAC key and let it do that work. https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples#markdown-header-producing-and-consuming-a-nested-signed-and-encrypted-jwt shows an example of similar usage.

Upvotes: 1

Related Questions