We’ve developed some resources to help you work effectively from home during COVID-19 Click to learn more

CRMscript, encodeSHA256 and special characters (æøå)

Hello, 

Im creating a CRMscript that posts a json-string to an external application. On the receiving end i want to validate that the request comes from SuperOffice so I add a hashed value to my header (similiar to Tony's example on how you can validate a webhook-post). 

I've noticed that when using the encodeSHA256-method in a my script the hashed value is not the same as i get when using an online generator ( https://www.freeformatter.com/hmac-generator.html ) . 

I have figured out that this is because my json contains special characters (æ/ø/å), so if i replace them with ae/o/aa the hashed values turn out the same.

Example NOT EQUAL
Online
 

SuperOffice
 



Example EQUAL

Online

 
SuperOffice
 




How are special characters handled in the encodeSHA256-function?
In my scenario I can just replace the 'problematic-characters' manually, but wouldnt it be better if SuperOffice handled special characters (in this function) the same way 'others do'? If the main purpose for the function is to create a hash others can validate the request with. 

//Eivind



RE: CRMscript, encodeSHA256 and special characters (æøå)

Read What every programmer should know about character encodings

SHA256 operates on bytes, not strings. How you convert a string into bytes can vary (by a lot), so it's easy to end up with different values when using the string directly.

Try BASE64 encoding first, that should make the differences stand out more.

Typically this is caused by one end assuming Latin1 encoding, and the other assuming UTF8 encoding.

Av: Christian Mogensen 14. jan 2020

RE: CRMscript, encodeSHA256 and special characters (æøå)

Hello, 

Thank you for the reply. 

Yes, in .net SHA256 (or HMACSHA256) operates on bytes. 
In CRMscript however I need to supply both secret and value as strings. 

So, i've tried multiple different variations in an attempt to figure this out: 

String secret = "zg/kFxIIvv0Q0FDTC/IxxA2PT62X3xA8LrocP9sCUBI=";
String jSon= "{\"Value\":\"Ødelagt\"}"; //This is a dummy-string but contains 'ø' so it still fails

//Try the string directly
printLine(encodeSHA256(secret, jSon));

//convert string toByteArray and put this into a string
Byte[] byteArray1 = jSon.toByteArray();
String base64string = encodeBase64(byteArray1);
printLine(encodeSHA256(secret, base64string));

//Try encodeHMSCHA256, havent found any good documentation on the difference between this and encodeSHA256, but encodeHMSCHA256 seems more logicall for my scenario
Byte[] byteArray2 = encodeHMACSHA256(secret, jSon);
printLine(encodeBase64(byteArray2));

I found Tony's example on webhooks and tried just implementing his 'logic' directly: 

private bool IsValidWebHook(string storedSecret, string headerValue, string body)
        {
            var validationResult = false;

            // ensure it is the correct encoding
            var secret = Encoding.UTF8.GetBytes(storedSecret);
            // hash and base64 encode the stored shared secret
            using (var hasher = new System.Security.Cryptography.HMACSHA256(secret))
            {
                var sha256 = hasher.ComputeHash(Encoding.UTF8.GetBytes(body));
                var base64 = Convert.ToBase64String(sha256);

                // verify the values match (this indicates the request came from SuperOffice)!

                if (base64 == headerValue)
                {
                    validationResult = true;
                }
            }

            return validationResult;
        }

The only difference is that i need to .GetBytes from the body/json/string, but this works well enough. 

Is there any real example on how to implement this correctly?
For now i will just pass along a secret-key that i can validate, but I would like to figure out how this is done. My still seems to be special characters so i shouldnt be that far off. 


//Eivind





Av: Eivind Johan Fasting 14. jan 2020

RE: CRMscript, encodeSHA256 and special characters (æøå)

Hi Eivind,

Methods accepts strings as input, however they are expecting strings to be ascii. If you first convert string containing special chars using utf8Encode it should provide results as you expect.

String secret = "zg/kFxIIvv0Q0FDTC/IxxA2PT62X3xA8LrocP9sCUBI=";
String jSon= "{\"Value\":\"Ødelagt\"}"; 

printLine(encodeSHA256(secret, jSon.utf8Encode()));
Av: Michel Krohn-Dale 16. jan 2020

RE: CRMscript, encodeSHA256 and special characters (æøå)

Ah, that was the piece i was missing, Michel, thank you! :) 

For anyone interrested I used the encodeHMACSHA256 instead of encodeSHA256, as that method returned the 'correct' hash if i compared the result to what i found in my local project and in some online editors ( you can test what output to expect in .net here https://dotnetfiddle.net/NldgxI  ).

This is what i ended up with in SuperOffice (notice that i create a Struct with the values then use the JSONBuilder to create my json-string). 

JSONBuilder jb;
customStruct.toJson(jb);
String jSon = jb.getString();
jSon = jSon.substitute(": ", ":"); 

Byte[] byteArray2 = encodeHMACSHA256(secret, jSon.utf8Encode());
printLine(encodeBase64(byteArray2));

HTTP h; 
h.addHeader('Content-Type', 'application/json');
h.addHeader('X-SuperOffice-Signature', encodeBase64(byteArray));
h.setOption('parameters', "{" + jSon + "}");  //Ignore the {} i have appended on each side, this has to do with what my proejct expects to receive
h.setOption('parametersUTF8', 'true'); //Seems to be mandatory to be able to do a successfull post?
Byte[] b = h.post('https://MyApp/someendpoint');
if(h.hasError()){
  h.getErrorMessage();
}
else
 print(String(b)); 

NB: I had to substitute/remove a space between key and value to make it the same as how i handle the incoming json in .net, this is probably not neccessary in other scenarios. 


This is the local project handling the POST. 

public async Task<HttpResponseMessage> Post([FromBody]JArray value)
        {
            IEnumerable<string> headerValues = Request.Headers.GetValues("X-SuperOffice-Signature");
            var headerValue = headerValues.FirstOrDefault();
            var storedSecret = sameSecretAsSuperOffice;
            var body = value.ToString(Formatting.None);
            var fromSuo = IsValidWebHook(storedSecret, headerValue, body);
            if (fromSuo)
            {
               //Magic
            }
        } 

private bool IsValidWebHook(string storedSecret, string headerValue, string body)
        {
            var validationResult = false;

            // ensure it is the correct encoding
            var secret = Encoding.UTF8.GetBytes(storedSecret);
            // hash and base64 encode the stored shared secret
            using (var hasher = new System.Security.Cryptography.HMACSHA256(secret))
            {
                var sha256 = hasher.ComputeHash(Encoding.UTF8.GetBytes(body));
                var base64 = Convert.ToBase64String(sha256);
                // verify the values match (this indicates the request came from SuperOffice)!
                //Log("X-SuperOfficeSignature: " + headerValue + ", Hash-check: " + base64);
                if (base64 == headerValue)
                {
                    validationResult = true;
                }
            }

            return validationResult;
        }


Hopefully this will be usefull for someone sometime ;) 

/Eivind

Av: Eivind Johan Fasting 16. jan 2020