Closed ahourlier closed 1 year ago
Are you sure the signed_data is correctly formatted, using json.dumps gives no guarantee whatsoever that the keys of a dictionary are in the same order, or that spacing/indntation or what not is the same. Even a single bit difference will cause the signature verification to fail.
At the very least both signer and verifier should use json.dumps(data, sort_keys=True), but probably better make sure that also indent and separators are defined explicitly.
Can you print(repr(jws.veryfylog))? Maybe there is some clue about what is the cause of the failure.
Thinking of it, probably better if you do not use json.dumps at all, base base64 the request_body as it comes, this will eliminate many potential sources of error. Of course this needs to be done both at the signer and the verifier.
Finally it is not clear to me why you do .encode("ascii") when you compose signed_data, what's the intent here?
Thx for your quick response.
Unfortunately, nothing more from verifylog
Which leads to this, but still it's not working with the same InvalidJWSSignature error.
import jwcrypto.jws as jws
import jwcrypto.jwk as jwk
try:
jws_body = base64url_encode(str(json_body).encode("utf-8"))
signed_data = b64_signature_header + "." + jws_body
jwkey = jwk.JWK.from_json(json.dumps(<key_as_dict>))
signature = jws.JWS.from_jose_token(<base64-jws-exemple>)
signature.verify(key=jwkey, detached_payload=signed_data)
except jws.InvalidJWSSignature:
import traceback
print(traceback.format_exc())
return False
Since the signer is a third party, i can only suppose that they have follow the RFC they joined in their documentation.
You need to ask the signer how exactly they format the body, you can't just guess it. All the RFC asks is that the payload if base64 encoded, but it doesn't tell you what the payload actually is, that is up to the signer to agree with the clients.
Although it is not your case (I checked the headers, with detached payloads there isn't even a requirement to base64 them. See https://www.rfc-editor.org/rfc/rfc7797#page-4 para: The "b64" Header Parameter.
Can you reveal who is the signer?
The signer is Universign (see following documentation, section Webhook authentication)
I can read
The input to the signature is the base64URL-encoded signature header concatenated with the base64URL-encoded request body
It seems this corresponds to my signed_data format (tried it with and without the dot between)
I'm currently waiting a response from them.
I think I know where the error is.
The detached_paylod should be exclusively jws_body
and not b64_signature_header + "." + jws_body
Hi,
I'm desperately trying to validate a JWS Signature send from a webhook event on my API.
Here is the JWS I receive
# Base64-encoded-URL JWS example containing public key in first part, detached body (empty 2nd part) and signature itself (3rd part) eyJhbGciOiJQUzI1NiIsICJraWQiOiJzY2RfNzhlZDdkMmJmMmE4YzRhZjRhM2QzNjUyYTg5ZmU4YjlmNDZhZGRiOGJjOGU3Y2I2OTEwNmE2NDY3OTAwZjVlNiJ9..kCk01LU9pu_5GTN8D8ejmHRLVT38NE8ikF3uaD7cXeiPzEGeQeFGbkQxWF_PCHaNyfKz0epqds1AyaliRKl54LQ_OxJAEwB6Tr2JtSodPFWsOBW9iVaydpYnHgY7uX5dJNv5QclCwvVbyy3v3uBac8dA7stawGKgDBMBSLBrJLazXW2t9EBaxxgvhv-OhobuGc72Ca4r-tvmtKKxF9p7sgGlgisTBv3KjBm8nYh__3AIdt-XvMOKTxaORgzPDRMlSZeM9eipSHM2W1qcfHlRZ27kl4x9Es-ot2Harz5KMB4-8yV-WM2dbf146tHTL79IAOScEWgL79p7MCDbdcWCqO3lxuLe0Jk8Bb8yFz3-U7PFy0NqzcStuCpGyZH3UZZOduQxy6hHDm41csRF68d3oBLFZ68SeN8oOt3HSCZ1XwAfqs6vQINZsB2JfgiZ-xbAPqJm8SJ7FJ_MhFCBo1VyjLDPxYR7uholD89-GugOctESZv1djvQHOJ_0uHmp4U-SyFm-ISZYyLsHIBOk9RNxEfQyjSbXU5OMKqmI9A9MjvyPT9lNLIFRx5TNB4LCl4cReBlQYIjFj45RxichNIz41VOqUsPh3CQq6JdHREwWzT0ZKCaQ8y2a3fzjqxuolHXzs3KDTQ4rym6OuYoT8yFaQUynnGIkRSdDC5QlvCkzluM
import jwcrypto.jws as jws import jwcrypto.jwk as jwk try: # Build payload to verify the signature against # (as described in RFC7515 https://datatracker.ietf.org/doc/html/rfc7515#section-5.2) jws_body = base64url_encode(json.dumps(request_body)) signed_data = (b64_signature_header + "." + jws_body).encode("ascii") # Build JWK obj from public key jwkey = jwk.JWK.from_json(json.dumps(<key_as_dict>)) # jwkey is well filled with 'key_id' & 'key_type' attributes # Build JWS obj from JWS send by webhook signature = jws.JWS.from_jose_token(<base64-jws-exemple>) # signature.objects['signature'] is well filled, and also payload is empty since it's detached # Try to verify signature with public key & detached payload, still failing signature.verify(key=jwkey, detached_payload=signed_data) except jws.InvalidJWSSignature: import traceback print(traceback.format_exc()) return False
Anyway i'm still having InvalidJWSSignature error (Verification failed for all signatures), whatever i try to pass to the function. Any idea on what i could have miss or misunderstood?
Hi @ahourlier,
Based on your example code below I see that you try to get the public key used to sign the data in the received header, but it was not. Only the key id is included, so you need to get the key from the url provided in the documentation (check the last chapter "How to verify the webhook signature").
Does it solve your problem ?
Best regards
Hi,
I'm desperately trying to validate a JWS Signature send from a webhook event on my API.
Here is the JWS I receive
Anyway i'm still having InvalidJWSSignature error (Verification failed for all signatures), whatever i try to pass to the function. Any idea on what i could have miss or misunderstood?