IDTokenValidator rejects alg=HS256 kid=xxx JWT with "Signed JWT rejected: No matching key(s) found"

Issue #200 resolved
Dzmitry Hubin created an issue

Dear support,

Apologies for the second issue I'm opening ;) But I'm facing with the following issue:

I'm doing JWS HMAC ID token validation using clientSecret. Here is a code block I'm executing:

private IDTokenClaimsSet validateIdToken(String idToken, String nonce){
        IDTokenValidator idTokenValidator = new IDTokenValidator(new Issuer(getRedirectUrl()), new ClientID(getClientId()), JWSAlgorithm.HS256, new Secret
                (getClientSecret()));
        try {
            JWT idTokenJWT = JWTParser.parse(idToken);
            IDTokenClaimsSet idTokenClaimsSet = idTokenValidator.validate(idTokenJWT, new Nonce(nonce));

            return idTokenClaimsSet;

        } catch (ParseException | JOSEException e) {
            throw  new ServiceApigeeException("Something wrong with JWT token parsing.");
        } catch (BadJOSEException e) {
            throw new IdTokenInvalidException("Id token is invalid!");
        }
    }

And when I invoke "validate" I get the following exception in "com.nimbusds.jwt.proc.DefaultJWTProcessor" -> "Signed JWT rejected: No matching key(s) found" Line #326

I followed this article to perform ID token validation -> http://connect2id.com/blog/how-to-validate-an-openid-connect-id-token

and I'm using that constructor:

ID tokens with HMAC using the client_secret as a key use the alternative constructor: new IDTokenValidator(iss, clientID, jwsAlg, clientSecret);

Any clues what's wrong ? Maybe I have to use some another constructor ? Or ?

Thanks, Dzmitry.

Comments (22)

  1. Dzmitry Hubin Account Deactivated reporter

    Actually from my investigations the issue is that jwkSet doesn't contain my secret key.. but at the same time inner LinkedList contains it. Please check out screen I've attached:

    Screenshot 2016-12-12 18.40.05.png

    That's why I have later the exception I mentioned in description

  2. Vladimir Dzhuvinov

    Hi there,

    The constructor should indeed be

    IDTokenValidator(Issuer expectedIssuer, ClientID clientID, com.nimbusds.jose.JWSAlgorithm expectedJWSAlg, Secret clientSecret)
    

    http://static.javadoc.io/com.nimbusds/oauth2-oidc-sdk/5.18.1/com/nimbusds/openid/connect/sdk/validators/IDTokenValidator.html#IDTokenValidator-com.nimbusds.oauth2.sdk.id.Issuer-com.nimbusds.oauth2.sdk.id.ClientID-com.nimbusds.jose.JWSAlgorithm-com.nimbusds.oauth2.sdk.auth.Secret-

    I'll try to find what exactly the error message could mean.

  3. Vladimir Dzhuvinov

    PS: Could paste the header of the ID token? That is, the first BASE64URL segment up to the first dot (.). I want to make sure that the ID token you're getting is indeed secured with "HS256" and not with some other alg.

  4. Connect2id OSS

    Hi Dzmitry,

    The error message that you're getting is indeed symptomatic of trying to do HS256 validation where an RSxxx alg (e.g. RS256) is expected (that requires an RSA public key of the IdP).

    Just added a test for this to illustrate: see commit ddbd86f

  5. Dzmitry Hubin Account Deactivated reporter

    Hi @vdzhuvinov, @c2id-support

    I have the following JWT header:

    {
      "typ": "JWT",
      "alg": "HS256",
      "kid": "KzQq5It4qAlgBMx3GS4m4MrHZnOTKXXh"
    }
    
  6. Connect2id OSS

    Here is a test that shows ID token validation with HS256:

    https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/src/f5631efa56105fe1bd4d68f9425739bda76032ef/src/test/java/com/nimbusds/openid/connect/sdk/validators/IDTokenValidatorTest.java?at=master&fileviewer=file-view-default#IDTokenValidatorTest.java-335

    If the client secret is incorrect then you should get the following exception: "Signed JWT rejected: Invalid signature"

    if you're getting "Signed JWT rejected: No matching key(s) found" this either means that the ID token is RS256 signed, or a constructor for a public RSA key (instead of client secret) was used.

    Let us know how the above test works.

    Cheers,

  7. Dzmitry Hubin Account Deactivated reporter

    Hi @c2id-support ,

    I've written the following test:

    public void testApp() {
            String idToken = "***MY_ID_TOKEN***";
            SignedJWT idTokenJWT;
            try {
                idTokenJWT = SignedJWT.parse(idToken);
                String secret = "***MY_16_BYTES_SECRET***";
    //            Secret clientSecret = new Secret(ByteUtils.byteLength(256));
    
                JWSVerifier verifier = new MACVerifier(secret.getBytes());
    
                assertTrue("ID token is invalid", idTokenJWT.verify(verifier));
    
            } catch(ParseException e) {
                e.printStackTrace();
                fail("ParseException exception has occurred" + e.getMessage());
            } catch(JOSEException e) {
                e.printStackTrace();
                fail("JOSEException has occurred: " + e.getMessage());
            }
        }
    

    So as a result that test fails with message:

    com.nimbusds.jose.KeyLengthException: The secret length must be at least 256 bits at com.nimbusds.jose.crypto.MACProvider.<init>(MACProvider.java:118) at com.nimbusds.jose.crypto.MACVerifier.<init>(MACVerifier.java:145) at com.nimbusds.jose.crypto.MACVerifier.<init>(MACVerifier.java:77) at com.nimbusds.jose.crypto.MACVerifier.<init>(MACVerifier.java:93) at by.test.AppTest.testApp(AppTest.java:45) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at junit.framework.TestCase.runTest(TestCase.java:154) at junit.framework.TestCase.runBare(TestCase.java:127) at junit.framework.TestResult$1.protect(TestResult.java:106) at junit.framework.TestResult.runProtected(TestResult.java:124) at junit.framework.TestResult.run(TestResult.java:109) at junit.framework.TestCase.run(TestCase.java:118)

    Can it be the root cause for my issue ? I have only 16 bytes secret long.

  8. Vladimir Dzhuvinov

    I don't know what library is used to HMAC the ID tokens, but please let them know that it's not up to the JOSE requirement on the HMAC key length, and security is weakened also with short keys.

  9. Dzmitry Hubin Account Deactivated reporter

    Thanks so much @c2id-support, @vdzhuvinov for pointing me out! Quick question. Should I implement secret validation at first before starting with ID token validation based on what we just concluded ?

  10. Dzmitry Hubin Account Deactivated reporter

    Also figured out why I have "Signed JWT rejected: No matching key(s) found" exception in DefaultJWTProcessor

    So I removed "kid": "KzQq5It4qAlgBMx3GS4m4MrHZnOTKXXh" from JWT header and it now complains on "The secret length must be at least 256 bits" as it should be.

  11. Dzmitry Hubin Account Deactivated reporter

    Hi @c2id-support ,

    At first thanks so much for the provided guidance. So At the moment I have 2 issues: unexpected Key ID in JWT header and short client secret. So regarding key ID: I'll try to ask my third-party ID token providers regarding it but at the moment I have no idea.

  12. Connect2id OSS

    There is a way to work around the key ID and ignore it. We'll try to post an example.

    As for the key length, this check must be upheld.

  13. Dzmitry Hubin Account Deactivated reporter

    @c2id-support Yes would be great if you can provide me with workaround to ignore this kid in header to proceed with validation. Short client secret issue I'll address to the ID Token signing authors.

  14. Dzmitry Hubin Account Deactivated reporter

    @c2id-support Apologies for too many questions. But Have u had a chance to look at my previous comment and give me a hint how to bypass this issue caused by "kid" param in ID token header ?

  15. Connect2id OSS

    This test shows how to process HSxxx protected ID tokens with a key ID: b2aa75b

    The client_secret is converted to a JSON Web Key (JWK), and then put in a JWK credentials set, where it's looked up by the key ID.

  16. Log in to comment