0

I have a jhipster spring boot application which accepts a token generated by a third party which has been encrypted with our public key. I have a JWTFilter which decrypts the token using our private key and creates an authentication object which is stored in the security context. Once execution reaches the controller, I intend to pull the username and password from the security context so I can make API calls back to the third party application.

This is working to some degree in our integration environment where the third-party has a link to a running instance of our application. To test locally, I am hitting the link in the integration environment, and copying the token. Then I make a request from Postman to an instance of our application I'm running locally with the token added in the headers, the same as our js client would.

I am using "com.nimbusds:nimbus-jose-jwt:4.23" for decryption, and I am getting a 'MAC check failed' error. I can change the value of macCheckPassed to true in the debugger, and the decryption will complete, allowing me to see the claims and load them into the security context. However, some other filter is catching my hack, and the request gets rejected with an authorization error.

public static byte[] decryptAuthenticated(final SecretKey secretKey,
                                      final byte[] iv,
                                      final byte[] cipherText,
                                      final byte[] aad,
                                      final byte[] authTag,
                                      final Provider ceProvider,
                      final Provider macProvider)
    throws JOSEException {


    // Extract MAC + AES/CBC keys from input secret key
    CompositeKey compositeKey = new CompositeKey(secretKey);

    // AAD length to 8 byte array
    byte[] al = AAD.computeLength(aad);

    // Check MAC
    int hmacInputLength = aad.length + iv.length + cipherText.length + al.length;
    byte[] hmacInput = ByteBuffer.allocate(hmacInputLength).
        put(aad).
        put(iv).
        put(cipherText).
        put(al).
        array();
    byte[] hmac = HMAC.compute(compositeKey.getMACKey(), hmacInput, macProvider);

    byte[] expectedAuthTag = Arrays.copyOf(hmac, compositeKey.getTruncatedMACByteLength());

    boolean macCheckPassed = true;

    if (! ConstantTimeUtils.areEqual(expectedAuthTag, authTag)) {
        // Thwart timing attacks by delaying exception until after decryption
        macCheckPassed = false;
    }

    byte[] plainText = decrypt(compositeKey.getAESKey(), iv, cipherText, ceProvider);

    if (! macCheckPassed) {

        throw new JOSEException("MAC check failed");
    }

    return plainText;
}

What is this MAC check? I thought it had to do with the origin of the token. Something along the lines of the token being encrypted with the MAC id of the source system, which throws an error when it doesn't synch up with my current host.

What other filter would be rejecting the request if the decryption passed? Is there some other flag I'm supposed to be setting so the framework will honor the request?

thejames42
  • 447
  • 6
  • 22

1 Answers1

1

JWE spec mandates authenticated encryption, to ensure the plain text is not just encrypted, but also protected against tampering. To ensure that an HMAC is applied after the content encryption.

The "Mac check failed" error can mean two things - the library that produced the original JWE / JWT has applied the HMAC incorrectly, or, the JWE / JWT was modified in transit.

Community
  • 1
  • 1
Vladimir Dzhuvinov
  • 772
  • 1
  • 6
  • 13