HMAC authentication problem
Hi! I'm banging my head to the wall with HMAC authentication. I have tried to implement this various ways but nothing seems to work.
If someone could help on this it would be great!
Here is a code that I have tried and working Javascript example, tested on Postman.
Set Appkey = "itsasecretkey"
Set requestTimeStamp = $ZDATETIME($HOROLOG,-2)
Set nonce = ..getRandomString()
Set signatureRawdata = Appid_requestTimeStamp_nonce
Set keyUTF8 = $zconvert(Appkey,"O","UTF8")
Set signatureRawdataUTF8 = $zconvert(signatureRawdata,"O","UTF8")
Set tSigningKey = $SYSTEM.Encryption.HMACSHA(256, signatureRawdataUTF8, keyUTF8)
Set tSignature = ##class(%xsd.hexBinary).LogicalToXSD(tSigningKey)
Set to_hmac = "hmac "_Appid_":"_tSignature_":"_nonce_":"_requestTimeStamp
Javascript:
var APPId = "itsasecretid";
var APIKey = "itsasecretkey";
var time = (new Date()).getTime();
var time_s = Math.floor(time / 1000);
pm.environment.set("timestamp", time_s);
var nonce = getRandomString();
pm.environment.set("nonce", nonce);
var rawData = APPId + time_s + nonce;
var signature = CryptoJS.enc.Utf8.parse(rawData);
var secretByteArray = CryptoJS.enc.Base64.parse(APIKey);
var signatureBytes = CryptoJS.HmacSHA256(signature, secretByteArray)
var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes);
var to_hmac = "hmac " + APPId + ":" + requestSignatureBase64String + ":" + nonce + ":" + time_s;
Can you be more specific about how your implementation isn't working? For example, do you have a known signature for known inputs that you are comparing against? Or is there some external system you are trying to authenticate to?
When I ran your IRIS code (hardcoding values for
requestTimeStamp
andnonce
), thetSignature
value matched the output of this tool: https://www.freeformatter.com/hmac-generator.html#ad-output("to_hmac" is not a valid variable name in ObjectScript, but I assume that's a copy-paste error.)
I wasn't familiar with the CryptoJS library before looking into this, but in the examples I was able to find, it looks like the 2nd arg to
HmacSHA256()
is typically passed in as a string, rather than a byte array: https://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-h...One thing I noticed when I tried running your JS code is that if I replace:
var signatureBytes = CryptoJS.HmacSHA256(signature, secretByteArray)
With:
var signatureBytes = CryptoJS.HmacSHA256(signature, APIKey)
Then then value of
requestSignatureBase64String
in your JS example matches the value oftSignatureBase64
in your IRIS example.So my guess is that you're getting the correct raw signature value in IRIS, however in your IRIS example you are using the hex representation of it while in your JS example you are using the base64 representation. Also, if you are using your JS example for reference, you may want to look into the expected format of the 2nd arg to
HmacSHA256()
.Hi Jorge and thanks for the answer!
Yes there is external system where I try to authenticate and with Postman that javasript works just fine.
Set tSecretByteArray = ..hex(##class(%SYSTEM.Encryption).Base64Decode(keyUTF8))
So do I have to get 32-bit wordArray like in JS var
signature
= CryptoJS.enc.Utf8.parse(rawData);If do some hardcoding and set "
signature
" value from Postman, I won't get right values fromSet tSignature = ..hex($SYSTEM.Encryption.HMACSHA(256, wordArray, tSecretByteArray))
Set tSignatureBase64 = ##class(%SYSTEM.Encryption).Base64Encode(tSignature)
And yes, to_hmac was copy-paste error ;)
As you can see I get same requestSignatureBase64String if I use string values in js and objectscript examples. Unfortunately web service is expecting value QxYkJH+b0xo+pCLMBMXgKhMqjMSV+mqQbNvPf7kX2k4=
I get same signature and secretByteArray values in js and objectscript, but difference is that objectscript value is String and js value is 32-bit WordArray.
How do I get correct signature and secretByteArray values that I can pass to the $SYSTEM.Encryption.HMACSHA method?
Cryptojs example
var message = "messageinabottle"
var secretByteArray = CryptoJS.enc.Base64.parse(key) //b1e72b7ad91e
var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes) //QxYkJH+b0xo+pCLMBMXgKhMqjMSV+mqQbNvPf7kX2k4=
var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes) //OrUVho2ak9cwJXiVEPn8QInT+0hyL7kJ9zMcx+E/Zxk=
ObjectScript example
Set message = "messageinabottle"
Set secretByteArray = $ZCONVERT(##class(%xsd.hexBinary).LogicalToXSD(##class(%SYSTEM.Encryption).Base64Decode(key)),"L") //b1e72b7ad91e
Set requestSignatureBase64String = ##class(%SYSTEM.Encryption).Base64Encode(signatureBytes) //kQeBc7Gu4SRUeicA7xVeN6V9sWX5b1bCLX9rUBK+lGA=
Set requestSignatureBase64String = ##class(%SYSTEM.Encryption).Base64Encode(signatureBytes) //OrUVho2ak9cwJXiVEPn8QInT+0hyL7kJ9zMcx+E/Zxk=
Solution was easier than I thought.
Set key = "secretkey"
Set message = "messageinabottle"
Set secretByteArray = ##class(%SYSTEM.Encryption).Base64Decode(key)
Set signatureBytes = $SYSTEM.Encryption.HMACSHA(256, message, secretByteArray)
Set requestSignatureBase64String = ##class(%SYSTEM.Encryption).Base64Encode(signatureBytes)