Why is verification failing for a JWS signature?

5k Views Asked by At

I'm trying to sign the message with a detached payload using the Nimbus JOSE JWT library in Java. The verification goes through locally but whenever I try to send it to the server using Postman I get: "The signature header x-jws-signature was parsed and has a valid JOSE header that complies with the specification. However, the signature itself could not be verified"

 JWSSigner signer = new RSASSASigner(privateKey);

        HashMap<String, Object> criticalParameters = new HashMap<>();
        criticalParameters.put("http://openbanking.org.uk/iat", 1501497671);
        criticalParameters.put("http://openbanking.org.uk/iss", orgId);
        criticalParameters.put("http://openbanking.org.uk/tan", "openbankingtest.org.uk");

        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.PS256)
                .type(JOSEObjectType.JOSE)
                .keyID(keyID)
                .criticalParams(criticalParameters.keySet())
                .customParams(criticalParameters)
                .build();

        // With encoding the payload
        JWSObject jwsObject = new JWSObject(header, payload);
        jwsObject.sign(signer);

        String jws = jwsObject.serialize(true);


        JWSObject parsedJWSObject = JWSObject.parse(jws, payload);

        if (parsedJWSObject.verify(new RSASSAVerifier(publicKey, criticalParameters.keySet()))) {
            System.out.println(parsedJWSObject.serialize(true));
        } else {
            System.out.println("Invalid");
        }
        //=============================

        // Without encoding the payload
        Base64URL signature = signer.sign(header, (header.toBase64URL().toString() + "." + payload).getBytes());
        JWSVerifier verifier = new RSASSAVerifier(publicKey, criticalParameters.keySet());

        boolean isValid = verifier.verify(header, (header.toBase64URL().toString() + "." + payload).getBytes(), signature);
        System.out.println(header.toBase64URL().toString() + ".." + signature.toString());
        System.out.println(isValid);
        //=============================

Both of the functions successfully sign and verify the JWS but for some reason, it doesn't work. If it helps, I'm trying to access the Open Banking API.

2

There are 2 best solutions below

0
On
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.PS256)
            .type(JOSEObjectType.JOSE)
            .keyID(keyID)
            .criticalParams(criticalParameters.keySet())
            .customParams(criticalParameters)
            .build();
     //simplyfy your payload json string before..remove all spaces.
      Gson gson = new GsonBuilder().disableHtmlEscaping().create();
     JsonElement el = JsonParser.parseString(payload);
    String simplePayload=gson.toJson(el);
    // With encoding the payload
    Payload detachedPayload =new Payload(new Base64URL(simplePayload).toString());
    JWSObject jwsObject = new JWSObject(header, detachedPayload );
    jwsObject.sign(signer);

    String jws = jwsObject.serialize(true);

  JWSObject parsedJWSObject = JWSObject.parse(jws, detachedPayload );
0
On

Got a similar problem very recently. I would suggest you to check the following:

  • Is the payload in the request exactly the same as the one used for the JW signature (without escaping or formatting characters)?
  • What's the order of the JSON properties in the payload and does the financial entity you are trying to interact with have specific requirements when it comes to the order of those JSON fields?

I know it's very questionable to expect the json properties in the payload to be in a specific order, but by experience I found out that some open banking implementations are assuming a specific order (not even alphabetical) and they will fail with that error when the order is not the one they expect.