coldfusioncoldfusion-9sha1hmaccoldfusion-11

Converting C# to ColdFusion


I have no experience with C# and was wondering how I can convert this C# snippet into ColdFusion.

string inputString = "abccde";
string securityKey = "abcdefghijk...";

// Convert security key into ASCII bytes using utf8 encoding
byte[] securityKeyBytes = UTF8Encoding.ASCII.GetBytes(securityKey);

// Create an HMACSHA1 hashing object that has been seeded with the security key bytes
HMACSHA1 hasher = new HMACSHA1(securityKeyBytes);

// Convert input string into ASCII bytes using utf8 encoding
byte[] inputBytes = UTF8Encoding.ASCII.GetBytes(inputString.ToCharArray());

// Compute the has value
byte[] hash = hasher.ComputeHash(inputBytes);

// Convert back to a base 64 string
string securityToken = Convert.ToBase64String(hash);

return securityToken;

I found this on Stack Overflow and here what I have so far. Am I going in the right direction?

<cffunction name="CFHMAC" output="false" returntype="string">
  <cfargument name="signMsg" type="string" required="true" />
  <cfargument name="signKey" type="string" required="true" />
  <cfset var key = createObject("java", "javax.crypto.spec.SecretKeySpec").init(signKey.getBytes(), "HmacSHA1") />
  <cfset var mac = createObject("java", "javax.crypto.Mac").getInstance("HmacSHA1") />
  <cfset mac.init(key) />
  <cfreturn toBase64(mac.doFinal(signMsg.getBytes())) />
</cffunction>

<cfset signMsg= "abccde">
<cfset signatureString = "abcdefghijk...">
<cfset result = CFHMAC(signMsg=signMsg, signKey=signatureString) />
<cfdump var="#result#" />

Solution

  • (Expanded from the comments)

    CF11 has an HMAC function already built in, so it is possible you may not need that UDF. I would suggest giving it a try. Take note that the C# code uses UTF8Encoding.ASCII:

    ASCII characters are limited to the lowest 128 Unicode characters, from U+0000 to U+007F....

    Note that the ASCII encoding has an 8th bit ambiguity that can allow malicious use, but the UTF-8 encoding removes ambiguity about the 8th bit. ... It uses replacement fallback to replace each string that it cannot encode and each byte that it cannot decode with a question mark ("?") character.

    Assuming you need to maintain compatibility with some other application, and must keep the same encoding, UTF8Encoding.ASCII should correspond to "US-ASCII" encoding in CF/Java, but do some testing to verify the handling of invalid characters. If you have the flexibility to change the encoding, I would recommend using UTF-8 instead. Keep in mind, the CF function always returns hexadecimal. If you need a base64 string instead, you will have to convert it with binaryEncode() and binaryDecode().

    Example:

    <!--- Only required when hard coding UTF8 characters into a script --->
    <cfprocessingdirective pageEncoding="utf-8">
    <cfset message = "Pi π and Sigma Σ.">
    <cfset key = "abcdefghijk">
    <cfset hexHash = hmac(message, key, "HMACSHA1", "US-ASCII")>
    <cfset base64Hash = binaryEncode(binaryDecode(hexHash, "hex"), "base64")>
    <cfdump var="#base64Hash#">
    

    Result:

    HMACSHA1 = J2AZf+zhrebIA/tK3i5PYb4b/Fo= 
    

    Side note, about the CFHMAC UDF: be very careful with encoding when extracting the bytes of a string. As I noted in the comments, it is best avoid using getBytes() because it assumes the default encoding, whatever that may be. That means it may not always return the desired results, and can vary from one jvm to another. Instead, it is better to always supply a charset explicitly ie getBytes("UTF-8") , getBytes("US-ASCII"), etcetera.