Reputation: 254
I am attempting to use ColdFusion's hmac() function to calculate an HMAC value using binaryEncode(binaryObj,'Base64')
instead of toBase64() since that function is deprecated. It works perfectly with toBase64()
but not with binaryEncode()
. The docs are not very informative. Can someone help me understand why I cannot get the same value using binaryEncode
?
From what I understand, the hmac()
function returns the results in hexadecimal format. binaryEncode()
expects a binary value, so thehmac()
results must be first converted from hex to binary, before it can be converted to base64.
<cfset string = "1234567890" />
<cfset secretKey = "abcdefghijklmnopqrstuvwxyz" />
<!--- Get Hex results from HMAC() --->
<cfset hmacHex = hmac(string,secretKey,'HMACSHA256') />
<!--- Decode the binary value from hex --->
<cfset hmacAsBinary = binaryDecode(hmacHex,'hex') />
<!--- Convert binary object to Base64 --->
<cfset hmacBase64 = binaryEncode(hmacAsBinary, 'base64') />
<cfoutput>
<!--- incorrect hmac signature --->
hmacBase64: #hmacBase64#<br>
<!--- correct hmac signature --->
toBase64: #toBase64(hmac(string,secretKey,'HMACSHA256'))#<br>
</cfoutput>
The results are:
hmacBase64: VEVGNnqg9b0eURaDCsA4yIOz5c+QtoJqIPInEZOuRm4=
toBase64: NTQ0NTQ2MzY3QUEwRjVCRDFFNTExNjgzMEFDMDM4Qzg4M0IzRTVDRjkwQjY4MjZBMjBGMjI3MTE5M0FFNDY2RQ==
One thing I noticed is the results are much longer when using toBase64()
. I can't seem to figure out why I can't use binaryEncode()
. However, I would like to, since toBase64()
is being deprecated. Any insight is much appreciated. Thanks!
Upvotes: 2
Views: 975
Reputation: 28873
Update based on comments:
Well using ToBase64(Hmac(...))
is not the correct way to convert a hex string to base64 ;-) However, it sounds like the API requires something other than a straight conversion. If so, just do what the ToBase64(hmac(...))
code is doing. ie Decode the hex string as UTF8 and re-encode it as base64:
matchingResult = binaryEncode(charsetDecode(hmacHex, "utf-8"), "base64")
Short answer:
The two methods are encoding totally different values. That is why the results do not match. The correct way to convert the hex string to base64 is using BinaryEncode/Decode().
Longer answer:
<!--- correct hmac signature --->
toBase64: #toBase64(hmac(string,secretKey,'HMACSHA256'))#<br>
Actually that is not the correct way to convert hex to base64.
Hexadecimal and Base64 are just different ways of representing a binary value. In order to get the same results, the two methods need to start with the same binary. In this case, are actually encoding totally different values. Hence the difference in the results.
With a hexadecimal string, each byte is represented by two characters. So the binary will be half the size of the original string. In the case of HMAC(HMACSHA256), the resulting hex string is 64 characters long. So the binary value should be 32 bytes. To obtain the correct binary value, the string must be decoded as hex:
original string length = #len(hmacHex)#
binary size = #arrayLen(binaryDecode(hmacHex, "hex"))#
The problem with ToBase64 is that it decodes the string incorrectly. It treats the input as UTF8 and decodes the characters in the string individually. So the binary value is double the size it should be. Notice it is 64 bytes, instead of 32? That is why the final string is longer as well.
UTF8 binary size = #arrayLen(charsetDecode(hmacHex, "utf-8"))#
ToBase64 binary size = #arrayLen(binaryDecode(toBase64(hmacHex), "base64"))#
So again, the two methods produce different results because they are encoding totally different values. However, strictly speaking, only the first method is correct. To re-encode a hex string as base64 use binaryEncode/binaryDecode:
correctResult = binaryEncode(binaryDecode(hmacHex, "hex"), "base64")
Upvotes: 6