Reputation: 23
I am using DocuDign REST API v2.1 using Java library. Until now I use DocuSign Connect without problems and I have a servlet to manage the callback from DocuSign. Now I want to use HMAC Security and to do this I have followed the instruction reported in https://developers.docusign.com/esign-rest-api/guides/connect-hmac that is:
on our account on DocuSign I have set for Connect the Include HMAC Signature and created a Connect Authentication Key;
when I submit the signature request I set EventNotification#setIncludeHMAC("true");
when DocuSign calls our servlet I extract from the HttpServletRequest request the fields
// Extract the text of the UTF-8 payload as an array of bytes. The entire body of the POST request is used, including line endings
byte[] body = IOUtils.toByteArray(request.getInputStream());
or
byte[] body = IOUtils.toByteArray(request.getReader(), "UTF-8");
// x-docusign-signature headers
String docuSignHmacSignature = request.getHeader("X-DocuSign-Signature-1");
// Connect Authentication Key create on DocuSign console
String hmacKey = "...";
Compute a SHA256 HMAC digest for the array of bytes of the body:
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
sha256_HMAC.init(new SecretKeySpec(hmacKey.getBytes("UTF-8"), "HmacSHA256"));
byte[] result = sha256_HMAC.doFinal(body);
String bodyHash = Base64.getEncoder().encodeToString(result);
compare bodyHash, the hashed string of the body, produced using hmacKey, with the value of x-docusign-signature headers, but it always fails.
If I use an Online HMAC Generator (such as https://www.liavaag.org/English/SHA-Generator/HMAC/) and insert the values of body, as text, and hmacKey I obtains the value of bodyHash, so I think that the hashing procedure of the HttpServletRequest body is correct.
Is there anyone who has any idea why my hash code is different from the one received from DocuSign? Is the way I recover the response body wrong?
Thanks
Upvotes: 1
Views: 1765
Reputation: 23
The problem is not in the code used to calculate the hash code of the body received from DocuSign; the problem is the difficulty in copying the HMAC Secret Key generated on DocuSign.
Upvotes: 1
Reputation: 8637
The DocuSign documentation omits some details. Two things I have noticed so far:
As per Larry K's answer, the base64 key should not be base64 decoded, instead the base64 characters (which are fundamentally 7 bit ASCII characters/codepoints), should be UTF-8 encoded (or ASCII encoded, as UTF-8 is a superset of ASCII for characters 0-127). Also ensure your UTF-8 encoder doesn't emit the byte-order-mark (BOM) characters. This is certainly an odd choice, and in my view this was a mistake.
The DocuSign code samples present the HTTP body as a 'string' which implies it was text decoded from whatever encoding was specified in the HTTP Content-Type header. The string is then re-encoded using UTF-8 (again, without a byte-order-mark). It is unclear to me whether this example code is correct or not. I.e. do they actually intend to take the raw HTTP content bytes, and calculate the hash on those; or do they really mean to text decode, re-encode to UTF-8, and then calc the hash? And what possible reason is there for those extra steps/complications?
I would guess this is just sloppy work by DocuSign, and whatever scheme they ended up with (possibly by accident) will simply become the de facto way of doing docusign HMAC authentication.
As a side note, by not base64 decoding the key, and UTF-8 encoding the base64 key characters instead, a theoretical crypto weakness is introduced, but probably not a real weakness. This is because the key bytes are now in a very limited set (the 64 'base64' ASCII characters, and the padding character '='), rather than a random distribution of all 256 possible byte values. HMAC 'fixes' this by calculating a hash on the key bytes before using them, so once again giving a good key, but really, ideally, we would simply start with a strong key, and the key hashing would make the crypto stronger (or at least no weaker).
Upvotes: 0
Reputation: 49104
There are three potential issues:
You're using the wrong secret or wrong signature. Check that you're using the entire secret produced by DocuSign. Don't Base64 decode it. Include the ending =
sign. Set DocuSign to only have one secret.
You're miscalculating the signature. There are binary HMAC signatures (expressed as a hex string) and Base64 HMAC signatures. Check that you're computing a Base64 signature.
This HMAC page calculates the HMAC signature the same as DocuSign.
Use InputType: Text
, SHA variant: SHA-256
, Output type: Base-64
You're not using the right payload data for the signature calculation. The proper payload is the entire body. That's the same as the entire XML content, with no new lines, line feeds, etc.
You can manually check the body and signature calculation:
x-docusign-signature-1
header.Upvotes: 1