Signing a message in the pre-request script

The following C# code creates the hash that I need to generate in a POSTMAN Pre-Request Script so that it can be added to the sent message.

using (HMACSHA1 hmac = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(KeyString)))
{
hash = Convert.ToBase64String( hmac.ComputeHash(ASCIIEncoding.ASCII.GetBytes(StringToHash)));
}

The KeyString and StringToHash are being created correctly, but the signature generated from the code below is not matching.

var signature = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA1(CryptoJS.enc.Hex.parse(StringToHash),CryptoJS.enc.Hex.parse(KeyString)));

Questions:

  1. I think the CryptoJS.enc.Base64.stringify is correct and the CryptoJS.HmacSHA1 is likely alright, but how do I create the equivalent ASCII encoded byte arrays?
  2. Can I link a simple C# .dll into POSTMAN to do the hashing in C#?
  3. I could not find any documentation for the POSTMAN OAUTH helper, would it “help” in this case?

Hi @briany,

It looks like you’re using ASCII-encoding for the key and message in the C# example, while you’re hex-encoding the key and message in the CryptoJS example.

You can change your CryptoJS code to look something like this, and it should start working:

const signature = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA1(StringToHash, KeyString));

Hope that helps.

Best,

Kevin

I will give it a shot, but I am not hopeful. The C# call to ASCIIEncoding.ASCII.GetBytes, converts the string to a Byte Array, I don’t think a simple string will do the trick, but I can easily try.

Nope,. no luck still not matching.

@briany,

Weird. The fix I proposed works for me when I test with both C# and CryptoJS.

Don’t worry about bytes vs. string. CryptoJS, by default, will encode as UTF-8 bytes. If your key and message are valid ASCII, the ASCII vs UTF-8 difference shouldn’t have an impact, either. But perhaps if they’re not valid ASCII, the conversion on the C# side could be off? You could try using UTF8Encoding.UTF8.GetBytes on the C# side.

Best,

Kevin

I can’t change the C# side as that is the server…the client must comply with what it says the truth is (and I cannot change its code).

Hey @briany,

In that case, you’ll have to modify the JavaScript to replace the non-ASCII characters with a question mark (?), which is what the .NET library will do. To verify, here’s my Program.cs.

using System;
using System.Security.Cryptography;
using System.Text;

namespace shatest
{
    class Program
    {
        static void Main()
        {
            var key = "keystring";
            var message = "stringtohashÝŁ";
            using (HMACSHA1 hmac = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(key)))
            {
                var hash = Convert.ToBase64String( hmac.ComputeHash(ASCIIEncoding.ASCII.GetBytes(message)));
                Console.WriteLine(hash);
            }
        }
    }
}

This outputs wrJjgiVU7/Qv7RCXOJCZmVzFf9M=.

Here’s my Postman test:

function convertToAscii(m) {
    return Array.from(m).map((c) => {
        return c.charCodeAt(0) > 255 ? '?' : c;
    }).join('');
}

pm.test('has equivalent signature', () => {
    const key = convertToAscii("keystring");
    const message = convertToAscii("stringtohashÝŁ");

    var signature = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA1(message, key));
    
    console.log(signature);
    
    pm.expect(signature).to.eql('wrJjgiVU7/Qv7RCXOJCZmVzFf9M=')
});

Notice the convertToAscii function. You’ll need this for compatibility. But really, there’s going to be a lingering problem here that should get addressed down the road. The C# side should be using a character encoding that supports the input.

Best,

Kevin

The return from convertToAscii(m) should be a Byte Array not a string. By default Bytes Arrays in C# are unsigned, so the code should read something like

function convertToAscii(m) {
return Uint8Array.from(m).map(© => {
return c.charCodeAt(0) > 255 ? 63 : c.charCodeAt(0));
}).join(’’);
}

But I really have not had time to look at it today, other than to confirm that it did not match with the code as originally provided…Caveat, I did not look all that hard at it…no time today.

@briany,

CryptoJS does the conversion internally. If you get this to work, I wouldn’t anticipate a different result. Each ASCII character is going to be a single byte represented by an unsigned 8-bit integer. A string of valid ASCII characters will get translated into an equivalent byte array by CryptoJS. The abstraction provided by CryptoJS means we don’t have to do this ourselves.

If this is still not working after following the advice previously given, please create a minimal reproduction that demonstrates the problem, and I’ll be happy to continue efforts!

Best,

Kevin